CVtoOSC Converter for Nucleo F303K8
Dependencies: DebouncedInterrupt MIDI mbed osc-cnmat BufferedSerial
OSC to CV Converter
http://gtbts.tumblr.com/post/142840140501/cvtoosc-converter-for-modular-synthesizer
main.cpp
- Committer:
- casiotone401
- Date:
- 2016-02-14
- Revision:
- 1:0a963a78f2bd
- Parent:
- 0:577ad245b1f5
- Child:
- 2:7997fb939580
File content as of revision 1:0a963a78f2bd:
/* CVtoOSC&MIDI Converter for Nucleo F303K8 https://developer.mbed.org/platforms/ST-Nucleo-F303K8/ */ #pragma O3 #pragma Otime #include <stdlib.h> #include <ctype.h> #include <math.h> #include <string> #include "mbed.h" #include "BufferedSerial.h" #include "DebouncedInterrupt.h" #include "MIDI.h" #include "OSCBundle.h" //------------------------------------------------------------- // Macros #define MIDI_OFFSET 4 #define MAP_OFFSET 0 #define CALIB_OFFSET 37 #define QUAN_RES1 116 // Quantize voltage Steps #define QUAN_RES2 68 #define QUAN_RES3 46 #define QUAN_RES4 40 #define QUAN_RES5 68 #define QUAN_RES6 68 #define QUAN_RES7 16 #define QUAN_RES8 58 #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 SCALE_TOTAL 9 #define SEND_BUFF_SIZE 384 // OSC send buffer size #define WAIT_SEND 20 // wait time(ms) for esp8266 transmission complete #define MIDI_CHANNEL 1 // MIDI channel //------------------------------------------------------------- // Global Variables // Silentway Calibration Data Mapping // http://www.expert-sleepers.co.uk/silentway.html // Chromatic Scale const float calibMap1[QUAN_RES1] = { 0.00076928, 0.00900736, 0.01724544, 0.02548352, 0.03372160, 0.04195968, 0.05019776, 0.05843584, 0.06667392, 0.07491200, 0.08315008, 0.09138816, 0.09962624, 0.10786432, 0.11610240, 0.12434047, 0.13258974, 0.14083999, 0.14909023, 0.15734047, 0.16559070, 0.17384095, 0.18209119, 0.19034143, 0.19859168, 0.20684192, 0.21509215, 0.22334240, 0.23159264, 0.23984288, 0.24809311, 0.25634655, 0.26460093, 0.27285531, 0.28110969, 0.28936407, 0.29761845, 0.30587283, 0.31412721, 0.32238159, 0.33063596, 0.33889034, 0.34714472, 0.35539910, 0.36365348, 0.37190786, 0.38017464, 0.38844886, 0.39672306, 0.40499726, 0.41327149, 0.42154568, 0.42981988, 0.43809411, 0.44636831, 0.45464250, 0.46291673, 0.47119093, 0.47946513, 0.48773935, 0.49601355, 0.50430328, 0.51260746, 0.52091163, 0.52921581, 0.53751999, 0.54582411, 0.55412829, 0.56243247, 0.57073665, 0.57904083, 0.58734500, 0.59564912, 0.60395330, 0.61225748, 0.62056166, 0.62890279, 0.63728637, 0.64566994, 0.65405351, 0.66243708, 0.67082065, 0.67920423, 0.68758780, 0.69597137, 0.70435494, 0.71273851, 0.72112209, 0.72950566, 0.73788923, 0.74627280, 0.75476575, 0.76334614, 0.77192658, 0.78050703, 0.78908741, 0.79766786, 0.80624831, 0.81482869, 0.82340914, 0.83198959, 0.84056997, 0.84915042, 0.85773087, 0.86631125, 0.87489170, 0.88425636, 0.89363104, 0.90300572, 0.91238040, 0.92175508, 0.93112975, 0.94050443, 0.94987911, 0.95925385, 0.96862853 }; // Major Scale const float calibMap2[QUAN_RES2] = { calibMap1[0], calibMap1[2], calibMap1[4], calibMap1[5], calibMap1[7], calibMap1[9], calibMap1[11], calibMap1[12], calibMap1[14], calibMap1[16], calibMap1[17], calibMap1[19], calibMap1[21], calibMap1[23], calibMap1[24], calibMap1[26], calibMap1[28], calibMap1[29], calibMap1[31], calibMap1[33], calibMap1[35], calibMap1[36], calibMap1[38], calibMap1[40], calibMap1[41], calibMap1[43], calibMap1[45], calibMap1[47], calibMap1[48], calibMap1[50], calibMap1[52], calibMap1[53], calibMap1[55], calibMap1[57], calibMap1[59], calibMap1[60], calibMap1[62], calibMap1[64], calibMap1[65], calibMap1[67], calibMap1[69], calibMap1[71], calibMap1[72], calibMap1[74], calibMap1[76], calibMap1[77], calibMap1[79], calibMap1[81], calibMap1[83], calibMap1[84], calibMap1[86], calibMap1[88], calibMap1[89], calibMap1[91], calibMap1[93], calibMap1[95], calibMap1[96], calibMap1[98], calibMap1[100], calibMap1[101], calibMap1[103], calibMap1[105], calibMap1[107], calibMap1[108], calibMap1[110], calibMap1[112], calibMap1[113], calibMap1[115] }; // M7(9) const float calibMap3[QUAN_RES3] = { calibMap1[0], calibMap1[4], calibMap1[7], calibMap1[11], calibMap1[12], calibMap1[14], calibMap1[16], calibMap1[19], calibMap1[23], calibMap1[24], calibMap1[26], calibMap1[28], calibMap1[31], calibMap1[35], calibMap1[36], calibMap1[38], calibMap1[40], calibMap1[43], calibMap1[47], calibMap1[48], calibMap1[50], calibMap1[52], calibMap1[55], calibMap1[59], calibMap1[60], calibMap1[62], calibMap1[64], calibMap1[67], calibMap1[71], calibMap1[72], calibMap1[76], calibMap1[79], calibMap1[83], calibMap1[84], calibMap1[86], calibMap1[88], calibMap1[91], calibMap1[95], calibMap1[96], calibMap1[100], calibMap1[103], calibMap1[107], calibMap1[108], calibMap1[110], calibMap1[112], calibMap1[115] }; // m7(9) const float calibMap4[QUAN_RES4] = { calibMap1[0], calibMap1[3], calibMap1[7], calibMap1[10], calibMap1[12], calibMap1[15], calibMap1[19], calibMap1[22], calibMap1[26], calibMap1[27], calibMap1[31], calibMap1[34], calibMap1[36], calibMap1[38], calibMap1[39], calibMap1[43], calibMap1[46], calibMap1[50], calibMap1[53], calibMap1[55], calibMap1[58], calibMap1[60], calibMap1[63], calibMap1[67], calibMap1[70], calibMap1[72], calibMap1[74], calibMap1[75], calibMap1[79], calibMap1[82], calibMap1[86], calibMap1[89], calibMap1[91], calibMap1[94], calibMap1[96], calibMap1[99], calibMap1[103], calibMap1[106], calibMap1[110], calibMap1[113] }; // Dorian Scale const float calibMap5[QUAN_RES5] = { calibMap1[0], calibMap1[2], calibMap1[3], calibMap1[5], calibMap1[7], calibMap1[9], calibMap1[10], calibMap1[12], calibMap1[14], calibMap1[15], calibMap1[17], calibMap1[19], calibMap1[20], calibMap1[21], calibMap1[24], calibMap1[26], calibMap1[27], calibMap1[29], calibMap1[31], calibMap1[33], calibMap1[34], calibMap1[36], calibMap1[38], calibMap1[39], calibMap1[41], calibMap1[43], calibMap1[45], calibMap1[46], calibMap1[48], calibMap1[50], calibMap1[51], calibMap1[53], calibMap1[55], calibMap1[57], calibMap1[58], calibMap1[60], calibMap1[62], calibMap1[63], calibMap1[65], calibMap1[67], calibMap1[69], calibMap1[70], calibMap1[72], calibMap1[74], calibMap1[75], calibMap1[77], calibMap1[79], calibMap1[81], calibMap1[82], calibMap1[84], calibMap1[86], calibMap1[87], calibMap1[89], calibMap1[91], calibMap1[93], calibMap1[94], calibMap1[96], calibMap1[98], calibMap1[99], calibMap1[101], calibMap1[103], calibMap1[105], calibMap1[106], calibMap1[108], calibMap1[110], calibMap1[111], calibMap1[113], calibMap1[115] }; // Minor Scale const float calibMap6[QUAN_RES6] = { calibMap1[0], calibMap1[2], calibMap1[3], calibMap1[5], calibMap1[7], calibMap1[8], calibMap1[10], calibMap1[12], calibMap1[14], calibMap1[15], calibMap1[17], calibMap1[19], calibMap1[20], calibMap1[22], calibMap1[24], calibMap1[26], calibMap1[27], calibMap1[29], calibMap1[31], calibMap1[32], calibMap1[34], calibMap1[36], calibMap1[38], calibMap1[39], calibMap1[41], calibMap1[43], calibMap1[44], calibMap1[46], calibMap1[48], calibMap1[50], calibMap1[51], calibMap1[53], calibMap1[55], calibMap1[56], calibMap1[58], calibMap1[60], calibMap1[62], calibMap1[63], calibMap1[65], calibMap1[67], calibMap1[68], calibMap1[70], calibMap1[72], calibMap1[74], calibMap1[75], calibMap1[77], calibMap1[79], calibMap1[80], calibMap1[82], calibMap1[84], calibMap1[86], calibMap1[87], calibMap1[89], calibMap1[91], calibMap1[92], calibMap1[94], calibMap1[96], calibMap1[98], calibMap1[99], calibMap1[101], calibMap1[103], calibMap1[104], calibMap1[106], calibMap1[108], calibMap1[110], calibMap1[111], calibMap1[113], calibMap1[115] }; // 5th const float calibMap7[QUAN_RES7] = { calibMap1[0], calibMap1[7], calibMap1[14], calibMap1[21], calibMap1[28], calibMap1[35], calibMap1[42], calibMap1[49], calibMap1[56], calibMap1[63], calibMap1[70], calibMap1[77], calibMap1[84], calibMap1[91], calibMap1[98], calibMap1[105] }; // Whole tone const float calibMap8[QUAN_RES8] = { calibMap1[0], calibMap1[1], calibMap1[2], calibMap1[6], calibMap1[8], calibMap1[10], calibMap1[12], calibMap1[14], calibMap1[16], calibMap1[18], calibMap1[20], calibMap1[22], calibMap1[24], calibMap1[26], calibMap1[28], calibMap1[30], calibMap1[32], calibMap1[34], calibMap1[36], calibMap1[38], calibMap1[40], calibMap1[42], calibMap1[44], calibMap1[46], calibMap1[48], calibMap1[50], calibMap1[52], calibMap1[54], calibMap1[56], calibMap1[58], calibMap1[60], calibMap1[62], calibMap1[64], calibMap1[66], calibMap1[68], calibMap1[70], calibMap1[72], calibMap1[74], calibMap1[76], calibMap1[78], calibMap1[80], calibMap1[82], calibMap1[84], calibMap1[86], calibMap1[88], calibMap1[90], calibMap1[92], calibMap1[94], calibMap1[96], calibMap1[98], calibMap1[100], calibMap1[102], calibMap1[104], calibMap1[106], calibMap1[108], calibMap1[110], calibMap1[112], calibMap1[114] }; // Major Scale const uint8_t calibMapMidi2[QUAN_RES2] = { 0, 2, 4, 5, 7, 9, 11, 12, 14, 16, 17, 19, 21, 23, 24, 26, 28, 29, 31, 33, 35, 36, 38, 40, 41, 43, 45, 47, 48, 50, 52, 53, 55, 57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77, 79, 81, 83, 84, 86, 88, 89, 91, 93, 95, 96, 98, 100, 101, 103, 105, 107, 108, 110, 112, 113, 115 }; // M7(9) const uint8_t calibMapMidi3[QUAN_RES3] = { 0, 4, 7, 11, 12, 14, 16, 19, 23, 24, 26, 28, 31, 35, 36, 38, 40, 43, 47, 48, 50, 52, 55, 59, 60, 62, 64, 67, 71, 72, 76, 79, 83, 84, 86, 88, 91, 95, 96, 100, 103, 107, 108, 110, 112, 115 }; // m7(9) const uint8_t calibMapMidi4[QUAN_RES4] = { 0, 3, 7, 10, 12, 15, 19, 22, 26, 27, 31, 34, 36, 38, 39, 43, 46, 50, 53, 55, 58, 60, 63, 67, 70, 72, 74, 75, 79, 82, 86, 89, 91, 94, 96, 99, 103, 106, 110, 113 }; // Dorian Scale const uint8_t calibMapMidi5[QUAN_RES5] = { 0, 2, 3, 5, 7, 9, 10, 12, 14, 15, 17, 19, 20, 21, 24, 26, 27, 29, 31, 33, 34, 36, 38, 39, 41, 43, 45, 46, 48, 50, 51, 53, 55, 57, 58, 60, 62, 63, 65, 67, 69, 70, 72, 74, 75, 77, 79, 81, 82, 84, 86, 87, 89, 91, 93, 94, 96, 98, 99, 101, 103, 105, 106, 108, 110, 111, 113, 115 }; // Minor Scale const uint8_t calibMapMidi6[QUAN_RES6] = { 0, 2, 3, 5, 7, 8, 10, 12, 14, 15, 17, 19, 20, 22, 24, 26, 27, 29, 31, 32, 34, 36, 38, 39, 41, 43, 44, 46, 48, 50, 51, 53, 55, 56, 58, 60, 62, 63, 65, 67, 68, 70, 72, 74, 75, 77, 79, 80, 82, 84, 86, 87, 89, 91, 92, 94, 96, 98, 99, 101, 103, 104, 106, 108, 110, 111, 113, 115 }; // 5th const uint8_t calibMapMidi7[QUAN_RES7] = { 0, 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84, 91, 98, 105 }; // Whole tone const uint8_t calibMapMidi8[QUAN_RES8] = { 0, 1, 2, 6, 8, 10, 12, 14, 16, 18,20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114 }; volatile bool changeFlag = false; //------------------------------------------------------------- // mbed Functions /* //Nucleo F303K8 //MCP3204 CS : pin 10 -- pin 8 (CS/SHDN) MOSI: pin 11 -- pin 9 (D in) MISO: pin 12 -- pin 10(D out) SCK : pin 13 -- pin 11(CLK) */ SPI spi(D11, D12, D13); DigitalOut csPin(D10); AnalogIn scalePin(A0); // Scale Pin AnalogIn potPin1(A1); // Pot1~2 AnalogIn potPin2(D6); DebouncedInterrupt switch1(D8); // SW1 DebouncedInterrupt switch2(D9); // SW2 // esp8266 wi-fi settings #define SSID "SSID" #define PASSWORD "PASSWORD" #define REMOTE_ADDRESS "192.168.1.2" #define REMOTE_PORT "8000" #define STATIC_IP // if you use DHCP comment out this line #define LOCAL_ADDRESS "192.168.1.8" #define LOCAL_PORT "9000" // Serial for esp8266 (tx, rx) BufferedSerial esp8266Serial(D1, D0); // Serial for debug (tx, rx) Serial pc(USBTX, USBRX); // MIDI (tx, rx) MIDI midi(A7, NC); // declare the OSC bundle OSCBundle bndl; // Timer Timer timer; //------------------------------------------------------------- // Functions int map(int, int, int, int, int); uint16_t adcRead(uint8_t); void interruptSW1(); void interruptSW2(); void getESP8266Response(); void connectWifi(); //------------------------------------------------------------- // Remaps value from one range to another range. inline int map(int x, int in_min, int in_max, int out_min, int out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } //------------------------------------------------------------- // Read MCP3208 AD Converter uint16_t adcRead(uint8_t ch) { uint8_t b1, b2; uint8_t address = 0B01100000 | ((ch & 0B11) << 2); csPin = 0; // CS pin Low spi.write(address); b1 = spi.write(0x00); b2 = spi.write(0x00); csPin = 1; // CS pin Hi return (b1 << 4) | (b2 >> 4); } //------------------------------------------------------------- // Check SW Status void interruptSW1() { volatile static uint8_t sw1State; if (!sw1State) { sw1State = 1; } else { sw1State = 0; } bndl.add("/sw1").add(sw1State); changeFlag = true; } void interruptSW2() { volatile static uint8_t sw2State; if (!sw2State) { sw2State = 1; } else { sw2State = 0; } bndl.add("/sw2").add(sw2State); changeFlag = true; } //------------------------------------------------------------- // get & print esp8266 response void getESP8266Response(int timeout) { char resBuff[256] = {0}; timer.start(); uint8_t i = 0; while (!(timer.read() > timeout)) { if (esp8266Serial.readable()) { resBuff[i++] = esp8266Serial.getc(); } } pc.printf(resBuff); timer.stop(); timer.reset(); } //------------------------------------------------------------- // Setup esp8266 wi-fi module void connectWifi() { pc.printf("\n---------- Starting ESP8266 Config ----------\r\n"); wait(2); // set station mode pc.printf("\n---------- Set Station Mode ----------\r\n"); esp8266Serial.printf("AT+CWMODE=1\r\n"); getESP8266Response(1); wait(1); // restart pc.printf("\n---------- Reset ESP8266 ----------\r\n"); esp8266Serial.printf("AT+RST\r\n"); getESP8266Response(2); wait(1); #ifdef STATIC_IP // set static IP pc.printf("\n---------- Setting Static IP ----------\r\n"); esp8266Serial.printf("AT+CIPSTA=\""); esp8266Serial.printf(LOCAL_ADDRESS); esp8266Serial.printf("\"\r\n"); getESP8266Response(3); wait(1); #endif // connect wifi pc.printf("\n---------- Wi-fi Connect ----------\r\n"); esp8266Serial.printf("AT+CWJAP=\""); esp8266Serial.printf(SSID); esp8266Serial.printf("\",\""); esp8266Serial.printf(PASSWORD); esp8266Serial.printf("\"\r\n"); getESP8266Response(3); wait(2); // single connection pc.printf("\n---------- Setting Connection Mode ----------\r\n"); esp8266Serial.printf("AT+CIPMUX=0\r\n"); getESP8266Response(1); wait(1); // set UDP connection pc.printf("\n---------- Setting UDP Connection ----------\r\n"); esp8266Serial.printf("AT+CIPSTART="); esp8266Serial.printf("\"UDP\",\""); esp8266Serial.printf(REMOTE_ADDRESS); esp8266Serial.printf("\","); esp8266Serial.printf(REMOTE_PORT); esp8266Serial.printf(","); esp8266Serial.printf(LOCAL_PORT); esp8266Serial.printf(","); esp8266Serial.printf("0\r\n"); getESP8266Response(3); wait(1); // set data transmission mode pc.printf("\n---------- Setting Data Transmission Mode ----------\r\n"); esp8266Serial.printf("AT+CIPMODE=1\r\n"); getESP8266Response(1); wait(1); // change Baud Rate pc.printf("\n---------- Change Baud Rate 4Mbps ----------\r\n"); esp8266Serial.printf("AT+UART_CUR=4000000,8,1,0,0\r\n"); getESP8266Response(3); esp8266Serial.baud(4000000); wait(1); // ready to send data pc.printf("\n---------- Ready to Send Data ----------\r\n"); esp8266Serial.printf("AT+CIPSEND\r\n"); getESP8266Response(2); wait(1); } //------------------------------------------------------------- // Main int main() { uint8_t i, idx; uint8_t oscBuff[SEND_BUFF_SIZE] = {0}; static uint8_t scale; uint8_t note[4] = {0}; static uint8_t _note[4]; uint16_t adc[4] = {0}; static uint16_t _adc[4]; float pot[2] = {0}; static float _pot[2] = {potPin1.read(), potPin2.read()}; // Init SPI (4MHz clock) spi.format(8, 0); spi.frequency(4000000); csPin = 1; // setup esp8266 wi-fi esp8266Serial.baud(115200); connectWifi(); // start MIDI (ch1) midi.begin(MIDI_CHANNEL); // start InterruptIn rising edge switch1.attach(&interruptSW1, IRQ_RISE, 10); switch2.attach(&interruptSW2, IRQ_RISE, 10); // start timer (wait untile esp8266 transmission completed) timer.start(); while (1) { scale = map(scalePin.read_u16(), 0, 51890, 0, (SCALE_TOTAL - 1)); for (i = 0; i < 4; ++i) { // read ADC adc[i] = adcRead(i); if (abs(adc[i] - _adc[i]) > 10) { changeFlag = true; switch (i) { case 0: bndl.add("/acv1").add(adc[i]); break; case 1: bndl.add("/acv2").add(adc[i]); break; case 2: bndl.add("/acv3").add(adc[i]); break; case 3: bndl.add("/acv4").add(adc[i]); break; } } if (abs(adc[i] - _adc[i]) > 31) { // MIDI quantize switch (scale) { case Lin: case Chr: note[i] = map(adc[i], 825, 3757, 0, (84 - MAP_OFFSET)) + CALIB_OFFSET; // C0(12) ~ C7(96) break; case Maj: note[i] = calibMapMidi2[map(adc[i], 0, 4095, 0, QUAN_RES2 - 1)] + MIDI_OFFSET; break; case M7: note[i] = calibMapMidi3[map(adc[i], 0, 4095, 0, QUAN_RES3 - 1)] + MIDI_OFFSET; break; case Min7: note[i] = calibMapMidi4[map(adc[i], 0, 4095, 0, QUAN_RES4 - 1)] + MIDI_OFFSET; break; case Dor: note[i] = calibMapMidi5[map(adc[i], 0, 4095, 0, QUAN_RES5 - 1)] + MIDI_OFFSET; break; case Min: note[i] = calibMapMidi6[map(adc[i], 0, 4095, 0, QUAN_RES6 - 1)] + MIDI_OFFSET; break; case S5th: note[i] = calibMapMidi7[map(adc[i], 0, 4095, 0, QUAN_RES7 - 1)] + MIDI_OFFSET; break; case Wht: note[i] = calibMapMidi8[map(adc[i], 0, 4095, 0, QUAN_RES8 - 1)] + MIDI_OFFSET; break; } if (_note[i] != note[i]) { midi.sendNoteOff(_note[i], 0, MIDI_CHANNEL); midi.sendNoteOn(note[i], 100, MIDI_CHANNEL); _note[i] = note[i]; } } _adc[i] = adc[i]; } // check potentiometer value pot[0] = potPin1.read(); pot[1] = potPin2.read(); if (fabs(pot[0] - _pot[0]) > 0.02f) { bndl.add("/pot1").add(pot[0] * 1023.0f); midi.sendControlChange(0x16, map(potPin1.read_u16(), 0, 65535, 0, 127), MIDI_CHANNEL); // microKorg Noise Level _pot[0] = pot[0]; changeFlag = true; } else if (fabs(pot[1] - _pot[1]) > 0.02f) { bndl.add("/pot2").add(pot[1] * 1023.0f); midi.sendControlChange(0x5E, map(potPin2.read_u16(), 0, 65535, 0, 127), MIDI_CHANNEL); // microKorg Delay Level _pot[1] = pot[1]; changeFlag = true; } if (changeFlag && timer.read_ms() > WAIT_SEND) { idx = 0; int len = bndl.send(oscBuff); while (len--) { // send OSC to esp8266 wifi esp8266Serial.putc(oscBuff[idx++]); } changeFlag = false; bndl.empty(); // empty the bundle to free room for a new one timer.reset(); } } }