;-----------------------------------------------------------------------------------------------; ; LICENSE AGREEMENT: ; ; This program is free software. You can redistribute it and/or modify it under the terms of ; ; the GNU General Public License as published by the Free Software Foundation, either version ; ; 2 of the License, or (at your option) any later version. No part of this code may be used ; ; in any commercial application without prior written permission. ; ; ; ; This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY, ; ; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ; ; See the GNU General Public License for more details. ; ; ; ; Copyright (c) February 12, 2006 David J. Brown ; ;-----------------------------------------------------------------------------------------------; ; Bass Pedals ; ; Developed by David J. Brown ; ; Email: davebr@earthlink.net ; ; Web site: http://modularsynthesis.com ; ;-----------------------------------------------------------------------------------------------; ; ; MIDI 13 note bass pedals ; Starts in command mode indicated by the led blinking ; Waits for a key to select one of the following modes: ; C selects mono mode (next note held off until current note released) ; C# selects portamento off ; D selects sticky mode (note held until next note) ; D# selects portamento on ; E selects poly mode (multi note) ; F (unused) ; F# selects MIDI channel (1-16, default is channel 1) ; G selects mono arpeggiator mode ; G# selects program change (0-127) ; A selects sticky arpeggiator mode ; A# selects panic mode (outputs 128 notes off) ; B selects arpeggiate once mode ; C (unused) ; ; Sends MIDI initialization for GM or Roland MT-32 (conditional compile option). ; Notes can be transposed up 1 or 2 octaves. The select switch is checked only when all keys are up. ; A trigger is sent on key down and the led flashes. Gate is true while any key is down. ; Alternate in-line code can use Aux2 input to set bass velocity. ; ; Aux1 tip trigger output (width defined by trig_w) ; Aux1 ring unused input/output ; Aux2 tip gate output ; Aux2 ring MIDI velocity input (disabled) ; ;---BASS PEDAL MIDI CHANNEL---------------------------------------------------------------------; bass_chan con 02 ;bass pedals MIDI channel (0-15) ; ;-----------------------------------------------------------------------------------------------; ; ; MIDI in data is merged with output and the led flashes. The MIDI channel may be replaced by a ; new value and the key data may be transposed. Note that the MIDI in channel must be different ; from the bas channel for correct operation. ; ;---ENABLE MIDI TRANSPOSE CONDITIONAL COMPILE OPTIONS-------------------------------------------; rcx_mask con $f0 ;set to $ff for normal, $f0 to enable channel transpose ; rcx_chan con 01 ;set to 0 for normal, 0 - 15 for transpose MIDI channel ; rcx_note con 00 ;set to 0 for normal, <>0 for note transpose delta ; ;-----------------------------------------------------------------------------------------------; ; ; A all-notes-off message is sent and the bass and pass-thru instruments are set. ; ;---MT-32 SYNTHESIZER OPTION--------------------------------------------------------------------; ; mt32 con 01 ;define for MT-32 synth (undefined is GM synth) ; ;-----------------------------------------------------------------------------------------------; ; ; MIDI-in and pedal note information can be sent to the PROGRAM serial connector at 38400 baud. ; An 'm' indicates MIDI notes and 'p' indicates pedal notes. ; ;---ENABLE LCD DISPLAY CONDITIONAL COMPILE OPTION-----------------------------------------------; ;display_en con 1 ;define to enable / comment to disable LCD display ; ;-----------------------------------------------------------------------------------------------; ; ;revision: 0.5S2 ; October 25, 2010 ;history: 0.5S2 changed serial for Studio 2, added trig_w to define trigger pulse width, ; changed default velocity to 64 ; 0.49 fixed real time message processing, added midi clock processing ; 0.48 changed display and MIDI transpose to conditionals, removed push and pop commands ; 0.47 changed DSOTM sequence ; 0.46 removed 'high P15' command which limited TxD (P15) to V(ol)=1.5 volts ; 0.45 changed display driver to 57600 baud ; 0.44 display driver added for s_out, set rcx_note to 0 ; 0.43 removed push and pop command from record routine which caused a crash and restart ; when executing hserin in pass_thru - root cause unknown ; 0.42 change dirs command for MIDI out pin ; 0.41 sends midi reset at initialization ; 0.4 converted midi to hserial, eliminated conditional compiles, midi channel selection ; error defaults to bass_chan ; 0.3 added pass-thru mode to merge MIDI-in to MIDI stream with note and channel transpose, ; changed to tx_status in send_midi, converted send_midi to use timerw interrupt ; 0.2 changed sequence selection to keys, changed sequence terminator to 128 ; 0.1 added aux2 control of velocity, added portamento on/off ; 0.0 first code written for Bass Pedals program ;date: September 26, 2004 ; ;-----------------------------------------------------------------------------------------------; ; ; Basicmicro AtomPro-24 Configuration ; P0 - Value control ;P0 - CV D/A bit 0 (lsb) Reserved for future capability ; P1 - CV D/A bit 1 ;Reserved for future capability ; P2 - CV D/A bit 2 ;Velocity input ; P3 - CV D/A bit 3 ;Reserved for future capability ; P4 - Trigger out ;+5 volt trigger ; P5 - Gate out ;+5 volt gate ; P6 - CV scale ;Reserved for future capability ; P7 - CV scale ;Reserved for future capability ; P8 - Key scan address bit 0 (lsb) ;Key address ; P9 - Key scan address bit 1 ;Key address ; P10 - Key scan address bit 2 ;Key address ; P11 - Key scan address bit 3 ;Key address ; 0=C ; 1=C# ; 2=D ; 3=D# ; 4=E ; 5=F ; 6=F# ; 7=G ; 8=G# ; 9=A ; 10=A# ; 11=B ; 12=High C ; 13=+2 octave scale ; 14=No octave scale (+1 octave scale if neither switch detected) ; 15=Command switch ; P12 - Key scan data ;Key state (0=closed) ; P13 - LED ;LED (low=on) ; P14 - MIDI in ;MIDI input ; P15 - MIDI out ;MIDI output ; ;-----------------------------------------------------------------------------------------------; ; ;pin declarations value con p0 ;control knob input aux1_in con p1 ;aux1 input (unused) aux2_in con p2 ;velocity input aux1_out con p4 ;trigger out aux2_out con p5 ;gate out key_in var in12 ;key scan input led con p13 ;led #ifdef display_en disp_data var byte ;midi note to be displayed note_a var byte ;note alpha letter note_n var byte ;note number note_d var byte ;date sent to display note_t bytetable $43,0,$43,$23,$44,0,$44,$23,$45,0,$46,0,$46,$23,$47,0,$47,$23,$41,0,$41,$23,$42,0 #endif ;MIDI variable declarations note_off con $80 ;MIDI note-off command note_on con $90 ;MIDI note-on command pgm_chg con $c0 ;MIDI program change command midi_chan var nib ;MIDI channel (0 - 15) midi_note var byte ;MIDI note value midi_vel var byte ;MIDI velocity value midi_pgm var byte ;MIDI program change value last_cmd var byte ;last MIDI command for running status midi_cmd var byte ;branch address for running status sys_cmd var byte ;branch address for system commands midi_data var byte ;MIDI data rcx_data_flg var bit ;received data flag: 1=data, 0=no data midi_cnt var word ;midi clock count start_flag var byte ;midi clock flag: 1=start, 0=stop ;timer declarations time_count var long ;1 mS timer count value led_end var long ;time value to turn off led trig_end var long ;time value to turn off trigger ;misc declarations tempb var byte ;temporary variable trig_w con 25 ;25 mS trigger width (40% duty cycle at fastest rate) con_data var byte ;data to send to console ;key declarations key_prev var bit(13) ;previous key state: newscan key_val var bit(13) ;current key state: newscan key_cnt var byte ;key scan counter: newscan key_scale var byte ;key scale factor: newscan key_dwn var byte ;number of keys currently down key_base con 24 ;lowest MIDI note (no scale) mode var byte ;mode flag: normal=0, sticky=1, once=2 last_key var byte ;last note key_dwn_val var byte ;key down value: keydown tempv var word ;temp variable ;arpeggiator variable declarations note_end var long ;next note time value root_note var word ;root note to arpeggiate note_flag var bit ;note flag: 1=output next note, 0=skip rel_flag var bit ;release flag: 0=key not released, 1=key released wait_flag var bit ;key flag: 1=wait for all keys up root_flag var bit ;root flag: 0=skip, 1=repeat root note seq_off var word ;arpeggiator sequence offset seq_ptr var word ;arpeggiator sequence counter tempo var word ;arpeggiator tempo ;arpeggiator sequence declarations ;values are +/- offset in semitones from root note ;terminate sequence with 128 ;pad with extra byte if necessary to make the sequence length even len1 con 8 ;seq 1 length ; seq1 bytetable 3,5,3,10,8,10,12,128 ;Pink FLoyd DSOTM (original) seq1 bytetable -12,-9,-5,-7,-9,-7,-2,128 ;Pink FLoyd DSOTM C2,C1,Eb1,G1,F1,Eb1,F1,Bb1 ; len2 con 8 ;seq 2 length seq2 bytetable 7,4,7,0,5,9,5,128 ;Doors Riders On The Storm ; len3 con 12 ;seq 3 length seq3 bytetable 4,7,10,5,0,4,7,-2,-3,-5,128,128 ;Unknown rif ; len4 con 8 ;seq 4 length seq4 bytetable 3,4,5,10,8,3,5,128 ;Unknown rif ; len5 con 10 ;seq 5 length seq5 bytetable 12,10,12,10,7,6,5,2,14,128 ;Hendrix Machine Gun ; ;sequence offset declarations off1 con 0 ;offset to seq1 off2 con off1+len1 ;offset from seq1 to seq2 off3 con off2+len2 ;offset from seq1 to seq3 off4 con off3+len3 ;offset from seq1 to seq4 off5 con off4+len4 ;offset from seq1 to seq5 off6 con off5+len5 ;offset from seq1 to seq6 (user record sequence) ;sequence buffer ; arp_buf_len must be equal to or greater than len1+len2+len3+len4+len5 arp_buf_len con 100+off6+1 ;buffer length=sequences + record + termination arp_buf var byte(arp_buf_len) ;arpeggiator buffer ; ;-----------------------------------------------------------------------------------------------; ; Start of program ; ;-----------------------------------------------------------------------------------------------; ; ;initialize pins dirs=%0010111111111000 ;configure pin direction (1=output, 0=input) ; inputs: MIDI in, key in, value in, aux1_in, aux2_in ; outputs: all other pins except MIDI out ; note: setting MIDI out pin sends erroneous data adin value,tempv ;read to initialize adin aux1_in,tempv ;read to initialize and ensure no conflicts at jack adin aux2_in,tempv ;read to initialize and ensure no conflicts at jack high led ;turn off led low aux1_out ;turn off trigger low aux2_out ;turn off gate ; ;initialize misc variables let midi_note=0 ;set MIDI note to 0 let midi_vel=64 ;set default velocity for key_cnt=0 to 12 let key_prev(key_cnt)=1 ;initialize all keys up let key_val(key_cnt)=1 ;initialize all keys up next let key_dwn=0 ;initialize no keys down let last_key=$ff ;reset last key let midi_cmd=$0f ;reset last MIDI command (invalid) let sys_cmd=$0f ;reset last MIDI command (invalid) let midi_cnt=0 ;reset midi clock count let start_flag=0 ;set midi stop flag ; ;initialize ram sequence buffer for seq_ptr=0 to off6 let arp_buf(seq_ptr)=seq1(seq_ptr) ;copy sequences to ram next for seq_ptr=off6 to arp_buf_len-1 let arp_buf(seq_ptr)=seq_ptr-off6-12;sequence 6=sequential notes next let arp_buf(arp_buf_len-1)=128 ;terminate sequence ; ;initialize timerW hardware for 1 mS interrupts let tmrw=%10001000 ;set Timer Mode Register to enable count let tcrw=%10110000 ;set Timer Control Register ; clear on compare match A ; prescalar S /8 = 2 MHz clock let gra=2000 ;count to 2000 for 1 mS ; ;enable timer interrupts let time_count=0 ;set real time counter to 0 let led_end=0 ;reset time to turn off let trig_end=0 ;reset time to turn off oninterrupt timerwint_imiea, tmw_isr enable timerwint_imiea ;enable timer interrupt ; ;initialize MIDI system sethserial1 h31200,h8databits,hnoparity,h1stopbits ; ;setup synthesizers #ifdef mt32 ;set MT-32 pass thru to clavi3 (#22) if rcx_mask=$f0 then ;check if pass thru midi channel is known let midi_chan=rcx_chan ;set pass thru channel gosub send_all_off ;make sure all notes off let midi_pgm=21 ;clavi3 #22-1 gosub send_prog_change let midi_data=$b0+rcx_chan ;pass thru CC hserout [midi_data] let midi_data=07 ;#7 volume hserout [midi_data] let midi_data=102 ;value of 80% hserout [midi_data] endif ;set MT-32 bass to syn-bass3 (#31) let midi_chan=bass_chan ;set default bass channel gosub send_all_off ;make sure all notes off let midi_pgm=30 ;syn-bass3 #31-1 gosub send_prog_change let midi_data=$b0+bass_chan ;bass channel CC hserout [midi_data] let midi_data=07 ;#7 volume hserout [midi_data] let midi_data=127 ;value of 100% hserout [midi_data] #else ;set GM pass thru to sawtooth (#82) if rcx_mask=$f0 then ;check if pass thru midi channel is known let midi_chan=rcx_chan ;set pass thru channel gosub send_all_off ;make sure all notes off let midi_pgm=21 ;sawtooth (#82) gosub send_prog_change endif ;set GM bass to synbass1 (#39) let midi_chan=bass_chan ;set default bass channel gosub send_all_off ;make sure all notes off let midi_pgm=39 ;synbass1 (#39) gosub send_prog_change #endif ; ;main command parser getmode: #ifdef display_en disable serout s_out,i57600,[$0a] serout s_out,i57600,[" Midi Bass Pedals "] serout s_out,i57600,[" -ModularSynthesis- "] enable #endif getmode1: toggle led ;blink led pause 50 gosub key_down ;check for any keys down if key_dwn_val<>$ff then getmode2 ;check twice during single blink pause 50 gosub key_down ;check for any keys down if key_dwn_val=$ff then getmode1 getmode2: low led ;turn on led gosub key_up ;wait for all keys up pause 500 ;long blink high led branch key_dwn_val,[mono,port_off,stick,port_on,poly,getmode,midic,arp_m,progc,arp_s,panic,arp_o,getmode] ; ;poly mode poly: let mode=0 ;poly mode gosub scale_sw ;check scale switches goto newscan ; ;sticky mode stick: let mode=1 ;sticky mode gosub scale_sw ;check scale switches newscan: let key_cnt=0 ;start with first key nextkey: out8=key_cnt.bit0 ;output key address out9=key_cnt.bit1 out10=key_cnt.bit2 out11=key_cnt.bit3 pause 1 ;let settle gosub pass_thru ;pass thru midi data pause 1 ;let settle gosub pass_thru ;pass thru midi data let key_prev(key_cnt)=key_val(key_cnt) ;save last value let key_val(key_cnt)=key_in ;get current value if key_prev(key_cnt)<>key_val(key_cnt) then ;key change detected let midi_note=key_cnt+key_scale ;calculate MIDI key value if key_val(key_cnt)=0 then ;key down if mode=1 then ;check sticky mode if last_key<>$ff then ;check if need to send note_off let tempb=midi_note ;save midi_note let midi_note=last_key ;send note off before next note on gosub send_note_off let midi_note=tempb ;restore midi_note let key_dwn=key_dwn-1 ;track keys down endif if midi_note=last_key then ;same note so release let last_key=$ff ;don't send key up if key_dwn=0 then ;all keys up low aux2_out ;release gate endif goto done else let last_key=midi_note endif endif gosub send_note_on high aux2_out ;set gate high aux1_out ;set trigger low led ;flash led disable timerwint_imiea trig_end=time_count+trig_w ;set trigger time led_end=time_count+20 ;set led time enable timerwint_imiea let key_dwn=key_dwn+1 ;track keys down else ;key up if mode=0 then ;check poly mode gosub send_note_off let key_dwn=key_dwn-1 ;track keys down if key_dwn=0 then ;all keys up low aux2_out ;release gate endif endif endif endif done: let key_cnt=key_cnt+1 ;increment to next key if key_cnt<>13 then nextkey ;check if done if key_dwn<>0 then newscan ;check octave switches when all keys up gosub scale_sw ;check scale switches gosub checkmode ;check for restart goto newscan ;start next scan ; ;mono mode mono: gosub scale_sw ;check scale switches gosub checkmode ;check for restart gosub pass_thru ;pass thru midi data gosub key_down ;check if any keys down if key_dwn_val=$ff then low aux2_out ;reset gate goto mono endif let midi_note=key_cnt+key_scale ;calculate MIDI key value gosub send_note_on high aux2_out ;set gate high aux1_out ;set trigger low led ;flash led disable timerwint_imiea trig_end=time_count+trig_w ;set trigger time led_end=time_count+20 ;set led time enable timerwint_imiea mono1: pause 1 ;let settle gosub pass_thru ;pass thru midi data pause 1 ;let settle gosub pass_thru ;pass thru midi data if key_in=0 then mono1 ;wait for key release before scanning next let midi_note=key_cnt+key_scale ;calculate MIDI key value gosub send_note_off goto mono ; ;mono arp mode arp_m: let mode=0 goto sel_seq ; ;sticky arp mode arp_s: let mode=1 goto sel_seq ; ;once arp mode arp_o: let mode=2 ; ;select arpeggiation sequence sel_seq: let seq_off=off1 ;set default sequence sel_seq1: low led ;led on indicates select mode gosub key_down ;check for any keys down if key_dwn_val<>$ff then preview out8=1 ;check command switch to end selection out9=1 out10=1 out11=1 if key_in=1 then sel_seq1 high led sel_seq2: ;wait until command switch release pause 50 if key_in=0 then sel_seq2 if mode=2 then once ;begin once mode goto waitkey ;begin mono or sticky mode ; ;preview sequence preview: if key_dwn_val=12 then record ;high C selects record if key_dwn_val=0 then let seq_off=off1 ;C=sequence 1 elseif key_dwn_val=2 let seq_off=off2 ;D=sequence 2 elseif key_dwn_val=4 let seq_off=off3 ;E=sequence 3 elseif key_dwn_val=5 let seq_off=off4 ;F=sequence 4 elseif key_dwn_val=7 let seq_off=off5 ;G=sequence 5 elseif key_dwn_val=9 let seq_off=off6;A=user record sequence 6 else goto sel_seq1 ;invalid selection endif gosub scale_sw ;set scale let seq_ptr=seq_off ;initialize sequence pointer let midi_note=key_scale ;output first note gosub send_note_on high aux2_out ;set gate to hi gosub get_tempo ;get tempo low led high aux1_out ;set trigger to hi disable timerwint_imiea led_end=time_count+20 ;set led time let trig_end=time_count+trig_w ;set trigger off time let note_end=time_count+tempo ;set note off delay enable timerwint_imiea if arp_buf(seq_ptr)=128 then preview2 ;check if single note sequence preview1: if note_flag=0 then preview1 ;check if time for next note in sequence let note_flag=0 ;set flag false gosub send_note_off let midi_note=key_scale+arp_buf(seq_ptr) gosub send_note_on gosub get_tempo ;recheck tempo low led high aux1_out ;set trigger to hi disable timerwint_imiea led_end=time_count+20 ;set led time let trig_end=time_count+trig_w ;set trigger off time let note_end=time_count+tempo ;set note off delay enable timerwint_imiea let seq_ptr=seq_ptr+1 ;increment point to next note if arp_buf(seq_ptr)<>128 then preview1 ;check if at end of sequence preview2: if note_flag=0 then preview2 ;check if time for last note off let note_flag=0 ;set flag false gosub send_note_off low aux2_out ;end gate goto sel_seq1 ;check for new sequence or end ; ;arpeggiate mono and sticky mode endseq: disable timerwint_imiea let trig_end=time_count ;reset trigger off let note_end=time_count ;reset note off delay let note_flag=0 ;reset note flag enable timerwint_imiea low aux1_out ;end trigger low aux2_out ;end gate gosub send_note_off let midi_note=128 if mode=1 then ;sticky mode if wait_flag=1 then ;key was released gosub key_up ;wait for all keys up endif endif waitkey: gosub checkmode ;check for restart gosub pass_thru ;pass thru midi data let rel_flag=0 ;reset release flag let wait_flag=0 ;reset wait flag let root_flag=0 ;reset repeat root note flag gosub scale_sw gosub key_down ;get key if key_dwn_val=$ff then waitkey let root_note=key_dwn_val ;save root note let midi_note=root_note+key_scale gosub get_tempo ;get tempo gosub send_note_on high aux2_out ;set gate to hi low led high aux1_out ;set trigger to hi disable timerwint_imiea led_end=time_count+20 ;set led time let trig_end=time_count+trig_w ;set trigger off time let note_end=time_count+tempo ;set note off delay enable timerwint_imiea let seq_ptr=seq_off ;initialize sequence pointer if arp_buf(seq_ptr)=128 then ;check if single note sequence let root_flag=1 ;set flag to repeat root note let seq_ptr=seq_off-1 ;repeat sequence (-1 for root note) endif let note_flag=0 ;reset in case midi clock has set checkkey: gosub pass_thru ;pass thru midi data if mode=0 then ;mono mode if key_in=1 then endseq ;check if key has gone up else gosub key_down ;sticky mode so get current key if key_dwn_val<>$ff then ;ignore key up if key_dwn_val<>root_note then goto endseq ;new note so terminate and restart else if rel_flag=1 then ;same note let wait_flag=1 ;set wait flag goto endseq ;end sequence endif endif else ;key went up let rel_flag=1 ;set flag endif endif if note_flag=0 then checkkey ;check if time for next note in sequence let note_flag=0 ;set flag false if midi_note<128 then gosub send_note_off endif if root_flag=1 then ;check if start of sequence let midi_note=root_note+key_scale ;repeat root note let root_flag=0 ;reset flag else let midi_note=root_note+key_scale+arp_buf(seq_ptr) endif gosub send_note_on gosub get_tempo ;get tempo low led high aux1_out ;set trigger to hi disable timerwint_imiea let led_end=time_count+20 ;set led time let trig_end=time_count+trig_w ;set trigger off time let note_end=time_count+tempo ;set note off delay enable timerwint_imiea let seq_ptr=seq_ptr+1 ;increment point to next note if arp_buf(seq_ptr)=128 then ;check if at end of sequence let root_flag=1 ;set flag to repeat root note let seq_ptr=seq_off-1 ;repeat sequence (-1 for root note) endif goto checkkey ; ;record sequence record: high led ;led off indicates record mode gosub key_up ;wait for release to start let seq_off=off6 ;select sequence 6 let seq_ptr=seq_off ;initialize sequence pointer let arp_buf(seq_ptr)=128 ;terminate sequence gosub scale_sw getfirst: gosub key_down ;get root note if key_dwn_val=$ff then getfirst let root_note=key_dwn_val ;set root note high aux2_out ;set gate high low led high aux1_out ;set trigger to hi disable timerwint_imiea let led_end=time_count+20 ;set led time let trig_end=time_count+trig_w ;set trigger off time enable timerwint_imiea let midi_note=root_note+key_scale gosub send_note_on ;send MIDI note gosub key_up ;wait for key to go up low aux2_out ;end gate gosub send_note_off getnext: out8=1 ;output 15 out9=1 out10=1 out11=1 if key_in=1 then getnext3 ;check command switch to end record mode low led ;led on indicates select mode getnext2: pause 50 if key_in=0 then getnext2 ;wait until command switch release goto sel_seq1 ;select sequence 6 ; getnext3: gosub key_down ;get next note if key_dwn_val=$ff then getnext high aux2_out ;set gate to high low led high aux1_out ;set trigger to high disable timerwint_imiea let led_end=time_count+20 ;set led time let trig_end=time_count+trig_w ;set trigger off time enable timerwint_imiea let midi_note=key_dwn_val+key_scale gosub send_note_on ;send MIDI note gosub key_up ;wait for key to go up low aux2_out ;set gate to low gosub send_note_off let arp_buf(seq_ptr)=key_dwn_val-root_note let seq_ptr=seq_ptr+1 ;increment pointer let arp_buf(seq_ptr)=128 ;terminate sequence if seq_ptr=(arp_buf_len-1) then sel_seq1;end if buffer full goto getnext ; ;arpeggiate once mode once: let seq_ptr=seq_off ;initialize sequence pointer once1: gosub scale_sw ;check scale switches gosub checkmode ;check for restart gosub pass_thru ;pass thru midi data gosub key_down ;check if any keys down if key_dwn_val=$ff then low aux2_out ;reset gate goto once1 endif let root_note=key_dwn_val ;set root note let midi_note=root_note+key_scale ;calculate MIDI key value gosub send_note_on high aux2_out ;set gate gosub get_tempo ;get tempo low led ;flash led high aux1_out ;set trigger disable timerwint_imiea trig_end=time_count+trig_w ;set trigger time led_end=time_count+20 ;set led time let note_end=time_count+tempo ;set note off delay enable timerwint_imiea let note_flag=0 ;reset in case midi clock has set if arp_buf(seq_ptr)=128 then once3 ;check if single note sequence once2: gosub pass_thru ;pass thru midi data if note_flag=0 then once2 ;check if time for next note in sequence let note_flag=0 ;set flag false gosub send_note_off let midi_note=root_note+key_scale+arp_buf(seq_ptr) gosub send_note_on gosub get_tempo ;get tempo low led high aux1_out ;set trigger to hi disable timerwint_imiea led_end=time_count+20 ;set led time let trig_end=time_count+trig_w ;set trigger off time let note_end=time_count+tempo ;set note off delay enable timerwint_imiea let seq_ptr=seq_ptr+1 ;increment point to next note if arp_buf(seq_ptr)<>128 then once2 ;check if at end of sequence once3: if note_flag=0 then once3 ;check if time for last note off let note_flag=0 ;set flag false low aux2_out ;end gate gosub send_note_off goto once ; ;portamento on port_on: let midi_data=$b0+midi_chan ;send portamento off hserout [midi_data] let midi_data=65 ;portamento command hserout [midi_data] let midi_data=127 ;127=on adin value,tempv ;get portamento let tempv=tempv*16 ;convert to 14 bits let midi_data=$b0+midi_chan hserout [midi_data] let midi_data=37 ;portamento lsb hserout [midi_data] let midi_data=tempv&$7f ;lower 7 bits let midi_data=$b0+midi_chan hserout [midi_data] let midi_data=05 ;portamento msb hserout [midi_data] let midi_data=tempv/128 ;higher 7 bits let midi_note=key_base+24 ;send first note gosub send_note_on pause 500 ;wait .5 seconds gosub send_note_off let midi_note=key_base+36 ;send second note to hear portamento gosub send_note_on pause 500 ;wait .5 seconds gosub send_note_off goto getmode ; ;portamento off port_off: let midi_data=$b0+midi_chan ;send portamento off hserout [midi_data] let midi_data=65 ;portamento command hserout [midi_data] let midi_data=0 ;0=off hserout [midi_data] let midi_note=key_base+24 ;send first note gosub send_note_on pause 500 ;wait .5 seconds gosub send_note_off let midi_note=key_base+36 ;send second note to hear portamento gosub send_note_on pause 500 ;wait .5 seconds gosub send_note_off goto getmode ; ;MIDI channel select midic: gosub key_down ;check for key down if key_dwn_val=$ff then midic ;wait for key down if key_dwn_val>1 then midic2 ;first digit must be 0 or 1 let midi_chan=10*key_dwn_val ;set MIDI channel 10s digit low led ;flash led disable timerwint_imiea led_end=time_count+20 ;set led time enable timerwint_imiea gosub key_up ;wait for key release midic1: gosub key_down ;check for key down if key_dwn_val=$ff then midic1 ;wait for key down if midi_chan=0 then if key_dwn_val=0 then midic2 ;next digit must be 1-9 if key_dwn_val>9 then midic2 else if key_dwn_val>6 then midic2 ;next digit must be 0-6 endif let midi_chan=midi_chan+key_dwn_val ;set MIDI channel 1s digit low led ;flash led disable timerwint_imiea led_end=time_count+20 ;set led time enable timerwint_imiea gosub key_up ;wait for key release goto midic3 midic2: low led ;invalid MIDI channel gosub key_up ;wait for key up let midi_chan=bass_chan ;reset MIDI channel to default midic3: ;output MIDI channel number of notes for tempv=1 to midi_chan let midi_note=key_base+24 ;send a note to hear gosub send_note_on pause 100 gosub send_note_off pause 200 next let midi_chan=midi_chan-1 ;set MIDI channel 1-16 to value 0-15 goto getmode ; ;program change select progc: let midi_pgm=0 ;start with program change 0 progc1: gosub checkmode ;check for restart gosub key_down ;check for key down if key_dwn_val=$ff then progc1 ;wait for key down if key_dwn_val>9 then progc5 ;send program change if key_dwn_val>1 then progc3 ;must be 10s digit let midi_pgm=100*key_dwn_val ;set program change to either 0 or 100 low led ;flash led disable timerwint_imiea led_end=time_count+20 ;set led time enable timerwint_imiea gosub key_up ;wait for key release progc2: gosub key_down ;check for key down if key_dwn_val=$ff then progc2 ;wait for key down if key_dwn_val>9 then progc5 ;send program change progc3: let midi_pgm=midi_pgm+(10*key_dwn_val) ;set program change 10s digit low led ;flash led disable timerwint_imiea led_end=time_count+20 ;set led time enable timerwint_imiea gosub key_up ;wait for key release progc4: gosub key_down ;check for key down if key_dwn_val=$ff then progc4 ;wait for key down if key_dwn_val>9 then progc5 ;send program change let midi_pgm=midi_pgm+key_dwn_val ;set program change 1s digit low led ;flash led disable timerwint_imiea led_end=time_count+20 ;set led time enable timerwint_imiea gosub key_up ;wait for key release progc5: gosub send_prog_change ;send program change let midi_note=key_base+24 ;send a note to hear program change gosub send_note_on pause 500 ;wait .5 seconds gosub send_note_off goto progc ; ;panic mode select panic: let midi_chan=rcx_chan gosub send_all_off ;send all notes off for midi_note=0 to 127 ;send all 128 notes off gosub send_note_off next let midi_chan=bass_chan gosub send_all_off ;send all notes off for midi_note=0 to 127 ;send all 128 notes off gosub send_note_off next goto getmode ; ;-----------------------------------------------------------------------------------------------; ; MIDI loop-through routine ; ;-----------------------------------------------------------------------------------------------; ; ;parse midi data for command ;written by Dave Brown pass_thru: hserin done_pass_thru,0,[midi_data] if midi_data=$fe then pass_thru ;ignore active status low led disable timerwint_imiea let led_end=time_count+20 ;set led time enable timerwint_imiea if midi_data>$f7 then ;check to see if real time goto real_time endif if midi_data.bit7=0 then running ;check to see if running status let last_cmd=midi_data ;save command to restore for running let midi_cmd=(midi_data>>4)&07 ;strip midi channel for branch and save for running branch midi_cmd,[mkey_up,mkey_dn,key_prs,ctrl_chg,prgm_chg,chan_prs,ptch_bnd,sys_com] ; done_pass_thru: return ;no more received data ; ;running status running: let tempb=last_cmd ;get last midi command to reconstruct command byte let tempb=(tempb&rcx_mask)|rcx_chan ;transpose midi channel hserout [tempb] branch midi_cmd,[mkey_upr,mkey_dnr,key_prsr,ctrl_chgr,prgm_chgr,chan_prsr,ptch_bndr,pass_thru] ; ;note off command mkey_up: let midi_data=(midi_data&rcx_mask)|rcx_chan ;transpose midi channel hserout [midi_data] gosub get_next_byte ;get data from received buffer mkey_upr: let midi_data=midi_data+rcx_note ;transpose midi note hserout [midi_data] gosub get_next_byte ;get data from received buffer hserout [midi_data] goto pass_thru ;check if more data in buffer ; ;note on command mkey_dn: let midi_data=(midi_data&rcx_mask)|rcx_chan ;transpose midi channel hserout [midi_data] gosub get_next_byte ;get data from received buffer mkey_dnr: let midi_data=midi_data+rcx_note ;transpose midi note hserout [midi_data] #ifdef display_en let disp_data=midi_data ;display note gosub key_display #endif gosub get_next_byte ;get data from received buffer hserout [midi_data] goto pass_thru ;check if more data in buffer ; ;key pressure command key_prs: let midi_data=(midi_data&rcx_mask)|rcx_chan ;transpose midi channel hserout [midi_data] gosub get_next_byte ;get data from received buffer key_prsr: hserout [midi_data] gosub get_next_byte ;get data from received buffer hserout [midi_data] goto pass_thru ;check if more data in buffer ; ;control change command ctrl_chg: let midi_data=(midi_data&rcx_mask)|rcx_chan ;transpose midi channel hserout [midi_data] gosub get_next_byte ;get data from received buffer ctrl_chgr: hserout [midi_data] gosub get_next_byte ;get data from received buffer hserout [midi_data] goto pass_thru ;check if more data in buffer ; ;program change command prgm_chg: let midi_data=(midi_data&rcx_mask)|rcx_chan ;transpose midi channel hserout [midi_data] gosub get_next_byte ;get data from received buffer prgm_chgr: hserout [midi_data] goto pass_thru ; ;channel pressure command chan_prs: let midi_data=(midi_data&rcx_mask)|rcx_chan ;transpose midi channel hserout [midi_data] gosub get_next_byte ;get data from received buffer chan_prsr: hserout [midi_data] goto pass_thru ; ;pitch bend command ptch_bnd: let midi_data=(midi_data&rcx_mask)|rcx_chan ;transpose midi channel hserout [midi_data] gosub get_next_byte ;get data from received buffer ptch_bndr: hserout [midi_data] gosub get_next_byte ;get data from received buffer hserout [midi_data] goto pass_thru ;check if more data in buffer ; ;system common command sys_com: let sys_cmd=midi_data&$07 ;get system common command branch sys_cmd,[sys_ex,pass_thru,song_ptr,song_sel,pass_thru,pass_thru,tune_req,pass_thru] ; ;system exclusive sys_ex: hserout [midi_data] sys_ex2: gosub get_next_byte ;get data from received buffer hserout [midi_data] ;send it if midi_data<>$f7 then sys_ex2 ;check for end of string goto pass_thru ; ;song pointer command song_ptr: hserout [midi_data] gosub get_next_byte ;get data from received buffer hserout [midi_data] gosub get_next_byte ;get data from received buffer hserout [midi_data] goto pass_thru ;check if more data in buffer ; ;song select command song_sel: hserout [midi_data] gosub get_next_byte ;get data from received buffer hserout [midi_data] goto pass_thru ;check if more data in buffer ; ;tune_request command tune_req: hserout [midi_data] goto pass_thru ; ;real time commands real_time: let sys_cmd=midi_data&07 ;strip high bits for branch branch sys_cmd,[time_clk,nul_cmd,start_cmd,cont_cmd,stop_cmd,nul_cmd,nul_cmd,reset_cmd] ; ;timing clock command time_clk: hserout [midi_data] if start_flag=0 then time_clk2 ;ignore until start received if midi_cnt=0 then let note_flag=1 ;start on first clock endif let midi_cnt=midi_cnt+1 ;increment midi clock count if midi_cnt=24 then ;received quarter note worth? let midi_cnt=0 ;reset midi clock count endif time_clk2: return ; ;reserved command nul_cmd: ;do nothing return ; ;start command start_cmd: hserout [midi_data] let start_flag=1 ;set midi start received flag return ; ;continue command cont_cmd: hserout [midi_data] let start_flag=1 ;set midi start received flag return ; ;start command stop_cmd: hserout [midi_data] let start_flag=0 ;set midi stop flag let note_flag=1 ;force end of note let midi_cnt=0 ;reset clock count return ; ;start command reset_cmd: hserout [midi_data] return ; ;wait for data and check for real time data get_next_byte: hserin get_next_byte,0,[midi_data] if midi_data=$fe then get_next_byte ;ignore active status if midi_data>$f7 then ;check for real time gosub real_time goto get_next_byte endif return ; ;-----------------------------------------------------------------------------------------------; ; Subroutines ; ;-----------------------------------------------------------------------------------------------; ; ;returns key_dwn_val as first key down ;returns $ff if no keys down key_down: let key_dwn_val=$ff ;assume no keys down let key_cnt=0 key_down2: out8=key_cnt.bit0 ;output key address out9=key_cnt.bit1 out10=key_cnt.bit2 out11=key_cnt.bit3 pause 1 ;let settle gosub pass_thru pause 1 ;let settle gosub pass_thru if key_in=0 then ;get current state let key_dwn_val=key_cnt ;set to key value goto key_down3 endif let key_cnt=key_cnt+1 if key_cnt<13 then key_down2 key_down3: return ; ;returns only when all keys up key_up: let key_cnt=0 key_up2: out8=key_cnt.bit0 ;output key address out9=key_cnt.bit1 out10=key_cnt.bit2 out11=key_cnt.bit3 pause 1 ;let settle gosub pass_thru pause 1 ;let settle gosub pass_thru if key_in=0 then key_up ;get current state let key_cnt=key_cnt+1 if key_cnt<13 then key_up2 return ; ;check scale switches scale_sw: out8=1 ;output 13 for scale switch out9=0 out10=1 out11=1 if key_in=0 then key_scale=key_base+24 ;scale up two octaves else key_scale=key_base+12 ;assume up one octave endif out8=0 ;output 14 out9=1 if key_in=0 then key_scale=key_base ;no scale endif return ; ;check mode switch for end of current mode checkmode: out8=1 ;output 15 for mode switch out9=1 out10=1 out11=1 if key_in=0 then exception getmode ;clear stack and return to mode endif return ; ;get tempo get_tempo: adin value,tempv ;calculate tempo with reverse semi-log scale if tempv > 768 then ;769-1023 let tempo = 63+((tempv^$3ff)/4) ;scales 769 -1023 to 126 to 65 elseif tempv > 511 ;512-768 let tempo=255-((tempv-512)/2) ;scales 512 - 768 to 255 - 127 else ;0 to 511 let tempo=255+(((tempv*3)/2)^$2ff) ;scales 0 - 511 to 1022 - 256 endif return ; ;output MIDI note-on command send_note_on: let midi_data=note_on+midi_chan hserout [midi_data] let midi_data=midi_note hserout [midi_data] let midi_data=midi_vel ;set velocity to default value ; adin aux2_in,tempv ;uncomment to enable aux2 to control velocity ; let midi_vel=tempv/8 ;uncomment to enable aux2 to control velocity hserout [midi_data] #ifdef display_en let disp_data=midi_note ;display note gosub ped_display #endif return ; ;output MIDI note-off command send_note_off: let midi_data=note_off+midi_chan hserout [midi_data] let midi_data=midi_note hserout [midi_data] let midi_data=0 hserout [midi_data] return ; ;output MIDI all notes off command send_all_off: let midi_data=$b0+midi_chan ;send all notes off hserout [midi_data] let midi_data=$7b hserout [midi_data] let midi_data=0 hserout [midi_data] return ; ;output 3 byte program change command send_prog_change: let midi_data=pgm_chg+midi_chan hserout [midi_data] let midi_data=midi_pgm hserout [midi_data] return ; #ifdef display_en ;routine to send data to console send_console: disable ;interrupts must be off for correct timing serout s_out,i38400,[hex con_data,13] enable return ; ;output disp_data to display ' p' ped_display: disable ;interrupts must be off for correct timing serout s_out,i57600,[" "] ;send space enable let note_n=disp_data/12 ;find octave number disable ;interrupts must be off for correct timing serout s_out,i57600,["p"] ;send space enable goto aux_display ; ;output disp_data to display ' m' key_display: disable ;interrupts must be off for correct timing serout s_out,i57600,[" "] ;send space enable let note_n=disp_data/12 ;find octave number disable ;interrupts must be off for correct timing serout s_out,i57600,["m"] ;send space enable aux_display: let note_a=(disp_data-(note_n*12))*2 ;find note alpha let note_d=note_t(note_a) ;lookup note number disable ;interrupts must be off for correct timing serout s_out,i57600,[note_d] ;send note letter enable let note_d=note_t(note_a+1) ;lookup # or nul if note_d>0 then disable ;interrupts must be off for correct timing serout s_out,i57600,[note_d] ;send # enable endif let note_d=note_n+$30 ;lookup note number disable ;interrupts must be off for correct timing serout s_out,i57600,[note_d] ;send note number enable return #endif ; ;-----------------------------------------------------------------------------------------------; ; Interrupt service routine ; ;-----------------------------------------------------------------------------------------------; ; ;interrupt service routine for timer ;increments time_count value ;turns off stop led and trigger at preset times ;written by Dave Brown tmw_isr: let time_count=time_count+1 ;increment real time count if time_count=led_end then ;value to turn off led high led endif if time_count=trig_end then ;value to turn off trigger low aux1_out endif if time_count=note_end then ;check note off time if start_flag=0 then let note_flag=1 ;set if midi clock not used endif endif resume ; ;-----------------------------------------------------------------------------------------------; ; End of program ; ;-----------------------------------------------------------------------------------------------;