;
; 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
External Labels :
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