KAMUI USB HOST MIDI-CV Example based on Peter Barrett's BlueUSB

Dependencies:   TextLCD mbed

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++;
+}