Radio Junk Box
/
KAMUI_USBHOST_MIDI-CV_Example
KAMUI USB HOST MIDI-CV Example based on Peter Barrett's BlueUSB
Diff: main.cpp
- Revision:
- 0:3b4e3e2ec6a5
diff -r 000000000000 -r 3b4e3e2ec6a5 main.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri May 11 15:31:59 2012 +0000 @@ -0,0 +1,646 @@ +//------------------------------------------------------------- +// KAMUI MIDI-CV Exapmple +// Copyright (C) 2012 RJB RadioJunkBox +// Released under the MIT License: http://mbed.org/license/mit +//------------------------------------------------------------- + +/* +Copyright (c) 2010 Peter Barrett + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "mbed.h" +#include "TextLCD.h" +#include "USBHost.h" +#include "Utils.h" +#include "FATFileSystem.h" +#include <stdlib.h> +#include <math.h> + +int MassStorage_ReadCapacity(int device, u32* blockCount, u32* blockSize); +int MassStorage_Read(int device, u32 blockAddr, u32 blockCount, u8* dst, u32 blockSize); +int MassStorage_Write(int device, u32 blockAddr, u32 blockCount, u8* dst, u32 blockSize); + +class USBFileSystem : public FATFileSystem +{ + int _device; + u32 _blockSize; + u32 _blockCount; + +public: + USBFileSystem() : FATFileSystem("usb"),_device(0),_blockSize(0),_blockCount(0) + { + } + + void SetDevice(int device) + { + _device = device; + } + + virtual int disk_initialize() + { + return MassStorage_ReadCapacity(_device,&_blockCount,&_blockSize); + } + + virtual int disk_write(const char *buffer, int block_number) + { + return MassStorage_Write(_device,block_number,1,(u8*)buffer,_blockSize); + } + + virtual int disk_read(char *buffer, int block_number) + { + return MassStorage_Read(_device,block_number,1,(u8*)buffer,_blockSize); + } + + virtual int disk_sectors() + { + return _blockCount; + } +}; + +void DumpFS(int depth, int count) +{ + DIR *d = opendir("/usb"); + if (!d) + { + printf("USB file system borked\n"); + return; + } + + printf("\nDumping root dir\n"); + struct dirent *p; + for(;;) + { + p = readdir(d); + if (!p) + break; + int len = sizeof( dirent); + printf("%s %d\n", p->d_name, len); + } + closedir(d); +} + +int OnDiskInsert(int device) +{ + USBFileSystem fs; + fs.SetDevice(device); + DumpFS(0,0); + return 0; +} + +/* + Simple test shell to exercise mouse,keyboard,mass storage and hubs. + Add 2 15k pulldown resistors between D+/D- and ground, attach a usb socket and have at it. +*/ + +Serial pc(USBTX, USBRX); +int GetConsoleChar() +{ + if (!pc.readable()) + return -1; + char c = pc.getc(); + pc.putc(c); // echo + return c; +} + +//------------------------------------------------------------- +// 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 + +#define MENULOOP_INTERVAL 25000 // 25ms + +//------------------------------------------------------------- +// Functions + +void MenuLoop(void); +void InitKamui(void); +void UpdateCV(void); +unsigned char CheckSW(unsigned char); + +void RcvMIDI(void); +void GetMIDI(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; + +int pot[4],_pot[4]; +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","--" }; + +//------------------------------------------------------------- +// 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; +Ticker gTICKER2; + +// Implemented in TestShell.cpp +void TestShell(); +void USBInit(); + +//------------------------------------------------------------- +// main + +int main() { + + int i; + + USBInit(); + + // 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(); + gTICKER2.attach_us(&MenuLoop, MENULOOP_INTERVAL); + TestShell(); +} + +//------------------------------------------------------------- +// Menu LOOP +void MenuLoop() +{ + int i; + static unsigned char ch; + + // 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("USBHOST MIDI-CV"); +} + +//------------------------------------------------------------- +// 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(); + GetMIDI(); + 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(); +} + +//------------------------------------------------------------- +// Get MIDI Data from Ring Buffer + +void GetMIDI() { + unsigned char rb; + + // 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); + gMIDI.putc(rb); + } +} + +//------------------------------------------------------------- +// 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] >> 8; + + // 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++; +}