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:
2013-01-28
Revision:
6:5796b63c70ef
Parent:
5:e305509d53f3
Child:
7:a04f8378662e

File content as of revision 6:5796b63c70ef:

//-------------------------------------------------------------
//                  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_LIN            0        // Linear      LinearCV Mode
#define MODE_QChr           1        // Chromatic   Quantize Mode
#define MODE_QMaj           2        // Major
#define MODE_QDor           3        // Dorian
#define MODE_Q5th           4        // 5th
#define MODE_QWht           5        // Wholetone
#define MODE_SEQ            6        // Sequencer & Shift Register
#define MODE_Calb           7        // Calibration (for VCO Tuning)

#define MODE_NUM            8        // Modes

#define QUAN_RES1           116      // Quantize voltage Steps
#define QUAN_RES2           69
#define QUAN_RES3           68  
#define QUAN_RES4           17
#define QUAN_RES5           58

#define SPI_RATE            40000000 // 40Mbps 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*);
int UpdateGate(int, int, int, int, int);
void SetCV(void);
void SeqCV(int);
void CheckModeSW(void);
void CVMeter(int, const unsigned int*);
void LCD();
void WriteCustomChar(unsigned char, unsigned char*);
int  SetupEthNetIf(void);
inline void onUDPSocketEvent(UDPSocketEvent);

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

//  Chromatic Scale
const float calibMap1[QUAN_RES1] = {
0.00663080,   0.01433030,   0.02202980,   0.02972930,   0.03742880,
0.04512830,   0.05282781,   0.06052731,   0.06822681,   0.07592630,
0.08362581,   0.09132531,   0.09902481,   0.10672431,   0.11442380,
0.12212331,   0.12951356,   0.13671936,   0.14392516,   0.15113096,
0.15833676,   0.16554256,   0.17274836,   0.17995416,   0.18715996,
0.19436575,   0.20157155,   0.20877735,   0.21598317,   0.22318897,
0.23039477,   0.23760056,   0.24480636,   0.25202271,   0.25926629,
0.26650983,   0.27375340,   0.28099698,   0.28824055,   0.29548413,
0.30272770,   0.30997124,   0.31721482,   0.32445839,   0.33170196,
0.33894554,   0.34618911,   0.35343266,   0.36067623,   0.36791980,
0.37516347,   0.38241133,   0.38965923,   0.39690709,   0.40415496,
0.41140282,   0.41865072,   0.42589858,   0.43314645,   0.44039431,
0.44764221,   0.45489007,   0.46213794,   0.46938580,   0.47663370,
0.48388156,   0.49112943,   0.49837729,   0.50566339,   0.51296055,
0.52025765,   0.52755481,   0.53485191,   0.54214907,   0.54944617,
0.55674326,   0.56404042,   0.57133752,   0.57863468,   0.58593178,
0.59322894,   0.60052603,   0.60782319,   0.61512029,   0.62241745,
0.62976688,   0.63714498,   0.64452308,   0.65190119,   0.65927929,
0.66665739,   0.67403549,   0.68141359,   0.68879169,   0.69616979,
0.70354789,   0.71092600,   0.71830410,   0.72568226,   0.73306036,
0.74043846,   0.74781656,   0.75820577,   0.76986063,   0.78151548,
0.79317033,   0.80482519,   0.81648004,   0.82813489,   0.83978975,
0.85144460,   0.86309946,   0.87475431,   0.90686423,   0.93941462,
0.97196496
};

//  Major Scale
const float calibMap2[QUAN_RES2] = {
0.00663080,   0.01433030,   0.02972930,   0.04512830,   0.05282781,
0.06822681,   0.08362581,   0.09902481,   0.10672431,   0.12212331,
0.13671936,   0.14392516,   0.15833676,   0.17274836,   0.18715996,
0.19436575,   0.20877735,   0.22318897,   0.23039477,   0.24480636,
0.25926629,   0.27375340,   0.28099698,   0.29548413,   0.30997124,
0.31721482,   0.33170196,   0.34618911,   0.36067623,   0.36791980,
0.38241133,   0.39690709,   0.40415496,   0.41865072,   0.43314645,
0.44764221,   0.45489007,   0.46938580,   0.48388156,   0.49112943,
0.50566339,   0.52025765,   0.53485191,   0.54214907,   0.55674326,
0.57133752,   0.57863468,   0.59322894,   0.60782319,   0.62241745,
0.62976688,   0.64452308,   0.65927929,   0.66665739,   0.68141359,
0.69616979,   0.71092600,   0.71830410,   0.73306036,   0.74781656,
0.75820577,   0.78151548,   0.80482519,   0.82813489,   0.83978975,
0.86309946,   0.90686423,   0.93941462
};

//  Dorian Scale
const float calibMap3[QUAN_RES3] = {
0.00663080,   0.01433030,   0.02972930,   0.04512830,   0.06052731,
0.06822681,   0.08362581,   0.09902481,   0.10672431,   0.12212331,
0.13671936,   0.15113096,   0.15833676,   0.17274836,   0.18715996,
0.19436575,   0.20877735,   0.22318897,   0.23760056,   0.24480636,
0.25926629,   0.27375340,   0.28099698,   0.29548413,   0.30997124,
0.32445839,   0.33170196,   0.34618911,   0.36067623,   0.36791980,
0.38241133,   0.39690709,   0.41140282,   0.41865072,   0.43314645,
0.44764221,   0.45489007,   0.46938580,   0.48388156,   0.49837729,
0.50566339,   0.52025765,   0.53485191,   0.54214907,   0.55674326,
0.57133752,   0.58593178,   0.59322894,   0.60782319,   0.62241745,
0.62976688,   0.64452308,   0.65927929,   0.67403549,   0.68141359,
0.69616979,   0.71092600,   0.71830410,   0.73306036,   0.74781656,
0.76986063,   0.78151548,   0.80482519,   0.82813489,   0.83978975,
0.86309946,   0.90686423,   0.97196496
};

//  5th
const float calibMap4[QUAN_RES4] = {
0.00663080,   0.06052731,   0.11442380,   0.16554256,   0.21598317,
0.26650983,   0.31721482,   0.36791980,   0.41865072,   0.46938580,
0.52025765,   0.57133752,   0.62241745,   0.67403549,   0.72568226,
0.79317033,   0.87475431

};

//  Whole tone
const float calibMap5[QUAN_RES5] = {
0.00663080,   0.02202980,   0.03742880,   0.05282781,   0.06822681,
0.08362581,   0.09902481,   0.11442380,   0.12951356,   0.14392516,
0.15833676,   0.17274836,   0.18715996,   0.20157155,   0.21598317,
0.23039477,   0.24480636,   0.25926629,   0.27375340,   0.28824055,
0.30272770,   0.31721482,   0.33170196,   0.34618911,   0.36067623,
0.37516347,   0.38965923,   0.40415496,   0.41865072,   0.43314645,
0.44764221,   0.46213794,   0.47663370,   0.49112943,   0.50566339,
0.52025765,   0.53485191,   0.54944617,   0.56404042,   0.57863468,
0.59322894,   0.60782319,   0.62241745,   0.63714498,   0.65190119,
0.66665739,   0.68141359,   0.69616979,   0.71092600,   0.72568226,
0.74043846,   0.75820577,   0.78151548,   0.80482519,   0.82813489,
0.85144460,   0.87475431,   0.93941462
    
};

//-------------------------------------------------------------
// 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

static float    gOSC_cv[8];
static float    gSeq_cv1[8];
static float    gSeq_cv2[8];
static float    gGlide;
volatile static int gMode;

// Variables for Control

float           gCtrl[2];
volatile int    gCtrlSW[4];

//-------------------------------------------------------------
// 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] = {p22, p23, 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() 
{ 
    static float pot, _pot;
    int bpm, _bpm;
    int shift1, shift2, shift3, shift4;
    
    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 = 0;
    gGlide = gMode = 0;
    bpm = _bpm = 120;
        
    LCD();
    gLCD.locate( 0, 1 );
    gLCD.printf("12345678 G>>%3.2f", gGlide);
    
// loop
    while(1) 
    {
        gGlide = pot = gAIN.read();  // Glide Value
        
        if(abs(pot - _pot) > 0.01f) 
        {
            gLCD.locate( 0, 1 );
            gLCD.printf("12345678 G>>%3.2f", gGlide);
            
            _pot = gAIN.read();
        }
        
        switch(gMode)
        {
            case MODE_SEQ:
                
                bpm = (gCtrl[0] * 300 + 10);                    // Set BPM (gCtrl[0])
            
                if(abs(bpm - _bpm) > 1)
                {
                    UpdateGate(bpm, NRESET, GATEALL, 3, 0);         // Reset (if bpm change)
                    _bpm = bpm;
                    
                } else if (gCtrlSW[0] == 1) {                       // Stop (gCtrlSW[0])
                        
                    bpm = 0;
                }
            
                if(gCtrlSW[2] == 0 && gCtrlSW[3] == 0)              // Sequencer Mode1
                {
                    shift1 = UpdateGate(bpm, N16TH, GATE1, 3, 0);   // Shift Timming 16th note
                
                    UpdateGate(bpm, N8TH, GATE2, 3, 0);
                    UpdateGate(bpm, NDOT8, GATE3, 3, 0);
                    UpdateGate(bpm, TRIP4, GATE4, 3, 0);
            
                    SeqCV(shift1);  // Do shift ch all
                    continue;
                
                } else if (gCtrlSW[2] == 1 && gCtrlSW[3] == 0) {    // Sequencer Mode2 (if gCtrlSW[2] ON)
                
                    shift1 = UpdateGate(bpm, N16TH, GATE1, 3, 0);
                    shift2 = UpdateGate(bpm, N8TH, GATE2, 3, 0);
                    shift3 = UpdateGate(bpm, NDOT4, GATE3, 3, 0);
                    shift4 = UpdateGate(bpm, TRIP8, GATE4, 3, 0);
            
                    SeqCV(shift1);  // Do shift ch 1~5
                    SeqCV(shift2);  // Do shift ch 6
                    SeqCV(shift3);  // Do shift ch 7
                    SeqCV(shift4);  // Do shift ch 8
                    continue;
                
                } else if (gCtrlSW[3] == 1) {                       // Sequencer Mode3 (if gCtrlSW[3] ON)
                                                                    // (ch6,7,8, short loop)
                    shift1 = UpdateGate(bpm, N16TH, GATE1, 3, 0);
                    shift2 = UpdateGate(bpm, N8TH, GATE2, 3, 0);
                    shift3 = UpdateGate(bpm, NDOT8, GATE3, 3, 0);
                    shift4 = UpdateGate(bpm, TRIP4, GATE4, 3, 0);
            
                    SeqCV(shift1);  // Do shift ch 1~5
                    SeqCV(shift2);  // Do shift ch 6
                    SeqCV(shift3);  // Do shift ch 7
                    SeqCV(shift4);  // Do shift ch 8
                    continue;
                }
                
            default:
            
                SetCV();
                continue;
        }
    }
}

//-------------------------------------------------------------
// 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();
}

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

int UpdateGate(int bpm, int beat, int ch, int length, int invert)
{
    static int gatetime[4];
    static int oldgatetime[4];
    static int bar;
    static int sync24;
    static int 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 TRIP2:
                
                gatetime[ch] = bar / 3;
                break;
                
            case TRIP4:
                
                gatetime[ch] = (bar / 2) / 3;
                break;
                
            case TRIP8:
                        
                gatetime[ch] = (bar / 4) / 3;
                break;
            
            case TRIP16:
                
                gatetime[ch] = (bar / 8) / 3;
                break;
            
            case TRIP32:
                
                gatetime[ch] = (bar / 16) / 3;
                break;
                
            case NRESET:
                
                for(int 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 == 0) {
        
        oldgatetime[ch] = time;
        gLEDS[ch] = gGATES[ch] = 1;
        
        return ch + 1;
    
    } else if (time > oldgatetime[ch] + gatetime[ch] && invert == 1) {
        
        oldgatetime[ch] = time;
        gLEDS[ch] = gGATES[ch] = 0;
        
        return 0;
        
    } else if (time > oldgatetime[ch] + (gatetime[ch] - gatetime[ch] / length) && invert == 0) {
        
        gLEDS[ch] = gGATES[ch] = 0;
        
        return 0;
        
    } else if (time > oldgatetime[ch] + (gatetime[ch] - gatetime[ch] / length) && invert == 1) {
        
        gLEDS[ch] = gGATES[ch] = 1;
        
        return ch + 1;
        
    } else {
        
        return -1;          
    }
}

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

void SetCV()
{
    static int ch;
    float glidecv[8];
    unsigned int cv[8];
    static float oldcv[8];
    static unsigned int quan;
    float qcv;

    switch(gMode) 
        {
            case MODE_LIN:
                
                glidecv[ch] = oldcv[ch] * gGlide + gOSC_cv[ch] * (1.0f - gGlide);
                oldcv[ch] = glidecv[ch];
                cv[ch] = (unsigned int)glidecv[ch];
                    
                UpdateCV(WRITE_UPDATE_N, ch, &cv[ch]);
                break;
                    
            case MODE_QChr:
            
                quan = 40616 / QUAN_RES1;
                qcv = calibMap1[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = oldcv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                oldcv[ch] = glidecv[ch];
                cv[ch] = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv[ch]);
                break;
                
            case MODE_QMaj:
                
                quan = 40616 / QUAN_RES2;
                qcv = calibMap2[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = oldcv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                oldcv[ch] = glidecv[ch];
                cv[ch] = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv[ch]);
                break;
                    
            case MODE_QDor:
                
                quan = 40616 / QUAN_RES3;
                qcv = calibMap3[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = oldcv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                oldcv[ch] = glidecv[ch];
                cv[ch] = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv[ch]);
                break;
                
            case MODE_Q5th:
                
                quan = 40616 / QUAN_RES4;
                qcv = calibMap4[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = oldcv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                oldcv[ch] = glidecv[ch];
                cv[ch] = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv[ch]);
                break;
                
            case MODE_QWht:
                
                quan = 40616 / QUAN_RES5;
                qcv = calibMap5[(unsigned int)(gOSC_cv[ch] / quan)];
                    
                glidecv[ch] = oldcv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide);
                oldcv[ch] = glidecv[ch];
                cv[ch] = (unsigned int)glidecv[ch];
                        
                UpdateCV(WRITE_UPDATE_N, ch, &cv[ch]);
                break;
                
            case MODE_Calb:
                
                cv[ch] = 19212;     // A440.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[ch]);
                break;
        }

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

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

void SeqCV(int shift)
{
    int i, j, k;
    static int ch;
    static int cnt1, cnt2, cnt3;
    static int cntloop1, cntloop2, cntloop3;
    static int SeqMode;
    static float glidecv[8];
    unsigned int cv[8];
    static float shiftcv[8];
    static float buffercv[9];
    static float loopcv[3];
    static unsigned int quan;
    float qcv;
        
    SeqMode = (unsigned int)(gCtrl[1] * (MODE_NUM - 3));    // Sequencer Quantize Mode (gCtrl[1])
    
    switch(SeqMode) 
        {
            case MODE_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[0] = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv[0]);
                break;
                    
            case MODE_QChr:
            
                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[0] = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv[0]);
                break;
                
            case MODE_QMaj:
                
                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[0] = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv[0]);
                break;
                    
            case MODE_QDor:
                
                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[0] = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv[0]);
                break;
                
            case MODE_Q5th:
                
                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[0] = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv[0]);
                break;
                
            case MODE_QWht:
                
                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[0] = (unsigned int)glidecv[0];
                
                UpdateCV(WRITE_UPDATE_N, 0, &cv[0]);
                break;
        }
        
        if((gCtrlSW[2] == 0 || gCtrlSW[2] == 1) && gCtrlSW[3] == 0)// Sequencer Mode1
        {
            for(i = 1; i < 8; ++i)
            {
                glidecv[i] = glidecv[i] * gGlide + shiftcv[i] * (1.0f - gGlide);
                cv[i] = (unsigned int)glidecv[i];
                    
                UpdateCV(WRITE_UPDATE_N, i, &cv[i]);
                
                if(shift == 1)                      // GATE1
                {
                    for(i = 1; i < 8; ++i)      // Shift ch2~8
                    {
                        shiftcv[i] = glidecv[i-1];
                    }
            
                    ch++;
                    ch &= 0x0F;
                }
            
                cnt1 = cnt2 = cnt3 = 0;
            }
                    
        } else if (gCtrlSW[2] == 1 && gCtrlSW[3] == 0) {            // Sequencer Mode2
                
            if(shift == 1)                      // GATE1
            {
                for(j = 1; j < 5; ++j)
                {
                    shiftcv[j] = glidecv[j-1];  // Shift ch2~5
                }
            
                ch++;
                ch &= 0x0F;
                
            } else if (shift == 2) {            // GATE2
                
                shiftcv[5] = glidecv[1];        // Shift ch6
                
            } else if (shift == 3) {            // GATE3
                
                shiftcv[6] = glidecv[2];        // Shift ch7
                
            } else if (shift == 4) {            // GATE4
                
                shiftcv[7] = glidecv[3];        // Shift ch8
            }   
                
        } else if (gCtrlSW[3] == 1) {                               // Sequencer Mode3
                
            for(j = 1; j < 5; ++j)
            {
                glidecv[j] = glidecv[j] * gGlide + shiftcv[j] * (1.0f - gGlide);
                cv[j] = (unsigned int)glidecv[j];
                    
                UpdateCV(WRITE_UPDATE_N, j, &cv[j]);            
            }
            
            for(k = 5; k < 8; ++k)
            {
                glidecv[k] = glidecv[k] * gGlide + loopcv[k - 5] * (1.0f - gGlide);
                cv[k] = (unsigned int)glidecv[k];
                    
                UpdateCV(WRITE_UPDATE_N, k, &cv[k]);            
            }
            
            if(shift == 1)                      // GATE1
            {
                for(i = 1; i < 8; ++i)
                {
                    shiftcv[i] = glidecv[i-1];  // Shift ch2~5
                }
            
                ch++;
                ch &= 0x0F;
                
            } else if (shift == 2) {            // GATE2
                
                if(cnt1 < 4) 
                {
                    loopcv[0] = buffercv[cnt1] = shiftcv[4];
                    
                    cnt1++;
                    
                } else if (cnt1 >= 4) {
                    
                    loopcv[0] = buffercv[cntloop1];
                    
                    cntloop1++;
                    cntloop1 &= 0x03;
                }
                        
            } else if (shift == 3) {            // GATE3
                
                if(cnt2 < 3) 
                {
                    loopcv[1] = buffercv[(cnt2 + 4)] = shiftcv[5];
                    
                    cnt2++;
                    
                } else if (cnt2 >= 3) {
                    
                    loopcv[1] = buffercv[(cntloop2 + 4)];
                    
                    cntloop2++;
                    cntloop2 &= 0x03;   
                }
                
            } else if (shift == 4) {            // GATE4
                
                if(cnt3 < 2) 
                {
                    loopcv[2] = buffercv[(cnt3 + 7)] = shiftcv[6];
                    
                    cnt3++;
                    
                } else if (cnt3 >= 2) {
                    
                    loopcv[2] = buffercv[(cntloop3 + 7)];
                    
                    cntloop3++;
                    cntloop3 &= 0x01;   
                }
            }
            
            if(gCtrlSW[1] == 1)                 // Update loop buffer (if gCtrlSW[1] ON)
            {
                cnt1 = cnt2 = cnt3 = 0;
            } 
        }
        
        if(ch < 8)
        {
            CVMeter(ch, &cv[0]);
                            
        } else {
                        
            CVMeter((ch-8), &cv[0]);
        }
}

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

void CheckModeSW()
{   
    wait(0.02);
    
    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();
}

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

void CVMeter(int ch, const unsigned int *level)
{
    static unsigned int cvmeter;
    
    cvmeter = *level / (SCALING_N / 7.9);
        
    gLCD.locate ( ch, 0 );
    gLCD.putc(cvmeter);     // put custom char
}

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

void LCD()
{
    switch(gMode) 
        {
            case MODE_LIN:
                gLCD.locate( 9, 0 );
                gLCD.printf("OSC-CV ");
                break;
                    
            case MODE_QChr:
                gLCD.locate( 9, 0 );
                gLCD.printf("QUAN_C ");
                break;
                
            case MODE_QMaj: 
                gLCD.locate( 9, 0 );
                gLCD.printf("QUAN_M ");
                break;
                    
            case MODE_QDor: 
                gLCD.locate( 9, 0 );
                gLCD.printf("QUAN_D ");
                break;
                
            case MODE_Q5th: 
                gLCD.locate( 9, 0 );
                gLCD.printf("QUAN_5 "); 
                break;
                    
            case MODE_QWht: 
                gLCD.locate( 9, 0 );
                gLCD.printf("QUAN_W "); 
                break;
                
            case MODE_SEQ:  
                gLCD.locate( 9, 0 );
                gLCD.printf("ASRSEQ ");
                break;
                
            case MODE_Calb: 
                gLCD.locate( 9, 0 );
                gLCD.printf("Calibr "); 
                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;
}

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

inline void onUDPSocketEvent(UDPSocketEvent e)
{
    union OSCarg msg[10];
    static int num;
    unsigned int absv;
        
    switch(e)
    {
        case UDPSOCKET_READABLE: // The only event for now
        char buf[256] = {0};
        Host host;
            
        while( int len = gUdp.recvfrom( buf, 256, &host ))
        {
            if(len <= 0)  break;
            //  printf("\r\nFrom %d.%d.%d.%d:\r\n", 
            //  host.getIp()[0], host.getIp()[1], host.getIp()[2], host.getIp()[3]);
            
            getOSCmsg(buf,msg);
            //  printf("OSCmsg: %s %s %f %i\r\n", 
            //  msg[0].address, msg[1].typeTag, msg[2].f, msg[2].i);
                
            len = strlen(msg[0].address);
            
            if(isdigit(msg[0].address[len-1]))
                
                num = msg[0].address[len-1] - '0' - 1;
            else
                num = -1;
            
            absv = msg[2].f + 0; //convert -0 to 0
            
         // address pattern SYNC & GATE (Type Tag int, float)
            if(strncmp(msg[0].address+(len-1)-4, "sync", 4)==0) { 
                if(absv >= 1 || msg[2].i >= 1) gCLOCKOUT = 1;
                else                           gCLOCKOUT = 0;
                break;
    
                } else if ((strncmp(msg[0].address+(len-1)-4, "gate", 4)==0) && (num != -1)) {
                    if(num > 3) break;
                    if(absv >= 1 || msg[2].i >= 1) gLEDS[num] = gGATES[num] = 1;
                    else                           gLEDS[num] = gGATES[num] = 0;
                    break;
                // (touchOSC Control push, toggle)
                    } else if ((strncmp(msg[0].address+(len-1)-4, "push", 4)==0) && (num != -1)) {     
                        if(num > 3) break;
                        if(absv >= 1 || msg[2].i >= 1) gLEDS[num] = gGATES[num] = 1;
                        else                           gLEDS[num] = gGATES[num] = 0;
                        break;
                    
                        } else if ((strncmp(msg[0].address+(len-1)-6, "toggle", 6)==0) && (num != -1)) {
                            if(num > 3) break;
                            if(absv >= 1 || msg[2].i >= 1) gLEDS[num] = gGATES[num] = 1;
                            else                           gLEDS[num] = gGATES[num] = 0;
                            break;
                            
                            } else if ((strncmp(msg[0].address,"/1/multipush",12)==0) && (num != -1)) {
                                if(num > 3) break;
                                if(absv >= 1 || msg[2].i >= 1) gLEDS[num] = gGATES[num] = 1;
                                else                           gLEDS[num] = gGATES[num] = 0;
                                break;
                            }

                        // address pattern CV (Type Tag float)
                            if((strncmp(msg[0].address+(len-1)-2, "cv", 2)==0) && (num != -1)) {
                                if(num > 7) break;
                                if(msg[1].typeTag[1] == 'f') gOSC_cv[num] = msg[2].f * (SCALING_N);
                                break;
                            // (touchOSC Control fader, rotary, xy, multixy, multifader)
                                } else if ((strncmp(msg[0].address+(len-1)-5, "fader", 5)==0) && (num != -1)) {
                                    if(num > 7) break;                                                     
                                    if(msg[1].typeTag[1] == 'f') gOSC_cv[num] = msg[2].f * (SCALING_N);
                                    break;

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