Radio Junk Box
/
KAMUI_MIDI-CV_Example
KAMUI MIDI-CV Example
Revision 0:25a282f1141a, committed 2012-05-05
- Comitter:
- radiojunkbox
- Date:
- Sat May 05 11:35:41 2012 +0000
- Commit message:
- Rev. 1.0
Changed in this revision
diff -r 000000000000 -r 25a282f1141a TextLCD.lib --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TextLCD.lib Sat May 05 11:35:41 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/simon/code/TextLCD/#44f34c09bd37
diff -r 000000000000 -r 25a282f1141a main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Sat May 05 11:35:41 2012 +0000 @@ -0,0 +1,512 @@ +//------------------------------------------------------------- +// KAMUI MIDI-CV Exapmple +// Copyright (C) 2012 RJB RadioJunkBox +// Released under the MIT License: http://mbed.org/license/mit +//------------------------------------------------------------- + +#include "mbed.h" +#include "TextLCD.h" +#include <stdlib.h> +#include <math.h> + +//------------------------------------------------------------- +// Define + +#define AD5551 // 14bitDAC + +#define SPI_RATE 1000000 // 1Mbps +#define MIDI_RATE 31250 // 31.25kbps +#define BEEP_FREQ 1760.0 // 1760Hz +#define UPDATE_INTERVAL 100 // 100us +#define SW_WATCH_INTERVAL (25000/UPDATE_INTERVAL) // 25ms +#define PARAM_GLIDE 6554.0 +#define PARAM_DP 6912.0 // UPDATE_INTERVAL = 100us +//#define PARAM_DP 8192.0 // UPDATE_INTERVAL = 50us + +#define UPDATE_MODE0 0 // Update Interval CV ch1-6 1200us, ch7,8 400us +#define UPDATE_MODE1 1 // Update Interval CV ch1-6 N/A, ch7,8 200us + +#define GATE1 0x01 +#define GATE2 0x02 +#define GATE3 0x04 +#define GATE4 0x08 + +#define SYNC1CLK 0x01 +#define SYNC1RUN 0x02 +#define SYNC2CLK 0x04 +#define SYNC2RUN 0x08 + +#define MODE_CV 0x00 +#define MODE_GATE 0x40 +#define MODE_SYNC 0x80 +#define MODE_SET_SYNC 0xC0 + +#define SW1 0x01 +#define SW2 0x02 +#define SW3 0x04 +#define SW4 0x08 +#define SYNC1CLK_IN 0x10 +#define SYNC1RUN_IN 0x20 +#define SYNC2CLK_IN 0x40 +#define GATE_IN 0x80 + +#define _ENABLE 0 +#define _DISABLE 1 + +#define BUFSIZE 32 // size of ring buffer (ex 4,8,16,32...) +#define LFO_WF_TRI 0 +#define LFO_WF_SQR 1 +#define LFO_WF_SAW 2 +#define LFO_WF_NONE 3 +#define MINIMUMNOTE 12 +#define SYNC_TURN_TIME (5000/UPDATE_INTERVAL) // 5ms + +//------------------------------------------------------------- +// Functions + +void InitKamui(void); +void UpdateCV(void); +unsigned char CheckSW(unsigned char); + +void RcvMIDI(void); +void MidiCV(void); +void CalcHzVTbl(void); +unsigned short OctVtoHzV(unsigned short); +void DinSync(void); +extern void MIDI_Parser(unsigned char); + +//------------------------------------------------------------- +// Global Variables + +int gUpdateMode; +unsigned short gCV[8]; +unsigned char gGATE; +unsigned char gSYNC; +unsigned char gSW; + +union { + unsigned short WORD; + struct { + unsigned char L; + unsigned char H; + } BYTE; +} gDAC; + +int gPtr_buf_in, gPtr_buf_out; +unsigned char gRxBuf[BUFSIZE]; + +float gGLIDE[4]; +unsigned short gLFO_DP[4]; +unsigned char gLFO_FORM[4]; +unsigned char gMIDI_CH[4]; +short gTblHzV[3072]; + +extern unsigned char gPlayNoteBuf[]; +extern unsigned char gGateBuf[]; +extern unsigned char gPitchBendBuf[]; +extern unsigned char gModWheelBuf[]; +extern unsigned char gMIDISYNC_CLK; +extern unsigned char gMIDISYNC_RUN; + +//------------------------------------------------------------- +// mbed Functions + +// TextLCD +TextLCD gLCD(p23, p24, p25, p26, p29, p30); // rs, e, d4-d7 + +// SPI +SPI gSPI(p11,p12,p13); +DigitalOut gCSA(p14); +DigitalOut gCSB(p22); + +// Sirial MIDI +Serial gMIDI(p9,p10); + +// AnalogIn +AnalogIn gAIN1(p15); // VR1 +AnalogIn gAIN2(p16); // VR2 +AnalogIn gAIN3(p17); // VR3 +AnalogIn gAIN4(p18); // VR4 +AnalogIn gAIN5(p19); // IN1 +AnalogIn gAIN6(p20); // IN2 + +// BEEP +PwmOut gBEEP(p21); + +// LED +DigitalOut gLED1(LED1); +DigitalOut gLED2(LED2); +DigitalOut gLED3(LED3); +DigitalOut gLED4(LED4); +BusOut gLEDS(LED1,LED2,LED3,LED4); + +// Ticker +Ticker gTICKER; + +//------------------------------------------------------------- +// main + +int main() { + + int i; + int pot[4],_pot[4]; + unsigned char rb; + unsigned char ch = 0; + unsigned char mode = 7; // for Intialize + unsigned char edit[4]; + int val[2][4] = { 0, 0, 0, 0, 50, 50, 50, 50 }; + char *wave[4] = { "TR","SQ","SW","--" }; + + // Initialize + gPtr_buf_in = gPtr_buf_out = 0; + for( i=0; i<4; i++) { + pot[i] = _pot[i] = 0; + edit[i] = 0; + gGLIDE[i] = 1.0 / expf(val[0][i]*656.0/PARAM_GLIDE); + gLFO_DP[i] = expf(val[1][i]*656.0/PARAM_DP); + gLFO_FORM[i] = LFO_WF_TRI; + gMIDI_CH[i] = i; + } + + for( i=0; i<16; i++) { // MIDI Data Buffers + gPlayNoteBuf[i] =24; + gGateBuf[i] = 0; + gPitchBendBuf[i] = 0x40; + gModWheelBuf[i] = 0; + } + + gSW = 1; // for Intialize + + CalcHzVTbl(); + InitKamui(); + + // loop + while(1) { + + // ring buffer empty? + if(gPtr_buf_in != gPtr_buf_out) { + + // get 1byte from ring buffer + gPtr_buf_out++; + gPtr_buf_out &= (BUFSIZE - 1); + rb = gRxBuf[gPtr_buf_out]; + MIDI_Parser(rb); + continue; + } + + // Read pot + pot[0] = gAIN1.read_u16(); + pot[1] = gAIN2.read_u16(); + pot[2] = gAIN3.read_u16(); + pot[3] = gAIN4.read_u16(); + + // change pot amount? + if(abs(pot[ch] - _pot[ch]) > 0x2000) edit[ch] = 1; + + if(edit[ch]) { + switch(mode) { + case 0: + gGLIDE[ch] = 1.0 / expf(pot[ch]/PARAM_GLIDE); + val[0][ch] = pot[ch] / 656; + break; + case 1: + gLFO_DP[ch] = expf(pot[ch]/PARAM_DP); + val[1][ch] = pot[ch] / 656; + break; + case 2: + gLFO_FORM[ch] = pot[ch] / 0x4000; + break; + case 3: + gMIDI_CH[ch] = pot[ch] / 0x1000; + break; + default: + break; + } + } + + // Push Mode SW + if(gSW & SW1) { + mode++; + mode &= 0x03; + for( i=0; i<4; i++) { + _pot[i] = pot[i]; + edit[i] = 0; + } + } + gSW = 0; + + // LCD Display + gLCD.locate( 0, 1 ); + switch(mode) { + case 0: + gLCD.printf("GLID %02d %02d %02d %02d", + val[0][0], val[0][1], val[0][2], val[0][3]); + break; + case 1: + gLCD.printf("FREQ %02d %02d %02d %02d", + val[1][0], val[1][1], val[1][2], val[1][3]); + break; + case 2: + gLCD.printf("FORM %s %s %s %s", + wave[gLFO_FORM[0]], wave[gLFO_FORM[1]], + wave[gLFO_FORM[2]], wave[gLFO_FORM[3]]); + break; + case 3: + gLCD.printf("MIDI %02d %02d %02d %02d", + gMIDI_CH[0]+1, gMIDI_CH[1]+1, + gMIDI_CH[2]+1, gMIDI_CH[3]+1); + break; + } + + ch++; + ch &= 0x03; + } +} + +//------------------------------------------------------------- +// Initialize KAMUI + +void InitKamui() +{ + // Init. Variables + for( int i=0; i<8; i++) { + gCV[i] = 0x8000; + } + gGATE = 0; + gSYNC = 0; + + gUpdateMode = UPDATE_MODE0; + + // Init. SPI + gCSA = _DISABLE; + gCSB = _DISABLE; + gSPI.format(8,0); + gSPI.frequency(SPI_RATE); + + // Init. Serial MIDI + gMIDI.baud(MIDI_RATE); + + // Ticker + gTICKER.attach_us(&UpdateCV, UPDATE_INTERVAL); + + // Beep + gBEEP.period(1.0/BEEP_FREQ); + gBEEP.write(0.5); + wait(0.2); + gBEEP.write(0.0); + + // Init Display + gLCD.locate( 0, 0 ); + // 123456789ABCDEF + gLCD.printf("MIDI-CV Example"); +} + +//------------------------------------------------------------- +// Update CV, GATE, SYNC + +void UpdateCV() +{ + unsigned char rcv,ch; + unsigned char ptn[] = { 0,1,6,7,2,3,6,7,4,5,6,7 }; + const int numptn = (sizeof ptn / sizeof ptn[0]) - 1; + static unsigned char cnt; + + // SET DAC + ch = ptn[cnt]; + if(gUpdateMode) ch |= 0x06; + +#ifdef AD5551 // 14bitDAC + gDAC.WORD = gCV[ch] >> 2; +#else + gDAC.WORD = gCV[ch]; +#endif + + gCSA = _ENABLE; + gSPI.write(gDAC.BYTE.H); + gSPI.write(gDAC.BYTE.L); + gCSA = _DISABLE; + + // GATE or SYNC OUT + if(cnt & 0x01) { + // GATE OUT + gCSB = _ENABLE; + rcv = gSPI.write(gGATE | MODE_GATE) & 0x0F; + gCSB = _DISABLE; + } + else { + // SYNC OUT + gCSB = _ENABLE; + rcv = gSPI.write(gSYNC | MODE_SYNC); + gCSB = _DISABLE; + } + + // SEL CV CHANNEL + gCSB = _ENABLE; + gSPI.write(ch); + gCSB = _DISABLE; + + cnt < numptn ? cnt++ : cnt = 0; + + gSW |= CheckSW(rcv); + RcvMIDI(); + DinSync(); + MidiCV(); +} + +//------------------------------------------------------------- +// Check SW + +unsigned char CheckSW(unsigned char c) { + + static unsigned char swbuf[2]; + static unsigned int cntsw; + unsigned char ret = 0; + + if(cntsw > SW_WATCH_INTERVAL) { + if(c &= 0x0F) { + if(!swbuf[1]) { + if( swbuf[0] == c) { + swbuf[1] = c; + ret = c; + } + else { + swbuf[0] = c; + } + } + } + else { + swbuf[1] = 0; + swbuf[0] = 0; + } + cntsw = 0; + } + cntsw++; + return ret; +} + +//------------------------------------------------------------- +// Receive MIDI Data & Store Ring Buffer + +void RcvMIDI() { + + if(!gMIDI.readable()) return; + + gPtr_buf_in++; + gPtr_buf_in &= (BUFSIZE - 1); + gRxBuf[gPtr_buf_in] = gMIDI.getc(); +} + +//------------------------------------------------------------- +// MIDI Data to CV, GATE + +void MidiCV() +{ + static unsigned char ch; + static unsigned short phase[4]; + static float cvf[4]; + int lfo,mod; + unsigned char midi_ch; + unsigned int cv; + unsigned int note; + + midi_ch = gMIDI_CH[ch]; + + note = gPlayNoteBuf[midi_ch]; + if( note < MINIMUMNOTE) note = MINIMUMNOTE; + note -= MINIMUMNOTE; + + // DDS Phase + phase[ch] += gLFO_DP[ch]; + + // LFO DDS Genelator + switch(gLFO_FORM[ch]) { + case LFO_WF_TRI: + if(phase[ch] < 32738) lfo = phase[ch] - 16384; + else lfo = (16383 + 32768) - phase[ch]; + break; + case LFO_WF_SQR: + if(phase[ch] < 32738) lfo = 32767; + else lfo = 0; + break; + case LFO_WF_SAW: + lfo = phase[ch] / 2 - 16384; + break; + default : + lfo = 0; + break; + } + + // Modulation amount + mod = lfo * gModWheelBuf[midi_ch] >> 7; + + // Calculate CV + cvf[ch] = ((float)(note << 8) - cvf[ch]) * gGLIDE[ch] + cvf[ch]; + cv = (unsigned int)cvf[ch] + (0x8000 - (0x0040 << 3)) + + (gPitchBendBuf[midi_ch] << 2) + mod; + if(cv > 0xFFFF) cv = 0xFFFF; + gCV[ch] = (unsigned short)cv; + gCV[ch+4] = OctVtoHzV(gCV[ch]); + + // GATE + gGateBuf[midi_ch] ? gGATE |= (1<<ch) : gGATE &= ~(1<<ch); + + ch++; + ch &= 0x03; +} + +//------------------------------------------------------------- +// Oct/V to Hz/V Converter + +void CalcHzVTbl() // Calc Conv. Table +{ + int i; + float v; + + for( i=0; i<3072; i++) { + v = 24576.0 * pow(2.0,(i/3072.0)); + gTblHzV[i] = (unsigned short)v; + } +} + +unsigned short OctVtoHzV( unsigned short vin) +{ + int oct,res; + unsigned short vout; + + if(vin > 0xE400) vin = 0xE400; // Maximum Note E8 Vin = 10.794V + if(vin < 0x6800) vin = 0x6800; // Minimum Note C-2 Vin = -2.000V + vin -= 0x6800; + + oct = vin / 0xC00; // 0xC00 : 3072 + res = vin % 0xC00; + + vout = ((unsigned short)gTblHzV[res] >> (10 - oct)) + 0x8000; + return vout; +} + +//------------------------------------------------------------- +// DIN SYNC Control + +void DinSync() +{ + static unsigned int cnt; + static unsigned int cnt24 = 10; + + if(gMIDISYNC_RUN) gSYNC |= (SYNC1RUN | SYNC2RUN); + else gSYNC &= ~(SYNC1RUN | SYNC2RUN); + + if(cnt >= SYNC_TURN_TIME) gSYNC &= ~(SYNC1CLK | SYNC2CLK); + + if(gMIDISYNC_CLK) { + gSYNC |= (SYNC1CLK | SYNC2CLK); + gMIDISYNC_CLK = 0; + cnt = 0; + cnt24++; + } + if(cnt24 >= 24) cnt24 = 0; + + gLED3 = gSYNC & SYNC1RUN ? 1 : 0; + gLED4 = cnt24 < 4 ? 1 : 0; + + cnt++; +} \ No newline at end of file
diff -r 000000000000 -r 25a282f1141a mbed.bld --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Sat May 05 11:35:41 2012 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/737756e0b479
diff -r 000000000000 -r 25a282f1141a midi_parser.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/midi_parser.c Sat May 05 11:35:41 2012 +0000 @@ -0,0 +1,259 @@ +//------------------------------------------------------------- +// KAMUI MIDI-CV Exapmle +// file : midi_parser.c +// Copyright (C) 2012 RJB RadioJunkBox +// Released under the MIT License: http://mbed.org/license/mit +//------------------------------------------------------------- + +#include "mbed.h" +#include "midi_parser.h" + +//------------------------------------------------------------- +// MIDI Parser + +void MIDI_Parser(unsigned char mididata) +{ + RxByte = mididata; + + if(MIDI_SystemMessage()) { + MIDI_ChannelMessage(); + } +} + +//------------------------------------------------------------- +// MIDI System Meassage + +int MIDI_SystemMessage(void) +{ + if(SysEx){ + if(RxByte == MIDI_EndSysEx){ + SysEx = FALSE; + } + } + else{ + if(RxByte < 0xF8){ + if(RxByte > 0x7F){ + if(RxByte == MIDI_StartSysEx){ + SysEx = TRUE; + } + else{ + MidiCh = RxByte & 0x0F; + } + PC = 0; + } + else{ + MByte[PC & 0x01] = RxByte; + } + return TRUE; + } + else { + MIDI_SystemRealtimeMessage(); + } + } + return FALSE; +} + +//------------------------------------------------------------- +// MIDI System Realtime Message + +void MIDI_SystemRealtimeMessage(void) +{ + switch(RxByte) { + case MIDI_TimingClock: + gMIDISYNC_CLK |= 0x01; + break; + case MIDI_Start: + gMIDISYNC_RUN = 0x01; + break; + case MIDI_Continue: + gMIDISYNC_RUN = 0x01; + break; + case MIDI_Stop: + gMIDISYNC_RUN = 0x00; + break; + case MIDI_ActiveSensing: + break; + case MIDI_SystemReset: + break; + } +} + +//------------------------------------------------------------- +// MIDI Channel Message + +void MIDI_ChannelMessage(void) +{ + switch(PC){ + case 0: + switch(RxByte & 0xF0){ + case MIDI_NoteOff: + PC = 2; + break; + case MIDI_NoteOn: + PC = 4; + break; + case MIDI_PolykeyPressure: + PC = 6; + break; + case MIDI_ProgramChange: + PC = 8; + break; + case MIDI_ControlChange: + PC = 10; + break; + case MIDI_ChannelPressure: + PC = 12; + break; + case MIDI_PitchBend: + PC = 14; + break; + } break; + + // Note OFF + case 2: + PC = 3; + break; + case 3: + PC = 2; + NoteOFF(); + break; + + // Note ON + case 4: + PC = 5; + break; + case 5: + PC = 4; + if( MByte[1] == 0){ + NoteOFF(); + } + else{ + NoteON(); + } + break; + + // Polyphonic Key Pressure + case 6: + PC = 7; + break; + case 7: + PC = 6; + break; + + // Program Change + case 8: + break; + + // Control Change + case 10: PC = 11; break; + case 11: + switch(MByte[0]) { + case MIDI_CC_Moduration: + gModWheelBuf[MidiCh] = MByte[1] >> 2; + break; + case MIDI_CC_DataEntry: + break; + case MIDI_CC_RPN_LSB: + break; + case MIDI_CC_RPN_MSB: + break; + case MIDI_MM_AllSoundOff: + break; + case MIDI_MM_ResetAllControl: + break; + case MIDI_MM_AllNoteOff: + break; + } + break; + + // Channel Pressure + case 12: + break; + + // Pitch Bend + case 14: + PC = 15; + break; + + case 15: + PC = 14; + gPitchBendBuf[MidiCh] = (MByte[1] << 1) | (MByte[0] >> 6); + break; + + default: + break; + } +} + +//------------------------------------------------------------- +// Note ON Message Processing + +void NoteON(void) +{ + unsigned char i; + unsigned char h = 0; // higheest note + + // ignore note if registed buffer + for(i = 0; i < NoteCnt[MidiCh]; i++) { + if(NoteBuf[MidiCh][i] == MByte[0]) { + return; + } + } + + // full note buffer? + if(NoteCnt[MidiCh] == MAX_NOTE_CNT) { + for(i = 0; i < (MAX_NOTE_CNT - 1); i++) { + NoteBuf[MidiCh][i] = NoteBuf[MidiCh][i+1]; + } + NoteBuf[MidiCh][MAX_NOTE_CNT - 1] = MByte[0]; + } + else { + NoteBuf[MidiCh][NoteCnt[MidiCh]] = MByte[0]; + NoteCnt[MidiCh]++; + } + + // set highest note + for(i = 0; i < NoteCnt[MidiCh]; i++) { + if(h < NoteBuf[MidiCh][i]) { + h = NoteBuf[MidiCh][i]; + } + } + gPlayNoteBuf[MidiCh] = h; + gGateBuf[MidiCh] = ON; +} + +//------------------------------------------------------------- +// Note OFF Message Processing + +void NoteOFF(void) +{ + unsigned char i; + unsigned char h = 0; // highest note + int flg = FALSE; + + // Delete Note If Registed Buffer + for(i = 0; i < NoteCnt[MidiCh]; i++) { + if(flg) { + NoteBuf[MidiCh][i-1] = NoteBuf[MidiCh][i]; + } + if(NoteBuf[MidiCh][i] == MByte[0]) { + flg = TRUE; + } + } + if(flg) NoteCnt[MidiCh]--; + + if(NoteCnt[MidiCh] == 0) { + // Empty Buffer then Gate OFF + gGateBuf[MidiCh] = OFF; + } + else { + // Highest Note + for(i = 0; i < NoteCnt[MidiCh]; i++) { + if( h < NoteBuf[MidiCh][i]) { + h = NoteBuf[MidiCh][i]; + } + } + gPlayNoteBuf[MidiCh] = h; + } +} +
diff -r 000000000000 -r 25a282f1141a midi_parser.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/midi_parser.h Sat May 05 11:35:41 2012 +0000 @@ -0,0 +1,80 @@ +//------------------------------------------------------------- +// KAMUI MIDI-CV Example +// file : midi_parser.h +// Copyright (C) 2012 RJB RadioJunkBox +// Released under the MIT License: http://mbed.org/license/mit +//------------------------------------------------------------- + +#ifndef MBED_MIDI_PARSER_H +#define MBED_MIDI_PARSER_H + +//------------------------------------------------------------- +// Define + +#define TRUE 1 +#define FALSE 0 +#define ON 1 +#define OFF 0 + +#define MAX_CH 16 +#define MAX_NOTE_CNT 16 + +// MIDI Massage +#define MIDI_NoteOff 0x80 +#define MIDI_NoteOn 0x90 +#define MIDI_PolykeyPressure 0xA0 +#define MIDI_ControlChange 0xB0 +#define MIDI_ProgramChange 0xC0 +#define MIDI_ChannelPressure 0xD0 +#define MIDI_PitchBend 0xE0 + +#define MIDI_StartSysEx 0xF0 +#define MIDI_TuneRequest 0xF6 +#define MIDI_EndSysEx 0xF7 + +#define MIDI_TimingClock 0xF8 +#define MIDI_Start 0xFA +#define MIDI_Continue 0xFB +#define MIDI_Stop 0xFC +#define MIDI_ActiveSensing 0xFE +#define MIDI_SystemReset 0xFF + +#define MIDI_CC_Moduration 0x01 +#define MIDI_CC_DataEntry 0x06 +#define MIDI_CC_RPN_LSB 0x64 +#define MIDI_CC_RPN_MSB 0x65 + +#define MIDI_MM_AllSoundOff 0x78 +#define MIDI_MM_ResetAllControl 0x79 +#define MIDI_MM_AllNoteOff 0x7B + +//------------------------------------------------------------- +// Global variables + +unsigned char gPlayNoteBuf[MAX_CH]; +unsigned char gGateBuf[MAX_CH]; +unsigned char gPitchBendBuf[MAX_CH]; +unsigned char gModWheelBuf[MAX_CH]; +unsigned char gMIDISYNC_CLK; +unsigned char gMIDISYNC_RUN; + +unsigned char NoteCnt[MAX_CH]; +unsigned char NoteBuf[MAX_CH][MAX_NOTE_CNT]; + +unsigned char RxByte; +unsigned char SysEx; +unsigned char MidiCh; +unsigned char PC; +unsigned char MByte[2]; + +//------------------------------------------------------------- +// Functions + +void MIDI_Parser(unsigned char); +int MIDI_SystemMessage(void); +void MIDI_SystemRealtimeMessage(void); +void MIDI_ChannelMessage(void); +void NoteON(void); +void NoteOFF(void); + +#endif