changing channel number in MIDI stream

Started by Thomeeque, January 26, 2011, 12:35:15 PM

Previous topic - Next topic

Thomeeque

 Hi!

I'm totally new to MIDI.

I have just bought SONUUS G2M and I've wanted to use my old BOSS JS-5 Jam Station as a MIDI module.

Unfortunately G2M "transmits" on Channel 1 and JS-5 expects:

Channel 2 for Bass
Channel 3 for Instrument 1
Channel 4 for Instrument 2
Channel 10 for Drums

None of these devices allows alter this mapping, it's "hardwired" (sort-of verified here).

Could somebody experienced please tell me, is it easy task to create device with MIDI IN and MIDI OUT, doing only one lousy job - changing channel number in MIDI stream? Would one PIC handle it?

I'm able to solder ;) and I'm able to program PIC (well, I have working programmer* so far and I have solid will to learn how to write program for PIC).

I'm still googling for some simple project, which could be bent to my need - so far I have found most info here, unfortunately most of those links are dead..

Thanks, T.

* JDM PIC Programmer - should support PIC12C5XX, 12C67X, 16C55X, 16C61, 16C62X, 16C71, 16C71X, 16C8X, 16F8X
Do you have a technical question? Please don't send private messages, use the FORUM!

ExpAnonColin

Yes, a pic or other similar microcontroller could do this.  You want to use the built in UART to receive and transmit MIDI.  The program would basically just receive a MIDI message, change the channel (a MIDI message is two bytes of data, the latter 4 bits of the first byte define the channel, see eg here: http://www.midi.org/techspecs/midimessages.php ) then output it.  There are many examples of using a PIC for MIDI, so I think a google search would be good!

-Colin

potul

Yes, a PIC will dot the trick. To make your life easier, better grab a PIC with USART, this way programming the MIDI portion is really simple, even in ASM.
I've build a couple of projects both sending and receiving MIDI using a 16f88, and it's perfect for the job. A 16f628 I think would work as well.

I've always used MIDI IN for sysex and for very specific things, so I have no experience on using interrupts and MIDI though. But for your simple application it could be done even without.

Google around for some examples, but if you don't find enough information let me know and I can give you some references or sample code in ASM (sorry... just started to use C recently)

Potul.

Thomeeque

 Thanks guys for all your responses (including PM's :))!

I did some googling based on your hints and I have found that even it is relatively very simple, it would still need few days of work which I don't have now. Plus I have bought MIDI/USB interface so I can experiment with my new G2M now on my PC..

I may return to this idea later (with more features /e.g. transposition, harmonization, instrument selection etc./), but for now it's frozen :)

Cheers, T.
Do you have a technical question? Please don't send private messages, use the FORUM!

David

Quote from: ExpAnonColin on January 26, 2011, 01:00:14 PM
Yes, a pic or other similar microcontroller could do this.  You want to use the built in UART to receive and transmit MIDI.  The program would basically just receive a MIDI message, change the channel (a MIDI message is two bytes of data, the latter 4 bits of the first byte define the channel, see eg here: http://www.midi.org/techspecs/midimessages.php ) then output it.  There are many examples of using a PIC for MIDI, so I think a google search would be good!

-Colin

Colin is correct.  However, it's easier to "bit bang" the transmit and you're not locked into specific pins or having to screw around with UART message setup.  Do a Google search for a guy named Josh Harle.  He mutated a MIDI program that was worked on by Ross Bencina and changed it to send a middle C to a synthesizer.  It ran on a 16F628 with no crystal, no fuss, no muss.

How do I know?  I used it as the kernel of a MIDI bass pedal unit.  Regrettably, I never finished it because I could never decide on the features I wanted.

Thomeeque

#5
 Hi guys!

This weekend I did defrost the project back to life :) and I have build MC628 (recommended by MIDI to PIC article) for my experiments:



It is just 16F628A @ 20MHz with basic MIDI IN / MIDI OUT interface hooked to USART RC/TX pins and with one "activity" LED hooked to RA4 pin plus there is an expansion header providing access to the rest of inputs and outputs of PIC and to supply voltages for future use - perfect for the start I would say.

After build it took me few hours yet to make it alive (I had to upgrade my JDM programmer to support 16F628 PIC first and then there was some twiddling around optocoupler - instructions say 4N28, so I have bought one, but schema and layout both expect some 8pin optocoupler which I did not have, I had to addapt it for 6pin optocoupler), but then it worked IMO correctly.

So I have tried to write my own firmware - for starters I've just wanted to program it to work as simple MIDI THRU (just to read data from the input and send them untouched to the output).

But that's where I have got stuck - I am able to read data (LED is blinking), but no data are send out. There is probably something wrong with my USART configuration (it is copied for original MC628 program, but original MC628 program does not use output at all, it just reads the input).

Here's my code: MIDI_THRU.ASM (based on PIC16F628A template from MPLAB installation)

If you see something obvious, please let me know.

Btw. MIDI to PIC article says:

;------------------------Write a MIDI byte-------
movwf RCREG ; Write W to the USART register


I would expect it should write to TXREG not to RCREG. Nevertheless none of these did work..

Thanks, T.
Do you have a technical question? Please don't send private messages, use the FORUM!

Thomeeque

Do you have a technical question? Please don't send private messages, use the FORUM!

potul

Quote from: Thomeeque on March 21, 2011, 10:24:20 AM
Btw. MIDI to PIC article says:

;------------------------Write a MIDI byte-------
movwf RCREG ; Write W to the USART register


I would expect it should write to TXREG not to RCREG. Nevertheless none of these did work..

Thanks, T.

Yeah, this seems wrong in the webpage, TXREG is the one you want to set when tansmitting.
I reviewed your code and compared to a working piece of code I have for the same PIC and couldn't find any obvious fail. The only thing I see different is that I usually add a loop to wait for the transmission to end, to ensure we don't try to load a new value to TXREG before the transmission is finished. Something like this:


;******************************************
;       transmission complete subroutine
;******************************************
txchar 
   banksel  TXSTA
   btfss TXSTA,1   ; test for end of transmission
   goto  $-1
   banksel   PORTA
   return


If you want I can post my working code and you can review. It is a MIDI program changer used to control a Whammy Pedal, and uses a 16F628 and its internal oscilator.

Regards,

Mat


potul

Oh, I forgot to mention. One of the most typical errors in this kind of project is the wiring of the MIDI OUT jack. You can invert the cables and try again.... it has happened to me multiple times that I got it wrongly connected.

Mat

Thomeeque


Thanks a lot Mat!

Quote from: potul on March 23, 2011, 09:50:40 AM
The only thing I see different is that I usually add a loop to wait for the transmission to end, to ensure we don't try to load a new value to TXREG before the transmission is finished.

Oh, I see - that makes sense, I'll try that!

Quote from: potul on March 23, 2011, 09:50:40 AM
If you want I can post my working code and you can review. It is a MIDI program changer used to control a Whammy Pedal, and uses a 16F628 and its internal oscilator.

Yes please, that would be great!

Quote from: potul on March 23, 2011, 09:53:53 AM
Oh, I forgot to mention. One of the most typical errors in this kind of project is the wiring of the MIDI OUT jack. You can invert the cables and try again.... it has happened to me multiple times that I got it wrongly connected.

I'm 99% sure this was not the case, I did many various checks of my wiring.
Do you have a technical question? Please don't send private messages, use the FORUM!

potul

Ok, here it goes.

This is the code I've used in many working devices. It uses a 16f628 internal OSC at 4Mhz, so the BRG setup is different than yours (of course). I don't receive MIDI, only send it.


;*******************************************************************
; Function:  Sends MIDI program change signal, at 31250 bauds
; Processor: PIC16F628 at 4 MHz using internal RC oscillator
; Hardware: 
; Filename:  prg_change.asm
; Author:     
; Website:   
; Credit:   
; Version: 1.3.  Store last favorite in EEPROM. Recall it on start. Code cleanup
; Version: 1.4.  Add min_program parameter
; Version: 1.5.  Config mode added. MIDI channel configuration
; Version: 1.6.  Max_program and min_program configuration added.
; Version: 1.7.  Edit LED added
; Version: 1.8.  Favorites editing and storing in eeprom
; Version: 1.9   Whammy: Added "activate" button
; Version: 1.11  Corrected bug in program number and channel calculation
;*******************************************************************

        LIST P=16F628A, R=DEC    ; Use the PIC16F628 and decimal system
        #include <p16f628A.inc>        ; processor specific variable definitions
        __config  _INTRC_OSC_NOCLKOUT & _LVP_OFF & _WDT_OFF & _PWRTE_ON & _BODEN_ON

; --------------------------------
; Variables
; --------------------------------

        CBLOCK 0x20             ; Declare variable addresses starting at 0x20
      dataL
        program
midich
dlyreg2
dlyreg3
inputs
base_addr ;variable to put eeprom addresses when read/write
SELECT ;variable to put eeprom data to be read/write
favorite ;active favorite program
max_prog ;max program when no favorites mode (0-127)
min_prog ;min program when no favorites more (0-127)
favorite_cnt
inputsB
active
ENDC

; --------------------------------
; Constants
; --------------------------------

#define MIDICHANNEL 0x00 ; midichannel can be changed here
#define ee_program 0x00 ; eeprom location where program is stored
#define ee_favorite 0x01 ; eeprom location where last favorite is stored
#define ee_midich 0x02 ; eeprom loaction where midichannel is stored
#define ee_maxprog 0x03 ; eeprom loaction where maxprog is stored
#define ee_minprog 0x04 ; eeprom loaction where minprog is stored
#define ee_favorite_table 0x05 ; eeprom location where favorites table is stored
#define MAX_PROG_CONST 0x10 ; initial max program
#define MIN_PROG_CONST 0x00 ; initial min program
#define ACTIVE_GAP 0x11 ; Delta between active and non-active programs (Whammy specific)


ORG 0x2100
de 0x01, 0x01, MIDICHANNEL, MAX_PROG_CONST, MIN_PROG_CONST
de 0x03,0x04,0x10,0xFF ; UP 2 OCT, UP 1 OCT, DWN OCT UP OCT ;m5l
de "Program Changer version 1.11"

    ORG    0x000            ; Program starts at 0x000


; --------------------------------
; SET ANALOG/DIGITAL INPUTS PORT A
; --------------------------------

movlw 7
movwf CMCON ; CMCON=7 set comparators off         

; ----------------
; INITIALIZE PORTS
; ----------------

movlw b'00000000'       ; set up portA
movwf PORTA
       
movlw b'00000100' ; RB2(TX)=1 others are 0
movwf PORTB

banksel TRISA           ; RAM PAGE 1
movlw 0xFF
movwf TRISA ; portA all pins input. RA6=ch_up, RA7=ch_down, RA4=favorites
movlw b'00001010' ; RB1(RX)=input, RB5= Edit LED, RB3=Activate button, others output
movwf TRISB

; ------------------------------------
; SET BAUD RATE TO COMMUNICATE WITH PC
; ------------------------------------
; Boot Baud Rate = 31250, No Parity, 1 Stop Bit

movlw 0x01              ; 0x19=9600 bps (0x0C=19200 bps);1 = 31250 baud
movwf SPBRG
movlw b'00100000'       ; brgh = low
movwf TXSTA             ; enable Async Transmission, set brgh

banksel RCSTA
movlw b'10010000'       ; enable Async Reception (not needed)
movwf RCSTA

; ------------------------------------
; Init variables
; ------------------------------------

banksel PORTA
;check for edit mode

btfsc PORTA,4
call config_range

btfsc PORTA,7
goto config_midi_ch
btfsc PORTA,6
goto config_favorites

;btfsc PORTA,6
;goto config_max_prog

;movlw MAX_PROG_CONST
;movwf max_prog


movlw b'00000001'
movwf active

movlw ee_maxprog ;read max_prog from eeprom
movwf base_addr
call  ee_rd
movfw SELECT
movwf max_prog

;movlw MIN_PROG_CONST
;movwf min_prog

movlw ee_minprog ;read max_prog from eeprom
movwf base_addr
call  ee_rd
movfw SELECT
movwf min_prog

movlw ee_midich ;read midichannel from eeprom
movwf base_addr
call  ee_rd
movfw SELECT
movwf midich

;movlw MIDICHANNEL ;Midichannel. This will be changed later to config (eeprom)
;movwf midich
;movlw 0x01
;movwf favorite

movlw ee_favorite ;read last favorite from eeprom
movwf base_addr
call  ee_rd
movfw SELECT
movwf favorite


movlw ee_program ;read last program from eeprom
movwf base_addr
call  ee_rd
movfw SELECT
movwf program
call prog_ch_out ;send last program via MIDI

; ---------
; MAIN LOOP
; ---------



loop   
movfw PORTA ;read inputs
andlw 0xC0 ;consider only 6-7
btfss STATUS,Z ;if is not skip
goto $+3
btfss PORTB,3
goto loop
call sw_dbnc    ;delay for debounce
movfw PORTA ;read again
andlw 0xC0 ;consider only 6-7
btfss STATUS,Z ;if is not skip
goto $+3
btfss PORTB,3
goto loop
movfw PORTA
movwf inputs
movfw PORTB
movwf inputsB
btfsc inputs,6
call program_up
btfsc inputs,7
call program_down
btfsc inputsB,3
call activate
call wait0
goto loop

; ------------------
; Wait for 0 inputs
; ------------------

wait0
movfw PORTA ;wait for 0
andlw 0xC0 ;consider only 6-7
btfss STATUS,Z ;wait for 0
goto wait0
btfsc PORTB,3
goto wait0
return

; -------------------------
; PROGRAM RANGE CONFIG
; -------------------------

config_range
btfsc PORTA,6
goto config_max_prog
btfsc PORTA,7
goto config_min_prog
return

; -------------------------
; FAVORITES CONFIG LOOP
; -------------------------

config_favorites

;here we have to add code to configure favorites list. This will require to change the favorites code as well and use eeprom instead of tables
;if UP +10, if DOWN +1 (how to reset to 0?)
;if FAVorites is pulsed, move to next setting
;store value in eeprom and 0xFF in next slot
clrf favorite_cnt
call sw_dbnc
call wait0
bsf PORTB,5
call sw_dbnc 
call sw_dbnc 
clrf favorite
;call save_favorite_table_ee
loop_config_favorite
btfsc PORTA,4
goto next_favorite
;we have to add an LED here?
movfw PORTA ;read inputs
andlw 0xC0 ;consider only 6-7
btfsc STATUS,Z ;if is zero, skip
goto loop_config_favorite
call sw_dbnc            ;delay for debounce
movfw PORTA ;read again
andlw 0xC0 ;consider only 6-7
btfsc STATUS,Z ;if is zero, skip
goto loop_config_favorite
movfw PORTA
movwf inputs
movlw 0x0A
btfsc inputs,7
addwf favorite,f ;increment 10
btfsc inputs,6
incf favorite,f ;increment 1
movlw 0x7F
andwf favorite,f
;call save_favorite_table_ee
call wait0
goto loop_config_favorite
;goto config_favorites

next_favorite
call sw_dbnc
btfsc PORTA,4
goto next_favorite
movf favorite,f
btfsc STATUS,Z
goto loop_config_favorite
decf favorite
call save_favorite_table_ee
incf favorite_cnt,f
clrf favorite
goto loop_config_favorite

; -------------------------
; MIDI CHANNEL CONFIG LOOP
; -------------------------

config_midi_ch
call sw_dbnc
call wait0
bsf PORTB,5
call sw_dbnc 
call sw_dbnc 
movlw 0FFh
movwf midich
loop_config_midi_ch
;we have to add an LED here?
movfw PORTA ;read inputs
andlw 0xC0 ;consider only 6-7
btfsc STATUS,Z ;if is zero, skip
goto loop_config_midi_ch
call sw_dbnc            ;delay for debounce
movfw PORTA ;read again
andlw 0xC0 ;consider only 6-7
btfsc STATUS,Z ;if is zero, skip
goto loop_config_midi_ch
movfw PORTA
movwf inputs
movlw 0x0A
btfsc inputs,7
addwf midich,f ;increment 10
btfsc inputs,6
incf midich,f ;increment 1
movlw 0x0F
andwf midich,f
call save_midich_ee
call wait0
goto loop_config_midi_ch

; -------------------------
; MAX PROG CONFIG LOOP
; -------------------------

config_max_prog
call sw_dbnc 
call wait0
bsf PORTB,5
call sw_dbnc 
movlw 0FFh
movwf  max_prog
loop_config_max_prog
;we have to add an LED here?
movfw PORTA ;read inputs
andlw 0xC0 ;consider only 6-7
btfsc STATUS,Z ;if is zero, skip
goto loop_config_max_prog
call sw_dbnc            ;delay for debounce
movfw PORTA ;read again
andlw 0xC0 ;consider only 6-7
btfsc STATUS,Z ;if is zero, skip
goto loop_config_max_prog
movfw PORTA
movwf inputs
movlw 0x0A
btfsc inputs,7
addwf max_prog,f ;increment 10
btfsc inputs,6
incf max_prog,f ;increment 1
movlw 0x7F
andwf max_prog,f
call save_maxprog_ee
call wait0
goto loop_config_max_prog

; -------------------------
; MIN PROG CONFIG LOOP
; -------------------------

config_min_prog
call sw_dbnc 
call wait0
bsf PORTB,5
call sw_dbnc 
call sw_dbnc 
movlw 0FFh
movwf min_prog
loop_config_min_prog
;we have to add an LED here?
movfw PORTA ;read inputs
andlw 0xC0 ;consider only 6-7
btfsc STATUS,Z ;if is zero, skip
goto loop_config_min_prog
call sw_dbnc            ;delay for debounce
movfw PORTA ;read again
andlw 0xC0 ;consider only 6-7
btfsc STATUS,Z ;if is zero, skip
goto loop_config_min_prog
movfw PORTA
movwf inputs
movlw 0x0A
btfsc inputs,7
addwf min_prog,f ;increment 10
btfsc inputs,6
incf min_prog,f ;increment 1
movlw 0x7F
andwf max_prog,f
call save_minprog_ee
call wait0
goto loop_config_min_prog

;**************************************
; Table Favorites
;**************************************
; List favorite programs here. Last one must be 0xFF (indicates End of data)

table_favorites
; addwf PCL,f
; nop ; keep it here! is ensures we get the first value of table.
; dt 0x01,0x02,0x04,0x06,0x0f,0x7F,0xFF
addlw ee_favorite_table
movwf base_addr
decf base_addr
call  ee_rd
movfw SELECT
return

; -------------------------------------------------------------
; SEND CHARACTER IN W VIA RS232 AND WAIT UNTIL FINISHED SENDING
; -------------------------------------------------------------

send
    movwf TXREG             ; send data in W
TransWt
banksel TXSTA
WtHere 
btfss TXSTA,TRMT        ; (1) transmission is complete if hi
    goto WtHere
    banksel PORTA
return

;******************************************
;       transmission complete subroutine
;***************+**************************
txchar 
banksel  TXSTA
btfss TXSTA,1   ; test for end of transmission
goto  $-1
banksel PORTA
return
;******************************************
;       Output Program Change
;******************************************
prog_ch_out

movfw    midich
addlw   0C0h ; Control Change ch. from midich
movwf   TXREG   ; send character from W
call    txchar
movfw   program
btfss active,0
addlw   ACTIVE_GAP
andlw   07Fh
movwf   TXREG   ; send character from W
call    txchar
return

;******************************************
;       Activate
;******************************************
activate
movlw 01
xorwf active,f
call prog_ch_out ;send last program via MIDI
return


;******************************************
;       Program up
;******************************************
program_up
btfsc PORTA,4
goto favorite_up
incf program,f
movfw program
subwf max_prog,w ;check if we reached the max_prog
btfsc STATUS,C
goto loop_up
movfw min_prog
movwf program
;clrf program
loop_up
call prog_ch_out
call save_program_ee
return

;******************************************
;       Program down
;******************************************
program_down
btfsc PORTA,4
goto favorite_down
decf program,f
movfw program
;comf program,w
subwf min_prog,w ;check if we reached the min_prog
btfss STATUS,C
goto loop_dwn
btfsc STATUS,Z
goto loop_dwn
movfw max_prog
movwf program
loop_dwn
movfw program
subwf max_prog,w ;check if we are beyond limits
btfsc STATUS,C
goto loop_dwn_2
movfw max_prog
movwf program
loop_dwn_2
call prog_ch_out
call save_program_ee
return

;******************************************
;       Favorite up
;******************************************
favorite_up
incf favorite,f
movfw favorite
call table_favorites
movwf program
btfss program,7
goto not_last
clrf favorite
goto program_up
not_last
andlw 07Fh
movwf program
call prog_ch_out
call save_program_ee
call save_favorite_ee
return

;******************************************
;       Favorite down
;******************************************
favorite_down
movf favorite,f
btfsc STATUS,Z
incf favorite,f
decfsz favorite,f
goto not_first
goto first
not_first
movfw favorite
call table_favorites
andlw 07Fh
movwf program
call prog_ch_out
call save_program_ee
call save_favorite_ee
return
first
incf favorite,f
movfw favorite
call table_favorites
movwf program
btfsc program,7
goto program_down
goto first

;******************************************
;       Save program in eeprom
;******************************************

save_program_ee
movfw program
movwf SELECT
movlw ee_program
movwf base_addr
call  ee_wr
return

;******************************************
;       Save favorite in eeprom
;******************************************

save_favorite_ee
movfw favorite
movwf SELECT
movlw ee_favorite
movwf base_addr
call  ee_wr
return

;******************************************
;       Save midi channel in eeprom
;******************************************

save_midich_ee
movfw midich
movwf SELECT
movlw ee_midich
movwf base_addr
call  ee_wr
return

;******************************************
;       Save max_prog in eeprom
;******************************************

save_maxprog_ee
movfw max_prog
movwf SELECT
movlw ee_maxprog
movwf base_addr
call  ee_wr
return

;******************************************
;       Save max_prog in eeprom
;******************************************

save_minprog_ee
movfw min_prog
movwf SELECT
movlw ee_minprog
movwf base_addr
call  ee_wr
return

;******************************************
;       Save max_prog in eeprom
;******************************************

save_favorite_table_ee
movfw favorite
movwf SELECT
movlw ee_favorite_table
addwf favorite_cnt,w
movwf base_addr
call  ee_wr
incf base_addr
movlw 0xFF
movwf SELECT
call ee_wr
return



;*************************************
;  Switch Debounce Delay  255mS
;*************************************
sw_dbnc
movlw 014h ; Delay routine variable
movwf dlyreg2
debnce
call delay3
decfsz dlyreg2,f
goto debnce
return

;*************************************
; Delay Subroutine (1 mS)
;*************************************
delay3
movlw 0A5h
movwf dlyreg3
dly3
decfsz dlyreg3,f
goto dly3
movlw 0A5h
movwf dlyreg3
dly4
decfsz dlyreg3,f
goto dly4
nop
return

;*****************************************************
; Write to EEPROM subroutine
;*****************************************************

ee_wr
movf base_addr,W ; start addr. EEPROM
banksel EEADR ;writing to eeprom
movwf EEADR
banksel SELECT
movf SELECT,W
banksel EEDATA
movwf EEDATA
bsf EECON1,WREN
bcf INTCON,GIE
movlw 55h
movwf EECON2
movlw 0AAh
movwf EECON2
bsf EECON1,WR

test_wr
btfsc EECON1,WR; is write finished?
goto test_wr
bsf INTCON,GIE
banksel PORTA
return


;**************************************
; Read EEPROM and write to select
;**************************************

ee_rd   
movf base_addr,W
banksel EEADR
movwf EEADR
bsf EECON1,RD
r1
btfsc EECON1,RD ; Wait to finish read.
goto r1
movf EEDATA,W
banksel SELECT
movwf SELECT
return

;**************************************
; END of program
;**************************************

END



Feel free to ask any question, and sorry for the long post, the code was longer than expected.

Mat


Thomeeque

#11
 Thanks Mat again!!

Quote from: potul on March 23, 2011, 01:29:16 PM
..and sorry for the long post, the code was longer than expected.

Actually forum's framework handles long code very smartly (at least I see code as cca 20-line high scrollable area), nice feature!

T.
Do you have a technical question? Please don't send private messages, use the FORUM!

potul

Quote from: Thomeeque on March 24, 2011, 06:52:52 AM
Thanks Mat again!!

Quote from: potul on March 23, 2011, 01:29:16 PM
..and sorry for the long post, the code was longer than expected.

Actually forum's framework handles long code very smartly (at least I see code as cca 20-line high scrollable area), nice feature!

T.

Yeah, I realized after posting. Nice "code" tag.