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
main.cpp
- Committer:
- casiotone401
- Date:
- 2016-01-02
- Revision:
- 17:55e5136790a6
- Parent:
- 16:1196b8c87bb7
- Child:
- 19:467f98c51e2d
File content as of revision 17:55e5136790a6:
//------------------------------------------------------------- // TI DAC8568 OSCtoCV Converter ver.2 // http://gtbts.tumblr.com/post/125663817741/osc-to-cv-converter-ver2-mbed-osctocv // // 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 "FastIO.h" //#include "FastAnalogIn.h" #include "DebouncedInterrupt.h" #include "BurstSPI.h" //#include "BufferedSoftSerial.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 "mbedOSC.h" #include "MIDI.h" #include <stdlib.h> #include <ctype.h> #include <math.h> //------------------------------------------------------------- // Define #define MODE_Calb 0 // Calibration (for VCO Tuning) #define MODE_OSC 1 // Mode OSCtoCV #define MODE_SEQ 2 // Mode Shift Sequencer #define MODE_185 3 // Mode M185 Sequencer #define MODE_EUC 4 // Mode Euclidean Sequencer #define MODE_TOTAL 5 // 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 SCALE_NUM 9 // Count Scale #define SCALE_AOUT (65535 / SCALE_NUM - 1) #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 SPI_RATE 20000000 // 10Mbps SPI Clock #define SCALING_N 32256.0f #define INPUT_PORT 12345 // Input Port Number #define POLLING_INTERVAL 21 // 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 //------------------------------------------------------------- // Gate Sequencer Macros #define _DISABLE 0 #define _ENABLE 1 #define GATE1 0 #define GATE2 1 #define GATE3 2 #define GATE4 3 #define SUBGATE 4 #define GATE_TOTAL 5 #define INVERT 1 #define NON_INVERT 0 #define GATESOUT_ON 0 #define GATESOUT_OFF 1 #define SYNC_ON 0 #define SYNC_OFF 1 //------------------------------------------------------------- // 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 3 // triplets #define TRIP4 6 #define TRIP8 12 #define TRIP16 24 #define TRIP32 48 #define SYNC24 96 #define NRESET 0 // Gate Reset //------------------------------------------------------------- // Sequencer Macros #define STEP_INDICATOR_ADDRESS "/seqstep/" // touchOSC multi toggle(1x16(8)) for Current Step Indicator #define RESET_COUNTER_ADDRESS "/reset" // touchOSC label for Sequencer reset count //------------------------------------------------------------- // M185 Macros #define PULSE_COUNT_ADDRESS "/pulse" // /pulse1 ~ pulse8 M185 Pulse Count #define GATE_MODE_ADDRESS "/gatemode" // /gatemode1 ~ gatemode8 M185 Gate Mode #define SINGLE 0 #define MUTE 1 #define MULTI 2 #define HOLD 3 //------------------------------------------------------------- // Euclidean Sequencer Macros #define READ_DELAY 10 // for debouncing #define MAXCHANNELS 4 #define MAXSTEPS 16 // max step length #define TRIGGER_DURATION 2200 #define DISPLAY_UPDATE 2000 // how long active channel display is shown #define MATRIX_ADDRESS "/matrix/" // touchOSC multi toggle(9x16) OSC address //------------------------------------------------------------- // Functions void InitOSCCV(void); inline void NetPoll(void); inline float MapFloat(float, float, float, float, float); inline void UpdateCV(int, int, const unsigned int*); inline void CalibrationCV(void); inline void SetCV(void); inline void ShiftCVSeq(int, bool); inline void M185Seq(int, bool); inline void SendCtrlState(uint8_t, uint8_t, uint8_t); inline int GateSeq(int, int, int, int, bool, bool, bool); inline int CheckBPM(void); inline void CheckModeSW(void); inline void LCD(); inline void UpdateCVMeter(int, const unsigned int*); void WriteCustomChar(unsigned char, unsigned char*); int SetupEthNetIf(void); inline size_t strlength(const char *); inline void onUDPSocketEvent(UDPSocketEvent); inline void ReceiveArduinoOSC(void); void EuclideanSeq(int, bool, bool); unsigned int Euclid(int, int, int); inline int BitRead(uint16_t, int); uint16_t BitReadOffset(int, uint16_t, uint16_t); unsigned int ConcatBin(unsigned int, unsigned int); void Sync(int, bool); int EncodeReadN(int); int EncodeReadK(int); int EncodeReadO(int); inline char * SetMatrixAddress(int, int, bool); //------------------------------------------------------------- // 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] }; //------------------------------------------------------------- // 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_cv[16]; float gGlide; int gMode; // Variables for Control /* gCtrl[0] /ctrl1 BPM gCtrl[1] /ctrl2 Quantize mode gCtrl[3] /ctrl4 Glide gCtrl[4] /ctrl5 M185 Reset Count gCtrlSW[0] /ctrlsw1 Sequencer STOP gCtrlSW[1] /ctrlsw2 Euclidean Sequencer reset gCtrlSW[2] /ctrlsw3 Sequencer Loop gCtrlSW[3] /ctrlsw4 Euclid Seq ON gCtrlSW[4] /ctrlsw5 ASR Analog Mode float gPulseCount[8] = {0}; M185 Pulse Count float gGateMode[8] = {0}; M185 Gate Mode float gSlide[8]; M185 Slide gEucA[0] /euca1 Euclidean Pattern length (n) ch1 gEucA[1] /euca2 Euclidean Pattern density (k) ch1 gEucA[2] /euca3 Euclidean Pattern offset (o) ch1 gEucA[3] ~ [5] /euca4 ~ /euca6 Euclidean Pattern nko ch2 gEucB[0] ~ [5] /eucb1 ~ /eucb6 Euclidean Pattern nko ch3 ~ ch4 */ float gCtrl[8]; bool gCtrlSW[8] = {false}; // Variables for Sequencer float gPulseCount[8] = {0}; float gGateMode[16] = {0}; float gSlide[16]; float gAccent[16] = {0}; // Euclidean SEQ Variables float gEucA[6], gEucB[6]; int channels = MAXCHANNELS; unsigned int beat_holder[MAXCHANNELS]; unsigned int channelbeats[MAXCHANNELS][5]; bool pulses_active = false; // is active while a beat pulse is playing bool lights_active = false; int pulse_length = TRIGGER_DURATION; //pulse length unsigned int last_read[MAXCHANNELS]; unsigned int last_changed[MAXCHANNELS]; unsigned int last_sync; unsigned int euc_time; // Variables for Arduino uint16_t gArdCV[4]; uint16_t gArdCtrl[4]; bool gArdSW[4] = {false}; //------------------------------------------------------------- // mbed Functions TextLCD gLCD(p9, p10, p11, p12, p13, p14); // rs, e, d4-d7 BurstSPI gSPI(p5,p6,p7); // SPI (p6 unconnected) FastOut<p15> gSYNCMODE; // SYNC DAC8568 FastOut<p16> gLDAC; // LDAC DAC8568 DigitalOut gGATES[4] = {p21, p22, p23, p24}; // GateOut FastOut<p19> gSUBGATE; // SubGateOut FastOut<p25> gCLOCKOUT; // ClockOut AnalogOut gAOUT(p18); AnalogIn gAIN(p17); DebouncedInterrupt gSW(p30); // Mode SW // Serial for Arduino //BufferedSoftSerial ardSerial(p25, p26); MIDI midi(p28, p27); Timer gTimer; // Timer Ticker gPoller; // Ticker for Polling // Ethernet EthernetNetIf gEth; /* static ip EthernetNetIf gEth( IpAddr(192,168,1,2), IpAddr(255,255,255,0), IpAddr(192,168,1,1), IpAddr(192,168,1,1) ); */ UDPSocket gUdp; // touchOSC Address uint8_t touchOSCAddress[] = { 192, 168, 1, 7 }; int touchOSCPort = 9000; OSCClass osc; OSCMessage sendMes; //------------------------------------------------------------- // main int main() { float pot, _pot; int bpm; //Clock Up -------------------------------------------------------------------- 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 = 0x00020038; /* 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) { gGATES[i] = true; wait(0.25); } return -1; } InitOSCCV(); gCtrl[3] = _pot = pot = gMode = 0; gGlide = gAIN.read(); LCD(); gLCD.locate( 0, 1 ); gLCD.printf("12345678 G>>%3.2f", gGlide); // Main loop while (1) { LCD(); // Check Text LCD pot = gAIN.read(); // Update glide value if (!pot) // when glide pot value == 0 { // use gCtrl[3] value if (abs(gCtrl[3] - _pot) > 0.01f) { _pot = gGlide = gCtrl[3]; gLCD.locate( 9, 1 ); gLCD.printf("G>>%3.2f", gGlide); } } else if (abs(pot - _pot) > 0.01f) { _pot = gGlide = gAIN.read(); gLCD.locate( 9, 1 ); gLCD.printf("G>>%3.2f", gGlide); } bpm = CheckBPM(); // check current BPM switch (gMode) { case MODE_OSC: // OSCtoCV mode SetCV(); break; case MODE_SEQ: // Shift Sequencer mode //ReceiveArduinoOSC(); ShiftCVSeq(GateSeq(bpm, N16TH, GATE1, 3, NON_INVERT, GATESOUT_OFF, SYNC_ON), gCtrlSW[0]); GateSeq(bpm, N8TH, GATE2, 3, NON_INVERT, GATESOUT_ON, SYNC_OFF); if (gCtrlSW[3]) { EuclideanSeq(GateSeq(bpm, N16TH, SUBGATE, 1, NON_INVERT, GATESOUT_OFF, SYNC_OFF), gCtrlSW[0], GATESOUT_OFF); } break; case MODE_185: // M185 Sequencer mode M185Seq(GateSeq(bpm, N16TH, GATE1, 3, NON_INVERT, GATESOUT_OFF, SYNC_ON), gCtrlSW[0]); GateSeq(bpm, N8TH, GATE2, 3, NON_INVERT, GATESOUT_ON, SYNC_OFF); if (gCtrlSW[3]) { EuclideanSeq(GateSeq(bpm, N16TH, SUBGATE, 1, NON_INVERT, GATESOUT_OFF, SYNC_OFF), gCtrlSW[0], GATESOUT_OFF); } break; case MODE_EUC: // Euclidean Sequencer mode ShiftCVSeq(GateSeq(bpm, N1ST, SUBGATE, 3, NON_INVERT, GATESOUT_OFF, SYNC_OFF), gCtrlSW[0]); EuclideanSeq(GateSeq(bpm, N16TH, GATE1, 1, NON_INVERT, GATESOUT_OFF, SYNC_OFF), gCtrlSW[0], GATESOUT_ON); break; default: // CV Calibration mode CalibrationCV(); break; } } } //------------------------------------------------------------- // Initialize OSCtoCV void InitOSCCV() { int i; // 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 // Initialize Euclid Sequencer channelbeats[0][0] = 16; channelbeats[0][1] = 8; channelbeats[0][2] = 0; channelbeats[0][3] = 0; channelbeats[1][0] = 16; channelbeats[1][1] = 9; channelbeats[1][2] = 0; channelbeats[1][3] = 0; channelbeats[2][0] = 16; channelbeats[2][1] = 7; channelbeats[2][2] = 0; channelbeats[2][3] = 0; channelbeats[3][0] = 16; channelbeats[3][1] = 9; channelbeats[3][2] = 0; channelbeats[3][3] = 0; for (i = 0; i < channels; ++i) { beat_holder[i] = Euclid(channelbeats[i][0], channelbeats[i][1], channelbeats[i][3]); } // Init BPM gCtrl[0] = 0.398f; // Init Sequence Data for (i = 0; i < 16; ++i) { gSeq_cv[i] = calibMap1[69] * SCALING_N; } // Init M185 Reset Count gCtrl[4] = 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}); // Set OSC message for sending sendMes.setIp(touchOSCAddress); sendMes.setPort(touchOSCPort); gSW.attach(&CheckModeSW,IRQ_RISE, 30); // InterruptIn rising edge(ModeSW) wait(0.5); gPoller.attach_us(&NetPoll, POLLING_INTERVAL); // Ticker Polling wait(1.5); // Begin Serial for Arduino //ardSerial.baud(115200); } //------------------------------------------------------------- // Ethernet Polling inline void NetPoll() { Net::poll(); } //------------------------------------------------------------- // Map Function inline float MapFloat(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } //------------------------------------------------------------- // SPI Transfer // DAC8568 data word length 32bit (8bit shift out) inline void UpdateCV(int control, int address, const unsigned int *data) { 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; } } //------------------------------------------------------------- // Calibration Mode inline void CalibrationCV() { static int ch; unsigned int cv; switch (gMode) { case MODE_Calb: cv = (unsigned int)(calibMap1[69] * SCALING_N); // A880.0Hz gSUBGATE = gGATES[0] = gGATES[1] = gGATES[2] = gGATES[3] = true; UpdateCV(WRITE_UPDATE_N, ch, &cv); break; } UpdateCVMeter(ch, &cv); ++ch; ch &= 0x07; } //------------------------------------------------------------- // Calculate CV inline void SetCV() { static int ch, qmode, amode, mcount; static float glidecv[8]; unsigned int cv; static float qcv; qmode = (gCtrl[1] * (SCALE_NUM - 1)); amode = SCALE_AOUT * qmode; gAOUT.write_u16(amode); switch (qmode) { case Lin: glidecv[ch] = glidecv[ch] * gGlide + gOSC_cv[ch] * (1.0f - gGlide); break; case Chr: qcv = calibMap1[(unsigned int)MapFloat(gOSC_cv[ch], 0, SCALING_N, 0, (QUAN_RES1 - 1))]; glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide); break; case Maj: qcv = calibMap2[(unsigned int)MapFloat(gOSC_cv[ch], 0, SCALING_N, 0, (QUAN_RES2 - 1))]; glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide); break; case M7: qcv = calibMap3[(unsigned int)MapFloat(gOSC_cv[ch], 0, SCALING_N, 0, (QUAN_RES3 - 1))]; glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide); break; case Min7: qcv = calibMap4[(unsigned int)MapFloat(gOSC_cv[ch], 0, SCALING_N, 0, (QUAN_RES4 - 1))]; glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide); break; case Dor: qcv = calibMap5[(unsigned int)MapFloat(gOSC_cv[ch], 0, SCALING_N, 0, (QUAN_RES5 - 1))]; glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide); break; case Min: qcv = calibMap6[(unsigned int)MapFloat(gOSC_cv[ch], 0, SCALING_N, 0, (QUAN_RES6 - 1))]; glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide); break; case S5th: qcv = calibMap7[(unsigned int)MapFloat(gOSC_cv[ch], 0, SCALING_N, 0, (QUAN_RES7 - 1))]; glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide); break; case Wht: qcv = calibMap8[(unsigned int)MapFloat(gOSC_cv[ch], 0, SCALING_N, 0, (QUAN_RES8 - 1))]; glidecv[ch] = glidecv[ch] * gGlide + (qcv * SCALING_N) * (1.0f - gGlide); break; } cv = (unsigned int)glidecv[ch]; UpdateCV(WRITE_UPDATE_N, ch, &cv); if (mcount == 0x1F) { UpdateCVMeter(ch, &cv); } ++ch; if (ch &= 0x07) { ++mcount; mcount &= 0x3F; } } //------------------------------------------------------------- // Sequence & Shift Out CV inline void ShiftCVSeq(int trigger, bool reset) { int i, j; static bool triggerState = false; static bool stepFoward = false; static bool _reset = false; static uint8_t currentStep; static int _resetCount, resetCount; static uint8_t gateMode; static uint8_t _gateMode[16]; static uint8_t ch, qmode, amode; static float glidecv[8], shiftcv[8]; unsigned int cv; static float qcv; static int jitterCount; static int jitter; qmode = (gCtrl[1] * (SCALE_NUM - 1.0f)); // Sequencer Quantize Mode (gCtrl[1]) amode = SCALE_AOUT * qmode; gAOUT.write_u16(amode); switch (qmode) { case Lin: glidecv[0] = glidecv[0] * gSlide[currentStep] + gSeq_cv[currentStep] * (1.0f - gSlide[currentStep]); break; case Chr: qcv = calibMap1[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES1 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Maj: qcv = calibMap2[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES2 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case M7: qcv = calibMap3[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES3 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Min7: qcv = calibMap4[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES4 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Dor: qcv = calibMap5[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES5 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Min: qcv = calibMap6[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES6 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case S5th: qcv = calibMap7[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES7 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Wht: qcv = calibMap8[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES8 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; } if (!gCtrlSW[4]) { jitter = 0; } else if (gCtrlSW[4] && jitterCount % 64 == 0) { // ASR Analog Mode jitter = (rand() % 100 - 50); } cv = (unsigned int)(glidecv[0] + jitter); UpdateCV(WRITE_UPDATE_N, 0, &cv); for (i = 1; i < 8; ++i) { glidecv[i] = glidecv[i] * gSlide[currentStep] + shiftcv[i] * (1.0f - gSlide[currentStep]); cv = (unsigned int)(glidecv[i] + jitter); UpdateCV(WRITE_UPDATE_N, i, &cv); } if (trigger && !triggerState) // trigger ON { stepFoward = triggerState = true; } else if (!trigger) { // trigger OFF if (gateMode != HOLD) { gGATES[0] = false; } triggerState = false; } // check & update touchOSC ctrl parameter if (_gateMode[ch] != (gGateMode[ch] * 3)) { _gateMode[ch] = (gGateMode[ch] * 3); if (_gateMode[ch] == MULTI) { _gateMode[ch] = HOLD; } SendCtrlState(ch, _gateMode[ch], 8); } if (reset && !_reset) // Stop & Reset { sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); currentStep = 0; sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); _reset = true; } else if (!reset) { _reset = false; } if (stepFoward) { if (gateMode != HOLD) // shift CV { for (j = 1; j < 8; ++j) { shiftcv[j] = glidecv[j-1]; } } sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); ++currentStep; if (gCtrlSW[2]) { resetCount = 3; } else { resetCount = gCtrl[4] * 15; } if (_resetCount != resetCount) { sendMes.setTopAddress(RESET_COUNTER_ADDRESS); sendMes.setArgs("i", (resetCount + 1)); osc.sendOsc(&sendMes); } if (currentStep > resetCount) // reset { currentStep = 0; } sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); if (currentStep < 8) { UpdateCVMeter(currentStep, &cv); } else { UpdateCVMeter((currentStep - 8), &cv); } gateMode = (gGateMode[currentStep] * 3); if (gateMode == MULTI) // omit MULTI mode { gateMode = HOLD; } if (gateMode != MUTE) { gGATES[0] = true; } if (gAccent[currentStep]) // accent { gGATES[2] = gGATES[3] = true; } else { gGATES[2] = gGATES[3] = false; } stepFoward = false; } ++ch; ch &= 0x0F; ++jitterCount; jitterCount &= 0x1FF; } //------------------------------------------------------------- // M185 Sequencer inline void M185Seq(int trigger, bool reset) { int i, j; static bool triggerState = false; static bool stepFoward = false; static bool _reset = false; static uint8_t currentStep; static int stepCount; static int _resetCount, resetCount; static uint8_t gateMode; static uint8_t _gateMode[8]; static uint8_t _pulseCount[8]; static uint8_t ch, qmode, amode; static float glidecv[8], shiftcv[8]; unsigned int cv; static float qcv; qmode = (gCtrl[1] * (SCALE_NUM - 1)); // Sequencer Quantize Mode (gCtrl[1]) amode = SCALE_AOUT * qmode; gAOUT.write_u16(amode); switch (qmode) { case Lin: glidecv[0] = glidecv[0] * gSlide[currentStep] + gSeq_cv[currentStep] * (1.0f - gSlide[currentStep]); break; case Chr: qcv = calibMap1[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES1 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Maj: qcv = calibMap2[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES2 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case M7: qcv = calibMap3[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES3 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Min7: qcv = calibMap4[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES4 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Dor: qcv = calibMap5[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES5 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Min: qcv = calibMap6[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES6 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case S5th: qcv = calibMap7[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES7 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; case Wht: qcv = calibMap8[(unsigned int)MapFloat(gSeq_cv[currentStep], 0, SCALING_N, 0, (QUAN_RES8 - 1))]; glidecv[0] = glidecv[0] * gSlide[currentStep] + (qcv * SCALING_N) * (1.0f - gSlide[currentStep]); break; } cv = (unsigned int)glidecv[0]; UpdateCV(WRITE_UPDATE_N, 0, &cv); for (i = 1; i < 8; ++i) { glidecv[i] = glidecv[i] * gSlide[currentStep] + shiftcv[i] * (1.0f - gSlide[currentStep]); cv = (unsigned int)glidecv[i]; UpdateCV(WRITE_UPDATE_N, i, &cv); } if (trigger && !triggerState) // trigger ON { if (gateMode == MULTI) { gGATES[0] = true; sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); } stepFoward = triggerState = true; } else if (!trigger) { // trigger OFF if (gateMode != HOLD) { gGATES[0] = false; } if (gateMode == MULTI) { sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); } triggerState = false; } // check & update touchOSC ctrl parameter if (_gateMode[ch] != gGateMode[ch] * 3 || _pulseCount[ch] != gPulseCount[ch] * 7) { _gateMode[ch] = (gGateMode[ch] * 3); _pulseCount[ch] = (gPulseCount[ch] * 7); SendCtrlState(ch, _gateMode[ch], _pulseCount[ch]); } if (reset && !_reset) // Stop & Reset { sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); currentStep = 0; sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); _reset = true; } else if (!reset) { _reset = false; } if (stepFoward) { if (gateMode != HOLD) // shift CV { for (j = 1; j < 8; ++j) { shiftcv[j] = glidecv[j-1]; } } --stepCount; if (stepCount == -1) { sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); ++currentStep; if (gCtrlSW[2]) { resetCount = 3; } else { resetCount = gCtrl[4] * 7; } if (_resetCount != resetCount) { sendMes.setTopAddress(RESET_COUNTER_ADDRESS); sendMes.setArgs("i", (resetCount + 1)); osc.sendOsc(&sendMes); } if (currentStep > resetCount) // reset { currentStep = 0; } sendMes.setTopAddress(SetMatrixAddress(0, currentStep, false)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); UpdateCVMeter(currentStep, &cv); // check Pulse Count & Gate Mode stepCount = (gPulseCount[currentStep] * 7); gateMode = (gGateMode[currentStep] * 3); if (gateMode != MUTE) { gGATES[0] = true; } } stepFoward = false; } ++ch; ch &= 0x07; } //------------------------------------------------------------- // Send M185 Sequencer Status to touchOSC inline void SendCtrlState(uint8_t step, uint8_t gateMode, uint8_t stepCount) { char pulseAddress[10] = PULSE_COUNT_ADDRESS; char gateModeAddress[10] = GATE_MODE_ADDRESS; char currentStep[2]; sprintf(currentStep, "%d", step + 1); strcat(gateModeAddress, currentStep); if(stepCount != 8) { strcat(pulseAddress, currentStep); sendMes.setTopAddress(pulseAddress); sendMes.setArgs("i", (stepCount + 1)); osc.sendOsc(&sendMes); } sendMes.setTopAddress(gateModeAddress); switch (gateMode) { case SINGLE: sendMes.setArgs("s", "|"); break; case MUTE: sendMes.setArgs("s", "O"); break; case MULTI: sendMes.setArgs("s", "||"); break; case HOLD: sendMes.setArgs("s", "|-"); break; } osc.sendOsc(&sendMes); } //------------------------------------------------------------- // Gate Sequencer beat(Note values) length(Gate time) invert(invert Gate) inline int GateSeq(int bpm, int beat, int ch, int length, bool invert, bool gatesoff, bool syncoff) { int i; static int gatetime[GATE_TOTAL], oldgatetime[GATE_TOTAL]; static int _bpm, bar, sync24, oldsynctime; int time = gTimer.read_us(); if (_bpm != bpm) { if (!bpm) { beat = NRESET; } else { bar = (60.0f / bpm) * 4000000; //sync24 = (bar / 4) / 24; // sync24 not tested _bpm = bpm; } } 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: gTimer.reset(); for (i = 0; i < GATE_TOTAL; ++i) // Reset { oldsynctime = oldgatetime[i] = gatetime[i] = NRESET; } return 0; default: gatetime[ch] = bar / beat; sync24 = bar / 16; break; } if (time > oldsynctime + sync24) // sync24 not tested { if (!syncoff) { oldsynctime = time; gCLOCKOUT = true; midi.sendRealTime(Clock); // MIDI Clock } } else if (time > oldsynctime - (sync24 - 2)) { if (!syncoff) { gCLOCKOUT = false; } } if (ch == GATE_TOTAL) { return -1; } else if (time > oldgatetime[ch] + gatetime[ch] && !invert) { oldgatetime[ch] = time; if (!gatesoff) { gGATES[ch] = true; } else if (ch == SUBGATE) { gSUBGATE = true; } return 1; } else if (time > oldgatetime[ch] + gatetime[ch] && invert) { oldgatetime[ch] = time; if (!gatesoff) { gGATES[ch] = false; } else if (ch == SUBGATE) { gSUBGATE = false; } return 0; } else if (time > oldgatetime[ch] + (gatetime[ch] - gatetime[ch] / length) && !invert) { if (!gatesoff) { gGATES[ch] = false; } else if (ch == SUBGATE) { gSUBGATE = false; } return 0; } else if (time > oldgatetime[ch] + (gatetime[ch] - gatetime[ch] / length) && invert) { if (!gatesoff) { gGATES[ch] = true; } else if (ch == SUBGATE) { gSUBGATE = true; } return 1; } else { return -1; } } //------------------------------------------------------------- // Check BPM inline int CheckBPM() { static int _bpm = -1; int bpm; if (gCtrlSW[0]) { bpm = 0; return bpm; } if (!gCtrl[0]) { bpm = gArdCtrl[0] * 0.25f + 5; if (abs(bpm - _bpm) > 1) { _bpm = bpm; sendMes.setTopAddress("/bpm"); sendMes.setArgs("i", bpm); osc.sendOsc(&sendMes); } } else if (gCtrl[0]) { bpm = (gCtrl[0] * 240 + 5); if (abs(bpm - _bpm) > 1) { _bpm = bpm; sendMes.setTopAddress("/bpm"); sendMes.setArgs("i", bpm); osc.sendOsc(&sendMes); } } return bpm; } //------------------------------------------------------------- // Check Mode SW inline void CheckModeSW() { if (gMode < MODE_TOTAL - 1) { ++gMode; } else { gMode = 0; } gCLOCKOUT = gGATES[0] = gGATES[1] = gGATES[2] = gGATES[3] = false; if (gMode == MODE_SEQ || gMode == MODE_185 || gMode == MODE_EUC) { gTimer.start(); // Sequencer Timer Start midi.begin(1); } else { gTimer.stop(); // Sequencer Timer Stop } } //------------------------------------------------------------- // Print LCD Mode Status inline void LCD() { static int _mode = -1; static int _qmode = -1; static int qmode; if (_mode != gMode) { sendMes.setTopAddress("/mode"); switch (gMode) { case MODE_Calb: gLCD.locate( 9, 0 ); gLCD.printf("CLB|880"); sendMes.setArgs("s", "Calibration"); osc.sendOsc(&sendMes); sendMes.setTopAddress("/scale"); sendMes.setArgs("s", "880Hz"); _qmode = -1; break; case MODE_OSC: gLCD.locate( 9, 0 ); gLCD.printf("OSC|"); sendMes.setArgs("s", "OSCtoCV"); break; case MODE_SEQ: gLCD.locate( 9, 0 ); gLCD.printf("ASR|"); sendMes.setArgs("s", "ASR SEQ"); break; case MODE_185: gLCD.locate( 9, 0 ); gLCD.printf("185|"); sendMes.setArgs("s", "M185 SEQ"); break; case MODE_EUC: gLCD.locate( 9, 0 ); gLCD.printf("EUC|"); sendMes.setArgs("s", "Euclidean SEQ"); break; default: break; } osc.sendOsc(&sendMes); _mode = gMode; } qmode = (gCtrl[1] * (SCALE_NUM - 1)); if (_qmode != qmode) { sendMes.setTopAddress("/scale"); switch (qmode) { case Lin: gLCD.locate( 13, 0 ); gLCD.printf("lin"); sendMes.setArgs("s", "Linear"); break; case Chr: gLCD.locate( 13, 0 ); gLCD.printf("chr"); sendMes.setArgs("s", "Chromatic"); break; case Maj: gLCD.locate( 13, 0 ); gLCD.printf("maj"); sendMes.setArgs("s", "Major"); break; case M7: gLCD.locate( 13, 0 ); gLCD.printf("ma7"); sendMes.setArgs("s", "Major7"); break; case Min7: gLCD.locate( 13, 0 ); gLCD.printf("mi7"); sendMes.setArgs("s", "Minor7"); break; case Dor: gLCD.locate( 13, 0 ); gLCD.printf("dor"); sendMes.setArgs("s", "Dorian"); break; case Min: gLCD.locate( 13, 0 ); gLCD.printf("min"); sendMes.setTopAddress("/scale"); sendMes.setArgs("s", "Minor"); break; case S5th: gLCD.locate( 13, 0 ); gLCD.printf("5th"); sendMes.setArgs("s", "5th"); break; case Wht: gLCD.locate( 13, 0 ); gLCD.printf("wht"); sendMes.setArgs("s", "Whole Tone"); break; default: break; } osc.sendOsc(&sendMes); _qmode = qmode; } } //------------------------------------------------------------- // CV Meter inline void UpdateCVMeter(int ch, const unsigned int *level) { gLCD.locate ( ch, 0 ); gLCD.putc(*level * 0.0002192f); // put custom char } //------------------------------------------------------------- // Write command Custom Char LCD CGRAM for 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 OSC UDP Packet inline void onUDPSocketEvent(UDPSocketEvent e) { static union OSCarg msg[10]; static char buf[896] = {0}; static int recvlen; static int num, len, offset; int messagepos = 0; bool bundleflag = false; Host host; switch (e) { case UDPSOCKET_READABLE: // The only event for now recvlen = gUdp.recvfrom(buf, 896, &host); // packet length if (recvlen <= 0) break; if (!bundleflag && buf[0] == '#') // #bundle { messagepos += 16; // skip #bundle & timetag recvlen -= 16; bundleflag = true; } do { if (bundleflag) { messagepos += 4; recvlen -= 4; if (recvlen <= 8) { bundleflag = false; break; } } if (getOSCmsg(buf + messagepos, msg) == -1) continue; len = strlength(msg[0].address); if (isdigit(msg[0].address[len-1])) { num = msg[0].address[len-1] - '0' - 1; offset = 1; if (isdigit(msg[0].address[len-2])) { offset = 2; num += 10; } } else { num = -1; } // address pattern SYNC & GATE (Type Tag int, float) if (!strncmp(msg[0].address + (len - offset) - 4, "sync", 4)) { if (msg[2].i != 0) gCLOCKOUT = true; else gCLOCKOUT = false; continue; } else if (!strncmp(msg[0].address + (len - offset) - 4, "gate", 4) && (num != -1)) { if (num > 3) continue; if (msg[2].i != 0) gGATES[num] = true; else gGATES[num] = false; continue; // (touchOSC Control push, toggle) } else if (!strncmp(msg[0].address + (len - offset) - 5, "fader", 5) && (num != -1)) { if (num > 7) continue; gOSC_cv[num] = msg[2].f * (SCALING_N); continue; } else if (!strncmp(msg[0].address + (len - offset) - 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 - offset) -12, "multifader1/", 12) && (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 - offset) -10, "sequencer/", 10) && (num != -1)) { if (num > 15) continue; gSeq_cv[num] = msg[2].f * (SCALING_N); continue; } else if (!strncmp(msg[0].address + (len - offset) - 6, "ctrlsw", 6) && (num != -1)) { if (num > 7) continue; if (msg[2].i != 0) gCtrlSW[num] = true; else gCtrlSW[num] = false; continue; } else if (!strncmp(msg[0].address + (len - offset) - 4, "ctrl", 4) && (num != -1)) { if (num > 7) continue; gCtrl[num] = msg[2].f; continue; } else if (!strncmp(msg[0].address + (len - offset) - 9, "pulsecnt/", 9) && (num != -1)) { if (num > 7) continue; gPulseCount[num] = msg[2].f; continue; } else if (!strncmp(msg[0].address + (len - offset) - 9, "gatemode/", 9) && (num != -1)) { if (num > 15) continue; gGateMode[num] = msg[2].f; continue; } else if (!strncmp(msg[0].address + (len - offset) - 6, "slide/", 6) && (num != -1)) { if (num > 15) continue; gSlide[num] = msg[2].f; continue; } else if (!strncmp(msg[0].address + (len - offset-2) - 7, "accent/", 7) && (num != -1)) { if (isdigit(msg[0].address[len-3])) { num = msg[0].address[len-3] - '0' - 1; } gAccent[num] = msg[2].i; continue; } else if (!strncmp(msg[0].address + (len - offset-3) - 7, "accent/", 7) && (num != -1)) { if (isdigit(msg[0].address[len-3])) { num = msg[0].address[len-3] - '0' - 1; if (isdigit(msg[0].address[len-4])) { num += 10; } } gAccent[num] = msg[2].i; continue; } else if (!strncmp(msg[0].address + (len - offset) - 4, "euca", 4) && (num != -1)) { if (num > 5) continue; gEucA[num] = msg[2].f; continue; } else if (!strncmp(msg[0].address + (len - offset) - 4, "eucb", 4) && (num != -1)) { if (num > 5) continue; gEucB[num] = msg[2].f; continue; } else { continue; } } while (bundleflag); } } //------------------------------------------------------------- // Handller receive Arduino OSC Serial Packet inline void ReceiveArduinoOSC() { static union OSCarg msg[10]; static char buf[512] = {0}; static int recvlen = 0; static int num, len; //int i = 0; int messagepos = 0; bool bundleflag = false; //pc.printf("%s", buf); /* while (ardSerial.readable()) { buf[i] = ardSerial.getc(); ++i; ++recvlen; if (!bundleflag && buf[i] == '#') // #bundle { messagepos += (16 + i); // skip #bundle & timetag recvlen -= (16 + i); bundleflag = true; } } */ do { if (bundleflag) { messagepos += 4; recvlen -= 4; if (recvlen <= 2) { bundleflag = false; break; } } if (getOSCmsg(buf + messagepos, msg) == -1) continue; len = strlength(msg[0].address); if (isdigit(msg[0].address[len-1])) { num = msg[0].address[len-1] - '0' - 1; } else { num = -1; } if (!strncmp(msg[0].address+(len-1)-2, "sw", 2)) { if (num > 3) continue; if (msg[2].i != 0) gArdSW[num] = true; else gArdSW[num] = false; continue; } else if (!strncmp(msg[0].address+(len-1)-3, "pot", 3) && (num != -1)) { if (num > 3 && (msg[2].i < 1024)) continue; gArdCtrl[num] = msg[2].i; continue; } else if (!strncmp(msg[0].address+(len-1)-2, "ch", 2) && (num != -1)) { if (num > 3 && (msg[2].i < 4096)) continue; gArdCV[num] = msg[2].i; continue; } else { continue; } } while (bundleflag); } //------------------------------------------------------------- // Euclidean Sequencer void EuclideanSeq(int trigger, bool reset, bool gatesoff) { /* What's in the loop: Update euc_time variable Check to see if it is euc_time go go to sleep Changes routine - update beat_holder when channelbeats changes - triggered by changes == true Trigger routines - on trigget update displays and pulse Read encoders Read switches */ static uint8_t nn, kk, oo; static uint8_t changes[MAXCHANNELS] = {0}; static int nknob, kknob, oknob; static int _nknob, _kknob, _oknob; static bool triggerState = false; uint8_t i, ch; uint8_t maxn = MAXSTEPS; // maximums and minimums for n and k uint8_t minn = 1; uint8_t mink = 1; uint8_t mino = 0; static uint8_t active_channel; euc_time = gTimer.read_ms(); nn = channelbeats[active_channel][0]; kk = channelbeats[active_channel][1]; oo = channelbeats[active_channel][3]; // UPDATE BEAT HOLDER WHEN KNOBS ARE MOVED if (changes[active_channel]) { beat_holder[active_channel] = Euclid(nn, kk, oo); switch (changes[active_channel]) { case 1: case 3: for (int i = 0; i < MAXSTEPS; ++i) { if (BitRead(beat_holder[active_channel], nn - 1 - i) && (i < nn)) { sendMes.setTopAddress(SetMatrixAddress(active_channel * 2, i, true)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); } else { sendMes.setTopAddress(SetMatrixAddress(active_channel * 2, i, true)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); } } break; case 2: for (int i = 0; i < MAXSTEPS; ++i) { if (i < nn) { sendMes.setTopAddress(SetMatrixAddress(active_channel * 2, i, true)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); } else { sendMes.setTopAddress(SetMatrixAddress(active_channel * 2, i, true)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); } } break; default: break; } changes[active_channel] = 0; last_changed[active_channel] = gTimer.read_ms(); } // ANALOG PULSE TRIGGER if (trigger && !triggerState) { Sync(active_channel, gatesoff); triggerState = true; } else if (!trigger) { triggerState = false; } // READ K KNOB kknob = EncodeReadK(active_channel); if (_kknob != kknob) { _kknob = kknob; if (kknob != 0 && (euc_time - last_read[active_channel] > READ_DELAY)) { if ((kk + kknob) > nn) { kknob = 0; kk = nn; } else if ((kk + kknob) < mink) { kknob = 0; kk = mink; }; kk = channelbeats[active_channel][1] = (kk + kknob); // update with encoder reading last_read[active_channel] = gTimer.read_ms(); changes[active_channel] = 1; // k change = 1 } } // READ N KNOB nknob = EncodeReadN(active_channel); if (_nknob != nknob) { _nknob = nknob; if (nknob != 0 && (euc_time - last_read[active_channel] > READ_DELAY)) { if ((nn + nknob) > maxn) { nknob = 0; nn = maxn; } else if ((nn + nknob) < minn) { nknob = 0; nn = minn; }; if (kk > (nn + nknob)) {// check if new n is lower than k + reduce K if it is channelbeats[active_channel][1] = (nn + nknob); }; if (oo > (nn + nknob - 1)) {// check if new n is lower than o + reduce o if it is channelbeats[active_channel][3] = (nn + nknob - 1); }; nn = channelbeats[active_channel][0] = (nn + nknob); // update with encoder reading oo = channelbeats[active_channel][3]; last_read[active_channel] = gTimer.read_ms(); changes[active_channel] = 2; // n change = 2 } } // READ O KNOB oknob = EncodeReadO(active_channel); if (_oknob != oknob) { _oknob = oknob; if (oknob != 0 && (euc_time - last_read[active_channel] > READ_DELAY)) { // Sense check o encoder reading to prevent crashes if ((oo + oknob) > (nn - 1)) { oknob = 0; oo = (nn - 1); } else if ((oo + oknob) < mino) { oknob = 0; oo = mino; } channelbeats[active_channel][3] = (oo + oknob); last_read[active_channel] = gTimer.read_ms(); changes[active_channel] = 3; // o change = 3 } } // ENABLE RESET BUTTON ** ADD FLASH RESET HERE *** if (gCtrlSW[1] && channelbeats[active_channel][2]) { for (ch = 0; ch < channels; ++ch) { channelbeats[ch][2] = 0; } } // Stop & Reset (gCtrlSW[0]) if (reset) { for (ch = 0; ch < channels; ++ch) { channelbeats[ch][2] = 0; for (i = 0; i < MAXSTEPS; ++i) { sendMes.setTopAddress(SetMatrixAddress(ch * 2 + 1, i, true)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); } } } // TURN OFF ANY LIGHTS THAT ARE ON if ((euc_time - last_sync) > pulse_length && lights_active) { for (ch = 0; ch < channels; ++ch) { sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 3 - ch, true)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 5, true)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); } lights_active = false; } // FINISH ANY PULSES THAT ARE ACTIVE - PULSES LAST 1/4 AS LONG AS LIGHTS if (euc_time - last_sync > (pulse_length / 4) && pulses_active) { for (ch = 0; ch < channels; ++ch) { if (!gatesoff) { gGATES[ch] = false; gCLOCKOUT = false; //digitalWrite(sparepin, LOW); } } pulses_active = false; } ++active_channel; active_channel &= (channels - 1); } //------------------------------------------------------------- // Euclid calculation function unsigned int Euclid(int n, int k, int o) { // inputs: n=total, k=beats, o = offset int pauses = (n - k); int pulses = k; int offset = o; int steps = n; int per_pulse = (pauses / k); int remainder = (pauses % pulses); unsigned int workbeat[n]; unsigned int outbeat; uint16_t outbeat2; int workbeat_count = n; int a_remainder, b_remainder; int groupa, groupb; int i, j; int trim_count; for (i = 0; i < n; ++i) { // Populate workbeat with unsorted pulses and pauses if (i < pulses) { workbeat[i] = 1; } else { workbeat[i] = 0; } } if (per_pulse > 0 && remainder < 2) { // Handle easy cases where there is no or only one remainer for (i = 0; i < pulses; ++i) { for (j = (workbeat_count - 1); j > (workbeat_count - per_pulse - 1); --j) { workbeat[i] = ConcatBin(workbeat[i], workbeat[j]); } workbeat_count = (workbeat_count - per_pulse); } outbeat = 0; // Concatenate workbeat into outbeat - according to workbeat_count for (i = 0; i < workbeat_count; ++i) { outbeat = ConcatBin(outbeat, workbeat[i]); } if (offset != 0) { outbeat2 = BitReadOffset(offset, outbeat, steps); // Add offset to the step pattern } else { outbeat2 = outbeat; } return outbeat2; } else { groupa = pulses; groupb = pauses; while (groupb > 1) { //main recursive loop if (groupa > groupb) { // more Group A than Group B a_remainder = (groupa - groupb); // what will be left of groupa once groupB is interleaved trim_count = 0; for (i = 0; i < (groupa - a_remainder); ++i) { //count through the matching sets of A, ignoring remaindered workbeat[i] = ConcatBin(workbeat[i], workbeat[workbeat_count - 1 - i]); ++trim_count; } workbeat_count = (workbeat_count - trim_count); groupa = groupb; groupb = a_remainder; } else if (groupb > groupa) { // More Group B than Group A b_remainder = (groupb - groupa); // what will be left of group once group A is interleaved trim_count = 0; for (i = workbeat_count-1; i >= (groupa + b_remainder); --i) { //count from right back through the Bs workbeat[workbeat_count - i - 1] = ConcatBin(workbeat[workbeat_count - 1 - i], workbeat[i]); ++trim_count; } workbeat_count = (workbeat_count - trim_count); groupb = b_remainder; } else if (groupa == groupb) { // groupa = groupb trim_count = 0; for (i = 0; i < groupa; ++i) { workbeat[i] = ConcatBin(workbeat[i], workbeat[workbeat_count - 1 - i]); ++trim_count; } workbeat_count = (workbeat_count - trim_count); groupb = 0; } } outbeat = 0; // Concatenate workbeat into outbeat - according to workbeat_count for (i = 0; i < workbeat_count; ++i) { outbeat = ConcatBin(outbeat, workbeat[i]); } if (offset != 0) { outbeat2 = BitReadOffset(offset, outbeat, steps); // Add offset to the step pattern } else { outbeat2 = outbeat; } return outbeat2; } } //------------------------------------------------------------- // Reads a bit of a number inline int BitRead(uint16_t b, int bitPos) { int x; x = b & (1 << bitPos); return x == 0 ? 0 : 1; } //------------------------------------------------------------- // Function to right rotate n by d bits uint16_t BitReadOffset(int shift, uint16_t value, uint16_t pattern_length) { uint16_t mask = ((1 << pattern_length) - 1); value &= mask; return ((value >> shift) | (value << (pattern_length - shift))) & mask; } //------------------------------------------------------------- // Function to find the binary length of a number by counting bitwise int findlength(unsigned int bnry) { bool lengthfound = false; int i; int length = 1; // no number can have a length of zero - single 0 has a length of one, but no 1s for the sytem to count for (i = 32; i >= 0; i--) { if ((BitRead(bnry, i)) && !lengthfound) { length = (i + 1); lengthfound = true; } } return length; } //------------------------------------------------------------- // Function to concatenate two binary numbers bitwise unsigned int ConcatBin(unsigned int bina, unsigned int binb) { int binb_len = findlength(binb); unsigned int sum = (bina << binb_len); sum = sum | binb; return sum; } //------------------------------------------------------------- // routine triggered by each beat void Sync(int active_channel, bool gatesoff) { int read_head, erase; int rand_vel, rand_len; int ch, i; static int masterclock; if (masterclock % 2 == 0) { sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 7, true)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); } else { sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 7, true)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); } // Cycle through channels for (ch = 0; ch < channels; ++ch) { read_head = (channelbeats[ch][0] - channelbeats[ch][2] - 1); if (ch != active_channel || (euc_time - last_changed[active_channel]) > DISPLAY_UPDATE) { if (channelbeats[ch][2] < MAXSTEPS) { for (i = 0; i < MAXSTEPS; ++i) { if (BitRead(beat_holder[ch],channelbeats[ch][0] - 1 - i) && i < channelbeats[ch][0]) { sendMes.setTopAddress(SetMatrixAddress(ch * 2, i, true)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); } else { sendMes.setTopAddress(SetMatrixAddress(ch * 2, i, true)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); } } } } if (channelbeats[ch][2]) { if (!masterclock) { erase = MAXSTEPS - 1; } else { erase = masterclock - 1; } sendMes.setTopAddress(SetMatrixAddress((ch * 2) + 1, erase, true)); sendMes.setArgs("i", 0); osc.sendOsc(&sendMes); sendMes.setTopAddress(SetMatrixAddress((ch * 2) + 1, masterclock, true)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); } // turn on pulses on channels where a beat is present if (BitRead(beat_holder[ch], read_head)) { if (!gatesoff) { gGATES[ch] = true; // pulse out } sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 3 - ch, true)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); lights_active = pulses_active = true; if (!ch || (ch == 2)) { rand_vel = 127 - (rand() / (RAND_MAX / 40)); // random velocity ch1, ch3 } else { rand_vel = 95 - (rand() / (RAND_MAX / 70)); // random velocity ch2, ch4 } rand_len = 127 - (rand() / (RAND_MAX / 110)); // random Amp EG Decay midi.sendControlChange(0x07, rand_vel, (ch + 1)); // volca sample Vol midi.sendControlChange(0x30, rand_len, (ch + 1)); // volca sample Amp EG Decay midi.sendNoteOn(0, 127, (ch + 1)); // volca sample trriger on } // send off pulses to spare output for the first channel if (!(BitRead(beat_holder[ch], read_head)) && !ch) { // only relates to first channel if (!gatesoff) { gCLOCKOUT = true; } sendMes.setTopAddress(SetMatrixAddress((MAXCHANNELS * 2), 5, true)); sendMes.setArgs("i", 1); osc.sendOsc(&sendMes); lights_active = pulses_active = true; } // move counter to next position, ready for next pulse ++channelbeats[ch][2]; if ((channelbeats[ch][2]) >= (channelbeats[ch][0])) { channelbeats[ch][2] = 0; } } ++masterclock; masterclock &= (MAXSTEPS - 1); pulse_length = ((euc_time - last_sync) / 5); last_sync = euc_time; } /* 3 functions to read each encoder returns +1, 0 or -1 dependent on direction Contains no internal debounce, so calls should be delayed */ //------------------------------------------------------------- // Check Euclidean Seq N(length) Value int EncodeReadN(int ch) { static float _enc[4]; int result = 0; switch (ch) { case 0: if (gEucA[0] == 0) { _enc[ch] = result = 0; } else if (gEucA[0] < _enc[ch]) { result = -1; _enc[ch] = gEucA[0]; } else if (gEucA[0] > _enc[ch]) { result = 1; _enc[ch] = gEucA[0]; } break; case 1: if (gEucA[3] == 0) { _enc[ch] = result = 0; } else if (gEucA[3] < _enc[ch]) { result = -1; _enc[ch] = gEucA[3]; } else if (gEucA[3] > _enc[ch]) { result = 1; _enc[ch] = gEucA[3]; } break; case 2: if (gEucB[0] == 0) { _enc[ch] = result = 0; } else if (gEucB[0] < _enc[ch]) { result = -1; _enc[ch] = gEucB[0]; } else if (gEucB[0] > _enc[ch]) { result = 1; _enc[ch] = gEucB[0]; } break; case 3: if (gEucB[3] == 0) { _enc[ch] = result = 0; } else if (gEucB[3] < _enc[ch]) { result = -1; _enc[ch] = gEucB[3]; } else if (gEucB[3] > _enc[ch]) { result = 1; _enc[ch] = gEucB[3]; } break; default: break; } return result; } //------------------------------------------------------------- // Check Euclidean Seq K(Density) Value int EncodeReadK(int ch) { static float _enc[4]; int result = 0; switch (ch) { case 0: if (gEucA[1] == 0) { _enc[ch] = result = 0; } else if (gEucA[1] < _enc[ch]) { result = -1; _enc[ch] = gEucA[1]; } else if (gEucA[1] > _enc[ch]) { result = 1; _enc[ch] = gEucA[1]; } break; case 1: if (gEucA[4] == 0) { _enc[ch] = result = 0; } else if (gEucA[4] < _enc[ch]) { result = -4; _enc[ch] = gEucA[4]; } else if (gEucA[4] > _enc[ch]) { result = 4; _enc[ch] = gEucA[4]; } break; case 2: if (gEucB[1] == 0) { _enc[ch] = result = 0; } else if (gEucB[1] < _enc[ch]) { result = -1; _enc[ch] = gEucB[1]; } else if (gEucB[1] > _enc[ch]) { result = 1; _enc[ch] = gEucB[1]; } break; case 3: if (gEucB[4] == 0) { _enc[ch] = result = 0; } else if (gEucB[4] < _enc[ch]) { result = -1; _enc[ch] = gEucB[4]; } else if (gEucB[4] > _enc[ch]) { result = 1; _enc[ch] = gEucB[4]; } break; default: break; } return result; } //------------------------------------------------------------- // Check Euclidean Seq O(Offset) Value int EncodeReadO(int ch) { static float _enc[4]; int result = 0; switch (ch) { case 0: if (gEucA[2] == 0) { _enc[ch] = result = 0; } else if (gEucA[2] < _enc[ch]) { result = -1; _enc[ch] = gEucA[2]; } else if (gEucA[2] > _enc[ch]) { result = 1; _enc[ch] = gEucA[2]; } break; case 1: if (gEucA[5] == 0) { _enc[ch] = result = 0; } else if (gEucA[5] < _enc[ch]) { result = -1; _enc[ch] = gEucA[5]; } else if (gEucA[5] > _enc[ch]) { result = 1; _enc[ch] = gEucA[5]; } break; case 2: if (gEucB[2] == 0) { _enc[ch] = result = 0; } else if (gEucB[2] < _enc[ch]) { result = -1; _enc[ch] = gEucB[2]; } else if (gEucB[2] > _enc[ch]) { result = 1; _enc[ch] = gEucB[2]; } break; case 3: if (gEucB[5] == 0) { _enc[ch] = result = 0; } else if (gEucB[5] < _enc[ch]) { result = -1; _enc[ch] = gEucB[5]; } else if (gEucB[5] > _enc[ch]) { result = 1; _enc[ch] = gEucB[5]; } break; default: break; } return result; } inline char * SetMatrixAddress(int row, int column, bool euclid) { static char address[32]; char col[2]; char ch[2]; if (euclid) { strcpy(address, MATRIX_ADDRESS); } else { strcpy(address, STEP_INDICATOR_ADDRESS); } sprintf(col, "%d", column + 1); strcat(address, col); if (euclid) { strcat(address, "/"); sprintf(ch, "%d", row + 1); strcat(address, ch); } else { strcat(address, "/1"); } return address; }