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
Diff: main.cpp
- Revision:
- 0:a4d93cd4c30d
- Child:
- 1:fd4f70088311
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Fri Dec 21 13:32:18 2012 +0000 @@ -0,0 +1,620 @@ +//------------------------------------------------------------- +// 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_QPen 4 // Pentatonic + +#define QUAN_RES1 116 // Quantize voltage Steps +#define QUAN_RES2 69 +#define QUAN_RES3 68 +#define QUAN_RES4 48 + +#define MODE_NUM 5 // Modes + +#define SPI_RATE 40000000 // 40Mbps SPI Clock +#define SCALING_N 38400.0 +#define INPUT_PORT 12345 // Input Port Number + +#define UPDATE_INTERVAL 60 // CV Update Interval (us) +#define POLLING_INTERVAL 25 // Polling Interval (us) + +//------------------------------------------------------------- +// DAC8568 Control Bits + +#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 + +//------------------------------------------------------------- +// Functions + +void NetPoll(void); +void InitOSCCV(void); +void UpdateCV(unsigned char, unsigned char, unsigned int); +void SetCV(void); +void CheckSW(void); +void CVMeter(unsigned int, unsigned int); +void WriteCustomChar(unsigned char, unsigned char*); +int SetupEthNetIf(void); +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 +}; + +// Pentatonic Scale +const float calibMap4[QUAN_RES4] = { +0.01433030, 0.02972930, 0.04512830, 0.06822681, 0.08362581, +0.10672431, 0.12212331, 0.13671936, 0.15833676, 0.17274836, +0.19436575, 0.20877735, 0.22318897, 0.24480636, 0.25926629, +0.28099698, 0.29548413, 0.30997124, 0.33170196, 0.34618911, +0.36791980, 0.38241133, 0.39690709, 0.41865072, 0.43314645, +0.45489007, 0.46938580, 0.48388156, 0.50566339, 0.52025765, +0.54214907, 0.55674326, 0.57133752, 0.59322894, 0.60782319, +0.62976688, 0.64452308, 0.65927929, 0.68141359, 0.69616979, +0.71830410, 0.73306036, 0.74781656, 0.78151548, 0.80482519, +0.83978975, 0.86309946, 0.90686423 +}; + +//------------------------------------------------------------- +// 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 gGlide; +unsigned int gMode; + +//------------------------------------------------------------- +// 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] = {p21, p22, p23, p24}; // GateOut +DigitalOut gLEDS[4] = {p18, p19, p20, p28}; // LED +DigitalOut gCLOCKOUT(p25); // ClockOut + +AnalogIn gAIN(p17); // Glide Potentiometer +InterruptIn gSW(p30); // Mode SW + +Ticker gSetter; // Ticker SetCV +Ticker gPoller; // Ticker Polling + +// Ethernet +EthernetNetIf gEth; +UDPSocket gUdp; + +//------------------------------------------------------------- +// main + +int main() +{ + int i; + + if(SetupEthNetIf() == -1) + { + for(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(); + + // loop + while(1) + { + gGlide = gAIN.read(); + + gLCD.locate( 0, 1 ); + gLCD.printf("12345678 G>>%3.2f", gGlide); + + 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_QPen: + gLCD.locate( 9, 0 ); + gLCD.printf("QUAN_P"); + break; + } + //CV Meter + for(i = 0; i < 8; i++) + { + CVMeter(i, (unsigned int)gOSC_cv[i]); + } + } +} + +//------------------------------------------------------------- +// Ethernet Polling + +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); + + gMode = 0; + + gSW.fall(&CheckSW); // InterruptIn falling edge(ModeSW) + gSetter.attach_us(&SetCV, UPDATE_INTERVAL); // Ticker SetCV + gPoller.attach_us(&NetPoll, POLLING_INTERVAL); // Ticker Polling + wait(0.5); +} + +//------------------------------------------------------------- +// SPI Transfer +// DAC8568 data word length 32bit (8bit shift out) + +void UpdateCV(unsigned char control, unsigned char address, unsigned int data) +{ + typedef unsigned char byte; + + __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(); +} + +//------------------------------------------------------------- +// Calculate CV + +void SetCV() +{ + static unsigned 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 = 65535 / 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 = 65535 / 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 = 65535 / 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_QPen: + + quan = 65535 / 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; + } + + ch++; + ch &= 0x07; +} + +//------------------------------------------------------------- +// Check SW + +void CheckSW() +{ + if (gMode < MODE_NUM - 1) + { + gMode++; + + } else { + + gMode = 0; + } +} + +//------------------------------------------------------------- +// CV meter + +void CVMeter(unsigned int ch, unsigned int level) +{ + unsigned int cvmeter; + + cvmeter = level / (SCALING_N / 7.9); + gLCD.locate ( ch, 0 ); + gLCD.putc(cvmeter); // put custom char +} + +//------------------------------------------------------------- +// 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(2.0); + + return 0; +} + +//------------------------------------------------------------- +// Handller receive UDP Packet + +void onUDPSocketEvent(UDPSocketEvent e) +{ + union OSCarg msg[10]; + static int num; + + 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; + + unsigned int absv = msg[2].f * 1; //convert -0 to 0 + + // address pattern SYNC & GATE (Type Tag int, float) + if((strncmp(msg[0].address,"/1/sync",7)==0) && (num == -1)) { + if(num > 1) break; + if(absv >= 1 || msg[2].i >= 1) gCLOCKOUT = 1; + else gCLOCKOUT = 0; + break; + + } else if ((strncmp(msg[0].address,"/1/gate",7)==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,"/1/push",7)==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/toggle",9)==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,"/1/cv",5)==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,"/1/fader",8)==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,"/1/rotary",9)==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,"/1/xy",5)==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,"/1/multixy",10)==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,"/1/multifader",13)==0) && (num != -1)) { + if(num > 7) break; + if(msg[1].typeTag[1] == 'f') gOSC_cv[num] = msg[2].f * (SCALING_N); + } + } + } + }