; ; midi.inc: the midi data handling routines ; ; author: Marko Kanala, raato ]a t[ mulletronic.com ; ; one pattern sysex: ; ; F0 71 55 17 01 ... 164 bytes of pattern data in sysex format ... F7 ; ; note: one pattern dump is always sent from and received to the ; pattern_data address. So the currently active pattern ; is sent when sending and overwritten when receiving. ; ; the default midi key numbers for sounds conform to the general midi level 1 ; percussion key map as follows: ; ; midi key number midi note drum sound output ; --------------- --------- ---------- ------ ; 35 B0 bass drum ; 36 C1 bass drum X ; 37 C#1 rim shot X ; 38 D1 snare drum ; 39 D#1 hand clap X ; 40 E1 snare drum X ; 41 F1 low tom X ; 42 F#1 closed hihat X ; 43 G1 low tom ; 44 G#1 closed hihat ; 45 A1 mid tom X ; 46 A#1 open hihat X ; 47 B1 mid tom ; 48 C2 high tom X ; 49 C#2 crash cymbal X ; 50 D2 high tom ; 51 D#2 ride cymbal X ; ; Anything below 35 and after 51 is ignored. ; ; LICENSE ; Creative Commons Attribution-NonCommercial-ShareALike 2.5 ; ; You are free: ; ; * to copy, distribute, display, and perform the work ; * to make derivative works ; ; Under the following conditions: ; ; 1. Attribution. You must attribute the work in the manner specified by the ; author or licensor. ; 2. Noncommercial. You may not use this work for commercial purposes. ; 3. Share Alike. If you alter, transform, or build upon this work, you may ; distribute the resulting work only under a license identical to this one. ; ; * For any reuse or distribution, you must make clear to others the license ; terms of this work. ; * Any of these conditions can be waived if you get permission from the ; copyright holder. ; ; Your fair use and other rights are in no way affected by the above. ; ; This is a human-readable summary of the Legal Code. ; See full license at http://creativecommons.org/licenses/by-nc-sa/2.5/legalcode ; ; Copyright: Marko Kanala (raato@mulletronic.com). ; ; ; full memory dump sysex: ; ; f0 71 55 17 00 ... 64*256 bytes of data in sysex format ... F7 ; ; ; note: the sequencer is probably prone for missing external clocks ; etc when handling system exclusive. ; called in busy loop while EVENT_SYSEX is set midi_receiving_sysex: ; i2c write of 256 bytes takes 40ms in worst case ; ; midi transmits ca. 3906 bytes / second ; so 3906 / 1000 * 40 = about 156 bytes in 40ms ; ; we do the flashing in two phases so ; worst case flash delay is 20ms = about 78 bytes ; which should be more than enough for flashing ; 128 bytes (256 / 2 phases). SETDISPLAY s_sysex call lcd_refresh ; refresh lcd if needed movlw 128 cpfseq sysexbytes bra midi_receiving_sysex_nofh ; 128 bytes of midi data received, we should flash ; the first half of the data movlw 0 call i2c_flash_save_sysex_128 return midi_receiving_sysex_nofh: movlw 255 cpfseq sysexbytes bra midi_receiving_sysex_nosh ; 255 bytes of midi data received, we should flash ; the second half of the data movlw 128 call i2c_flash_save_sysex_128 ; second half written, advance to next "page" incf currentpattern movlw 16 cpfseq currentpattern bra midi_receiving_sysex_leds ; advance to next bank incf currentbank clrf currentpattern movlw 4 cpfseq currentbank bra midi_receiving_sysex_leds ; 4 x 64 256-byte "pages" written, mem dump done! clrf currentbank ; set A01 as current pattern clrf currentpattern movlw -1 movwf sysexcounter ; terminates sysex dump CLEAREVENT EVENT_SYSEX SETDISPLAY s_sysexdone midi_receiving_sysex_nosh: return midi_receiving_sysex_leds: ; light up leds while receiving movf currentbank, W call get_bit movwf leds_busb_1 movlw 8 cpfslt currentpattern bra midi_receiving_sysex_over7 movf currentpattern, W call get_bit movwf leds_busa_1 clrf leds_busa_2 return midi_receiving_sysex_over7: subwf currentpattern, W ; -8 call get_bit clrf leds_busa_1 movwf leds_busa_2 return ; called from irq when USART receive irq is set midi_handlemidi: movff RCREG, midibuffer btfss RCSTA, OERR bra midi_handlebuffer ; overrun error ? should be handled somehow bcf RCSTA, CREN ; movlw 255 ; call tr_delay bsf RCSTA, CREN movlw -1 ; abort sysex (?) movwf sysexcounter CLEAREVENT EVENT_SYSEX return midi_handlebuffer: ; processes the received midi buffer movf sysexcounter, W ; if sysexcounter positive we are bnn midi_sysex_data ; waiting for data ; ignore sync commands if midi sync in is set off IFSETTINGOFF SETTING_MIDIS_IN, midi_handlebuffer_skipsync movlw 0xF8 ; clock xorwf midibuffer, W bz midi_clock movlw 0xFA ; start xorwf midibuffer, W bz midi_start movlw 0xFB ; continue xorwf midibuffer, W bz midi_continue movlw 0xFC ; stop xorwf midibuffer, W bz midi_stop midi_handlebuffer_skipsync: ; ; ignore incoming notes if midi notes in is set off ; IFSETTINGOFF SETTING_MIDIN_IN, midi_handlebuffer_skipnotes ; ; decf settings_midi_channel, W ; iorlw 0x90 ; note on ; xorwf midibuffer, W ; bz midi_note_on ; ; decf settings_midi_channel, W ; iorlw 0x80 ; xorwf midibuffer, W ; bz midi_note_off ; ;midi_handlebuffer_skipnotes: movlw 0xF0 ; sysex start xorwf midibuffer, W bz midi_sysex ; something else ? return ; tstfsz midi_in_buffer ; bra midi_handlebuffer_gotnote ; return ; nothing in buffer, just ignore ;midi_handlebuffer_gotnote: ; tstfsz midi_in_buffer_note ; bra midi_handlebuffer_gotvel ; ; the value we have must be a note value ; movff midibuffer, midi_in_buffer_note ; return ;midi_handlebuffer_gotvel: ; ; the value we have must be a velocity value and ; ; the midi_in_buffer is ready for processing ; movff midibuffer, midi_in_buffer_vel ; ; movlw 0x8F ; if over 0x8f must be note on ; cpfsgt midi_in_buffer ; bra midi_handlebuffer_noteoff ; call midi_note_on_ready ; return ;midi_handlebuffer_noteoff: ; call midi_note_off_ready ; return ; midi clock received midi_clock: IFEVENT EVENT_MIDIRUN, midi_clock_run ; ignore all midi clocks if we're not running return midi_clock_run: ; echo din sync on midi clock call din_clock ; midi sync 24 ppqn, call player twice call player_tick call player_tick ; send midi clock out also if desired IFSETTINGONCALL SETTING_MIDIS_OUT, midi_out_clock return ; midi start received midi_start: call player_start SETEVENT EVENT_MIDIRUN IFSETTINGONCALL SETTING_MIDIS_OUT, midi_out_start return ; midi continue received midi_continue: call player_continue SETEVENT EVENT_MIDIRUN IFSETTINGONCALL SETTING_MIDIS_OUT, midi_out_continue return ; midi stop received midi_stop: call player_stop CLEAREVENT EVENT_MIDIRUN IFSETTINGONCALL SETTING_MIDIS_OUT, midi_out_stop return ;; midi note on/off received ;midi_note_on: ;midi_note_off: ; movff midibuffer, midi_in_buffer ; store the first byte ; return ; ;; all three bytes of note on message received ;midi_note_on_ready: ;midi_note_off_ready: ; ; TODO these ; clrf midi_in_buffer ; clrf midi_in_buffer_note ; clrf midi_in_buffer_vel ; return ; midi sysex received midi_sysex: clrf sysexcounter ; reset sysexcounter to start ; the id matching clrf sysexbytes return ; bytes after 0xF0 midi_sysex_data: btfsc midibuffer, 7 ; control byte / data byte ? bra midi_sysex_Fx ; if sysexcounter >= 3 id matched movlw 3 cpfslt sysexcounter bra midi_sysex_data_in ; match the sysex id TABLE_ADDR sysex_id movf sysexcounter, W addwf TBLPTRL tblrd* movf TABLAT, W xorwf midibuffer, W bz midi_sysex_data_idmatch ; id mismatch, ignore movlw -1 movwf sysexcounter return midi_sysex_data_idmatch: incf sysexcounter return midi_sysex_data_in: ; data after F0 71 55 17 movlw 3 cpfsgt sysexcounter bra midi_sysex_data_checktype ; first byte determines the ; message type ; type determined, read the raw data btfsc sysexcounter, 0 bra midi_sysex_data_odd ; even byte, read bit 8 rrncf midibuffer, W ; 80000000 movwf sysexbuf incf sysexcounter return midi_sysex_data_odd: movf midibuffer, W iorwf sysexbuf ; 87654321, ready! decf sysexcounter ; write sysexbuf to sysexdata lfsr FSR2, sysexdata movf sysexbytes, W addwf FSR2L movff sysexbuf, POSTINC2 movlw SYSEX_TYPE_MEM cpfsgt sysextype bra midi_sysex_data_memdump ;midi_sysex_data_patterndump: ; one pattern takes 164 bytes movlw PATTERN_SIZE_FLASH cpfseq FSR2L bra midi_sysex_data_continue ; 164 bytes read, copy sysexdata to current pattern, ; shitty code, but thrashes only FSR2 midi_sysex_writepattern_loop: lfsr FSR2, sysexdata movf sysexbytes, W addwf FSR2L movff INDF2, sysexbuf ; source byte lfsr FSR2, pattern_data movf sysexbytes, W addwf FSR2L movff sysexbuf, INDF2 ; ... to destination decfsz sysexbytes bra midi_sysex_writepattern_loop ; sequencer should probably be stopped on pattern receive ;;; call player_stop return midi_sysex_data_memdump: ; whole memory dump takes 64 * 256 pages tstfsz FSR2L ; 256 bytes read ? ; nope, wait for a full page bra midi_sysex_data_continue ; 256 bytes of data now in sysexdata, ; NOTE: data is written to flash in busyloop, just rollover clrf sysexbytes return midi_sysex_data_continue: incf sysexbytes return midi_sysex_data_checktype: ; midibuffer 00 = whole memory dump ; midibuffer 01 = pattern dump movff midibuffer, sysextype ; store type incf sysexcounter tstfsz sysextype ; check type return ; pattern dump, just return ; was full dump, set sysex flag SETEVENT EVENT_SYSEX return midi_sysex_Fx: ; midi control byte received after F0, must match F7 movlw 0xF7 ; sysex end xorwf midibuffer, W bz midi_sysex_end ; something else in middle of sysex, ; must be real-time messages, just ignore them return midi_sysex_error: bsf sysexstatus, SYSEX_ERROR midi_sysex_end: ; TODO: should handle the bytes received this far ? movlw -1 ; cancel sysex movwf sysexcounter CLEAREVENT EVENT_SYSEX return ; midi out --> ; player started, transmit start midi_out_start: movlw 0xFA WAITMIDI movwf TXREG return ; player continued, transmit continue midi_out_continue: movlw 0xFB WAITMIDI movwf TXREG return ; player stopped, transmit stop midi_out_stop: movlw 0xFC WAITMIDI movwf TXREG return ; transmit clock midi_out_clock: movlw 0xF8 WAITMIDI movwf TXREG return ; ; triggers out current midi notes ; ; midi_ variables should be loaded with the velocities ; to the notes to be triggered, 0 == off ; midi_do_triggers: lfsr FSR2, midi_velocities movlw MIDI_NOTE_BDRUM call midi_do_trigger movlw MIDI_NOTE_SDRUM call midi_do_trigger movlw MIDI_NOTE_LTOM call midi_do_trigger movlw MIDI_NOTE_MTOM call midi_do_trigger movlw MIDI_NOTE_HTOM call midi_do_trigger movlw MIDI_NOTE_RSHOT call midi_do_trigger movlw MIDI_NOTE_HCLAP call midi_do_trigger movlw MIDI_NOTE_CHIHAT call midi_do_trigger movlw MIDI_NOTE_OHIHAT call midi_do_trigger movlw MIDI_NOTE_CRASH call midi_do_trigger movlw MIDI_NOTE_RIDE call midi_do_trigger return midi_do_trigger: movwf temptrig ; note movf POSTINC2, W bz midi_do_not_trigger ; vel == 0 ? movwf temptrigvol ; transmit note off decf settings_midi_channel, W addlw 0x80 WAITMIDI movwf TXREG ; 0x80 + channel movf temptrig, W WAITMIDI movwf TXREG ; note number movlw 64 WAITMIDI movwf TXREG ; velocity ; transmit note on ; ; 0x9n channel + note number (0 - 127) + velocity (0 - 127) decf settings_midi_channel, W addlw 0x90 WAITMIDI movwf TXREG ; 0x90 + channel movf temptrig, W WAITMIDI movwf TXREG ; note number movf temptrigvol, W WAITMIDI movwf TXREG ; velocity midi_do_not_trigger: return ; transmits the F0 <sysex id> sequence to midi, ; note: thrashes table pointers midi_out_sysex_id: WAITMIDI movlw 0xF0 ; sysex start movwf TXREG TABLE_ADDR sysex_id tblrd*+ movf TABLAT, W WAITMIDI movwf TXREG ; sys tblrd*+ movf TABLAT, W WAITMIDI movwf TXREG ; ex tblrd* movf TABLAT, W WAITMIDI movwf TXREG ; id return midi_out_sysex_end: WAITMIDI movlw 0xF7 movwf TXREG return ; transmit full memory dump as sysex, ; sequencer should be stopped, ; note: thrashes nextbank & nextpattern midi_out_dump_all: clrf nextbank clrf nextpattern call midi_out_sysex_id ; write sysex id WAITMIDI movlw SYSEX_TYPE_MEM movwf TXREG ; pattern dump id WAITMIDI midi_out_dump_all_getloop: ; light up leds while dumping movf nextbank, W call get_bit movwf leds_busb_1 movlw 8 cpfslt nextpattern bra midi_out_dump_all_over7 movf nextpattern, W call get_bit movwf leds_busa_1 clrf leds_busa_2 bra midi_out_dump_all_cont midi_out_dump_all_over7: subwf nextpattern, W ; -8 call get_bit clrf leds_busa_1 movwf leds_busa_2 midi_out_dump_all_cont: ; 1. read 256 bytes of data from flash to sysexdata call i2c_flash_read_sysex_256 ; 2. write sysexdata to midi lfsr FSR1, sysexdata midi_out_dump_all_putloop: call midi_out_byte_fsr1 tstfsz FSR1L ; 256 bytes sent ? bra midi_out_dump_all_putloop ; 64 pages of 256 bytes sent ? incf nextpattern movlw 16 cpfseq nextpattern bra midi_out_dump_all_getloop clrf nextpattern incf nextbank movlw 4 cpfseq nextbank bra midi_out_dump_all_getloop ; all done, reset next* pointers movff currentpattern, nextpattern movff currentbank, nextbank bra midi_out_sysex_end ; transmit current pattern as sysex, ; note: thrashes FSR1 midi_out_dump_patt: call midi_out_sysex_id ; write sysex id movlw SYSEX_TYPE_PAT WAITMIDI movwf TXREG ; pattern dump id ; write data from pattern_data to midi lfsr FSR1, pattern_data midi_out_dump_patt_loop: call midi_out_byte_fsr1 movlw PATTERN_SIZE_FLASH ; done ? cpfseq FSR1L bra midi_out_dump_patt_loop bra midi_out_sysex_end ; writes out a byte from fsr1 to midi as ; %00000008 %07654321 midi_out_byte_fsr1: movlw b'10000000' andwf INDF1, W ; %80000000 rlncf WREG ; %00000008 WAITMIDI movwf TXREG movlw b'01111111' andwf POSTINC1, W ; %07654321 WAITMIDI movwf TXREG return
lcd_refresh i2c_flash_save_sysex_128 get_bit din_clock player_tick player_start player_continue player_stop midi_do_trigger midi_out_sysex_id i2c_flash_read_sysex_256 midi_out_byte_fsr1