OSC-CV Converter

Dependencies:   Bonjour OSCReceiver TextLCD mbed mbed-rpc BurstSPI DebouncedInterrupt FastIO MIDI OSC OSCtoCV ClockControl

OSC to CV Converter

http://gtbts.tumblr.com/post/125663817741/osc-to-cv-converter-ver2-mbed-osctocv

/media/uploads/casiotone401/tumblr_nsg7y4pkfg1qlle9fo1_540.png

main.cpp

Committer:
casiotone401
Date:
2014-10-16
Revision:
14:977f3c5a4b4e
Parent:
13:3f42e451a8d3
Child:
15:3e4bc47d6a39

File content as of revision 14:977f3c5a4b4e:

//-------------------------------------------------------------
//                  TI DAC8568  OSC-CV Converter
//
//   DAC8568 16bit Octal DAC http://www.ti.com/product/dac8568
//
//   referred to
//   xshige's OSCReceiver
//   http://mbed.org/users/xshige/programs/OSCReceiver/
//   radiojunkbox's OSC-CV_Example
//   http://mbed.org/users/radiojunkbox/code/KAMUI_OSC-CV_Example/
//   Robin Price's Homebrew midi-cv box
//   http://crx091081gb.net/?p=69
//   Masahiro Hattori's TextLCD Module Functions
//   http://www.eleclabo.com/denshi/device/lcd1602/gcram.html
//   Dirk-Willem van Gulik's BonjourLib
//   http://mbed.org/users/dirkx/code/BonjourLib/file/bb6472f455e8/services/mDNS
//
// Released under the MIT License: http://mbed.org/license/mit
//-------------------------------------------------------------

#pragma O3
#pragma Otime

#include "mbed.h"
#include "TextLCD.h"       //edit "writeCommand" "writeData" protected -> public
#include "EthernetNetIf.h"
#include "HTTPServer.h"
#include "mDNSResponder.h" // mDNS response to announce oneselve
#include "UDPSocket.h"
#include "OSCReceiver.h"
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

//-------------------------------------------------------------
// Define

#define MODE_Calb           0        // Calibration (for VCO Tuning)
#define MODE_LIN            1        // Linear LinearCV
#define MODE_SEQ            2        // Mode Shift

#define MODE_NUM            3        // Modes

#define Lin                 0        // Linear LinearCV
#define Chr                 1        // Chromatic
#define Maj                 2        // Major
#define M7                  3        // Major7
#define Min7                4        // Minor7
#define Dor                 5        // Dorian
#define Min                 6        // Minor
#define S5th                7        // 5th
#define Wht                 8        // Wholetone

#define QUAN_RES1           115      // Quantize voltage Steps
#define QUAN_RES2           67
#define QUAN_RES3           39
#define QUAN_RES4           48
#define QUAN_RES5           67
#define QUAN_RES6           67  
#define QUAN_RES7           17
#define QUAN_RES8           57

#define SPI_RATE            20000000 // 20Mbps SPI Clock
#define SCALING_N           38400.0
#define INPUT_PORT          12345    // Input Port Number

#define POLLING_INTERVAL    20       // Polling Interval (us)

//-------------------------------------------------------------
// DAC8568 Control Bits (See datasheet)

#define WRITE               0x00
#define UPDATE              0x01
#define WRITE_UPDATE_ALL    0x02     // LDAC Write to Selected Update All
#define WRITE_UPDATE_N      0x03     // LDAC Write to Selected Update Respective
#define POWER               0x04
#define CLR                 0x05     // Clear Code Register
#define WRITE_LDAC_REG      0x06
#define RESET               0x07     // Software Reset DAC8568
#define SETUP_INTERNAL_REF  0x08

//------------------------------------------------------------- 

#define _DISABLE            0
#define _ENABLE             1

#define GATE1               0
#define GATE2               1
#define GATE3               2
#define GATE4               3
#define GATEALL             4

//------------------------------------------------------------- 
// Beats (Note values)

#define N1ST                1   // whole
#define N2ND                2   // harf
#define N4TH                4   // quarter
#define N8TH                8
#define N16TH               16
#define N32TH               32
#define N64TH               64
#define NDOT2               3   // dotted
#define NDOT4               7
#define NDOT8               9
#define NDOT16              11
#define NDOT32              13
#define TRIP2               15  // triplets
#define TRIP4               17
#define TRIP8               19
#define TRIP16              21
#define TRIP32              23
#define NRESET              0   // Gate Reset

//-------------------------------------------------------------
// Functions

inline void NetPoll(void);
void InitOSCCV(void);
inline void UpdateCV(int, int, const unsigned int*);
inline void CalibCV(void);
inline void SetSCV(void);
inline void SeqCV(int);
inline void Seq(void);
inline int UpdateGate(int, int, int, int, bool);
inline void UpdateSync(int, int, bool);
void CheckModeSW(void);
inline void CVMeter(int, const unsigned int*);
void LCD();
void WriteCustomChar(unsigned char, unsigned char*);
int  SetupEthNetIf(void);
inline size_t strlength(const char *);
inline void onUDPSocketEvent(UDPSocketEvent);

//-------------------------------------------------------------
// Silentway Calibration Data Mapping
// http://www.expert-sleepers.co.uk/silentway.html

// Chromatic Scale
const float calibMap1[QUAN_RES1] = {
0.00331362,   0.01097947,   0.01864531,   0.02631116,   0.03397701, 
0.04164285,   0.04930869,   0.05697454,   0.06464039,   0.07230624, 
0.07997208,   0.08763793,   0.09530377,   0.10296962,   0.11063547, 
0.11830132,   0.12590303,   0.13306051,   0.14021800,   0.14737549, 
0.15453300,   0.16169049,   0.16884798,   0.17600547,   0.18316296, 
0.19032045,   0.19747795,   0.20463544,   0.21179293,   0.21895042, 
0.22610791,   0.23326540,   0.24042290,   0.24758039,   0.25476459, 
0.26196238,   0.26916021,   0.27635801,   0.28355584,   0.29075363, 
0.29795146,   0.30514926,   0.31234708,   0.31954488,   0.32674271, 
0.33394051,   0.34113833,   0.34833613,   0.35553396,   0.36273175, 
0.36992958,   0.37713069,   0.38433966,   0.39154866,   0.39875764, 
0.40596664,   0.41317561,   0.42038459,   0.42759359,   0.43480256, 
0.44201156,   0.44922054,   0.45642951,   0.46363851,   0.47084749, 
0.47805649,   0.48526546,   0.49247447,   0.49968344,   0.50694764, 
0.51421440,   0.52148110,   0.52874786,   0.53601462,   0.54328132, 
0.55054808,   0.55781484,   0.56508154,   0.57234830,   0.57961506, 
0.58688176,   0.59414852,   0.60141528,   0.60868198,   0.61594874, 
0.62321550,   0.63052768,   0.63785475,   0.64518178,   0.65250880, 
0.65983582,   0.66716284,   0.67448992,   0.68181694,   0.68914396, 
0.69647098,   0.70379800,   0.71112502,   0.71845210,   0.72577912, 
0.73310614,   0.74043316,   0.74776018,   0.75861782,   0.77102989, 
0.78344196,   0.79585397,   0.80826604,   0.82067811,   0.83309019, 
0.84550226,   0.85791427,   0.87032634,   0.90296346,   0.94781560
};

//  Major Scale
const float calibMap2[QUAN_RES2] = {
0.01097947,   0.02631116,   0.03397701,   0.04930869,   0.06464039,
0.07997208,   0.08763793,   0.10296962,   0.11830132,   0.12590303,
0.14021800,   0.15453300,   0.16884798,   0.17600547,   0.19032045,
0.20463544,   0.21179293,   0.22610791,   0.24042290,   0.25476459,
0.26196238,   0.27635801,   0.29075363,   0.29795146,   0.31234708,
0.32674271,   0.34113833,   0.34833613,   0.36273175,   0.37713069,
0.38433966,   0.39875764,   0.41317561,   0.42759359,   0.43480256,
0.44922054,   0.46363851,   0.47084749,   0.48526546,   0.49968344,
0.51421440,   0.52148110,   0.53601462,   0.55054808,   0.55781484,
0.57234830,   0.58688176,   0.60141528,   0.60868198,   0.62321550,
0.63785475,   0.64518178,   0.65983582,   0.67448992,   0.68914396,
0.69647098,   0.71112502,   0.72577912,   0.73310614,   0.74776018,
0.77102989,   0.79585397,   0.80826604,   0.83309019,   0.85791427,
0.87032634,   0.94781560
};

//  M7(9)
const float calibMap3[QUAN_RES3] = {
0.02631116,   0.04930869,   0.07997208,   0.08763793,   0.11830132,
0.14021800,   0.16884798,   0.17600547,   0.20463544,   0.22610791,
0.25476459,   0.26196238,   0.29075363,   0.31234708,   0.34113833,
0.34833613,   0.37713069,   0.39875764,   0.42759359,   0.43480256,
0.44922054,   0.46363851,   0.48526546,   0.51421440,   0.52148110,
0.55054808,   0.57234830,   0.60141528,   0.60868198,   0.62321550,
0.63785475,   0.65983582,   0.68914396,   0.69647098,   0.72577912,
0.74776018,   0.79585397,   0.85791427,   0.94781560
};

//  m7(9)
const float calibMap4[QUAN_RES4] = {
0.01097947,   0.01864531,   0.03397701,   0.04930869,   0.07230624,
0.08763793,   0.11063547,   0.14021800,   0.16169049,   0.17600547,
0.19747795,   0.22610791,   0.24042290,   0.24758039,   0.26196238,
0.29075363,   0.31234708,   0.33394051,   0.34833613,   0.36273175,
0.36992958,   0.38433966,   0.39875764,   0.41317561,   0.42038459,
0.43480256,   0.45642951,   0.48526546,   0.50694764,   0.52148110,
0.53601462,   0.55781484,   0.57234830,   0.59414852,   0.62321550,
0.63052768,   0.64518178,   0.65983582,   0.68181694,   0.69647098,
0.71845210,   0.74776018,   0.77102989,   0.78344196,   0.80826604,
0.83309019,   0.84550226,   0.94781560
};

//  Dorian Scale
const float calibMap5[QUAN_RES5] = {
0.01097947,   0.02631116,   0.04164285,    0.04930869,   0.06464039,
0.07997208,   0.08763793,   0.10296962,    0.11830132,   0.13306051,
0.14021800,   0.15453300,   0.16884798,    0.17600547,   0.19032045,
0.20463544,   0.21895042,   0.22610791,    0.24042290,   0.25476459,
0.26196238,   0.27635801,   0.29075363,    0.30514926,   0.31234708,
0.32674271,   0.34113833,   0.34833613,    0.36273175,   0.37713069,
0.39154866,   0.39875764,   0.41317561,    0.42759359,   0.43480256,
0.44922054,   0.46363851,   0.47805649,    0.48526546,   0.49968344,
0.51421440,   0.52148110,   0.53601462,    0.55054808,   0.56508154,
0.57234830,   0.58688176,   0.60141528,    0.60868198,   0.62321550,
0.63785475,   0.65250880,   0.65983582,    0.67448992,   0.68914396,
0.69647098,   0.71112502,   0.72577912,    0.74043316,   0.74776018,
0.77102989,   0.79585397,   0.80826604,    0.83309019,   0.85791427,
0.90296346,   0.94781560
};

//  Minor Scale
const float calibMap6[QUAN_RES6] = {
0.01097947,   0.01864531,   0.03397701,   0.04930869,   0.05697454,
0.07230624,   0.08763793,   0.10296962,   0.11063547,   0.12590303,
0.14021800,   0.14737549,   0.16169049,   0.17600547,   0.19032045,
0.19747795,   0.21179293,   0.22610791,   0.23326540,   0.24758039,
0.26196238,   0.27635801,   0.28355584,   0.29795146,   0.31234708,
0.31954488,   0.33394051,   0.34833613,   0.36273175,   0.36992958,
0.38433966,   0.39875764,   0.40596664,   0.42038459,   0.43480256,
0.44922054,   0.45642951,   0.47084749,   0.48526546,   0.49247447,
0.50694764,   0.52148110,   0.53601462,   0.54328132,   0.55781484,
0.57234830,   0.57961506,   0.59414852,   0.60868198,   0.62321550,
0.63052768,   0.64518178,   0.65983582,   0.66716284,   0.68181694,
0.69647098,   0.71112502,   0.71845210,   0.73310614,   0.74776018,
0.75861782,   0.78344196,   0.80826604,   0.83309019,   0.84550226,
0.87032634,   0.94781560
};

//  5th
const float calibMap7[QUAN_RES7] = {
0.01097947,  0.06464039,   0.11830132,    0.16884798,    0.21895042,
0.26916021,  0.31954488,   0.36992958,    0.42038459,    0.47084749,
0.52148110,  0.57234830,   0.62321550,    0.67448992,    0.72577912,
0.79585397,  0.90296346
};

//  Whole tone
const float calibMap8[QUAN_RES8] = {
0.01097947,   0.02631116,   0.04164285,   0.05697454,    0.07230624,
0.08763793,   0.10296962,   0.11830132,   0.13306051,    0.14737549,
0.16169049,   0.17600547,   0.19032045,   0.20463544,    0.21895042,
0.23326540,   0.24758039,   0.26196238,   0.27635801,    0.29075363,
0.30514926,   0.31954488,   0.33394051,   0.34833613,    0.36273175,
0.37713069,   0.39154866,   0.40596664,   0.42038459,    0.43480256,
0.44922054,   0.46363851,   0.47805649,   0.49247447,    0.50694764,
0.52148110,   0.53601462,   0.55054808,   0.56508154,    0.57961506,
0.59414852,   0.60868198,   0.62321550,   0.63785475,    0.65250880,
0.66716284,   0.68181694,   0.69647098,   0.71112502,    0.72577912,
0.74043316,   0.75861782,   0.78344196,   0.80826604,    0.83309019,
0.85791427,   0.90296346
};

//-------------------------------------------------------------
// CV Meter Custom Character

unsigned char str1[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F};
unsigned char str2[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F};
unsigned char str3[8] = {0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F};
unsigned char str4[8] = {0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F,0x1F};
unsigned char str5[8] = {0x00,0x00,0x00,0x1F,0x1F,0x1F,0x1F,0x1F};
unsigned char str6[8] = {0x00,0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F};
unsigned char str7[8] = {0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F};
unsigned char str8[8] = {0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F};

//-------------------------------------------------------------
// Global Variables

float   gOSC_cv[8];
float   gSeq_cv1[8], gSeq_cv2[8];
float   gGlide;
int     gMode;

// Variables for Control

float gCtrl[6];
bool gCtrlSW[6] = {false};

//-------------------------------------------------------------
// mbed Functions

TextLCD gLCD(p9, p10, p11, p12, p13, p14); // rs, e, d4-d7

SPI         gSPI(p5,p6,p7);     // SPI (p6 unconnected)
DigitalOut  gSYNCMODE(p15);     // SYNC DAC8568
DigitalOut  gLDAC(p16);         // LDAC DAC8568

DigitalOut  gGATES[4] = {p23, p24, p24, p25};   // GateOut
DigitalOut  gLEDS[4] = {p18, p19, p20, p21};    // LED
DigitalOut  gCLOCKOUT(p26);                     // ClockOut

AnalogIn    gAIN(p17);  // Glide Potentiometer
InterruptIn gSW(p30);   // Mode SW

Timer       gTimer;     // Timer
Ticker      gPoller;    // Ticker Polling

// Ethernet
EthernetNetIf   gEth;
UDPSocket       gUdp;

//-------------------------------------------------------------
// main
 
int main() 
{ 
    float pot, _pot;
    
//Clock Up 110Mhz -------------------------------------------------------------
    LPC_SC->PLL0CON   = 0x00;             /* PLL0 Disable                    */
    LPC_SC->PLL0FEED  = 0xAA;
    LPC_SC->PLL0FEED  = 0x55;
 
    LPC_SC->CCLKCFG   = 0x00000003;       /* Select Clock Divisor = 4        */
    LPC_SC->PLL0CFG   = 0x00020037;       /* configure PLL0                  */
    LPC_SC->PLL0FEED  = 0xAA;             /* divide by 3 then multiply by 50 */
    LPC_SC->PLL0FEED  = 0x55;             /* PLL0 frequency = 400,000,000    */
 
    LPC_SC->PLL0CON   = 0x01;             /* PLL0 Enable                     */
    LPC_SC->PLL0FEED  = 0xAA;
    LPC_SC->PLL0FEED  = 0x55;
    while (!(LPC_SC->PLL0STAT & (1<<26)));/* Wait for PLOCK0                 */
 
    LPC_SC->PLL0CON   = 0x03;             /* PLL0 Enable & Connect           */
    LPC_SC->PLL0FEED  = 0xAA;
    LPC_SC->PLL0FEED  = 0x55;
    while (!(LPC_SC->PLL0STAT & ((1<<25) | (1<<24))));/* Wait for PLLC0_STAT & PLLE0_STAT */
    
    SystemCoreClockUpdate();
//-----------------------------------------------------------------------------
    
    if(SetupEthNetIf() == -1)
    {
        for(int i = 0; i < 4; i++) 
        {
            gLEDS[i] = 1;
            wait(0.25);
        }
        
        return -1;
    }
    
// mdns (Bonjour)
    HTTPServer svr;
    mDNSResponder mdns;
    
    svr.addHandler<SimpleHandler>("/");
    svr.bind(INPUT_PORT);
    IpAddr ip = gEth.getIp();
    mdns.announce(ip, "OSCtoCV", "_osc._udp", INPUT_PORT, "mbed(OSCtoCV)", (char *[]) {"path=/",NULL});
    
    InitOSCCV();
    
    _pot = pot = gMode = 0;
    gGlide = gCtrl[3] = gAIN.read();
        
    LCD();
    gLCD.locate( 0, 1 );
    gLCD.printf("12345678 G>>%3.2f", gGlide);
    
// loop
    while(1) 
    {
        pot = gAIN.read();  // Glide Value
        
        if(pot == 0) 
        {
            if(abs(gCtrl[3] - _pot) > 0.01f)
            {
                _pot = gGlide = gCtrl[3];
            
                gLCD.locate( 0, 1 );
                gLCD.printf("12345678 G>>%3.2f", gGlide);
            }
        
        } else if (abs(pot - _pot) > 0.01f) {
            
            gGlide = _pot = gAIN.read();
            
            gLCD.locate( 0, 1 );
            gLCD.printf("12345678 G>>%3.2f", gGlide);
        }
        
        switch(gMode)
        {               
            case MODE_LIN:
            
                SetSCV();
                break;
                
            case MODE_SEQ:
        
                Seq();
                break;
                
            default:
                
                CalibCV();
                break;
        }
    }
}

//-------------------------------------------------------------
// Ethernet Polling

inline void NetPoll()
{
    Net::poll();
}

//-------------------------------------------------------------
// Initialize OSC-CV

void InitOSCCV()
{
// write custom char LCD CGRAM
    WriteCustomChar(0x00, str1);
    WriteCustomChar(0x01, str2);
    WriteCustomChar(0x02, str3);
    WriteCustomChar(0x03, str4);
    WriteCustomChar(0x04, str5);
    WriteCustomChar(0x05, str6);
    WriteCustomChar(0x06, str7);
    WriteCustomChar(0x07, str8);
    
// Init. SPI
    gLDAC = _ENABLE;
    gSPI.format(8,1);           // Data word length 8bit, Mode=1
    gSPI.frequency(SPI_RATE);
    
    UpdateCV(CLR, 0, 0);        // Ignore CLR Pin
    
    gSW.mode(PullUp);           // Use internal pullup for ModeSW
    wait(.001);
    
    gSW.rise(&CheckModeSW);     // InterruptIn rising edge(ModeSW)
    gPoller.attach_us(&NetPoll, POLLING_INTERVAL);  // Ticker Polling
    
    wait(0.2);
}

//-------------------------------------------------------------
// SPI Transfer
// DAC8568 data word length 32bit (8bit shift out)

inline void UpdateCV(int control, int address, const unsigned int *data)
{
    __disable_irq();
    
    switch(control)
    {
        case WRITE_UPDATE_N:

            gSYNCMODE = _DISABLE;
            gSPI.write(00000000|control);            // padding at beginning of byte and control bits
            gSPI.write(address << 4 | *data >> 12);  // address(ch) bits
            gSPI.write((*data << 4) >> 8);           // middle 8 bits of data
            gSPI.write((*data << 12) >> 8 | 00001111);
            gSYNCMODE = _ENABLE;
            gLDAC = _DISABLE;
            gLDAC = _ENABLE;
            break;

        case RESET:

            gSYNCMODE = _DISABLE;
            gSPI.write(00000111);   // Software RESET
            gSPI.write(00000000);
            gSPI.write(00000000);
            gSPI.write(00000000);
            gSYNCMODE = _ENABLE;
            break;

        case CLR:
        
            gSYNCMODE = _DISABLE;
            gSPI.write(00000101);   // CLR Register
            gSPI.write(00000000);
            gSPI.write(00000000);
            gSPI.write(00000011);   // Ignore CLR Pin
            gSYNCMODE = _ENABLE;
            break;
    }
    
    __enable_irq();
}

//-------------------------------------------------------------
// Calibrate Mode

inline void CalibCV()
{
    static int ch;
    unsigned int cv;

    switch(gMode) 
        {
            case MODE_Calb:
                
                cv = (unsigned int)((calibMap1[68] + 0.0007) * SCALING_N);    // A880.0Hz
                
                gGATES[0] = gGATES[1] = gGATES[2] = gGATES[3] = 1;
                gLEDS[0] = gLEDS[1] = gLEDS[2] = gLEDS[3] = 1;
                
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
        }

        CVMeter(ch, &cv);
        
        ch++;
        ch &= 0x07;
}

//-------------------------------------------------------------
// Calculate CV

inline void SetSCV()
{
    static int ch, quan, mode, mcount;
    static float glidecv[8];
    unsigned int cv;
    static float qcv;

    mode = (gCtrl[1] * 8);

    switch(mode) 
        {
            case Lin:
                
                glidecv[ch] = glidecv[ch] * gGlide + gOSC_cv[ch] * (1.0f - gGlide);
                cv = (unsigned int)glidecv[ch];
                    
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
                    
            case Chr:
            
                quan = (40616 / QUAN_RES1);
                qcv = calibMap1[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
                
            case Maj:
                
                quan = (40616 / QUAN_RES2);
                qcv = calibMap2[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
            
            case M7:
            
                quan = (40616 / QUAN_RES3);
                qcv = calibMap3[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
                
            case Min7:
                
                quan = (40616 / QUAN_RES4);
                qcv = calibMap4[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
                    
            case Dor:
                
                quan = (40616 / QUAN_RES5);
                qcv = calibMap5[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
        
            case Min:
                
                quan = (40616 / QUAN_RES6);
                qcv = calibMap6[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
                
            case S5th:
                
                quan = (40616 / QUAN_RES7);
                qcv = calibMap7[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
                
            case Wht:
                
                quan = (40616 / QUAN_RES8);
                qcv = calibMap8[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv);
                break;
        }

        if(mcount % 16 == 0) 
        {
            CVMeter(ch, &cv);
        }

        ch++;
        
        if(ch &= 0x07)
        {
            mcount ++;
            mcount &= 0x3F;
        }
        
}

//-------------------------------------------------------------
// Sequence & Shift Out CV

inline void SeqCV(int shift)
{
    int i, j;
    static int ch, quan, mode;
    static float glidecv[8], shiftcv[8];
    unsigned int cv;
    static float qcv;
        
    mode = (gCtrl[1] * 8);  // Sequencer Quantize Mode (gCtrl[1])
    
    switch(mode) 
        {
            case Lin:
            
                if(ch < 8)
                {
                    glidecv[0] = glidecv[0] * gGlide + gSeq_cv1[ch] * (1.0f - gGlide);
                            
                } else {
                        
                    glidecv[0] = glidecv[0] * gGlide + gSeq_cv2[ch-8] * (1.0f - gGlide);
                }
                
                cv = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv);
                break;
                    
            case Chr:
            
                quan = 40616 / QUAN_RES1;
                
                if(ch < 8)
                {
                    qcv = calibMap1[(unsigned int)(gSeq_cv1[ch] / quan)];
                            
                } else {
                        
                    qcv = calibMap1[(unsigned int)(gSeq_cv2[ch-8] / quan)];
                }
                    
                glidecv[0] = glidecv[0] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv);
                break;
                
            case Maj:
                
                quan = 40616 / QUAN_RES2;
                
                if(ch < 8)
                {
                    qcv = calibMap2[(unsigned int)(gSeq_cv1[ch] / quan)];
                            
                } else {
                        
                    qcv = calibMap2[(unsigned int)(gSeq_cv2[ch-8] / quan)];
                }
                    
                glidecv[0] = glidecv[0] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv);
                break;
            
            case M7:
                
                quan = 40616 / QUAN_RES3;
                
                if(ch < 8)
                {
                    qcv = calibMap3[(unsigned int)(gSeq_cv1[ch] / quan)];
                            
                } else {
                        
                    qcv = calibMap3[(unsigned int)(gSeq_cv2[ch-8] / quan)];
                }
                    
                glidecv[0] = glidecv[0] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv);
                break;
                    
            case Min7:
                
                quan = 40616 / QUAN_RES4;
                
                if(ch < 8)
                {
                    qcv = calibMap4[(unsigned int)(gSeq_cv1[ch] / quan)];
                            
                } else {
                        
                    qcv = calibMap4[(unsigned int)(gSeq_cv2[ch-8] / quan)];
                }
                
                glidecv[0] = glidecv[0] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv);
                break;
                            
            case Dor:
                
                quan = 40616 / QUAN_RES5;
                
                if(ch < 8)
                {
                    qcv = calibMap5[(unsigned int)(gSeq_cv1[ch] / quan)];
                            
                } else {
                        
                    qcv = calibMap5[(unsigned int)(gSeq_cv2[ch-8] / quan)];
                }
                
                glidecv[0] = glidecv[0] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv);
                break;
                
            case Min:
                
                quan = 40616 / QUAN_RES6;
                
                if(ch < 8)
                {
                    qcv = calibMap6[(unsigned int)(gSeq_cv1[ch] / quan)];
                            
                } else {
                        
                    qcv = calibMap6[(unsigned int)(gSeq_cv2[ch-8] / quan)];
                }
                
                glidecv[0] = glidecv[0] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv);
                break;
            
            case S5th:
                
                quan = 40616 / QUAN_RES7;
                
                if(ch < 8)
                {
                    qcv = calibMap7[(unsigned int)(gSeq_cv1[ch] / quan)];
                            
                } else {
                        
                    qcv = calibMap7[(unsigned int)(gSeq_cv2[ch-8] / quan)];
                }
                
                glidecv[0] = glidecv[0] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv);
                break;
                
            case Wht:
                
                quan = 40616 / QUAN_RES8;
                
                if(ch < 8)
                {
                    qcv = calibMap8[(unsigned int)(gSeq_cv1[ch] / quan)];
                            
                } else {
                        
                    qcv = calibMap8[(unsigned int)(gSeq_cv2[ch-8] / quan)];
                }
                    
                glidecv[0] = glidecv[0] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                cv = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv);
                break;
        }
            
        for(i = 1; i < 8; ++i)
        {
            glidecv[i] = glidecv[i] * gGlide + shiftcv[i] * (1.0f - gGlide);
            cv = (unsigned int)glidecv[i];
                
            UpdateCV(WRITE_UPDATE_N, i, &cv);
        }
        
        if(shift == 1)                  // GATE1
        {
            for(j = 1; j < 8; ++j)      // Shift ch2~8
            {
                shiftcv[j] = glidecv[j-1];
            }
        
            ch++;
            ch &= 0x0F;
        }
        
     
        if(ch < 8)
        {
            CVMeter(ch, &cv);
                            
        } else {
                        
            CVMeter((ch-8), &cv);
        }

}

//-------------------------------------------------------------
// Sequencer Mode

inline void Seq()
{
    static int bpm, _bpm;
    
    bpm = (gCtrl[0] * 300 + 10);                    // Set BPM (gCtrl[0])
            
    if(abs(bpm - _bpm) > 1)
    {
        UpdateGate(bpm, NRESET, GATEALL, 3, false); // Reset (if bpm change)
        _bpm = bpm;
                    
    } else if (gCtrlSW[0]) {                        // Stop (gCtrlSW[0])
                        
        bpm = 0;
    }
            
    if(!gCtrlSW[2] && !gCtrlSW[3])                  // Sequencer Mode1
    {
        SeqCV((UpdateGate(bpm, N16TH, GATE1, 3, false)));   // Shift Timming 16th note
        UpdateGate(bpm, N8TH, GATE2, 3, 0);
        UpdateGate(bpm, NDOT8, GATE3, 3, 0);
        UpdateGate(bpm, TRIP4, GATE4, 3, 0);            
    }
    
}

//-------------------------------------------------------------
// Check SW

void CheckModeSW()
{   
    wait(0.05);
    
    if(gMode < MODE_NUM - 1) 
    {   
        gMode++;
            
    } else {
                
        gMode = 0;
    }
    
    gCLOCKOUT = gGATES[0] = gGATES[1] = gGATES[2] = gGATES[3] = 0;
    gLEDS[0] = gLEDS[1] = gLEDS[2] = gLEDS[3] = 0;
    
    if(gMode == MODE_SEQ)
    {
        gTimer.start();     // Sequencer Timer Start
        
    } else {
            
        gTimer.stop();      // Sequencer Timer Stop
    }
    
    LCD();
}

//-------------------------------------------------------------
// GateOutSequence  beat(Note values) length(Gate time) invert(invert Gate)

inline int UpdateGate(int bpm, int beat, int ch, int length, bool invert)
{
    int i;
    static int gatetime[4], oldgatetime[4];
    static int bar, sync24, oldsynctime;
    
    int time = gTimer.read_us();
    
    bar = (60.0f / bpm) * 4000000;
    sync24 = (bar / 4) / 24; // sync24 not tested
    
    switch(beat)                                // Calculate Note values 
        {
            case NDOT2:
                
                gatetime[ch] = (bar / 4) * 3;
                break;
                    
            case NDOT4:
            
                gatetime[ch] = (bar / 8) * 3;
                break;
                
            case NDOT8:
                
                gatetime[ch] = (bar / 16) * 3;
                break;
                    
            case NDOT16:
                
                gatetime[ch] = (bar / 32) * 3;
                break;
        
            case NDOT32:
                
                gatetime[ch] = (bar / 64) * 3;
                break;
                
            case NRESET:
                
                for(i = 0; i < GATEALL; ++i)    // Reset
                {
                    gTimer.reset();
                    oldsynctime = oldgatetime[i] = gatetime[i] = NRESET;
                }
                break;
                
            default:
                
                gatetime[ch] = bar / beat;
        }
        
        if(time > oldsynctime + sync24)  // sync24 not tested
        {
            oldsynctime = time;
            gCLOCKOUT = 1;
            
        } else if (time > sync24 - (sync24 - 2)) {
        
            gCLOCKOUT = 0;
        }
    
    if (ch == GATEALL) 
    {
        return -1;
        
    } else if (time > oldgatetime[ch] + gatetime[ch] && !invert) {
        
        oldgatetime[ch] = time;
        gLEDS[ch] = gGATES[ch] = 1;
        
        return ch + 1;
    
    } else if (time > oldgatetime[ch] + gatetime[ch] && invert) {
        
        oldgatetime[ch] = time;
        gLEDS[ch] = gGATES[ch] = 0;
        
        return 0;
        
    } else if (time > oldgatetime[ch] + (gatetime[ch] - gatetime[ch] / length) && !invert) {
        
        gLEDS[ch] = gGATES[ch] = 0;
        
        return 0;
        
    } else if (time > oldgatetime[ch] + (gatetime[ch] - gatetime[ch] / length) && invert) {
        
        gLEDS[ch] = gGATES[ch] = 1;
        
        return ch + 1;
        
    } else {
        
        return -1;          
    }
}

//-------------------------------------------------------------
// SyncOut Sequence  beat(Note values) invert(invert Gate)

inline void UpdateSync(int bpm, int beat, bool invert)
{
    static int bar, synctime, oldsynctime;
    
    int time = gTimer.read_us();
    
    bar = (60.0f / bpm) * 4000000;

    synctime = bar / beat;
    
    if(beat == NRESET) 
    {
        bar = synctime = oldsynctime = gCLOCKOUT = 0;
    }
    
    if((time > oldsynctime + synctime) && !invert)
    {
        oldsynctime = time;
        gCLOCKOUT = 1;
        
    } else if ((time > synctime - (synctime / 2)) && !invert) {
    
        gCLOCKOUT = 0;
        
    } else if((time > oldsynctime + synctime) && invert) {
        
        oldsynctime = time;
        gCLOCKOUT = 0;
        
    } else if ((time > synctime - (synctime / 2)) && invert) {
    
        gCLOCKOUT = 1;
    }
    
}

//-------------------------------------------------------------
// CV meter

inline void CVMeter(int ch, const unsigned int *level)
{       
    gLCD.locate ( ch, 0 );
    gLCD.putc(*level * 0.000205729);     // put custom char
}

//-------------------------------------------------------------
// Print LCD Mode Status

void LCD()
{
    switch(gMode) 
        {
            case MODE_Calb: 
                gLCD.locate( 9, 0 );
                gLCD.printf("Calibr "); 
                break;
                
            case MODE_LIN:
                gLCD.locate( 9, 0 );
                gLCD.printf("OSC-CV ");
                break;

            case MODE_SEQ:  
                gLCD.locate( 9, 0 );
                gLCD.printf("SHIFTCV");
                break;
        }
}

//-------------------------------------------------------------
// Write command Custom Char LCD CGRAM(CV Meter) 

void WriteCustomChar(unsigned char addr, unsigned char *c)
{   
    char cnt = 0;
    addr = ((addr << 3) | 0x40);
    
    while(cnt < 0x08)
    {
        gLCD.writeCommand(addr | cnt);
        gLCD.writeData(*c);
        
        cnt++;
        c++;
    }
}

//-------------------------------------------------------------
// Setup Ethernet port

int SetupEthNetIf()
{
    gLCD.locate( 0, 1 );
    gLCD.printf("Setting up...   ");
//  printf("Setting up...\r\n");
    EthernetErr ethErr = gEth.setup();
    
    if(ethErr)
    {
        gLCD.locate( 0, 1 );
        gLCD.printf("Error in setup.");
//  printf("Error %d in setup.\r\n", ethErr);
        return -1;
    }
//  printf("Setup OK\r\n");
 
//  printf("IP address %d.%d.%d.%d\r\n", gEth.getIp()[0], gEth.getIp()[1], gEth.getIp()[2], gEth.getIp()[3]);
    Host broadcast(IpAddr(gEth.getIp()[0], gEth.getIp()[1], gEth.getIp()[2], 255), INPUT_PORT, NULL);
    gUdp.setOnEvent(&onUDPSocketEvent);
    gUdp.bind(broadcast);
    
    gLCD.locate( 0, 1 );
    gLCD.printf("%03d.%03d.%03d.%03d", gEth.getIp()[0], gEth.getIp()[1], gEth.getIp()[2], gEth.getIp()[3]);
    wait(1.0);
    
    return 0;
}

//-------------------------------------------------------------
// Fast strlen function http://www.strchr.com/optimized_strlen_function

size_t strlength(const char *s) 
{
    size_t len = 0;
    
    for(;;) 
    {
        unsigned x = *(unsigned*)s;
        if((x & 0xFF) == 0) return len;
        if((x & 0xFF00) == 0) return len + 1;
        if((x & 0xFF0000) == 0) return len + 2;
        if((x & 0xFF000000) == 0) return len + 3;
        s += 4, len += 4;
    }
}

//-------------------------------------------------------------
// Handller receive UDP Packet

inline void onUDPSocketEvent(UDPSocketEvent e)
{
    union OSCarg msg[10];
    char buf[768] = {0};
    int num, len;
    int recvlen;
    int messagepos = 0;
    bool bundleflag = false;
    
    Host host;
    
    recvlen = gUdp.recvfrom(buf, 768, &host);  // packet length
            
    switch(e)
    {
        case UDPSOCKET_READABLE: // The only event for now
            
        if(recvlen <= 0)  break;
        
        if(buf[0] == '#') // #bundle
        {
            messagepos += 16;     // skip #bundle & timetag
            recvlen -= 16;
            
            bundleflag = true;  
        }
        
        do {
                if(bundleflag)
                {
                    messagepos += 4;
                    recvlen -= 4;
                
                    if(recvlen <= 0)
                    {
                        bundleflag = false;
                        break;
                    }
                }
                
                if(getOSCmsg(buf + messagepos, msg) == -1)  continue;
            
                len = strlen(msg[0].address);
        
                if(isdigit(msg[0].address[len-1])) 
                {       
                    num = msg[0].address[len-1] - '0' - 1;
            
                } else {
            
                    num = -1;
                }
        
            // address pattern SYNC & GATE (Type Tag int, float)
                if(!strncmp(msg[0].address+(len-1)-4, "sync", 4)) 
                { 
                    if(msg[2].i != 0) gCLOCKOUT = 1;
                    else              gCLOCKOUT = 0;
                    continue;

                } else if (!strncmp(msg[0].address+(len-1)-4, "gate", 4) && (num != -1)) {
                    if(num > 3) continue;
                    if(msg[2].i != 0) gLEDS[num] = gGATES[num] = 1;
                    else              gLEDS[num] = gGATES[num] = 0;
                    continue;
            // (touchOSC Control push, toggle)
                } else if (!strncmp(msg[0].address+(len-1)-4, "push", 4) && (num != -1)) {     
                    if(num > 4) continue;
                    if(msg[2].i != 0) gLEDS[num] = gGATES[num] = 1;
                    else              gLEDS[num] = gGATES[num] = 0;
                    continue;
                
                } else if (!strncmp(msg[0].address+(len-1)-6, "toggle", 6) && (num != -1)) {
                    if(num > 4) continue;
                    if(msg[2].i != 0) gLEDS[num] = gGATES[num] = 1;
                    else              gLEDS[num] = gGATES[num] = 0;
                    continue;
                        
                } else if (!strncmp(msg[0].address,"/1/multipush",12) && (num != -1)) {
                    if(num > 4) continue;
                    if(msg[2].i != 0) gLEDS[num] = gGATES[num] = 1;
                    else              gLEDS[num] = gGATES[num] = 0;
                    continue;
            // address pattern CV (Type Tag float)  
                } else if(!strncmp(msg[0].address+(len-1)-2, "cv", 2) && (num != -1)) {
                    if(num > 7) continue;
                    if(msg[1].typeTag[1] == 'f') gOSC_cv[num] = msg[2].f * (SCALING_N);
                    continue;
            // (touchOSC Control fader, rotary, xy, multixy, multifader)        
                } else if (!strncmp(msg[0].address+(len-1)-5, "fader", 5) && (num != -1)) {
                    if(num > 7) continue;                                              
                    if(msg[1].typeTag[1] == 'f') gOSC_cv[num] = msg[2].f * (SCALING_N);
                    continue;

                } else if (!strncmp(msg[0].address+(len-1)-6, "rotary", 6) && (num != -1)) { 
                    if(num > 7) continue;
                    if(msg[1].typeTag[1] == 'f') gOSC_cv[num] = msg[2].f * (SCALING_N);
                    continue;
                
                } else if (!strncmp(msg[0].address+(len-1)-2, "xy", 2) && (num != -1)) {
                    if(num > 7) continue;
                    if(msg[1].typeTag[1] == 'f') gOSC_cv[num] = msg[2].f * (SCALING_N);
                    if(msg[1].typeTag[1] == 'f') gOSC_cv[++num] = msg[3].f * (SCALING_N);
                    continue;
                        
                } else if (!strncmp(msg[0].address+(len-1)-9, "multixy1/", 9) && (num != -1)) {
                    if(num > 7) continue;
                    if(msg[1].typeTag[1] == 'f') gOSC_cv[num] = msg[2].f * (SCALING_N);
                    if(msg[1].typeTag[1] == 'f') gOSC_cv[++num] = msg[3].f * (SCALING_N);
                    continue;
    
                } else if (!strncmp(msg[0].address+(len-1)-12, "multifader1/", 12) && (num != -1)) {
                    if(num > 7) continue;
                    if(msg[1].typeTag[1] == 'f') gOSC_cv[num] = msg[2].f * (SCALING_N);
                    continue;
            // (touchOSC multifader for Sequencer Mode)
                } else if (!strncmp(msg[0].address+(len-1)-11, "sequencer1/", 11) && (num != -1)) {
                    if(num > 7) continue;
                    if(msg[1].typeTag[1] == 'f') gSeq_cv1[num] = msg[2].f * (SCALING_N);
                    continue;
                            
                } else if (!strncmp(msg[0].address+(len-1)-11, "sequencer2/", 11) && (num != -1)) {
                    if(num > 7) continue;
                    if(msg[1].typeTag[1] == 'f') gSeq_cv2[num] = msg[2].f * (SCALING_N);
                    continue;
            // address pattern for control
                } else if (!strncmp(msg[0].address+(len-1)-6, "ctrlsw", 6) && (num != -1)) {
                    if(num > 5) continue;
                    if(msg[2].i != 0) gCtrlSW[num] = true;
                    else              gCtrlSW[num] = false;
                    continue;
                                                
                } else if (!strncmp(msg[0].address+(len-1)-4, "ctrl", 4) && (num != -1)) {
                    if(num > 5) continue;                                             
                    if(msg[1].typeTag[1] == 'f') gCtrl[num] = msg[2].f;
                    continue;
                }
                
            } while(bundleflag);
                    
    }
}