;-----------------------------------------------------------------------------------------------; ; Program: CV2Midi ; ; Developed by David J. Brown ; ; Copyright (c) April 13, 2004 David J. Brown ; ; Email: davebr@earthlink.net ; ; Web site: http://modularsynthesis.com ; ;-----------------------------------------------------------------------------------------------; ; LICENSE AGREEMENT: ; ; This program is free software. You can redistribute it and/or modify it. ; ; ; ; 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. ; ;-----------------------------------------------------------------------------------------------; ' ' Description: ' ' This program converts a cv input to midi note. A valid ' note is determined by two modes: start selects gate or ' cv mode, and stop selects gate-only mode. ' ' ProgramChange and Note Velocity is sent to the LCD display ' in sysex format. ' ' A true gate transition indicates a valid note so a program change ' (if necessary) is sent followed by a midi note-on sequence. A false ' gate indicates no keys are depressed so a midi note-off sequence is ' sent. In gate or cv mode, a change in cv while gate is true will ' simulate a key release. A 5 mS trigger will be output and since the ' gate will still be true a new valid note will be detected and the ' new midi note-on sequence sent. Gate true can be defined as either ' a 0 or 1 by the following declaration: gate_true_val con 1 ' ' inputs: ' start = gate or cv mode ' stop = gate-only mode ' in-1 = cv in ' in-2 = velocity ' in-3 = octave scale (0 to 3) ' in-4 = program change ' aux = gate in ' midi-in = ' ' outputs: ' out-1 = pseudo-trigger out (5 mS) ' out-2 = gate out (goes false during pseudo-trigger true) ' out-3 = 0 ' out-4 = 0 ' midi-out= midi program change, note, and velocity ' speakjet= voice output ' ' run led = toggles @ 0.5 S (off during mode select) ' stop led = flashes when valid note detected (continuous on during mode select) ' ' written by David J. Brown ' revision: 0.4S2 ' Sept 24, 2010 ' history: 0.4S2 changed serial for Studio 2 ' 0.4X changed shiftout for Studio, removed serial speakjet ' 0.4 changed hex2ascii to eliminate push/pop ' 0.3 added display of program name ' 0.2 changed to hserout command, added program change display ' 0.1 added gate_true_val declaration, changed cv sampling to ' interrupt driven averaged mode ' 0.0 initial release of cv2midi program ' date: June 13, 2004 ' '########################################### ' ' ### CONDITIONAL COMPILING OPTION ### ' set MIDI INTERRUPT mode ' ' comment out next line for polled mode ' midi_int_en con 1 'define to enable midi-in interrupts ' comment to set polled midi-in mode ' '########################################### ' ' ### CONDITIONAL COMPILING OPTION ### ' set TIMER INTERRUPT mode ' note: Run led will blink if timer_int_en defined ' ' comment out next line to disable timer interrupts timer_int_en con 1 'define to enable timer interrupts ' comment to disable timer interrupts ' '########################################### ' ' ### CONDITIONAL COMPILING OPTION ### ' set INPUT INTERRUPT mode ' note: Requires timer_int_en to be defined ' ' comment out next line to disable input interrupts input_int_en con 1 'define to enable input interrupt mode ' comment to set polled input mode ' '########################################### ' 'djb template written by David J. Brown 'based on code from Brice Hornback and Grant Richter 'revision: 0.42 ' May 13, 2004 'history: 0.42 renamed start_b to start_j, renamed stop_b to ' stop_j ' 0.41 upper case conversion to lower, variables and ' labels renamed ' 0.4 added averaged input input driver (polled or ' interrupt mode), display_led driver (for debug), ' speakjet driver, renamed program to djb template ' 0.3 added program header information, modified ' pin initialization ' 0.2 initialize aux output low and speakjet pins ' 0.1 added DAC & led initialization, 1 mS timestamp, ' stop led blinks when midi data received, run ' led toggles @ 0.5 S for health indication ' 0.0 initial release of midi input & output program 'date: April 13, 2004 '******************************************* ' module: PSIM-1 REV1b ' processor Type: Basic Micro - Basic Atom Pro24M '******************************************* ' ' Basic Micro Atom Pro-24M Configuration ' ' P0 - in-1 ' P1 - in-2 ' P2 - in-3 ' P3 - in-4 ' P4 - start button (momentary normally open switch) ' P5 - stop button (momentary normally open switch) ' P6 - j3 pin 2 (speakjet buffer half full) ' P7 - j3 pin 1 (serial data to speakjet) ' P8 - aux digital I/O ' P9 - stop led ' P10 - run led ' P11 - load_dacs ' P12 - ser_data ' P13 - clock ' P14 - j5 pin 1 (midi-in) ' P15 - j5 pin 2 (midi-out) ' '******************************************* ' 'pin constants pin_j1 con p0 'in-1 pin pin_j2 con p1 'in-2 pin pin_j3 con p2 'in-3 pin pin_j4 con p3 'in-4 pin start_j var in4 ;start jack and switch stop_j var in5 ;stop jack and switch aux_j con p8 ;aux jack (output mode) aux_in var in8 ;aux jack (input mode) stop_led con p9 ;stop led (red) stop_led_out var out9 ;alias for output run_led con p10 ;run led (green) run_led_out var out10 ;alias for output load_dacs con p11 'load dac pin ser_data con p12 'serial data to dac pin clock con p13 'dac clock pin midi_sdata con p15 ;midi data serial output 'i/o variable declarations in_j1 var word 'in-1 value: get_inputs, get_inputs_avg, avg1 in_j2 var word 'in-2 value: get_inputs, get_inputs_avg, avc2 in_j3 var word 'in-3 value: get_inputs, get_inputs_avg, avg3 in_j4 var word 'in-4 value: get_inputs, get_inputs_avg, avg4 out_j1 var word 'out-1 value: load_outputs out_j2 var word 'out-2 value: load_outputs out_j3 var word 'out-3 value: load_outputs out_j4 var word 'out-4 value: load_outputs 'midi variable declarations note_off con $80 'midi note-off command: send_note_on, send_note_off note_on con $90 'midi note-on command: send_note_on, send_note_off pgm_chg con $c0 'midi program change command:send_prog_change midi_chan var nib 'midi channel (0 - 15): send_note_on, send_note_off midi_note var byte 'midi note value: send_note_on, send_note_off last_note var byte 'last note sent midi_vel var byte 'midi velocity value: send_note_on, send_note_off new_vel var byte 'new midi velocity for compare midi_pgm var byte 'midi program change value:send_prog_change new_pgm var byte 'new midi program change for compare 'midi driver variable declarations midi_data var byte 'variable: check_midi, send_midi, get_rcx_bfr, send_note_on, send_note_off midi_data_isr var byte 'variable: midi_in_isr rcx_status var byte 'variable: check_midi, send_midi rcx_ptr_strt var byte 'received data start pointer (first data in buffer): get_rcx_bfr rcx_ptr_end var byte 'received data end pointer (last data in buffer+1): check_midi, midi_in_isr rcx_bfr_num var byte 'number of bytes in data buffer: check_midi, get_rcx_bfr, midi_in_isr rcx_data_flg var bit 'received data flag (1=data, 0=no data): get_rcx_bfr rcx_bfr_len con 32 'midi-in buffer length: rcx_bfr rcx_bfr var byte(rcx_bfr_len) 'midi-in data buffer: check_midi, get_rcx_bfr, midi_in_isr 'timer variable declarations #ifdef timer_int_en time_count var long '1 mS timer count value: tm_isr turn_off var long 'time value to turn off stop led: tm_isr #endif 'input variable declarations in_j1_ptr var byte 'in-1 buffer pointer: avgx, get_inputs_avg #ifdef input_int_en in_j2_ptr var byte 'in-2 buffer pointer: avgx in_j3_ptr var byte 'in-3 buffer pointer: avgx in_j4_ptr var byte 'in-4 buffer pointer: avgx isr_cnt var nib 'timer pass counter: tm_isr #endif in_j1_tmp var long 'in-1 temporary variable: avgx, get_inputs_avg in_j2_tmp var long 'in-2 temporary variable: avgx, get_inputs_avg in_j3_tmp var long 'in-3 temporary variable: avgx, get_inputs_avg in_j4_tmp var long 'in-4 temporary variable: avgx, get_inputs_avg in_j1_bfr var long(2) 'in-1 last 4 samples buffer: avgx, get_inputs_avg in_j2_bfr var long(2) 'in-2 last 4 samples buffer: avgx, get_inputs_avg in_j3_bfr var long(2) 'in-3 last 4 samples buffer: avgx, get_inputs_avg in_j4_bfr var long(2) 'in-4 last 4 samples buffer: avgx, get_inputs_avg 'misc variable declarations tempb var byte 'temp byte led_data var byte 'variable: display_led skip_cv var bit 'flag for skip cv check num_data var byte 'variable: hex2ascii huns var byte 'hundreds digit tens var byte 'tens digit ones var byte 'ones digit ' '******************************************* ' 'initialize pins 'note: setting midi-out, i2c_clock, and i2c_data to outputs can send glitches so initialize as inputs dirs=%0011111000000000 'configure pin direction (1=output, 0=input) 'inputs: midi-in, aux, start, stop, in-4, in-3, in-2, in-1 'outputs: load_dacs, ser_data, clock, stop led, run led, i2c clock & data low run_led 'set run led off low stop_led 'set stop led off high load_dacs 'set dac load-0 high low ser_data 'set dac data low low clock 'set dac clock low let out_j1=0 'set out-1 low let out_j2=0 'set out-2 low let out_j3=0 'set out-3 low let out_j4=0 'set out-4 low gosub load_outputs ' #ifdef timer_int_en 'initialize timer hardware for 1 mS interrupts let tmrw=%10001000 'set Timer Mode Register to enable count let tcrw=%10110000 'set Timer Control Register ' TCNT cleared by compare match ' /8 internal clock let tierw=%01110000 'set Timer Interrupt register to disable overflow interrupt let tsrw=%01110000 'set Timer Status Register to default let tior0=%10001000 'set Timer I/O Regiseter 0 to default let tior1=%10001000 'set Timer I/O Register 1 to default let gra=2000 'set General Register A ' 16 MHz clock /8 = 2 MHz ' 2000 counts = 1 mS interrupt #endif ' 'initialize misc variables let rcx_ptr_strt=0 'set receive start pointer let rcx_ptr_end=0 'set receive end pointer let rcx_bfr_num=0 'set receive buffer to empty let midi_chan=0 'set midi channel to 0 let midi_note=0 'set midi note to 0 let last_note=0 'set last note to 0 let midi_pgm=128 'set program change invalid let led_data=0 'set led data to 0 let in_j1_ptr=0 'set in-1 pointer to 0 #ifdef input_int_en let in_j2_ptr=0 'set in-2 pointer to 0 let in_j3_ptr=0 'set in-3 pointer to 0 let in_j4_ptr=0 'set in-4 pointer to 0 let isr_cnt=0 'set isr pass counter to 0 #endif let in_j1_bfr(0)=0 'set in-1 buffer data to 0 let in_j1_bfr(1)=0 let in_j2_bfr(0)=0 'set in-2 buffer data to 0 let in_j2_bfr(1)=0 let in_j3_bfr(0)=0 'set in-3 buffer data to 0 let in_j3_bfr(1)=0 let in_j4_bfr(0)=0 'set in-4 buffer data to 0 let in_j4_bfr(1)=0 let midi_data=$b0+midi_chan 'send all notes off gosub send_midi let midi_data=$7b gosub send_midi let midi_data=0 gosub send_midi high stop_led 'stop led indicates wait for mode ' 'enable midi interrupts sethserial1 h31200,h8databits,hnoparity,h1stopbits hserout [$b0,$7b,$00] 'send all notes off hserout [$f0,$7d,$0a,$18,"CV2Midi",$f7]'clear display, overwrite, program name ' mode_sel: let skip_cv=0 'assume start if start_j=1 then start 'check for start let skip_cv=1 'assume stop if stop_j=1 then start 'check for stop goto mode_sel start: low stop_led ' 'enable timer interrupts #ifdef timer_int_en let time_count=0 'set real time counter to 0 let turn_off=0 'set time to turn off to time_count oninterrupt timerwint_imiea,tm_isr enable timerwint_imiea 'enable timer interrupt #endif ' 'main program gate_true: if aux_in<>gate_true_val then gate_true 'wait for gate to go true high stop_led 'flash stop led #ifdef timer_int_en disable timerwint_imiea turn_off=time_count+20 'set turn off time as current + 20 mS enable timerwint_imiea #endif let out_j2=1920 'output gate high gosub load_outputs #ifndef input_int_en gosub get_inputs 'get cv, vel, scale, and program change #endif #ifdef input_int_en pause 5 'allow time for cv to average #endif let new_pgm=in_j4/8 'program change 0 to 127 range if new_pgm<>midi_pgm then let midi_pgm=new_pgm 'save value for compare gosub send_prog_change 'send program change if different gosub update 'update display endif let new_vel=in_j2/8 'velocity 0 to 127 range if new_vel<>midi_vel then let midi_vel=new_vel 'save value for compare gosub update 'update display endif let last_note=((in_j1+4)*2)/17 'convert to semitone and save for compare let midi_note=last_note+12 'shift up 1 octave let midi_note=midi_note+((in_j3/256)*12) gosub send_note_on 'send note on sequence gate_false: if aux_in<>gate_true_val then 'wait for gate to go false #ifndef timer_int_en low stop_led #endif gosub send_note_off 'send note off sequence let out_j2=0 'output gate low gosub load_outputs goto gate_true endif if skip_cv=1 then gate_false #ifndef input_int_en adin pin_j1,in_j1 'check if cv changed #endif if (((in_j1+4)*2)/17)=last_note then gate_false 'cv changed so force gate out low followed by high #ifndef timer_int_en low stop_led #endif gosub send_note_off 'send note off sequence let out_j1=1920 'output trigger high let out_j2=0 'output gate low gosub load_outputs pause 5 'trigger pulse width 'also allows cv to settle before new sample in case there is any lag let out_j1=0 'output trigger low gosub load_outputs goto gate_true ' '******************************************* ' subroutines '******************************************* ' 'update display update: hserout [$f0,$7d,$0b,"P"] 'home line 2 let num_data=midi_pgm 'convert midi_pgm gosub hex2ascii hserout [huns,tens,ones] 'display number let num_data=midi_vel 'convert midi_pgm gosub hex2ascii hserout ["V",huns,tens,ones,$f7] 'display number and finish sysex return ' 'convert lcd_data to ascii hex2ascii: let tempb=num_data if num_data >99 then let huns=num_data/100 let num_data=num_data-(huns*100) let huns=huns+$30 else let huns=$30 endif if num_data >9 then let tens=num_data/10 let num_data=num_data-(tens*10) let tens=tens+$30 else let tens=$30 endif let ones=num_data+$30 let num_data=tempb return ' 'output 3 byte midi note-on command send_note_on: let midi_data=note_on+midi_chan gosub send_midi let midi_data=midi_note gosub send_midi let midi_data=midi_vel gosub send_midi return ' 'output 3 byte midi note-off command send_note_off: let midi_data=note_off+midi_chan gosub send_midi let midi_data=midi_note gosub send_midi let midi_data=midi_vel gosub send_midi return ' 'output 3 byte program change command send_prog_change: let midi_data=pgm_chg+midi_chan gosub send_midi let midi_data=midi_pgm gosub send_midi return ' 'subroutine to send midi_data send_midi: hserout [midi_data] ;put data in transmit buffer return ' #ifndef input_int_en 'sample in-1 to in-4 'injx is input value (0 - 1023) '150 uS execution time get_inputs: adin pin_j1,in_j1 adin pin_j2,in_j2 adin pin_j3,in_j3 adin pin_j4,in_j4 return ' 'sample and average in-1 to in-4 'injx is input value averaged over last four samples (0 - 1023) 'injx_bfr(0) & injx_bfr(1) are last four word samples '600 uS execution time 'written by David J. Brown get_inputs_avg: adin pin_j1,in_j1_bfr.word1(in_j1_ptr) 'get in-1 into buffer let in_j1_tmp=in_j1_bfr(0)+in_j1_bfr(1) 'add two high words together and low words together let in_j1=(in_j1_tmp.word1+in_j1_tmp.word0)/4 adin pin_j2,in_j2_bfr.word1(in_j1_ptr) 'get in-2 into buffer let in_j2_tmp=in_j2_bfr(0)+in_j2_bfr(1) 'add two high words together and low words together let in_j2=(in_j2_tmp.word1+in_j2_tmp.word0)/4 adin pin_j3,in_j3_bfr.word1(in_j1_ptr) 'get in-3 into buffer let in_j3_tmp=in_j3_bfr(0)+in_j3_bfr(1) 'add two high words together and low words together let in_j3=(in_j3_tmp.word1+in_j3_tmp.word0)/4 adin pin_j4,in_j4_bfr.word1(in_j1_ptr) 'get in-4 into buffer let in_j4_tmp=in_j4_bfr(0)+in_j4_bfr(1) 'add two high words together and low words together let in_j4=(in_j4_tmp.word1+in_j4_tmp.word0)/4 let in_j1_ptr=(in_j1_ptr+1)&$03 'increment pointer and wrap at 3 return #endif ' 'routine to display led_data to out leds 'note: sets outjx values to 0 'written by David J. Brown display_led: let out_j1=0 'output high nibble of data let out_j2=0 let out_j3=0 let out_j4=0 let out_j1.bit11=led_data.bit7 'sets output value to 2048 if bit is 1 let out_j2.bit11=led_data.bit6 let out_j3.bit11=led_data.bit5 let out_j4.bit11=led_data.bit4 gosub load_outputs pause 750 'display high nibble let out_j1.bit11=led_data.bit3 'output low nibble of data let out_j2.bit11=led_data.bit2 let out_j3.bit11=led_data.bit1 let out_j4.bit11=led_data.bit0 gosub load_outputs pause 750 'display low nibble let out_j4=0 let out_j3=0 let out_j2=0 let out_j1=0 gosub load_outputs 'turn all leds off return ' 'output outjx values to dacs 'based on code written by Grant Richter '750 uS execution time load_outputs: 'add addresses to values 'shift out 16 bits mode 4 shiftout ser_data,clock,fastmsbpre,[(out_j1+49152)\16] pulsout load_dacs,1 'pulse loaddacs for each channel shiftout ser_data,clock,fastmsbpre,[(out_j2+32768)\16] pulsout load_dacs,1 'pulse loaddacs for each channel shiftout ser_data,clock,fastmsbpre,[(out_j3+16384)\16] pulsout load_dacs,1 'pulse loaddacs for each channel shiftout ser_data,clock,fastmsbpre,[out_j4\16] pulsout load_dacs,1 'pulse loaddacs for each channel return ' '******************************************* 'interrupt service routines '******************************************* ' #ifdef timer_int_en 'interrupt service routine for timer 'increments time_count value 'turn off stop led at turn_off value 'toggle run led at 512 mS intervals 'written by David J. Brown tm_isr: let time_count=time_count+1 'increment real time count if time_count=turn_off then 'value to turn off stop led low stop_led endif let run_led_out=time_count.bit9 'toggle run led at 512 mS intervals #endif #ifndef input_int_en resume 'resume if input polled mode #endif #ifdef input_int_en 'continue if input interrupt mode 'sample in-1 - in-4 at specific intervals 'injx is input averaged over last 4 samples (0 - 1023) 'injx_bfr(0) & injx_bfr(1) are last 4 word samples let isr_cnt=isr_cnt+1 'change the following table to modify the scan rate for each input '175 uS execution time so maximum of one sample and average per interrupt 'currently set for: in-1 @ 2 mS, in-2 @ 8 mS, in-3 @ 8 mS, in-4 @ 4 mS branch isr_cnt, [avg1, avg4, avg1, avg3, avg1, avg4, avg1, avg2, | avg1, avg4, avg1, avg3, avg1, avg4, avg1, avg2] 'sample in-1 avg1: adin pin_j1, in_j1_bfr.word1(in_j1_ptr) 'get in-1 into buffer let in_j1_tmp=in_j1_bfr(0)+in_j1_bfr(1) 'add two high words together and low words together let in_j1=(in_j1_tmp.word1+in_j1_tmp.word0)/4 let in_j1_ptr=(in_j1_ptr+1)&$03 'increment pointer and wrap at 3 resume 'sample in-2 avg2: adin pin_j2, in_j2_bfr.word1(in_j2_ptr) 'get in-2 into buffer let in_j2_tmp=in_j2_bfr(0)+in_j2_bfr(1) 'add two high words together and low words together let in_j2=(in_j2_tmp.word1+in_j2_tmp.word0)/4 let in_j2_ptr=(in_j2_ptr+1)&$03 'increment pointer and wrap at 3 resume 'sample in-3 avg3: adin pin_j3, in_j3_bfr.word1(in_j3_ptr) 'get in-3 into buffer let in_j3_tmp=in_j3_bfr(0)+in_j3_bfr(1) 'add two high words together and low words together let in_j3=(in_j3_tmp.word1+in_j3_tmp.word0)/4 let in_j3_ptr=(in_j3_ptr+1)&$03 'increment pointer and wrap at 3 resume 'sample in-4 avg4: adin pin_j4, in_j4_bfr.word1(in_j4_ptr) 'get in-4 into buffer let in_j4_tmp=in_j4_bfr(0)+in_j4_bfr(1) 'add two high words together and low words together let in_j4=(in_j4_tmp.word1+in_j4_tmp.word0)/4 let in_j4_ptr=(in_j4_ptr+1)&$03 'increment pointer and wrap at 3 resume #endif ' '******************************************* 'end of program '*******************************************