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

Dependencies:   TextLCD mbed

main.cpp

Committer:
radiojunkbox
Date:
2012-05-11
Revision:
0:3b4e3e2ec6a5

File content as of revision 0:3b4e3e2ec6a5:

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