KAMUI MIDI-CV Example

Dependencies:   TextLCD mbed

main.cpp

Committer:
radiojunkbox
Date:
2012-05-05
Revision:
0:25a282f1141a

File content as of revision 0:25a282f1141a:

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