├── .gitattributes ├── 2.jpg ├── 3.PNG ├── Beatmaster2k.ino ├── README.md ├── audioFunctions.cpp ├── audioFunctions.h ├── launchPad.cpp ├── launchPad.h ├── midiFunctions.cpp ├── midiFunctions.h ├── pcb ├── midiSplitter │ └── midiSplitter │ │ ├── fp-info-cache │ │ ├── fp-lib-table │ │ ├── midiSplitter-backups │ │ ├── midiSplitter-2022-11-21_195521.zip │ │ ├── midiSplitter-2022-11-21_200521.zip │ │ ├── midiSplitter-2022-11-21_201521.zip │ │ ├── midiSplitter-2022-11-21_202540.zip │ │ ├── midiSplitter-2022-11-21_203139.zip │ │ ├── midiSplitter-2022-11-22_195557.zip │ │ ├── midiSplitter-2022-11-22_200557.zip │ │ ├── midiSplitter-2022-11-22_201557.zip │ │ ├── midiSplitter-2022-11-22_202557.zip │ │ ├── midiSplitter-2022-11-22_203557.zip │ │ ├── midiSplitter-2022-11-23_203649.zip │ │ └── midiSplitter-2022-12-05_123926.zip │ │ ├── midiSplitter.kicad_pcb │ │ ├── midiSplitter.kicad_prl │ │ ├── midiSplitter.kicad_pro │ │ └── midiSplitter.kicad_sch └── sm2k_breakout │ ├── fp-info-cache │ ├── sm2k_breakout-backups │ ├── sm2k_breakout-2022-10-06_202728.zip │ ├── sm2k_breakout-2022-10-06_203728.zip │ ├── sm2k_breakout-2022-10-06_204749.zip │ ├── sm2k_breakout-2022-10-06_210152.zip │ ├── sm2k_breakout-2022-10-06_211341.zip │ ├── sm2k_breakout-2022-10-07_100752.zip │ ├── sm2k_breakout-2022-10-07_102006.zip │ ├── sm2k_breakout-2022-10-07_103006.zip │ ├── sm2k_breakout-2022-10-07_104006.zip │ ├── sm2k_breakout-2022-10-07_105207.zip │ ├── sm2k_breakout-2022-10-08_123534.zip │ ├── sm2k_breakout-2022-10-08_124534.zip │ ├── sm2k_breakout-2022-10-08_125225.zip │ ├── sm2k_breakout-2022-10-09_074246.zip │ ├── sm2k_breakout-2022-10-25_201056.zip │ ├── sm2k_breakout-2022-11-06_082357.zip │ ├── sm2k_breakout-2022-11-06_155041.zip │ ├── sm2k_breakout-2022-11-06_155700.zip │ ├── sm2k_breakout-2022-11-06_160354.zip │ ├── sm2k_breakout-2022-11-07_174113.zip │ ├── sm2k_breakout-2022-11-07_175113.zip │ ├── sm2k_breakout-2022-11-07_175824.zip │ ├── sm2k_breakout-2022-11-07_204841.zip │ ├── sm2k_breakout-2022-11-07_205841.zip │ ├── sm2k_breakout-2022-11-12_120407.zip │ └── sm2k_breakout-2022-11-21_174555.zip │ ├── sm2k_breakout.kicad_pcb │ ├── sm2k_breakout.kicad_prl │ ├── sm2k_breakout.kicad_pro │ └── sm2k_breakout.kicad_sch ├── sdMgr.cpp ├── sdMgr.h ├── sequencer.cpp ├── sequencer.h ├── src ├── SM2k_control_wm8731.cpp ├── SM2k_control_wm8731.h ├── effect_dynamics.cpp ├── effect_dynamics.h ├── effect_ensemble.cpp ├── effect_ensemble.h ├── effect_platervbstereo.cpp ├── effect_platervbstereo.h └── samples │ ├── AudioSampleBd_linn.cpp │ ├── AudioSampleBd_linn.h │ ├── AudioSampleHh_rytmmpc60_2.cpp │ ├── AudioSampleHh_rytmmpc60_2.h │ ├── AudioSampleHho_rytmmpc60_3.cpp │ ├── AudioSampleHho_rytmmpc60_3.h │ ├── AudioSampleSd_linn.cpp │ ├── AudioSampleSd_linn.h │ ├── AudioSampleTom_linn.cpp │ ├── AudioSampleTom_linn.h │ ├── AudioSampleTomh_linn.cpp │ ├── AudioSampleTomh_linn.h │ ├── AudioSampleTomhh_linn.cpp │ ├── AudioSampleTomhh_linn.h │ ├── AudioSampleToml_linn.cpp │ └── AudioSampleToml_linn.h ├── track.cpp ├── track.h ├── ui.cpp └── ui.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/2.jpg -------------------------------------------------------------------------------- /3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/3.PNG -------------------------------------------------------------------------------- /Beatmaster2k.ino: -------------------------------------------------------------------------------- 1 | #include "midiFunctions.h" 2 | #include "audioFunctions.h" 3 | #include "sequencer.h" 4 | #include "ui.h" 5 | #include "sdMgr.h" 6 | 7 | void setup() 8 | { 9 | Serial.println(F("START")); 10 | if (CrashReport) Serial.print(CrashReport); 11 | setupUI(); 12 | setupMidi(); 13 | setupAudio(); 14 | initSDcard(); 15 | initSequencer(); 16 | } 17 | 18 | void loop() 19 | { 20 | updateMidi(); 21 | updateUI(); 22 | updateSequencer(); 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Beatmaster2k 2 | 3 | A rudimentary step sequencer based on a Teensy 4.1 and a Novation Launchpad. 4 | It is mainly intended as a "jamming partner", not a full-fledged production tool. 5 | 6 | Some basic features/specs: 7 | 8 | - 7 Tracks. Can be set to output midi on serial, device or host ports 9 | - 8 Patterns per track currently, but lots of room for expansion 10 | - 64 Scenes, i.e combination of patterns 11 | 12 | - Patterns are edited on the Launchpad. Works during playing 13 | - Patterns can be set to any length between 1 and 128 steps 14 | - Patterns have configurable speed 15 | 16 | - Patterns and other settings stored on SD card 17 | 18 | Component list: 19 | - Teensy 4.1 20 | - SD card 21 | - ILI9341 display (touch is not used atm) 22 | - 2 encoders 23 | - 8 buttons via an MPC23017 expander 24 | - Powered usb hub 25 | - Novation Launchpad Mini Mk3 26 | 27 | 28 | ![prototype1](2.jpg) 29 | 30 | (prototype assembly) 31 | 32 | ![cad](3.PNG) 33 | 34 | (parts for 3D print and milling in Onshape) 35 | -------------------------------------------------------------------------------- /audioFunctions.cpp: -------------------------------------------------------------------------------- 1 | #include "audioFunctions.h" 2 | 3 | SM2k_AudioControlWM8731 codecControl1; 4 | //SM2k_AudioControlWM8731 codecControl2; 5 | 6 | // GUItool: begin automatically generated code 7 | AudioPlayMemory player[NR_PLAYERS]; 8 | AudioMixer4 playerMixL1; 9 | AudioMixer4 playerMixL2; 10 | AudioMixer4 playerMixR1; 11 | AudioMixer4 playerMixR2; 12 | AudioMixer4 playerMixL3; 13 | AudioMixer4 playerMixR3; 14 | AudioEffectDynamics dynamicsL; 15 | AudioEffectDynamics dynamicsR; 16 | AudioEffectPlateReverb reverb; 17 | 18 | AudioMixer4 finalMixL; 19 | AudioMixer4 finalMixR; 20 | 21 | AudioOutputI2S i2s1; 22 | //AudioOutputI2SQuad i2s_quad1; 23 | 24 | AudioConnection patchCord1(player[0], 0, playerMixL1, 0); 25 | AudioConnection patchCord2(player[0], 0, playerMixR1, 0); 26 | AudioConnection patchCord3(player[1], 0, playerMixL1, 1); 27 | AudioConnection patchCord4(player[1], 0, playerMixR1, 1); 28 | AudioConnection patchCord5(player[2], 0, playerMixL1, 2); 29 | AudioConnection patchCord6(player[2], 0, playerMixR1, 2); 30 | AudioConnection patchCord7(player[3], 0, playerMixL1, 3); 31 | AudioConnection patchCord8(player[3], 0, playerMixR1, 3); 32 | 33 | AudioConnection patchCord9(player[4], 0, playerMixL2, 0); 34 | AudioConnection patchCord10(player[4], 0, playerMixR2, 0); 35 | AudioConnection patchCord11(player[5], 0, playerMixL2, 1); 36 | AudioConnection patchCord12(player[5], 0, playerMixR2, 1); 37 | AudioConnection patchCord13(player[6], 0, playerMixL2, 2); 38 | AudioConnection patchCord14(player[6], 0, playerMixR2, 2); 39 | AudioConnection patchCord15(player[7], 0, playerMixL2, 3); 40 | AudioConnection patchCord16(player[7], 0, playerMixR2, 3); 41 | 42 | AudioConnection patchCord17(playerMixL1, 0, playerMixL3, 0); 43 | AudioConnection patchCord18(playerMixL2, 0, playerMixL3, 1); 44 | AudioConnection patchCord19(playerMixR1, 0, playerMixR3, 0); 45 | AudioConnection patchCord20(playerMixR2, 0, playerMixR3, 1); 46 | 47 | AudioConnection patchCord21(playerMixL3, 0, dynamicsL, 0); 48 | AudioConnection patchCord22(playerMixR3, 0, dynamicsR, 0); 49 | 50 | AudioConnection patchCord23(dynamicsL, 0, finalMixL, 0); // dry L 51 | AudioConnection patchCord24(dynamicsR, 0, finalMixR, 0); // dry R 52 | 53 | AudioConnection patchCord25(dynamicsL, 0, reverb, 0); // reverb send L 54 | AudioConnection patchCord26(dynamicsR, 0, reverb, 1); // reverb send R 55 | 56 | AudioConnection patchCord27(reverb, 0, finalMixL, 1); // reverb return L 57 | AudioConnection patchCord28(reverb, 1, finalMixR, 1); // reverb return R 58 | 59 | AudioConnection patchCord29(finalMixL, 0, i2s1, 0); 60 | AudioConnection patchCord30(finalMixR, 0, i2s1, 1); 61 | 62 | 63 | String sampleNames[NR_SAMPLES] = {"Bd Linn", "Sd Linn", "Hh Mpc60", "Ho Mpc60", "T0 Linn", "T1 Linn", "T2 Linn", "T3 Linn"}; 64 | 65 | const unsigned int * samples[NR_SAMPLES] = { 66 | AudioSampleBd_linn, 67 | AudioSampleSd_linn, 68 | AudioSampleHh_rytmmpc60_2, 69 | AudioSampleHho_rytmmpc60_3, 70 | AudioSampleToml_linn, 71 | AudioSampleTom_linn, 72 | AudioSampleTomh_linn, 73 | AudioSampleTomhh_linn 74 | }; 75 | 76 | drumKitParameters DrumKitParameters; 77 | 78 | void setupAudio() 79 | { 80 | Serial.println(F("AUDIO SETUP")); 81 | AudioMemory(196); 82 | 83 | codecControl1.setAddress(0x1A); 84 | //codecControl2.setAddress(0x1B); 85 | codecControl1.enable(); 86 | //codecControl2.enable(); 87 | 88 | reverb.size(DrumKitParameters.reverb_size); 89 | reverb.hidamp(DrumKitParameters.reverb_hidamp); 90 | reverb.lodamp(DrumKitParameters.reverb_lodamp); 91 | reverb.lowpass(DrumKitParameters.reverb_lowpass); 92 | reverb.diffusion(DrumKitParameters.reverb_diffusion); 93 | 94 | for (uint8_t playerId = 0; playerId < NR_PLAYERS - 1; playerId++) 95 | { 96 | setPlayerMix(playerId); 97 | } 98 | 99 | finalMixL.gain(0, DrumKitParameters.mainOut_dry); 100 | finalMixR.gain(0, DrumKitParameters.mainOut_dry); 101 | 102 | finalMixL.gain(1, DrumKitParameters.mainOut_reverb); 103 | finalMixR.gain(1, DrumKitParameters.mainOut_reverb); 104 | 105 | dynamicsL.compression(DrumKitParameters.dynThreshold, DrumKitParameters.dynAttack, 106 | DrumKitParameters.dynRelease, DrumKitParameters.dynRatio, 107 | DrumKitParameters.dynKneeWidth, DrumKitParameters.dynInGain); 108 | 109 | dynamicsR.compression(DrumKitParameters.dynThreshold, DrumKitParameters.dynAttack, 110 | DrumKitParameters.dynRelease, DrumKitParameters.dynRatio, 111 | DrumKitParameters.dynKneeWidth, DrumKitParameters.dynInGain); 112 | 113 | } 114 | 115 | void voiceNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) 116 | { 117 | uint8_t playerId = constrain(note, 0, NR_PLAYERS - 1); 118 | uint8_t sampleId = DrumKitParameters.sampleId[playerId]; 119 | if (DrumKitParameters.choke[playerId] > -1) player[DrumKitParameters.choke[playerId]].stop(); 120 | player[playerId].play(samples[sampleId]); 121 | } 122 | 123 | void voiceNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) 124 | { 125 | 126 | } 127 | 128 | void setPlayerLevel(uint8_t playerId, float level) 129 | { 130 | DrumKitParameters.level[playerId] = level; 131 | setPlayerMix(playerId); 132 | } 133 | 134 | void setPlayerPanning(uint8_t playerId, float pan) 135 | { 136 | DrumKitParameters.pan[playerId] = (int)pan; 137 | setPlayerMix(playerId); 138 | } 139 | 140 | void setPlayerMix(uint8_t playerId) 141 | { 142 | float level = DrumKitParameters.level[playerId]; 143 | float pan = DrumKitParameters.pan[playerId]; 144 | float levelR = level; 145 | float levelL = level; 146 | if (pan > 0) levelL = level * (1.0 - pan / 64.0); 147 | if (pan < 0) levelR = level * (1.0 - pan / 64.0); 148 | 149 | if (playerId < 4) 150 | { 151 | playerMixL1.gain(playerId, levelL); 152 | playerMixR1.gain(playerId, levelR); 153 | } 154 | else 155 | { 156 | playerMixL2.gain(playerId - 4, levelL); 157 | playerMixR2.gain(playerId - 4, levelR); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /audioFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "src/SM2k_control_wm8731.h" 5 | #include "src/effect_dynamics.h" 6 | #include "src/effect_platervbstereo.h" 7 | 8 | // --- Bd --- 9 | #include "src/samples/AudioSampleBd_linn.h" 10 | 11 | // --- Sd --- 12 | #include "src/samples/AudioSampleSd_linn.h" 13 | 14 | // --- Hh --- 15 | #include "src/samples/AudioSampleHh_rytmmpc60_2.h" 16 | 17 | // --- Ho --- 18 | #include "src/samples/AudioSampleHho_rytmmpc60_3.h" 19 | 20 | // --- Cy --- 21 | 22 | // --- Tm --- 23 | #include "src/samples/AudioSampleToml_linn.h" 24 | #include "src/samples/AudioSampleTom_linn.h" 25 | #include "src/samples/AudioSampleTomh_linn.h" 26 | #include "src/samples/AudioSampleTomhh_linn.h" 27 | 28 | #define NR_PLAYERS 8 29 | #define NR_SAMPLES 8 30 | 31 | struct drumKitParameters 32 | { 33 | uint8_t sampleId[NR_PLAYERS] = {0, 1, 2, 3, 4, 5, 6, 7}; 34 | 35 | float level[NR_PLAYERS] = {0.8, 0.8, 0.2, 0.2, 0.8, 0.8, 0.8, 0.8,}; 36 | int pan[NR_PLAYERS] = {0, 0, 0, 0, -60, -25, 25, 60}; 37 | int choke[NR_PLAYERS] = {-1, -1, 3, 2, -1, -1, -1, -1}; 38 | 39 | float dynThreshold = -2.0; 40 | float dynAttack = 0.03; 41 | float dynRelease = 0.25; 42 | float dynRatio = 2; 43 | float dynKneeWidth = 0.0; 44 | float dynInGain = 0.0; 45 | 46 | float reverb_size = 0.75; 47 | float reverb_hidamp = 0.7; 48 | float reverb_lodamp = 0.7; 49 | float reverb_lowpass = 0.4; 50 | float reverb_diffusion = 0.65; 51 | 52 | float mainOut_dry = 1.0; 53 | float mainOut_reverb = 0.4; 54 | 55 | }; 56 | 57 | void setupAudio(); 58 | 59 | void voiceNoteOn(uint8_t channel, uint8_t note, uint8_t velocity); 60 | void voiceNoteOff(uint8_t channel, uint8_t note, uint8_t velocity); 61 | 62 | void setPlayerLevel(uint8_t playerId, float level); 63 | void setPlayerPanning(uint8_t playerId, float pan); 64 | void setPlayerMix(uint8_t playerId); 65 | -------------------------------------------------------------------------------- /launchPad.cpp: -------------------------------------------------------------------------------- 1 | #include "launchPad.h" 2 | 3 | LaunchPad::LaunchPad(uint8_t LP_id) 4 | { 5 | _LP_id = LP_id; 6 | shift = false; 7 | page = 0; 8 | yScrollStep = 4; 9 | } 10 | 11 | void LaunchPad::begin(MIDIDevice_BigBuffer *midiDevice) 12 | { 13 | _midiDevice = midiDevice; 14 | } 15 | 16 | void LaunchPad::setProgrammerMode() 17 | { 18 | uint8_t sysexData[] = {0, 32, 41, 2, 13, 0, 127}; 19 | _midiDevice->sendSysEx(7, sysexData, false); 20 | //_midiDevice->send_now(); 21 | } 22 | 23 | void LaunchPad::setColumnColor(uint8_t column, uint8_t color) 24 | { 25 | uint8_t sysexData[] = {0, 32, 41, 2, 13, 3, 0, (uint8_t)(11 + column), color, 0, (uint8_t)(21 + column), color, 0, (uint8_t)(31 + column), color, 0, (uint8_t)(41 + column), color, 0, (uint8_t)(51 + column), color, 0, (uint8_t)(61 + column), color, 0, (uint8_t)(71 + column), color, 0, (uint8_t)(81 + column), color}; 26 | _midiDevice->sendSysEx(sizeof(sysexData), sysexData, false); 27 | _midiDevice->send_now(); 28 | } 29 | 30 | void LaunchPad::setRowColor(uint8_t row, uint8_t color) 31 | { 32 | uint8_t sysexData[] = {0, 32, 41, 2, 13, 3, 0, (uint8_t)(11 + row * 10), color, 0, (uint8_t)(11 + row * 10 + 1), color, 0, (uint8_t)(11 + row * 10 + 2), color, 0, (uint8_t)(11 + row * 10 + 3), color, 0, (uint8_t)(11 + row * 10 + 4), color, 0, (uint8_t)(11 + row * 10 + 5), color, 0, (uint8_t)(11 + row * 10 + 6), color, 0, (uint8_t)(11 + row * 10 + 7), color}; 33 | _midiDevice->sendSysEx(sizeof(sysexData), sysexData, false); 34 | _midiDevice->send_now(); 35 | } 36 | 37 | void LaunchPad::setPadColor(uint8_t padId, uint8_t color) 38 | { 39 | uint8_t sysexData[] = {0, 32, 41, 2, 13, 3, 0, padId, color}; 40 | _midiDevice->sendSysEx(9, sysexData, false); 41 | _midiDevice->send_now(); 42 | } 43 | 44 | void LaunchPad::setPadColorRGB(uint8_t padId, uint8_t R, uint8_t G, uint8_t B) 45 | { 46 | uint8_t sysexData[] = {0, 32, 41, 2, 13, 3, 3, padId, R, G, B}; 47 | _midiDevice->sendSysEx(11, sysexData, false); 48 | _midiDevice->send_now(); 49 | } 50 | 51 | void LaunchPad::setMultiplePadColorState(uint8_t padStateArray[], uint8_t arrayLength) 52 | { 53 | uint8_t sysexData[arrayLength + 6]; 54 | uint8_t sysexHeader[6] = {0, 32, 41, 2, 13, 3}; 55 | memcpy(sysexData, sysexHeader, 6); 56 | memcpy(&sysexData[6], padStateArray, arrayLength); 57 | _midiDevice->sendSysEx(arrayLength + 6, sysexData, false); 58 | _midiDevice->send_now(); 59 | } 60 | 61 | void LaunchPad::setPadColorFlashing(uint8_t padId, uint8_t color) 62 | { 63 | // uint8_t sysexData[] = {0, 32, 41, 2, 13, 3, 2, padId, color}; // pulsing 64 | uint8_t sysexData[] = {0, 32, 41, 2, 13, 3, 1, padId, color, LP_OFF}; // flashing 65 | _midiDevice->sendSysEx(10, sysexData, false); 66 | _midiDevice->send_now(); 67 | } 68 | 69 | void LaunchPad::resetPage() 70 | { 71 | for (uint8_t column = 0; column < 7; column++) 72 | { 73 | setColumnColor(column, LP_OFF); 74 | } 75 | } 76 | 77 | void LaunchPad::setSliderHorizontal(uint8_t row, uint8_t rightmostPad, uint8_t color) 78 | { 79 | rightmostPad = constrain(rightmostPad, 0, 7); 80 | for (uint8_t column = 0; column <= rightmostPad; column++) 81 | { 82 | uint8_t padId = 11 + 10 * row + column; 83 | setPadColor(padId, color); 84 | } 85 | for (uint8_t column = rightmostPad + 1; column < 8; column++) 86 | { 87 | uint8_t padId = 11 + 10 * row + column; 88 | setPadColor(padId, LP_OFF); 89 | } 90 | } 91 | 92 | void LaunchPad::setPageIndicator(uint8_t page, uint8_t color) 93 | { 94 | for (uint8_t column = 0; column <= page; column++) 95 | { 96 | uint8_t padId = 11 + 10 * 6 + column; 97 | setPadColor(padId, color); 98 | } 99 | } 100 | 101 | void LaunchPad::setSliderVertical(uint8_t column, uint8_t topmostPad, uint8_t color){} 102 | 103 | void LaunchPad::setActiveTrack(uint8_t pad, uint8_t color) 104 | { 105 | setPadColor(29, LP_OFF); 106 | setPadColor(39, LP_OFF); 107 | setPadColor(49, LP_OFF); 108 | setPadColor(59, LP_OFF); 109 | setPadColor(69, LP_OFF); 110 | setPadColor(79, LP_OFF); 111 | setPadColor(89, LP_OFF); 112 | 113 | setPadColor(pad, color); 114 | } 115 | 116 | // non-class LP functions 117 | uint8_t LPnoteToPadColumn(byte note) 118 | { 119 | return note % 10 - 1; 120 | } 121 | 122 | uint8_t LPnoteToPadRow(byte note) 123 | { 124 | return note/10 - 1; 125 | } 126 | -------------------------------------------------------------------------------- /launchPad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Arduino.h" 4 | #include 5 | #include 6 | 7 | #define LP_RED 5 8 | #define LP_BLUE 41 9 | #define LP_GREEN 22 10 | #define LP_DARKGREEN 123 11 | #define LP_PURPLE 49 12 | #define LP_CYAN 33 13 | #define LP_ORANGE 9 14 | #define LP_PINK 57 15 | #define LP_GHOST 1 16 | #define LP_OFF 0 17 | #define LP_DARKBLUE 45 18 | #define LP_YELLOW 13 19 | 20 | class LaunchPad { 21 | private: 22 | uint8_t _LP_id; 23 | 24 | MIDIDevice_BigBuffer *_midiDevice = nullptr; // Note: BigBuffer is not really needed 25 | public: 26 | LaunchPad(uint8_t LP_id); 27 | bool shift; 28 | uint8_t page; 29 | uint8_t yScrollStep; 30 | void begin(MIDIDevice_BigBuffer *midiDevice); 31 | void setProgrammerMode(); 32 | void setPadColor(uint8_t pad, uint8_t color); 33 | void setPadColorRGB(uint8_t padId, uint8_t R, uint8_t G, uint8_t B); 34 | void setPadColorFlashing(uint8_t padId, uint8_t color); 35 | void setMultiplePadColorState(uint8_t padStateArray[], uint8_t arrayLength); 36 | void setColumnColor(uint8_t column, uint8_t color); 37 | void setRowColor(uint8_t row, uint8_t color); 38 | void setSliderHorizontal(uint8_t row, uint8_t rightmostPad, uint8_t color); 39 | void setSliderVertical(uint8_t column, uint8_t topmostPad, uint8_t color); 40 | void resetPage(); 41 | void setActiveTrack(uint8_t pad, uint8_t color); 42 | void setPageIndicator(uint8_t page, uint8_t color); 43 | void drawGhostLineLower(uint8_t page, uint8_t color); 44 | }; 45 | 46 | uint8_t LPnoteToPadColumn(byte note); 47 | 48 | uint8_t LPnoteToPadRow(byte note); 49 | -------------------------------------------------------------------------------- /midiFunctions.cpp: -------------------------------------------------------------------------------- 1 | #include "midiFunctions.h" 2 | 3 | USBHost myusb; 4 | USBHub hub1(myusb); 5 | USBHub hub2(myusb); 6 | MIDIDevice_BigBuffer midi1(myusb); // Launchpad or Midi device 7 | MIDIDevice_BigBuffer midi2(myusb); // Launchpad or Midi device 8 | MIDIDevice_BigBuffer midi3(myusb); // Launchpad or Midi device 9 | MIDIDevice_BigBuffer midi4(myusb); // Launchpad or Midi device 10 | MIDIDevice_BigBuffer midi5(myusb); // Launchpad or Midi device 11 | LaunchPad LP1 = LaunchPad(1); 12 | 13 | MIDIDevice_BigBuffer * midiDrivers[5] = {&midi1, &midi2, &midi3, &midi4, &midi5}; 14 | const char * driver_names[5] = {"midi1", "midi2", "midi3", "midi4", "midi5"}; 15 | #define CNT_DEVICES (sizeof(midiDrivers)/sizeof(midiDrivers[0])) 16 | uint8_t launchPadMidiIndex = 0; 17 | 18 | MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); 19 | 20 | const float DIV127 = (1.0/127.0); 21 | 22 | void setupMidi() 23 | { 24 | Serial.println(F("MIDI SETUP")); 25 | delay(1500); 26 | myusb.begin(); 27 | MIDI.begin(MIDI_CHANNEL_OMNI); 28 | MIDI.setHandleNoteOn(transposeMidiIn); 29 | locateUsbComponents(); 30 | LPinit(); 31 | } 32 | 33 | void locateUsbComponents() 34 | { 35 | bool LPassigned = false; 36 | uint8_t LP_index = 0; 37 | Serial.println(F("Searching for usb components...")); 38 | while (! (LPassigned == true)) 39 | { 40 | for (uint8_t index = 0; index < CNT_DEVICES; index++) 41 | { 42 | if (*midiDrivers[index]) 43 | { 44 | const uint8_t * productName = midiDrivers[index]->product(); 45 | char buf[16]; 46 | snprintf(buf, 16, "%s", productName); 47 | outputNames[index + 3] = buf; 48 | uint16_t productId = midiDrivers[index]->idProduct(); 49 | Serial.print(driver_names[index]); 50 | Serial.print(": PID 0x"); 51 | Serial.print(productId, HEX); 52 | Serial.printf(" %s\n", productName); 53 | if (productId == 0x113) 54 | { 55 | LP_index = index; 56 | LPassigned = true; 57 | } 58 | } 59 | delay(10); 60 | } 61 | } 62 | configureLaunchPad(LP_index); 63 | configureMidiInputDevices(LP_index); 64 | } 65 | 66 | void getUsbDeviceName(uint8_t usbIndex, char * buf, uint8_t maxBufferSize) 67 | { 68 | char na[4] = {'N', '/', 'A'}; 69 | if (*midiDrivers[usbIndex]) 70 | { 71 | const uint8_t * productName = midiDrivers[usbIndex]->product(); 72 | for(uint8_t i = 0; i < maxBufferSize; i++) buf[i] = productName[i]; 73 | } 74 | else 75 | { 76 | for(uint8_t i = 0; i < 4; i++) buf[i] = na[i]; 77 | } 78 | } 79 | 80 | void configureLaunchPad(uint8_t driverIndex) 81 | { 82 | LP1.begin(midiDrivers[driverIndex]); 83 | midiDrivers[driverIndex]->setHandleNoteOn(LPNoteOn); 84 | midiDrivers[driverIndex]->setHandleNoteOff(LPNoteOff); 85 | midiDrivers[driverIndex]->setHandleControlChange(LPControlChange); 86 | Serial.print(F("Initializing LP @ port: ")); 87 | Serial.println(driver_names[driverIndex]); 88 | launchPadMidiIndex = driverIndex; 89 | LP1.setProgrammerMode(); 90 | } 91 | 92 | //void setLaunchPadKeyboardMode(bool keyboardActive) 93 | //{ 94 | // if (keyboardActive) 95 | // { 96 | // midiDrivers[launchPadMidiIndex]->setHandleNoteOn(nullptr); 97 | // midiDrivers[launchPadMidiIndex]->setHandleNoteOff(nullptr); 98 | // midiDrivers[launchPadMidiIndex]->setHandleControlChange(nullptr); 99 | // } 100 | // else 101 | // { 102 | // midiDrivers[launchPadMidiIndex]->setHandleNoteOn(LPNoteOn); 103 | // midiDrivers[launchPadMidiIndex]->setHandleNoteOff(LPNoteOff); 104 | // midiDrivers[launchPadMidiIndex]->setHandleControlChange(LPControlChange); 105 | // } 106 | //} 107 | 108 | //char LPkeyboardPoll() 109 | //{ 110 | // char character = 0; 111 | // uint8_t index = 0; 112 | // if (midiDrivers[launchPadMidiIndex]->read() && (midiDrivers[launchPadMidiIndex]->getType() == 144) ) 113 | // { 114 | // uint8_t data1 = midiDrivers[launchPadMidiIndex]->getData1(); 115 | // uint8_t row = LPnoteToPadRow(data1); 116 | // uint8_t column = LPnoteToPadColumn(data1); 117 | // index = 56 - row * 8 + column; 118 | // 119 | // if (index < 26) character = index + 65; 120 | // if (index >= 26 && index <= 34) character = index + 22; 121 | // //Serial.printf("Index: %d, Char: %d\n", index, character); 122 | // } 123 | // 124 | // //Serial.printf("Type: %d, data1: %d, data2: %d\n", type, data1, data2); 125 | // return character; 126 | //} 127 | 128 | void configureMidiInputDevices(uint8_t LP_index) 129 | { 130 | for (uint8_t driverIndex = 0; driverIndex < CNT_DEVICES; driverIndex++) 131 | { 132 | if (driverIndex != LP_index) midiDrivers[driverIndex]->setHandleNoteOn(transposeMidiIn); 133 | } 134 | } 135 | 136 | void transposeMidiIn(uint8_t channel, uint8_t note, uint8_t velocity) { setTranspose(note - 36); } 137 | 138 | void deviceNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) 139 | { 140 | //Serial.printf("Device send, type %d, channel %d, note %d, velocity %d\n", usbMIDI.NoteOn, channel, note, velocity); 141 | usbMIDI.sendNoteOn(note, velocity, channel); 142 | } 143 | 144 | void deviceNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) 145 | { 146 | usbMIDI.sendNoteOff(note, 127, channel); 147 | } 148 | 149 | //void voiceNoteOn(uint8_t channel, uint8_t note, uint8_t velocity) 150 | //{ 151 | // //Voices[channel].noteOn(note, velocity); 152 | //} 153 | //void voiceNoteOff(uint8_t channel, uint8_t note, uint8_t velocity) 154 | //{ 155 | // //Voices[channel].noteOff(note, velocity); 156 | //} 157 | 158 | void serialMidiNoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity) { MIDI.sendNoteOn(noteValue, 127, channel); } 159 | void serialMidiNoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity) { MIDI.sendNoteOff(noteValue, 127, channel); } 160 | 161 | void midi1NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi1.sendNoteOn(noteValue, 127, channel); } 162 | void midi1NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi1.sendNoteOff(noteValue, 127, channel); } 163 | void midi2NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi2.sendNoteOn(noteValue, 127, channel); } 164 | void midi2NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi2.sendNoteOff(noteValue, 127, channel); } 165 | void midi3NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi3.sendNoteOn(noteValue, 127, channel); } 166 | void midi3NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi3.sendNoteOff(noteValue, 127, channel); } 167 | void midi4NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi4.sendNoteOn(noteValue, 127, channel); } 168 | void midi4NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi4.sendNoteOff(noteValue, 127, channel); } 169 | void midi5NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi5.sendNoteOn(noteValue, 127, channel); } 170 | void midi5NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity) { midi5.sendNoteOff(noteValue, 127, channel); } 171 | 172 | void myControlChange(byte channel, byte control, byte value) 173 | { 174 | switch (control) 175 | { 176 | case CC_MODWHEEL: 177 | break; 178 | } 179 | } 180 | 181 | void updateMidi() 182 | { 183 | myusb.Task(); 184 | midi1.read(); 185 | midi2.read(); 186 | midi3.read(); 187 | midi4.read(); 188 | usbMIDI.read(); 189 | MIDI.read(); 190 | } 191 | 192 | void sendMidiClock() 193 | { 194 | MIDI.sendClock(); 195 | for (uint8_t index = 0; index < CNT_DEVICES; index++) 196 | { 197 | if (*midiDrivers[index] && (index != launchPadMidiIndex)) midiDrivers[index]->sendRealTime(usbMIDI.Clock); 198 | midiDrivers[index]->send_now(); 199 | } 200 | } 201 | 202 | void sendMidiStart() 203 | { 204 | MIDI.sendStart(); 205 | for (uint8_t index = 0; index < CNT_DEVICES; index++) 206 | { 207 | if (*midiDrivers[index]) midiDrivers[index]->sendRealTime(usbMIDI.Start); 208 | midiDrivers[index]->send_now(); 209 | } 210 | } 211 | 212 | void sendMidiStop() 213 | { 214 | MIDI.sendStop(); 215 | for (uint8_t index = 0; index < CNT_DEVICES; index++) 216 | { 217 | if (*midiDrivers[index]) midiDrivers[index]->sendRealTime(usbMIDI.Stop); 218 | } 219 | } 220 | 221 | void LPNoteOn(byte channel, byte note, byte velocity) 222 | { 223 | uint8_t padColumn = LPnoteToPadColumn(note) + LP1.page * 8; 224 | uint16_t tickTemp = padColumn * TICKS_PER_COLUMN; 225 | uint8_t lowerRow = tracks[currentTrack]->lowerRow; 226 | uint8_t padRow = LPnoteToPadRow(note); 227 | 228 | //TODO: Move all this to sequencer functions 229 | 230 | if ( ( LPdisplayMode == LPMODE_PATTERN ) && ( sequencerEditMode == MODE_PATTERNEDIT) && (velocity > 0) ) 231 | { 232 | // Add or remove events 233 | uint8_t padState = 0; 234 | bool auditTrack = true; //sequencerState == STATE_STOPPED; 235 | 236 | if (tracks[currentTrack]->getEventsInTickInterval(tickTemp, tickTemp + TICKS_PER_COLUMN - 1 , lowerRow + padRow) == 0) // no matching events 237 | { 238 | tracks[currentTrack]->addEvent(tickTemp, lowerRow + padRow, tracks[currentTrack]->getTrackDefaultVelocity(), tracks[currentTrack]->getTrackDefaultNoteLengthTicks(), auditTrack); 239 | padState = tracks[currentTrack]->color; 240 | } 241 | else 242 | { 243 | tracks[currentTrack]->removeEvents(tickTemp, tickTemp + TICKS_PER_COLUMN -1, lowerRow + padRow); 244 | padState = LP_OFF; 245 | } 246 | LPcopy_updateSingleEvent_converter(note, padState); 247 | } 248 | 249 | if ( ( LPdisplayMode == LPMODE_PATTERN ) && ( sequencerEditMode == MODE_EVENTEDIT ) && (velocity > 0) ) 250 | { 251 | // Select existing events 252 | currentEvent = tracks[currentTrack]->getEventId(tickTemp, lowerRow + padRow); 253 | } 254 | 255 | if ( ( LPdisplayMode == LPMODE_SCENE ) && (velocity > 0) ) 256 | { 257 | // add / remove patterns 258 | bool inPatternRange = (velocity > 0 && note >= 21 && note <= 88); // in pattern range 259 | if (inPatternRange) 260 | { 261 | uint8_t padColumn = LPnoteToPadColumn(note); 262 | uint8_t padRow = LPnoteToPadRow(note); 263 | uint8_t trackId = padRow - 1; 264 | uint8_t patternId = padColumn; 265 | if (sequencerState == STATE_STOPPED) tracks[trackId]-> setPatternId(patternId); 266 | if (sequencerState == STATE_RUNNING) tracks[trackId]-> cuePatternId(patternId); 267 | updateSceneConfiguration(currentScene); 268 | currentPattern = patternId; 269 | currentTrack = trackId; 270 | } 271 | bool inSceneRange = (velocity > 0 && note >= 11 && note <= 18); // in scene range 272 | if (inSceneRange) 273 | { 274 | currentScene = LP1.page * 8 + note - 11; 275 | setScene(currentScene); 276 | } 277 | } 278 | 279 | if ( ( LPdisplayMode == LPMODE_SONG ) && (velocity > 0) ) 280 | { 281 | static uint8_t currentArrScene = 255; 282 | static uint8_t arrIndex = 0; 283 | 284 | bool inArrangementRange = (velocity > 0 && note >= 21 && note <= 88); // in arrangement range 285 | if (inArrangementRange) 286 | { 287 | uint8_t row = LPnoteToPadRow(note); 288 | uint8_t column = LPnoteToPadColumn(note); 289 | arrIndex = 56 - row * 8 + column; 290 | if (sequencerEditMode != MODE_EVENTEDIT) SequencerData.arrangement[arrIndex] = currentArrScene; 291 | else 292 | { 293 | currentArrPosition = arrIndex; 294 | setSceneNr(SequencerData.arrangement[currentArrPosition]); 295 | } 296 | } 297 | 298 | bool inSceneRange = (velocity > 0 && note >= 11 && note <= 18); // in scene range 299 | if (inSceneRange) 300 | { 301 | currentArrScene = LP1.page * 8 + note - 11; 302 | } 303 | } 304 | } 305 | 306 | void LPNoteOff(byte channel, byte note, byte velocity) 307 | { 308 | 309 | } 310 | 311 | void LPControlChange(byte channel, byte control, byte value) 312 | { 313 | 314 | if (value > 0) 315 | { 316 | //Serial.printf("LP CC: %d, %d\n", control, value); 317 | uint8_t trackTemp = (control - 9) / 10 - 2; 318 | switch (control) 319 | { 320 | case CCtrack1: 321 | case CCtrack2: 322 | case CCtrack3: 323 | case CCtrack4: 324 | case CCtrack5: 325 | case CCtrack6: 326 | case CCtrack7: 327 | if (LPdisplayMode == LPMODE_SCENE || LPdisplayMode == LPMODE_SONG) LPtoggleMute(trackTemp); 328 | else setCurrentTrack(trackTemp); 329 | break; 330 | case CCstartStop: 331 | if (sequencerState == STATE_STOPPED) startSequencer(); 332 | else stopSequencer(); 333 | break; 334 | case CCscrollUp: 335 | LP1.setPadColor(CCscrollUp, LP_GREEN); 336 | LPscrollUp(); 337 | break; 338 | case CCscrollDown: 339 | LP1.setPadColor(CCscrollDown, LP_GREEN); 340 | LPscrollDown(); 341 | break; 342 | case CCpageDecrease: 343 | LP1.setPadColor(CCpageDecrease, LP_GREEN); 344 | LPpageDecrease(); 345 | break; 346 | case CCpageIncrease: 347 | LP1.setPadColor(CCpageIncrease, LP_GREEN); 348 | LPpageIncrease(); 349 | break; 350 | case CCeditMode: 351 | // toggle between add/delete and edit modes 352 | if (sequencerEditMode == MODE_PATTERNEDIT) 353 | { 354 | LP1.setPadColor(CCeditMode, LP_BLUE); 355 | sequencerEditMode = MODE_EVENTEDIT; 356 | } 357 | else 358 | { 359 | LP1.setPadColor(CCeditMode, LP_RED); 360 | sequencerEditMode = MODE_PATTERNEDIT; 361 | } 362 | break; 363 | case CCsongMode: 364 | setLPsongMode(); 365 | break; 366 | case CCsceneMode: 367 | setLPsceneMode(); 368 | break; 369 | case CCpatternMode: 370 | setLPpatternMode(); 371 | break; 372 | } 373 | } 374 | if (value == 0) 375 | { 376 | switch (control) 377 | { 378 | case CCscrollUp: 379 | LP1.setPadColor(CCscrollUp, LP_OFF); 380 | break; 381 | case CCscrollDown: 382 | LP1.setPadColor(CCscrollDown, LP_OFF); 383 | break; 384 | case CCpageDecrease: 385 | LP1.setPadColor(CCpageDecrease, LP_OFF); 386 | break; 387 | case CCpageIncrease: 388 | LP1.setPadColor(CCpageIncrease, LP_OFF); 389 | break; 390 | } 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /midiFunctions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "launchPad.h" 6 | #include "track.h" 7 | #include "ui.h" 8 | #include "sequencer.h" 9 | 10 | extern LaunchPad LP1; 11 | 12 | void setupMidi(); 13 | void locateUsbComponents(); 14 | void getUsbDeviceName(uint8_t usbIndex, char * buf, uint8_t maxBufferSize); 15 | void configureLaunchPad(uint8_t driverIndex); 16 | void configureMidiInputDevices(uint8_t LP_index); 17 | void updateMidi(); 18 | void deviceNoteOn(uint8_t channel, uint8_t note, uint8_t velocity); 19 | void deviceNoteOff(uint8_t channel, uint8_t note, uint8_t velocity); 20 | void myControlChange(uint8_t channel, uint8_t control, uint8_t value); 21 | 22 | //void voiceNoteOn(uint8_t channel, uint8_t note, uint8_t velocity); 23 | //void voiceNoteOff(uint8_t channel, uint8_t note, uint8_t velocity); 24 | void serialMidiNoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity); 25 | void serialMidiNoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity); 26 | void midi1NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity); 27 | void midi1NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity); 28 | void midi2NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity); 29 | void midi2NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity); 30 | void midi3NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity); 31 | void midi3NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity); 32 | void midi4NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity); 33 | void midi4NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity); 34 | void midi5NoteOn(uint8_t channel, uint8_t noteValue, uint8_t velocity); 35 | void midi5NoteOff(uint8_t channel, uint8_t noteValue, uint8_t velocity); 36 | 37 | void LPNoteOn(uint8_t channel, uint8_t note, uint8_t velocity); 38 | void LPNoteOff(uint8_t channel, uint8_t note, uint8_t velocity); 39 | void LPControlChange(byte channel, byte control, byte value); 40 | 41 | void transposeMidiIn(uint8_t channel, uint8_t note, uint8_t velocity); 42 | 43 | void sendMidiClock(); 44 | void sendMidiStart(); 45 | void sendMidiStop(); 46 | 47 | #define CC_MODWHEEL 1 48 | 49 | // -- MIDI CC from LaunchPad -- 50 | 51 | // Right side pads (19 lowest) 52 | #define CCtrack7 89 53 | #define CCtrack6 79 54 | #define CCtrack5 69 55 | #define CCtrack4 59 56 | #define CCtrack3 49 57 | #define CCtrack2 39 58 | #define CCtrack1 29 59 | #define CCstartStop 19 60 | 61 | // Top pads (91 is rightmost) 62 | #define CCscrollUp 91 63 | #define CCscrollDown 92 64 | #define CCpageDecrease 93 65 | #define CCpageIncrease 94 66 | #define CCsongMode 95 67 | #define CCsceneMode 96 68 | #define CCpatternMode 97 69 | #define CCeditMode 98 70 | 71 | #define CCindicator 99 72 | 73 | // Pads for pattern mode 74 | #define notePatternClear 11 75 | #define notePatternCopy 12 76 | -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/fp-lib-table: -------------------------------------------------------------------------------- 1 | (fp_lib_table 2 | (lib (name "Eurocad")(type "KiCad")(uri "C:/Repos/eurocad/Eurocad.pretty")(options "")(descr "")) 3 | ) 4 | -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_195521.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_195521.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_200521.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_200521.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_201521.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_201521.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_202540.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_202540.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_203139.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-21_203139.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_195557.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_195557.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_200557.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_200557.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_201557.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_201557.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_202557.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_202557.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_203557.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-22_203557.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-23_203649.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-11-23_203649.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-12-05_123926.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/midiSplitter/midiSplitter/midiSplitter-backups/midiSplitter-2022-12-05_123926.zip -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 44, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_nets": [], 7 | "high_contrast_mode": 0, 8 | "net_color_mode": 1, 9 | "opacity": { 10 | "pads": 1.0, 11 | "tracks": 1.0, 12 | "vias": 1.0, 13 | "zones": 0.6 14 | }, 15 | "ratsnest_display_mode": 0, 16 | "selection_filter": { 17 | "dimensions": false, 18 | "footprints": false, 19 | "graphics": false, 20 | "keepouts": false, 21 | "lockedItems": false, 22 | "otherItems": false, 23 | "pads": false, 24 | "text": false, 25 | "tracks": true, 26 | "vias": false, 27 | "zones": false 28 | }, 29 | "visible_items": [ 30 | 0, 31 | 1, 32 | 2, 33 | 3, 34 | 4, 35 | 5, 36 | 8, 37 | 9, 38 | 10, 39 | 11, 40 | 12, 41 | 13, 42 | 14, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36 64 | ], 65 | "visible_layers": "fffefcf_ffffffff", 66 | "zone_display_mode": 0 67 | }, 68 | "meta": { 69 | "filename": "midiSplitter.kicad_prl", 70 | "version": 3 71 | }, 72 | "project": { 73 | "files": [] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /pcb/midiSplitter/midiSplitter/midiSplitter.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "design_settings": { 4 | "defaults": { 5 | "board_outline_line_width": 0.09999999999999999, 6 | "copper_line_width": 0.19999999999999998, 7 | "copper_text_italic": false, 8 | "copper_text_size_h": 1.5, 9 | "copper_text_size_v": 1.5, 10 | "copper_text_thickness": 0.3, 11 | "copper_text_upright": false, 12 | "courtyard_line_width": 0.049999999999999996, 13 | "dimension_precision": 4, 14 | "dimension_units": 3, 15 | "dimensions": { 16 | "arrow_length": 1270000, 17 | "extension_offset": 500000, 18 | "keep_text_aligned": true, 19 | "suppress_zeroes": false, 20 | "text_position": 0, 21 | "units_format": 1 22 | }, 23 | "fab_line_width": 0.09999999999999999, 24 | "fab_text_italic": false, 25 | "fab_text_size_h": 1.0, 26 | "fab_text_size_v": 1.0, 27 | "fab_text_thickness": 0.15, 28 | "fab_text_upright": false, 29 | "other_line_width": 0.15, 30 | "other_text_italic": false, 31 | "other_text_size_h": 1.0, 32 | "other_text_size_v": 1.0, 33 | "other_text_thickness": 0.15, 34 | "other_text_upright": false, 35 | "pads": { 36 | "drill": 0.762, 37 | "height": 1.524, 38 | "width": 1.524 39 | }, 40 | "silk_line_width": 0.15, 41 | "silk_text_italic": false, 42 | "silk_text_size_h": 1.0, 43 | "silk_text_size_v": 1.0, 44 | "silk_text_thickness": 0.15, 45 | "silk_text_upright": false, 46 | "zones": { 47 | "45_degree_only": false, 48 | "min_clearance": 0.508 49 | } 50 | }, 51 | "diff_pair_dimensions": [ 52 | { 53 | "gap": 0.0, 54 | "via_gap": 0.0, 55 | "width": 0.0 56 | } 57 | ], 58 | "drc_exclusions": [], 59 | "meta": { 60 | "version": 2 61 | }, 62 | "rule_severities": { 63 | "annular_width": "error", 64 | "clearance": "error", 65 | "copper_edge_clearance": "error", 66 | "courtyards_overlap": "error", 67 | "diff_pair_gap_out_of_range": "error", 68 | "diff_pair_uncoupled_length_too_long": "error", 69 | "drill_out_of_range": "error", 70 | "duplicate_footprints": "warning", 71 | "extra_footprint": "warning", 72 | "footprint_type_mismatch": "error", 73 | "hole_clearance": "error", 74 | "hole_near_hole": "error", 75 | "invalid_outline": "error", 76 | "item_on_disabled_layer": "error", 77 | "items_not_allowed": "error", 78 | "length_out_of_range": "error", 79 | "malformed_courtyard": "error", 80 | "microvia_drill_out_of_range": "error", 81 | "missing_courtyard": "ignore", 82 | "missing_footprint": "warning", 83 | "net_conflict": "warning", 84 | "npth_inside_courtyard": "ignore", 85 | "padstack": "error", 86 | "pth_inside_courtyard": "ignore", 87 | "shorting_items": "error", 88 | "silk_over_copper": "warning", 89 | "silk_overlap": "warning", 90 | "skew_out_of_range": "error", 91 | "through_hole_pad_without_hole": "error", 92 | "too_many_vias": "error", 93 | "track_dangling": "warning", 94 | "track_width": "error", 95 | "tracks_crossing": "error", 96 | "unconnected_items": "error", 97 | "unresolved_variable": "error", 98 | "via_dangling": "warning", 99 | "zone_has_empty_net": "error", 100 | "zones_intersect": "error" 101 | }, 102 | "rules": { 103 | "allow_blind_buried_vias": false, 104 | "allow_microvias": false, 105 | "max_error": 0.005, 106 | "min_clearance": 0.0, 107 | "min_copper_edge_clearance": 0.0, 108 | "min_hole_clearance": 0.25, 109 | "min_hole_to_hole": 0.25, 110 | "min_microvia_diameter": 0.19999999999999998, 111 | "min_microvia_drill": 0.09999999999999999, 112 | "min_silk_clearance": 0.0, 113 | "min_through_hole_diameter": 0.3, 114 | "min_track_width": 0.19999999999999998, 115 | "min_via_annular_width": 0.049999999999999996, 116 | "min_via_diameter": 0.39999999999999997, 117 | "solder_mask_clearance": 0.0, 118 | "solder_mask_min_width": 0.0, 119 | "use_height_for_length_calcs": true 120 | }, 121 | "track_widths": [ 122 | 0.0 123 | ], 124 | "via_dimensions": [ 125 | { 126 | "diameter": 0.0, 127 | "drill": 0.0 128 | } 129 | ], 130 | "zones_allow_external_fillets": false, 131 | "zones_use_no_outline": true 132 | }, 133 | "layer_presets": [] 134 | }, 135 | "boards": [], 136 | "cvpcb": { 137 | "equivalence_files": [] 138 | }, 139 | "erc": { 140 | "erc_exclusions": [], 141 | "meta": { 142 | "version": 0 143 | }, 144 | "pin_map": [ 145 | [ 146 | 0, 147 | 0, 148 | 0, 149 | 0, 150 | 0, 151 | 0, 152 | 1, 153 | 0, 154 | 0, 155 | 0, 156 | 0, 157 | 2 158 | ], 159 | [ 160 | 0, 161 | 2, 162 | 0, 163 | 1, 164 | 0, 165 | 0, 166 | 1, 167 | 0, 168 | 2, 169 | 2, 170 | 2, 171 | 2 172 | ], 173 | [ 174 | 0, 175 | 0, 176 | 0, 177 | 0, 178 | 0, 179 | 0, 180 | 1, 181 | 0, 182 | 1, 183 | 0, 184 | 1, 185 | 2 186 | ], 187 | [ 188 | 0, 189 | 1, 190 | 0, 191 | 0, 192 | 0, 193 | 0, 194 | 1, 195 | 1, 196 | 2, 197 | 1, 198 | 1, 199 | 2 200 | ], 201 | [ 202 | 0, 203 | 0, 204 | 0, 205 | 0, 206 | 0, 207 | 0, 208 | 1, 209 | 0, 210 | 0, 211 | 0, 212 | 0, 213 | 2 214 | ], 215 | [ 216 | 0, 217 | 0, 218 | 0, 219 | 0, 220 | 0, 221 | 0, 222 | 0, 223 | 0, 224 | 0, 225 | 0, 226 | 0, 227 | 2 228 | ], 229 | [ 230 | 1, 231 | 1, 232 | 1, 233 | 1, 234 | 1, 235 | 0, 236 | 1, 237 | 1, 238 | 1, 239 | 1, 240 | 1, 241 | 2 242 | ], 243 | [ 244 | 0, 245 | 0, 246 | 0, 247 | 1, 248 | 0, 249 | 0, 250 | 1, 251 | 0, 252 | 0, 253 | 0, 254 | 0, 255 | 2 256 | ], 257 | [ 258 | 0, 259 | 2, 260 | 1, 261 | 2, 262 | 0, 263 | 0, 264 | 1, 265 | 0, 266 | 2, 267 | 2, 268 | 2, 269 | 2 270 | ], 271 | [ 272 | 0, 273 | 2, 274 | 0, 275 | 1, 276 | 0, 277 | 0, 278 | 1, 279 | 0, 280 | 2, 281 | 0, 282 | 0, 283 | 2 284 | ], 285 | [ 286 | 0, 287 | 2, 288 | 1, 289 | 1, 290 | 0, 291 | 0, 292 | 1, 293 | 0, 294 | 2, 295 | 0, 296 | 0, 297 | 2 298 | ], 299 | [ 300 | 2, 301 | 2, 302 | 2, 303 | 2, 304 | 2, 305 | 2, 306 | 2, 307 | 2, 308 | 2, 309 | 2, 310 | 2, 311 | 2 312 | ] 313 | ], 314 | "rule_severities": { 315 | "bus_definition_conflict": "error", 316 | "bus_entry_needed": "error", 317 | "bus_label_syntax": "error", 318 | "bus_to_bus_conflict": "error", 319 | "bus_to_net_conflict": "error", 320 | "different_unit_footprint": "error", 321 | "different_unit_net": "error", 322 | "duplicate_reference": "error", 323 | "duplicate_sheet_names": "error", 324 | "extra_units": "error", 325 | "global_label_dangling": "warning", 326 | "hier_label_mismatch": "error", 327 | "label_dangling": "error", 328 | "lib_symbol_issues": "warning", 329 | "multiple_net_names": "warning", 330 | "net_not_bus_member": "warning", 331 | "no_connect_connected": "warning", 332 | "no_connect_dangling": "warning", 333 | "pin_not_connected": "error", 334 | "pin_not_driven": "error", 335 | "pin_to_pin": "warning", 336 | "power_pin_not_driven": "error", 337 | "similar_labels": "warning", 338 | "unannotated": "error", 339 | "unit_value_mismatch": "error", 340 | "unresolved_variable": "error", 341 | "wire_dangling": "error" 342 | } 343 | }, 344 | "libraries": { 345 | "pinned_footprint_libs": [], 346 | "pinned_symbol_libs": [] 347 | }, 348 | "meta": { 349 | "filename": "midiSplitter.kicad_pro", 350 | "version": 1 351 | }, 352 | "net_settings": { 353 | "classes": [ 354 | { 355 | "bus_width": 12.0, 356 | "clearance": 0.2, 357 | "diff_pair_gap": 0.25, 358 | "diff_pair_via_gap": 0.25, 359 | "diff_pair_width": 0.2, 360 | "line_style": 0, 361 | "microvia_diameter": 0.3, 362 | "microvia_drill": 0.1, 363 | "name": "Default", 364 | "pcb_color": "rgba(0, 0, 0, 0.000)", 365 | "schematic_color": "rgba(0, 0, 0, 0.000)", 366 | "track_width": 0.25, 367 | "via_diameter": 0.8, 368 | "via_drill": 0.4, 369 | "wire_width": 6.0 370 | }, 371 | { 372 | "bus_width": 12.0, 373 | "clearance": 0.2, 374 | "diff_pair_gap": 0.25, 375 | "diff_pair_via_gap": 0.25, 376 | "diff_pair_width": 0.2, 377 | "line_style": 0, 378 | "microvia_diameter": 0.3, 379 | "microvia_drill": 0.1, 380 | "name": "Power", 381 | "nets": [ 382 | "GND", 383 | "V5V" 384 | ], 385 | "pcb_color": "rgba(0, 0, 0, 0.000)", 386 | "schematic_color": "rgba(0, 0, 0, 0.000)", 387 | "track_width": 0.5, 388 | "via_diameter": 0.8, 389 | "via_drill": 0.4, 390 | "wire_width": 6.0 391 | } 392 | ], 393 | "meta": { 394 | "version": 2 395 | }, 396 | "net_colors": null 397 | }, 398 | "pcbnew": { 399 | "last_paths": { 400 | "gencad": "", 401 | "idf": "", 402 | "netlist": "", 403 | "specctra_dsn": "", 404 | "step": "", 405 | "vrml": "" 406 | }, 407 | "page_layout_descr_file": "" 408 | }, 409 | "schematic": { 410 | "annotate_start_num": 0, 411 | "drawing": { 412 | "default_line_thickness": 6.0, 413 | "default_text_size": 50.0, 414 | "field_names": [], 415 | "intersheets_ref_own_page": false, 416 | "intersheets_ref_prefix": "", 417 | "intersheets_ref_short": false, 418 | "intersheets_ref_show": false, 419 | "intersheets_ref_suffix": "", 420 | "junction_size_choice": 3, 421 | "label_size_ratio": 0.375, 422 | "pin_symbol_size": 25.0, 423 | "text_offset_ratio": 0.15 424 | }, 425 | "legacy_lib_dir": "", 426 | "legacy_lib_list": [], 427 | "meta": { 428 | "version": 1 429 | }, 430 | "net_format_name": "", 431 | "ngspice": { 432 | "fix_include_paths": true, 433 | "fix_passive_vals": false, 434 | "meta": { 435 | "version": 0 436 | }, 437 | "model_mode": 0, 438 | "workbook_filename": "" 439 | }, 440 | "page_layout_descr_file": "", 441 | "plot_directory": "", 442 | "spice_adjust_passive_values": false, 443 | "spice_external_command": "spice \"%I\"", 444 | "subpart_first_id": 65, 445 | "subpart_id_separator": 0 446 | }, 447 | "sheets": [ 448 | [ 449 | "e63e39d7-6ac0-4ffd-8aa3-1841a4541b55", 450 | "" 451 | ] 452 | ], 453 | "text_variables": {} 454 | } 455 | -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_202728.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_202728.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_203728.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_203728.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_204749.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_204749.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_210152.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_210152.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_211341.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-06_211341.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_100752.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_100752.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_102006.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_102006.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_103006.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_103006.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_104006.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_104006.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_105207.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-07_105207.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-08_123534.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-08_123534.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-08_124534.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-08_124534.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-08_125225.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-08_125225.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-09_074246.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-09_074246.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-25_201056.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-10-25_201056.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-06_082357.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-06_082357.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-06_155041.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-06_155041.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-06_155700.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-06_155700.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-06_160354.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-06_160354.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_174113.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_174113.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_175113.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_175113.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_175824.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_175824.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_204841.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_204841.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_205841.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-07_205841.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-12_120407.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-12_120407.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-21_174555.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canvasus/Beatmaster2k/3e9099fb8d498318f85ce10ea27bac0d01198a24/pcb/sm2k_breakout/sm2k_breakout-backups/sm2k_breakout-2022-11-21_174555.zip -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout.kicad_prl: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "active_layer": 31, 4 | "active_layer_preset": "", 5 | "auto_track_width": true, 6 | "hidden_nets": [], 7 | "high_contrast_mode": 0, 8 | "net_color_mode": 1, 9 | "opacity": { 10 | "pads": 1.0, 11 | "tracks": 1.0, 12 | "vias": 1.0, 13 | "zones": 0.6 14 | }, 15 | "ratsnest_display_mode": 0, 16 | "selection_filter": { 17 | "dimensions": true, 18 | "footprints": true, 19 | "graphics": true, 20 | "keepouts": true, 21 | "lockedItems": true, 22 | "otherItems": true, 23 | "pads": true, 24 | "text": true, 25 | "tracks": true, 26 | "vias": true, 27 | "zones": true 28 | }, 29 | "visible_items": [ 30 | 0, 31 | 1, 32 | 2, 33 | 3, 34 | 4, 35 | 5, 36 | 8, 37 | 9, 38 | 10, 39 | 11, 40 | 12, 41 | 13, 42 | 14, 43 | 15, 44 | 16, 45 | 17, 46 | 18, 47 | 19, 48 | 20, 49 | 21, 50 | 22, 51 | 23, 52 | 24, 53 | 25, 54 | 26, 55 | 27, 56 | 28, 57 | 29, 58 | 30, 59 | 32, 60 | 33, 61 | 34, 62 | 35, 63 | 36 64 | ], 65 | "visible_layers": "801d0f3_ffffffff", 66 | "zone_display_mode": 1 67 | }, 68 | "meta": { 69 | "filename": "sm2k_breakout.kicad_prl", 70 | "version": 3 71 | }, 72 | "project": { 73 | "files": [] 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /pcb/sm2k_breakout/sm2k_breakout.kicad_pro: -------------------------------------------------------------------------------- 1 | { 2 | "board": { 3 | "design_settings": { 4 | "defaults": { 5 | "board_outline_line_width": 0.09999999999999999, 6 | "copper_line_width": 0.19999999999999998, 7 | "copper_text_italic": false, 8 | "copper_text_size_h": 1.5, 9 | "copper_text_size_v": 1.5, 10 | "copper_text_thickness": 0.3, 11 | "copper_text_upright": false, 12 | "courtyard_line_width": 0.049999999999999996, 13 | "dimension_precision": 4, 14 | "dimension_units": 3, 15 | "dimensions": { 16 | "arrow_length": 1270000, 17 | "extension_offset": 500000, 18 | "keep_text_aligned": true, 19 | "suppress_zeroes": false, 20 | "text_position": 0, 21 | "units_format": 1 22 | }, 23 | "fab_line_width": 0.09999999999999999, 24 | "fab_text_italic": false, 25 | "fab_text_size_h": 1.0, 26 | "fab_text_size_v": 1.0, 27 | "fab_text_thickness": 0.15, 28 | "fab_text_upright": false, 29 | "other_line_width": 0.15, 30 | "other_text_italic": false, 31 | "other_text_size_h": 1.0, 32 | "other_text_size_v": 1.0, 33 | "other_text_thickness": 0.15, 34 | "other_text_upright": false, 35 | "pads": { 36 | "drill": 0.762, 37 | "height": 1.524, 38 | "width": 1.524 39 | }, 40 | "silk_line_width": 0.15, 41 | "silk_text_italic": false, 42 | "silk_text_size_h": 1.0, 43 | "silk_text_size_v": 1.0, 44 | "silk_text_thickness": 0.15, 45 | "silk_text_upright": false, 46 | "zones": { 47 | "45_degree_only": false, 48 | "min_clearance": 0.508 49 | } 50 | }, 51 | "diff_pair_dimensions": [ 52 | { 53 | "gap": 0.0, 54 | "via_gap": 0.0, 55 | "width": 0.0 56 | } 57 | ], 58 | "drc_exclusions": [], 59 | "meta": { 60 | "version": 2 61 | }, 62 | "rule_severities": { 63 | "annular_width": "error", 64 | "clearance": "error", 65 | "copper_edge_clearance": "error", 66 | "courtyards_overlap": "error", 67 | "diff_pair_gap_out_of_range": "error", 68 | "diff_pair_uncoupled_length_too_long": "error", 69 | "drill_out_of_range": "error", 70 | "duplicate_footprints": "warning", 71 | "extra_footprint": "warning", 72 | "footprint_type_mismatch": "error", 73 | "hole_clearance": "error", 74 | "hole_near_hole": "error", 75 | "invalid_outline": "error", 76 | "item_on_disabled_layer": "error", 77 | "items_not_allowed": "error", 78 | "length_out_of_range": "error", 79 | "malformed_courtyard": "error", 80 | "microvia_drill_out_of_range": "error", 81 | "missing_courtyard": "ignore", 82 | "missing_footprint": "warning", 83 | "net_conflict": "warning", 84 | "npth_inside_courtyard": "ignore", 85 | "padstack": "error", 86 | "pth_inside_courtyard": "ignore", 87 | "shorting_items": "error", 88 | "silk_over_copper": "warning", 89 | "silk_overlap": "warning", 90 | "skew_out_of_range": "error", 91 | "through_hole_pad_without_hole": "error", 92 | "too_many_vias": "error", 93 | "track_dangling": "warning", 94 | "track_width": "error", 95 | "tracks_crossing": "error", 96 | "unconnected_items": "error", 97 | "unresolved_variable": "error", 98 | "via_dangling": "warning", 99 | "zone_has_empty_net": "error", 100 | "zones_intersect": "error" 101 | }, 102 | "rules": { 103 | "allow_blind_buried_vias": false, 104 | "allow_microvias": false, 105 | "max_error": 0.005, 106 | "min_clearance": 0.0, 107 | "min_copper_edge_clearance": 0.0, 108 | "min_hole_clearance": 0.25, 109 | "min_hole_to_hole": 0.25, 110 | "min_microvia_diameter": 0.19999999999999998, 111 | "min_microvia_drill": 0.09999999999999999, 112 | "min_silk_clearance": 0.0, 113 | "min_through_hole_diameter": 0.3, 114 | "min_track_width": 0.19999999999999998, 115 | "min_via_annular_width": 0.049999999999999996, 116 | "min_via_diameter": 0.39999999999999997, 117 | "solder_mask_clearance": 0.0, 118 | "solder_mask_min_width": 0.0, 119 | "use_height_for_length_calcs": true 120 | }, 121 | "track_widths": [ 122 | 0.0 123 | ], 124 | "via_dimensions": [ 125 | { 126 | "diameter": 0.0, 127 | "drill": 0.0 128 | } 129 | ], 130 | "zones_allow_external_fillets": false, 131 | "zones_use_no_outline": true 132 | }, 133 | "layer_presets": [] 134 | }, 135 | "boards": [], 136 | "cvpcb": { 137 | "equivalence_files": [] 138 | }, 139 | "erc": { 140 | "erc_exclusions": [], 141 | "meta": { 142 | "version": 0 143 | }, 144 | "pin_map": [ 145 | [ 146 | 0, 147 | 0, 148 | 0, 149 | 0, 150 | 0, 151 | 0, 152 | 1, 153 | 0, 154 | 0, 155 | 0, 156 | 0, 157 | 2 158 | ], 159 | [ 160 | 0, 161 | 2, 162 | 0, 163 | 1, 164 | 0, 165 | 0, 166 | 1, 167 | 0, 168 | 2, 169 | 2, 170 | 2, 171 | 2 172 | ], 173 | [ 174 | 0, 175 | 0, 176 | 0, 177 | 0, 178 | 0, 179 | 0, 180 | 1, 181 | 0, 182 | 1, 183 | 0, 184 | 1, 185 | 2 186 | ], 187 | [ 188 | 0, 189 | 1, 190 | 0, 191 | 0, 192 | 0, 193 | 0, 194 | 1, 195 | 1, 196 | 2, 197 | 1, 198 | 1, 199 | 2 200 | ], 201 | [ 202 | 0, 203 | 0, 204 | 0, 205 | 0, 206 | 0, 207 | 0, 208 | 1, 209 | 0, 210 | 0, 211 | 0, 212 | 0, 213 | 2 214 | ], 215 | [ 216 | 0, 217 | 0, 218 | 0, 219 | 0, 220 | 0, 221 | 0, 222 | 0, 223 | 0, 224 | 0, 225 | 0, 226 | 0, 227 | 2 228 | ], 229 | [ 230 | 1, 231 | 1, 232 | 1, 233 | 1, 234 | 1, 235 | 0, 236 | 1, 237 | 1, 238 | 1, 239 | 1, 240 | 1, 241 | 2 242 | ], 243 | [ 244 | 0, 245 | 0, 246 | 0, 247 | 1, 248 | 0, 249 | 0, 250 | 1, 251 | 0, 252 | 0, 253 | 0, 254 | 0, 255 | 2 256 | ], 257 | [ 258 | 0, 259 | 2, 260 | 1, 261 | 2, 262 | 0, 263 | 0, 264 | 1, 265 | 0, 266 | 2, 267 | 2, 268 | 2, 269 | 2 270 | ], 271 | [ 272 | 0, 273 | 2, 274 | 0, 275 | 1, 276 | 0, 277 | 0, 278 | 1, 279 | 0, 280 | 2, 281 | 0, 282 | 0, 283 | 2 284 | ], 285 | [ 286 | 0, 287 | 2, 288 | 1, 289 | 1, 290 | 0, 291 | 0, 292 | 1, 293 | 0, 294 | 2, 295 | 0, 296 | 0, 297 | 2 298 | ], 299 | [ 300 | 2, 301 | 2, 302 | 2, 303 | 2, 304 | 2, 305 | 2, 306 | 2, 307 | 2, 308 | 2, 309 | 2, 310 | 2, 311 | 2 312 | ] 313 | ], 314 | "rule_severities": { 315 | "bus_definition_conflict": "error", 316 | "bus_entry_needed": "error", 317 | "bus_label_syntax": "error", 318 | "bus_to_bus_conflict": "error", 319 | "bus_to_net_conflict": "error", 320 | "different_unit_footprint": "error", 321 | "different_unit_net": "error", 322 | "duplicate_reference": "error", 323 | "duplicate_sheet_names": "error", 324 | "extra_units": "error", 325 | "global_label_dangling": "warning", 326 | "hier_label_mismatch": "error", 327 | "label_dangling": "error", 328 | "lib_symbol_issues": "warning", 329 | "multiple_net_names": "warning", 330 | "net_not_bus_member": "warning", 331 | "no_connect_connected": "warning", 332 | "no_connect_dangling": "warning", 333 | "pin_not_connected": "error", 334 | "pin_not_driven": "error", 335 | "pin_to_pin": "warning", 336 | "power_pin_not_driven": "error", 337 | "similar_labels": "warning", 338 | "unannotated": "error", 339 | "unit_value_mismatch": "error", 340 | "unresolved_variable": "error", 341 | "wire_dangling": "error" 342 | } 343 | }, 344 | "libraries": { 345 | "pinned_footprint_libs": [], 346 | "pinned_symbol_libs": [] 347 | }, 348 | "meta": { 349 | "filename": "sm2k_breakout.kicad_pro", 350 | "version": 1 351 | }, 352 | "net_settings": { 353 | "classes": [ 354 | { 355 | "bus_width": 12.0, 356 | "clearance": 0.2, 357 | "diff_pair_gap": 0.25, 358 | "diff_pair_via_gap": 0.25, 359 | "diff_pair_width": 0.2, 360 | "line_style": 0, 361 | "microvia_diameter": 0.3, 362 | "microvia_drill": 0.1, 363 | "name": "Default", 364 | "pcb_color": "rgba(0, 0, 0, 0.000)", 365 | "schematic_color": "rgba(0, 0, 0, 0.000)", 366 | "track_width": 0.25, 367 | "via_diameter": 0.8, 368 | "via_drill": 0.4, 369 | "wire_width": 6.0 370 | } 371 | ], 372 | "meta": { 373 | "version": 2 374 | }, 375 | "net_colors": null 376 | }, 377 | "pcbnew": { 378 | "last_paths": { 379 | "gencad": "", 380 | "idf": "", 381 | "netlist": "", 382 | "specctra_dsn": "", 383 | "step": "", 384 | "vrml": "" 385 | }, 386 | "page_layout_descr_file": "" 387 | }, 388 | "schematic": { 389 | "annotate_start_num": 0, 390 | "drawing": { 391 | "default_line_thickness": 6.0, 392 | "default_text_size": 50.0, 393 | "field_names": [], 394 | "intersheets_ref_own_page": false, 395 | "intersheets_ref_prefix": "", 396 | "intersheets_ref_short": false, 397 | "intersheets_ref_show": false, 398 | "intersheets_ref_suffix": "", 399 | "junction_size_choice": 3, 400 | "label_size_ratio": 0.375, 401 | "pin_symbol_size": 25.0, 402 | "text_offset_ratio": 0.15 403 | }, 404 | "legacy_lib_dir": "", 405 | "legacy_lib_list": [], 406 | "meta": { 407 | "version": 1 408 | }, 409 | "net_format_name": "", 410 | "ngspice": { 411 | "fix_include_paths": true, 412 | "fix_passive_vals": false, 413 | "meta": { 414 | "version": 0 415 | }, 416 | "model_mode": 0, 417 | "workbook_filename": "" 418 | }, 419 | "page_layout_descr_file": "", 420 | "plot_directory": "", 421 | "spice_adjust_passive_values": false, 422 | "spice_external_command": "spice \"%I\"", 423 | "subpart_first_id": 65, 424 | "subpart_id_separator": 0 425 | }, 426 | "sheets": [ 427 | [ 428 | "e63e39d7-6ac0-4ffd-8aa3-1841a4541b55", 429 | "" 430 | ] 431 | ], 432 | "text_variables": {} 433 | } 434 | -------------------------------------------------------------------------------- /sdMgr.cpp: -------------------------------------------------------------------------------- 1 | #include "sdMgr.h" 2 | 3 | const int chipSelect = BUILTIN_SDCARD; 4 | 5 | const char NAstring[11] = {'N', '/', 'A', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; 6 | fileInfo FileInfo; 7 | 8 | void initSDcard() 9 | { 10 | if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); } 11 | } 12 | 13 | uint8_t peekFileName(uint8_t fileNr, char * buf) 14 | { 15 | char fileName[18]; 16 | sprintf(fileName, "BM2K_DATAFILE_%03d", fileNr); 17 | if (SD.exists(fileName)) 18 | { 19 | File dataFile = SD.open(fileName, FILE_READ); 20 | dataFile.read((uint8_t *)&FileInfo, sizeof(FileInfo)); 21 | dataFile.close(); 22 | memcpy(buf, FileInfo.name, 11); 23 | return 1; 24 | } 25 | else 26 | { 27 | //Serial.printf(F("Patch nr %d not found on SD\n"), fileNr); 28 | memcpy(buf, NAstring, 11); 29 | return 0; 30 | } 31 | } 32 | 33 | void loadFile(uint8_t fileNr) 34 | { 35 | //Serial.print(F("Loading patch nr: ")); 36 | //Serial.println(fileNr); 37 | char fileName[18]; 38 | sprintf(fileName, "BM2K_DATAFILE_%03d", fileNr); 39 | if (SD.exists(fileName)) 40 | { 41 | File dataFile = SD.open(fileName, FILE_READ); 42 | dataFile.read((uint8_t *)&FileInfo, sizeof(FileInfo)); 43 | if (FileInfo.dataSize == DATA_SIZE) 44 | { 45 | for (uint8_t trackId = 0; trackId < NR_TRACKS; trackId++) 46 | { 47 | for (uint8_t patternId = 0; patternId < NR_PATTERNS; patternId++) 48 | { 49 | Pattern tempPattern; 50 | dataFile.read((uint8_t *)&tempPattern, sizeof(tempPattern)); 51 | tracks[trackId]->setPattern(patternId, tempPattern); 52 | } 53 | } 54 | dataFile.read((uint8_t *)&SequencerData, sizeof(SequencerData)); 55 | } 56 | else Serial.println("Invalid data size on SD, not loaded"); 57 | dataFile.close(); 58 | } 59 | else Serial.println("File does not exist on SD"); 60 | } 61 | 62 | void saveFile(uint8_t fileNr) 63 | { 64 | //Serial.print("Storing patch nr: "); 65 | //Serial.println(fileNr); 66 | char fileName[18]; 67 | sprintf(fileName, "BM2K_DATAFILE_%03d", fileNr); 68 | SD.remove(fileName); 69 | File dataFile = SD.open(fileName, FILE_WRITE); 70 | FileInfo.dataSize = DATA_SIZE; 71 | dataFile.write((uint8_t *)&FileInfo, sizeof(FileInfo)); 72 | for (uint8_t trackId = 0; trackId < NR_TRACKS; trackId++) 73 | { 74 | for (uint8_t patternId = 0; patternId < NR_PATTERNS; patternId++) 75 | { 76 | Pattern tempPattern = tracks[trackId]->getPattern(patternId); 77 | dataFile.write((uint8_t *)&tempPattern, sizeof(tempPattern)); 78 | } 79 | } 80 | dataFile.write((uint8_t *)&SequencerData, sizeof(SequencerData)); 81 | dataFile.close(); 82 | } 83 | -------------------------------------------------------------------------------- /sdMgr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "midiFunctions.h" 5 | #include "sequencer.h" 6 | 7 | #define MAX_FILES 128 8 | 9 | #define DATA_SIZE_OLD sizeof(pattern) * NR_TRACKS * NR_PATTERNS + sizeof(SequencerDataOld) 10 | #define DATA_SIZE sizeof(pattern) * NR_TRACKS * NR_PATTERNS + sizeof(SequencerData) 11 | 12 | struct fileInfo 13 | { 14 | uint16_t dataSize = 0; 15 | char name[11] = {'I', 'N', 'I', 'T', ' ', 'P', 'A', 'T', 'C', 'H'}; 16 | }; 17 | 18 | extern fileInfo FileInfo; 19 | 20 | void initSDcard(); 21 | uint8_t peekFileName(uint8_t fileNr, char * buf); 22 | void loadFile(uint8_t fileNr); 23 | void saveFile(uint8_t fileNr); 24 | -------------------------------------------------------------------------------- /sequencer.cpp: -------------------------------------------------------------------------------- 1 | #include "sequencer.h" 2 | 3 | Track track0(0, "T0", LP_BLUE); 4 | Track track1(1, "T1", LP_CYAN); 5 | Track track2(2, "T2", LP_PURPLE); 6 | Track track3(3, "T3", LP_PINK); 7 | Track track4(4, "T4", LP_ORANGE); 8 | Track track5(5, "T5", LP_GREEN); 9 | Track track6(6, "T6", LP_YELLOW); 10 | const uint8_t nrTracks = 7; 11 | Track *tracks[nrTracks] = {&track0, &track1, &track2, &track3, &track4, &track5, &track6}; 12 | 13 | sequencerData SequencerData; 14 | 15 | uint8_t sequencerState = STATE_STOPPED; 16 | uint8_t columnTimer = 0; 17 | uint16_t oneTickUs = 1000 * 60000 / (SequencerData.bpm * ticksPerBeat); 18 | uint8_t sequencerEditMode = MODE_PATTERNEDIT; 19 | uint8_t selectionId = SELECTION_PATTERN; 20 | uint8_t actionId = ACTION_COPY; 21 | int8_t toolTranspose = 0; 22 | bool sendClock = false; 23 | uint8_t playMode = PLAYMODE_NONE; 24 | 25 | uint8_t currentTrack = 0; 26 | uint8_t currentPattern = 0; 27 | int16_t currentEvent = -1; 28 | uint8_t currentScene = 0; 29 | uint8_t currentArrPosition = 0; 30 | uint8_t currentArrPositionLength = 0; 31 | uint8_t fourthCounter = 0; 32 | 33 | uint8_t sceneLengthColumns = 16; // TEMPORARY, MOVE TO SequencerData if used 34 | 35 | TrackEvent eventClipBoard[NR_TRACK_EVENTS]; 36 | uint8_t nrClipBoardEvents = 0; 37 | Pattern patternClipBoard; 38 | 39 | IntervalTimer sequencerUpdateTimer; 40 | 41 | MIDIcallback outputNoteOnFunctions[] = {voiceNoteOn, serialMidiNoteOn, deviceNoteOn, midi1NoteOn, midi2NoteOn, midi3NoteOn, midi4NoteOn, midi5NoteOn}; 42 | MIDIcallback outputNoteOffFunctions[] = {voiceNoteOff, serialMidiNoteOff, deviceNoteOff, midi1NoteOff, midi2NoteOff, midi3NoteOff, midi4NoteOff, midi5NoteOff}; 43 | String outputNames[] = {"Dummy", "Serial", "USB device", "USB 0", "USB 1", "USB 2", "USB 3", "USB 4"}; 44 | uint8_t nrOutputFunctions = sizeof(outputNoteOnFunctions)/sizeof(outputNoteOnFunctions[0]); 45 | const String noYesSelected[] = {"No", "Yes", "Selected"}; 46 | 47 | elapsedMicros debugTimer; 48 | 49 | // ---- SEQUENCER CONTROL ---- 50 | 51 | void initSequencer() 52 | { 53 | sequencerUpdateTimer.priority(100); 54 | for (uint8_t sceneId = 0;sceneId < NR_SCENES; sceneId++) SequencerData.sceneColors[sceneId] = LP_GREEN; 55 | for (uint8_t arrId = 0;arrId < NR_ARRPOSITIONS; arrId++) SequencerData.arrangement[arrId] = NO_SCENE; 56 | sequencerUpdateTimer.begin(tickTracks, oneTickUs); 57 | } 58 | 59 | void tickTracks() 60 | { 61 | // 24 ppq 62 | if (sequencerState == STATE_RUNNING) 63 | { 64 | for (uint8_t trackId = 0;trackId < NR_TRACKS; trackId++) tracks[trackId]->tickTrack(); 65 | if (sendClock) sendMidiClock(); 66 | fourthCounter++; 67 | if (fourthCounter > ( ticksPerBeat - 1 )) fourthCounter = 0; 68 | } 69 | else fourthCounter = 0; 70 | } 71 | 72 | void startSequencer() 73 | { 74 | LP1.setPadColor(CCstartStop, LP_GREEN); 75 | if (playMode == PLAYMODE_CHAIN_FROM_START) 76 | { 77 | currentArrPosition = 0; 78 | currentScene = SequencerData.arrangement[currentArrPosition]; 79 | setSceneNr(SequencerData.arrangement[currentArrPosition]); 80 | } 81 | LP1.setPadColor(CCindicator, LP_CYAN); 82 | if (sendClock) sendMidiStart(); 83 | sequencerState = STATE_RUNNING; 84 | } 85 | 86 | void stopSequencer() 87 | { 88 | if (sendClock) sendMidiStop(); 89 | LP1.setPadColor(CCstartStop, LP_RED); 90 | LP1.setPadColor(CCindicator, LP_OFF); 91 | sequencerState = STATE_STOPPED; 92 | resetTracks(); 93 | flushTracksPlayedBuffers(); 94 | } 95 | 96 | void setBpm(float newBpm) 97 | { 98 | SequencerData.bpm = (uint16_t)newBpm; 99 | oneTickUs = 1000 * 60000 / (SequencerData.bpm * ticksPerBeat); 100 | sequencerUpdateTimer.update(oneTickUs); 101 | } 102 | 103 | float getBpm() { return SequencerData.bpm; } 104 | 105 | float getPlaymode() { return (float)playMode; } 106 | void setPlaymode(float mode) { playMode = (uint8_t)mode; } 107 | 108 | void setMidiClockOnOff(float onOff) { sendClock = (bool)onOff; } 109 | float getMidiClockOnOff() { return sendClock; } 110 | 111 | 112 | // ---- TRACK CONFIGURATON ---- 113 | 114 | void setTrackOutput(float value) 115 | { 116 | uint8_t outputIndex = (uint8_t)value; 117 | tracks[currentTrack]->setHandleNoteOn(outputNoteOnFunctions[outputIndex]); 118 | tracks[currentTrack]->setHandleNoteOff(outputNoteOffFunctions[outputIndex]); 119 | tracks[currentTrack]->outputId = outputIndex; 120 | } 121 | 122 | void setCurrentTrack(uint8_t track) 123 | { 124 | currentTrack = track; 125 | LP1.page = 0; //min(LP1.page, maxPages); 126 | } 127 | 128 | float getTrackOutput() { return tracks[currentTrack]->outputId; } 129 | String getOutputEnum(uint8_t value) 130 | { 131 | if (value > 2 && value < nrOutputFunctions) 132 | { 133 | char buf[16]; 134 | getUsbDeviceName(value - 3, buf, 16); 135 | outputNames[value] = buf; 136 | } 137 | return outputNames[value]; 138 | } 139 | 140 | float getTrackChannel() { return (float)(tracks[currentTrack]->getTrackChannel()); } 141 | void setTrackChannel(float channel) { tracks[currentTrack]->setTrackChannel((uint8_t)channel); } 142 | String get0_16_note(uint8_t channel) 143 | { 144 | if (channel < 17) return String(channel); 145 | else return "NoteVal"; 146 | } 147 | 148 | float getTrackLengthColumns() { return (float)(tracks[currentTrack]->getPatternLengthColumns(TICKS_PER_COLUMN)); } 149 | void setTrackLengthColumns(float columns) { tracks[currentTrack]->setPatternLengthColumns((uint16_t)columns, TICKS_PER_COLUMN); } 150 | 151 | float getTrackDefLength() { return (float)(tracks[currentTrack]->getTrackDefaultNoteLengthTicks()); } 152 | void setTrackDefLength(float length) { tracks[currentTrack]->setTrackDefaultNoteLengthTicks((uint16_t)length); } 153 | 154 | float getTrackPatternNr() { return (float)(tracks[currentTrack]->getActivePatternId()); } 155 | 156 | void setTrackPatternNr(float patternNr) 157 | { 158 | if (sequencerState == STATE_STOPPED) tracks[currentTrack]-> setPatternId((uint8_t)patternNr); 159 | if (sequencerState == STATE_RUNNING) tracks[currentTrack]-> cuePatternId((uint8_t)patternNr); 160 | currentPattern = (uint8_t)patternNr; 161 | } 162 | 163 | float getTransposeStatus() { return (float)tracks[currentTrack]->trackRespondToTranspose; } 164 | void setTransposeStatus(float status) { tracks[currentTrack]->trackRespondToTranspose = (uint8_t)status; } 165 | String getNoYesSelectedEnum(uint8_t value) { return noYesSelected[constrain(value, 0, 2)]; } 166 | 167 | void setTranspose(int transpose) 168 | { 169 | for (uint8_t trackId = 0;trackId < NR_TRACKS; trackId++) 170 | { 171 | if (tracks[trackId]->trackRespondToTranspose == 1) tracks[trackId]->transpose = transpose; 172 | if (tracks[trackId]->trackRespondToTranspose == 2 && trackId == currentTrack ) tracks[trackId]->transpose = transpose; 173 | } 174 | } 175 | 176 | 177 | // ---- PATTERN CONFIGURATON ---- 178 | 179 | void setPatternSpeed(float speed) 180 | { 181 | uint8_t patternId = tracks[currentTrack]->getActivePatternId(); 182 | tracks[currentTrack]->setPatternSpeed(patternId, (uint8_t)speed); 183 | } 184 | 185 | float getPatternSpeed() 186 | { 187 | uint8_t patternId = tracks[currentTrack]->getActivePatternId(); 188 | return (float)tracks[currentTrack]->getPatternSpeed(patternId); 189 | } 190 | 191 | String getPatternSpeedEnum(uint8_t value) 192 | { 193 | switch (value) 194 | { 195 | case PATTERN_SPEED_x1_4: return "x1/4"; 196 | case PATTERN_SPEED_x1_3: return "x1/3"; 197 | case PATTERN_SPEED_x1_2: return "x1/2"; 198 | case PATTERN_SPEED_x1: return "x1"; 199 | case PATTERN_SPEED_x2: return "x2"; 200 | default: return "ERR"; 201 | } 202 | } 203 | 204 | void resetTracks() 205 | { 206 | for (uint8_t trackId = 0;trackId < NR_TRACKS; trackId++) tracks[trackId]->loopResetTrack(); 207 | } 208 | 209 | void flushTracksPlayedBuffers() 210 | { 211 | for (uint8_t trackId = 0;trackId < NR_TRACKS; trackId++) tracks[trackId]->flushPlayedBuffer(); 212 | } 213 | 214 | float getEventLength() 215 | { 216 | TrackEvent tempEvent = tracks[currentTrack]->getEvent(currentPattern, currentEvent); 217 | return (float)(tempEvent.noteLength); 218 | } 219 | 220 | void setEventLength(float length) 221 | { 222 | if (currentEvent != -1) 223 | { 224 | TrackEvent tempEvent = tracks[currentTrack]->getEvent(currentPattern, currentEvent); 225 | tempEvent.noteLength = (uint16_t)length; 226 | tracks[currentTrack]->setEvent(currentPattern, currentEvent, tempEvent); 227 | } 228 | } 229 | 230 | 231 | // --- SCENE CONFIGURATION --- 232 | 233 | void updateSceneConfiguration(uint8_t sceneId) 234 | { 235 | // update pattern configuration in selected scene 236 | for (uint8_t trackId = 0; trackId < NR_TRACKS; trackId++) 237 | { 238 | SequencerData.scenes[trackId][sceneId] = tracks[trackId]->getActivePatternId(); 239 | } 240 | } 241 | 242 | void setScene(uint8_t sceneId) 243 | { 244 | // set or cue patterns from scene 245 | uint8_t sceneLength = 0; 246 | for (uint8_t trackId = 0; trackId < NR_TRACKS; trackId++) 247 | { 248 | if ( (sequencerState == STATE_STOPPED) && ( tracks[trackId]->getActivePatternId() != SequencerData.scenes[trackId][sceneId]) ) tracks[trackId]->setPatternId(SequencerData.scenes[trackId][sceneId]); 249 | if (sequencerState == STATE_RUNNING && ( tracks[trackId]->getActivePatternId() != SequencerData.scenes[trackId][sceneId]) ) tracks[trackId]->cuePatternId(SequencerData.scenes[trackId][sceneId]); 250 | //uint8_t patternLength = tracks[trackId]->getPatternLengthColumns_indexed(SequencerData.scenes[trackId][sceneId], TICKS_PER_COLUMN); 251 | //sceneLength = max(sceneLength, patternLength); 252 | } 253 | //currentArrPositionLength = sceneLength; 254 | //Serial.printf("Current scene length: %d\n", sceneLength); 255 | } 256 | 257 | float getSceneNr() { return (float)currentScene; } 258 | 259 | void setSceneNr(float sceneId) 260 | { 261 | currentScene = (uint8_t)sceneId; 262 | setScene(sceneId); 263 | if (LPdisplayMode == LPMODE_SONG) LPsetPageFromSongData(true); 264 | } 265 | 266 | float getSceneColor() { return SequencerData.sceneColors[currentScene]; } 267 | void setSceneColor(float color) 268 | { 269 | SequencerData.sceneColors[currentScene] = (uint8_t)color; 270 | LPsetSceneButtonsSceneMode(true); 271 | } 272 | 273 | 274 | // --- CLIPBOARD TOOLS --- 275 | 276 | void setToolSelection(float selection) { selectionId = (uint8_t)selection; } 277 | float getToolSelection() { return (float)selectionId; } 278 | String getSelectionEnum(uint8_t selection) 279 | { 280 | switch (selection) 281 | { 282 | case SELECTION_PATTERN: 283 | return "Pattern"; 284 | break; 285 | case SELECTION_COLUMNS: 286 | return "Columns"; 287 | break; 288 | case SELECTION_VIEW: 289 | return "View"; 290 | break; 291 | case SELECTION_SONG: 292 | return "Song"; 293 | break; 294 | default: 295 | return "ERR"; 296 | } 297 | } 298 | 299 | void setToolAction(float action) {actionId = (uint8_t)action;} 300 | float getToolAction() { return (float)actionId;} 301 | String getActionEnum(uint8_t action) 302 | { 303 | switch (action) 304 | { 305 | case ACTION_CLEAR: 306 | return "Clear"; 307 | break; 308 | case ACTION_COPY: 309 | return "Copy"; 310 | break; 311 | case ACTION_PASTE: 312 | return "Paste"; 313 | break; 314 | default: 315 | return "ERR"; 316 | } 317 | } 318 | 319 | void setToolTranspose(float transpose) {toolTranspose = (int8_t)transpose;} 320 | float getToolTranspose() { return (float)toolTranspose;} 321 | 322 | void doToolAction() 323 | { 324 | switch(actionId) 325 | { 326 | case ACTION_CLEAR: 327 | clearSelection(); 328 | break; 329 | case ACTION_COPY: 330 | copySelection(); 331 | break; 332 | case ACTION_PASTE: 333 | pasteSelection(); 334 | break; 335 | } 336 | } 337 | 338 | void clearEventClipBoard() 339 | { 340 | for (uint8_t eventId = 0; eventId < NR_TRACK_EVENTS; eventId++) eventClipBoard[eventId] = empty_trackEvent; 341 | nrClipBoardEvents = 0; 342 | } 343 | 344 | void normalizeEventClipBoard() 345 | { 346 | for(uint8_t clipBoardEventId = 0; clipBoardEventId < nrClipBoardEvents; clipBoardEventId++) 347 | { 348 | eventClipBoard[clipBoardEventId].tick = eventClipBoard[clipBoardEventId].tick % (8 * TICKS_PER_COLUMN); 349 | } 350 | } 351 | 352 | void copySelection() 353 | { 354 | clearEventClipBoard(); 355 | 356 | if (selectionId == SELECTION_PATTERN) patternClipBoard = tracks[currentTrack]->getPattern(currentPattern); 357 | 358 | if (selectionId == SELECTION_COLUMNS || selectionId == SELECTION_VIEW) 359 | { 360 | uint8_t noteStart = tracks[currentTrack]->lowerRow; 361 | uint8_t noteEnd = noteStart + 8; 362 | uint8_t eventCounter = 0; 363 | 364 | if (selectionId == SELECTION_COLUMNS) 365 | { 366 | noteStart = 0; 367 | noteEnd = 127; 368 | } 369 | 370 | for (uint8_t column = 0; column < 8; column++) 371 | { 372 | uint16_t tick = LP1.page * 8 * TICKS_PER_COLUMN + column * TICKS_PER_COLUMN; 373 | for(uint8_t note = noteStart; note <= noteEnd; note++) 374 | { 375 | int16_t eventId = tracks[currentTrack]->getEventId(tick, note); 376 | if (eventId > -1) 377 | { 378 | eventClipBoard[eventCounter] = tracks[currentTrack]->getEvent(currentPattern, eventId); 379 | eventCounter++; 380 | } 381 | } 382 | } 383 | nrClipBoardEvents = eventCounter; 384 | normalizeEventClipBoard(); 385 | } 386 | } 387 | 388 | 389 | void pasteSelection() 390 | { 391 | if (selectionId == SELECTION_PATTERN) tracks[currentTrack]->setPattern(currentPattern, patternClipBoard); 392 | 393 | if (selectionId == SELECTION_COLUMNS || selectionId == SELECTION_VIEW) 394 | { 395 | uint16_t destinationPageTickStart = LP1.page * 8 * TICKS_PER_COLUMN; 396 | for(uint8_t clipBoardEventId = 0; clipBoardEventId < nrClipBoardEvents; clipBoardEventId++) 397 | { 398 | tracks[currentTrack]->addEvent(eventClipBoard[clipBoardEventId].tick + destinationPageTickStart, 399 | eventClipBoard[clipBoardEventId].noteValue + toolTranspose, 400 | eventClipBoard[clipBoardEventId].noteVelocity, 401 | eventClipBoard[clipBoardEventId].noteLength, 402 | false); 403 | } 404 | } 405 | 406 | LPsetPageFromTrackData(); 407 | LPcopy_update(false); 408 | } 409 | 410 | void clearSelection() 411 | { 412 | if (selectionId == SELECTION_PATTERN) tracks[currentTrack]->clearPattern(tracks[currentTrack]->getActivePatternId()); 413 | 414 | if (selectionId == SELECTION_SONG) 415 | { 416 | for (uint8_t arrId = 0;arrId < NR_ARRPOSITIONS; arrId++) SequencerData.arrangement[arrId] = NO_SCENE; 417 | } 418 | 419 | if (selectionId == SELECTION_COLUMNS || selectionId == SELECTION_VIEW) 420 | { 421 | uint16_t tickStart = (LP1.page * 8) * TICKS_PER_COLUMN; 422 | uint16_t tickEnd = tickStart + 8 * TICKS_PER_COLUMN - 1; 423 | if (selectionId == SELECTION_COLUMNS) 424 | { 425 | for(uint8_t note = 0; note < 128; note++) tracks[currentTrack]->removeEvents(tickStart, tickEnd, note); 426 | } 427 | if (selectionId == SELECTION_VIEW) 428 | { 429 | uint8_t noteStart = tracks[currentTrack]->lowerRow; 430 | uint8_t noteEnd = noteStart + 8; 431 | for(uint8_t note = noteStart; note <= noteEnd; note++) tracks[currentTrack]->removeEvents(tickStart, tickEnd, note); 432 | } 433 | } 434 | } 435 | 436 | // --- SONG MODE, SCENE CHAINING --- 437 | 438 | void updateSequencer() 439 | { 440 | if ( (sequencerState == STATE_RUNNING) && ( (playMode == PLAYMODE_CHAIN_FROM_SELECTION) || (playMode == PLAYMODE_CHAIN_FROM_START) ) ) updateSongMode(); 441 | } 442 | 443 | void updateSongMode() 444 | { 445 | // NOTE: many improvements needed. This concept does not handle repetitions of same scene 446 | 447 | bool allTracksInCurrentScene = true; 448 | uint8_t sceneId = SequencerData.arrangement[currentArrPosition]; 449 | static uint8_t sceneAgeColumns = 0; 450 | 451 | for (uint8_t trackId = 0; trackId < NR_TRACKS; trackId++) 452 | { 453 | if (tracks[trackId]->getActivePatternId() != SequencerData.scenes[trackId][sceneId]) allTracksInCurrentScene = false; 454 | } 455 | 456 | if ( allTracksInCurrentScene && (SequencerData.arrangement[currentArrPosition + 1] != NO_SCENE)) 457 | { 458 | setSceneNr(SequencerData.arrangement[currentArrPosition + 1]); 459 | currentArrPosition++; 460 | } 461 | } 462 | 463 | String getSongModeEnum(uint8_t modeId) 464 | { 465 | switch (modeId) 466 | { 467 | case PLAYMODE_NONE: return F("Off"); 468 | case PLAYMODE_CHAIN_FROM_SELECTION: return F("From Selection"); 469 | case PLAYMODE_CHAIN_FROM_START: return F("From Start"); 470 | default: return F("ERR"); 471 | } 472 | } 473 | -------------------------------------------------------------------------------- /sequencer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "track.h" 5 | #include "midiFunctions.h" 6 | #include "audioFunctions.h" 7 | 8 | #define STATE_STOPPED 0 9 | #define STATE_RUNNING 1 10 | 11 | #define PLAYMODE_NONE 0 12 | #define PLAYMODE_CHAIN_FROM_SELECTION 1 13 | #define PLAYMODE_CHAIN_FROM_START 2 14 | 15 | #define MODE_PATTERNEDIT 0 16 | #define MODE_EVENTEDIT 1 17 | 18 | #define SELECTION_PATTERN 0 19 | #define SELECTION_COLUMNS 1 20 | #define SELECTION_VIEW 2 21 | #define SELECTION_SONG 3 22 | 23 | #define ACTION_CLEAR 0 24 | #define ACTION_COPY 1 25 | #define ACTION_PASTE 2 26 | 27 | #define NR_TRACKS 7 28 | #define NR_PATTERNS_IN_ARRANGEMENT 8 29 | #define NO_SCENE 255 30 | 31 | #define NR_SCENES 16 32 | 33 | #define NR_ARRPOSITIONS 56 // Max scenes in arrangement 34 | 35 | #define ticksPerBeat 24 // 1/4 * 1/24 = 1/96th resolution 36 | #define TICKS_PER_BEAT 24 37 | #define TICKS_PER_COLUMN 6 38 | 39 | struct sequencerData 40 | { 41 | uint8_t bpm = 120; 42 | uint8_t scenes[NR_TRACKS][NR_SCENES]; 43 | uint8_t sceneColors[NR_SCENES]; 44 | uint8_t arrangement[NR_ARRPOSITIONS]; 45 | }; 46 | 47 | extern sequencerData SequencerData; 48 | 49 | extern uint8_t sequencerState; 50 | extern uint8_t sequencerEditMode; 51 | extern String outputNames[]; 52 | extern Track *tracks[NR_TRACKS]; 53 | extern uint8_t currentTrack; 54 | extern uint8_t currentPattern; 55 | extern int16_t currentEvent; 56 | extern uint8_t currentScene; 57 | extern uint8_t currentArrPosition; 58 | extern uint8_t fourthCounter; 59 | 60 | void initSequencer(); 61 | void tickTracks(); 62 | 63 | void startSequencer(); 64 | void stopSequencer(); 65 | 66 | void setBpm(float bpm); 67 | float getBpm(); 68 | 69 | float getPlaymode(); 70 | void setPlaymode(float mode); 71 | 72 | void setMidiClockOnOff(float onOff); 73 | float getMidiClockOnOff(); 74 | 75 | void setCurrentTrack(uint8_t track); 76 | 77 | void setTrackOutput(float value); 78 | float getTrackOutput(); 79 | String getOutputEnum(uint8_t value); 80 | 81 | float getTrackChannel(); 82 | void setTrackChannel(float channel); 83 | String get0_16_note(uint8_t channel); 84 | 85 | float getTrackPatternNr(); 86 | void setTrackPatternNr(float patternNr); 87 | 88 | float getTrackLengthColumns(); 89 | void setTrackLengthColumns(float columns); 90 | 91 | float getTrackDefLength(); 92 | void setTrackDefLength(float length); 93 | 94 | float getEventLength(); 95 | void setEventLength(float length); 96 | 97 | void setTranspose(int transpose); 98 | float getTransposeStatus(); 99 | void setTransposeStatus(float status); 100 | String getNoYesSelectedEnum(uint8_t value); 101 | 102 | void setPatternSpeed(float speed); 103 | float getPatternSpeed(); 104 | String getPatternSpeedEnum(uint8_t value); 105 | 106 | void resetTracks(); 107 | void flushTracksPlayedBuffers(); 108 | 109 | void updateSceneConfiguration(uint8_t sceneId); 110 | void setScene(uint8_t sceneId); 111 | float getSceneNr(); 112 | void setSceneNr(float sceneId); 113 | float getSceneColor(); 114 | void setSceneColor(float color); 115 | float getSceneLength(); 116 | void setSceneLength(float length); 117 | 118 | void setToolSelection(float selection); 119 | float getToolSelection(); 120 | String getSelectionEnum(uint8_t selection); 121 | 122 | void setToolAction(float action); 123 | float getToolAction(); 124 | String getActionEnum(uint8_t action); 125 | 126 | void setToolTranspose(float selection); 127 | float getToolTranspose(); 128 | 129 | void doToolAction(); 130 | void copySelection(); 131 | void pasteSelection(); 132 | void clearSelection(); 133 | void clearEventClipBoard(); 134 | void normalizeEventClipBoard(); 135 | 136 | void updateSequencer(); 137 | void updateSongMode(); 138 | String getSongModeEnum(uint8_t modeId); 139 | -------------------------------------------------------------------------------- /src/SM2k_control_wm8731.cpp: -------------------------------------------------------------------------------- 1 | /* Audio Library for Teensy 3.X 2 | * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com 3 | * 4 | * Development of this audio library was funded by PJRC.COM, LLC by sales of 5 | * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop 6 | * open source software by purchasing Teensy or other PJRC products. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice, development funding notice, and this permission 16 | * notice shall be included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include "SM2k_control_wm8731.h" 29 | #include "Wire.h" 30 | 31 | #define WM8731_I2C_ADDR 0x1A 32 | //#define WM8731_I2C_ADDR 0x1B 33 | 34 | #define WM8731_REG_LLINEIN 0 35 | #define WM8731_REG_RLINEIN 1 36 | #define WM8731_REG_LHEADOUT 2 37 | #define WM8731_REG_RHEADOUT 3 38 | #define WM8731_REG_ANALOG 4 39 | #define WM8731_REG_DIGITAL 5 40 | #define WM8731_REG_POWERDOWN 6 41 | #define WM8731_REG_INTERFACE 7 42 | #define WM8731_REG_SAMPLING 8 43 | #define WM8731_REG_ACTIVE 9 44 | #define WM8731_REG_RESET 15 45 | 46 | void SM2k_AudioControlWM8731::setAddress(uint8_t address) 47 | { 48 | i2c_address = address; 49 | } 50 | 51 | bool SM2k_AudioControlWM8731::enable(void) 52 | { 53 | Wire.begin(); 54 | delay(100); 55 | #if 1 56 | if (!write(WM8731_REG_RESET, 0)) { 57 | return false; // no WM8731 chip responding 58 | } 59 | #endif 60 | write(WM8731_REG_INTERFACE, 0x02); // I2S, 16 bit, MCLK slave 61 | write(WM8731_REG_SAMPLING, 0x20); // 256*Fs, 44.1 kHz, MCLK/1 62 | 63 | // In order to prevent pops, the DAC should first be soft-muted (DACMU), 64 | // the output should then be de-selected from the line and headphone output 65 | // (DACSEL), then the DAC powered down (DACPD). 66 | 67 | write(WM8731_REG_DIGITAL, 0x08); // DAC soft mute 68 | write(WM8731_REG_ANALOG, 0x00); // disable all 69 | 70 | write(WM8731_REG_POWERDOWN, 0x00); // codec powerdown 71 | 72 | write(WM8731_REG_LHEADOUT, 0x80); // volume off 73 | write(WM8731_REG_RHEADOUT, 0x80); 74 | 75 | delay(100); // how long to power up? 76 | 77 | write(WM8731_REG_ACTIVE, 1); 78 | delay(5); 79 | write(WM8731_REG_DIGITAL, 0x00); // DAC unmuted 80 | write(WM8731_REG_ANALOG, 0x10); // DAC selected 81 | 82 | return true; 83 | } 84 | 85 | // WM8731 has flaky I2C communication, especially when MCLK has ringing, 86 | // overshoot or other high-speed signal quality issues. Chips like 87 | // Teensy 3.6 with very high bandwidth I/O pins should be used with 88 | // caution. A resistor or low-pass circuit may be needed. 89 | // https://forum.pjrc.com/threads/55334?p=201494&viewfull=1#post201494 90 | 91 | bool SM2k_AudioControlWM8731::write(unsigned int reg, unsigned int val) 92 | { 93 | int attempt=0; 94 | //Serial.printf("WM8731 %d, reg %d, val %d: ", i2c_address, reg, val); 95 | while (1) { 96 | attempt++; 97 | //Wire.beginTransmission(WM8731_I2C_ADDR); 98 | Wire.beginTransmission(i2c_address); 99 | Wire.write((reg << 1) | ((val >> 8) & 1)); 100 | Wire.write(val & 0xFF); 101 | int status = Wire.endTransmission(); 102 | if (status == 0) { 103 | //Serial.printf("write ok, %d tries\n", attempt); 104 | return true; 105 | } 106 | if (attempt >= 12) { 107 | Serial.printf("write failed %d tries\n", attempt); 108 | return false; 109 | } 110 | delayMicroseconds(80); 111 | } 112 | } 113 | 114 | bool SM2k_AudioControlWM8731::volumeInteger(unsigned int n) 115 | { 116 | // n = 127 for max volume (+6 dB) 117 | // n = 48 for min volume (-73 dB) 118 | // n = 0 to 47 for mute 119 | if (n > 127) n = 127; 120 | //Serial.print("volumeInteger, n = "); 121 | //Serial.println(n); 122 | write(WM8731_REG_LHEADOUT, n | 0x180); 123 | write(WM8731_REG_RHEADOUT, n | 0x80); 124 | return true; 125 | } 126 | 127 | bool SM2k_AudioControlWM8731::inputLevel(float n) 128 | { 129 | // range is 0x00 (min) - 0x1F (max) 130 | 131 | int _level = int(n * 31.f); 132 | 133 | _level = _level > 0x1F ? 0x1F : _level; 134 | write(WM8731_REG_LLINEIN, _level); 135 | write(WM8731_REG_RLINEIN, _level); 136 | return true; 137 | } 138 | 139 | bool SM2k_AudioControlWM8731::inputSelect(int n) 140 | { 141 | if (n == AUDIO_INPUT_LINEIN) { 142 | write(WM8731_REG_ANALOG, 0x12); 143 | } else if (n == AUDIO_INPUT_MIC) { 144 | write(WM8731_REG_ANALOG, 0x15); 145 | } else { 146 | return false; 147 | } 148 | return true; 149 | } 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/SM2k_control_wm8731.h: -------------------------------------------------------------------------------- 1 | /* Audio Library for Teensy 3.X 2 | * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com 3 | * 4 | * Development of this audio library was funded by PJRC.COM, LLC by sales of 5 | * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop 6 | * open source software by purchasing Teensy or other PJRC products. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice, development funding notice, and this permission 16 | * notice shall be included in all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #ifndef SM2k_control_wm8731_h_ 28 | #define SM2k_control_wm8731_h_ 29 | 30 | #include "AudioControl.h" 31 | 32 | class SM2k_AudioControlWM8731 : public AudioControl 33 | { 34 | public: 35 | bool enable(void); 36 | bool disable(void) { return false; } 37 | bool volume(float n) { return volumeInteger(n * 80.0f + 47.499f); } 38 | bool inputLevel(float n); // range: 0.0f to 1.0f 39 | bool inputSelect(int n); 40 | void setAddress(uint8_t address); 41 | protected: 42 | bool write(unsigned int reg, unsigned int val); 43 | bool volumeInteger(unsigned int n); // range: 0x2F to 0x7F 44 | uint8_t i2c_address; 45 | }; 46 | 47 | #endif -------------------------------------------------------------------------------- /src/effect_dynamics.cpp: -------------------------------------------------------------------------------- 1 | /* Audio Library for Teensy 3.X 2 | Dynamics Processor (Gate, Compressor & Limiter) 3 | Copyright (c) 2017, Marc Paquette (marc@dacsystemes.com) 4 | Based on analyse_rms & mixer objects by Paul Stoffregen 5 | 6 | Development of this audio library was funded by PJRC.COM, LLC by sales of 7 | Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop 8 | open source software by purchasing Teensy or other PJRC products. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice, development funding notice, and this permission 18 | notice shall be included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | */ 28 | #if !defined(KINETISL) 29 | 30 | #include "effect_dynamics.h" 31 | #include "utility/dspinst.h" 32 | #include "utility/sqrt_integer.h" 33 | 34 | //static float analyse_rms(int16_t *data) { 35 | // 36 | // uint32_t *p = (uint32_t *)data; 37 | // const uint32_t *end = p + AUDIO_BLOCK_SAMPLES / 2; 38 | // int64_t sum = 0; 39 | // do { 40 | // uint32_t n1 = *p++; 41 | // uint32_t n2 = *p++; 42 | // uint32_t n3 = *p++; 43 | // uint32_t n4 = *p++; 44 | // sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n1, n1); 45 | // sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n2, n2); 46 | // sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n3, n3); 47 | // sum = multiply_accumulate_16tx16t_add_16bx16b(sum, n4, n4); 48 | // 49 | // } while (p < end); 50 | // if (sum == 0) return 0; 51 | // int32_t meansq = sum / AUDIO_BLOCK_SAMPLES; 52 | // return sqrt_uint32(meansq) / 32767.0f; 53 | //} 54 | 55 | //static void applyGain(int16_t *data, int32_t mult1, int32_t mult2) { 56 | // 57 | // uint32_t *p = (uint32_t *)data; 58 | // const uint32_t *end = p + AUDIO_BLOCK_SAMPLES / 2; 59 | // int32_t inc = (mult2 - mult1) / (AUDIO_BLOCK_SAMPLES / 2); 60 | // 61 | // do { 62 | // uint32_t tmp32 = *p; // read 2 samples from *data 63 | // int32_t val1 = signed_multiply_32x16b(mult1, tmp32); 64 | // mult1 += inc; 65 | // int32_t val2 = signed_multiply_32x16t(mult1, tmp32); 66 | // mult1 += inc; 67 | // val1 = signed_saturate_rshift(val1, 16, 0); 68 | // val2 = signed_saturate_rshift(val2, 16, 0); 69 | // *p++ = pack_16b_16b(val2, val1); 70 | // } while (p < end); 71 | //} 72 | 73 | /* ---------------------------------------------------------------------- 74 | https://community.arm.com/tools/f/discussions/4292/cmsis-dsp-new-functionality-proposal/22621#22621 75 | Fast approximation to the log2() function. It uses a two step 76 | process. First, it decomposes the floating-point number into 77 | a fractional component F and an exponent E. The fraction component 78 | is used in a polynomial approximation and then the exponent added 79 | to the result. A 3rd order polynomial is used and the result 80 | when computing db20() is accurate to 7.984884e-003 dB. 81 | ** ------------------------------------------------------------------- */ 82 | 83 | float log2f_approx_coeff[4] = { 1.23149591368684f, -4.11852516267426f, 6.02197014179219f, -3.13396450166353f }; 84 | 85 | float log2f_approx(float X) { 86 | float *C = &log2f_approx_coeff[0]; 87 | float Y; 88 | float F; 89 | int E; 90 | 91 | // This is the approximation to log2() 92 | F = frexpf(fabsf(X), &E); 93 | 94 | // Y = C[0]*F*F*F + C[1]*F*F + C[2]*F + C[3] + E; 95 | Y = *C++; 96 | Y *= F; 97 | Y += (*C++); 98 | Y *= F; 99 | Y += (*C++); 100 | Y *= F; 101 | Y += (*C++); 102 | Y += E; 103 | return (Y); 104 | } 105 | 106 | // https://codingforspeed.com/using-faster-exponential-approximation/ 107 | inline float expf_approx(float x) { 108 | x = 1.0f + x / 1024; 109 | x *= x; 110 | x *= x; 111 | x *= x; 112 | x *= x; 113 | x *= x; 114 | x *= x; 115 | x *= x; 116 | x *= x; 117 | x *= x; 118 | x *= x; 119 | return x; 120 | } 121 | 122 | inline float unitToDb(float unit) { 123 | return 6.02f * log2f_approx(unit); 124 | } 125 | 126 | inline float dbToUnit(float db) { 127 | return expf_approx(db * 2.302585092994046f * 0.05f); 128 | } 129 | 130 | void AudioEffectDynamics::update(void) { 131 | 132 | audio_block_t *block; 133 | 134 | block = receiveWritable(0); 135 | 136 | if (!block) return; 137 | 138 | if (!compEnabled && !limiterEnabled) { 139 | //Transmit & release 140 | transmit(block); 141 | release(block); 142 | return; 143 | } 144 | 145 | for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { 146 | 147 | // unsigned int sampleIndexPlus1 = (sampleIndex + 1) % sampleBufferSize; 148 | // 149 | // uint32_t sampleToRemove = samplesSquared[sampleIndexPlus1]; 150 | // sumOfSamplesSquared -= (sampleToRemove * sampleToRemove); 151 | 152 | int16_t sample = block->data[i]; 153 | 154 | // samplesSquared[sampleIndex] = abs(sample); 155 | // uint32_t sampleSquared = sample * sample; 156 | // sumOfSamplesSquared += sampleSquared; 157 | // 158 | // sampleIndex = (sampleIndex + 1) % sampleBufferSize; 159 | // 160 | // float rms = sqrt(sumOfSamplesSquared / float(sampleBufferSize)) / 32768.0; 161 | 162 | //Compute block RMS level in Db 163 | float inputdb = MIN_DB; 164 | // if (rms > 0) inputdb = unitToDb(rms); 165 | 166 | //Compressor 167 | if (compEnabled) { 168 | float attdb = MAX_DB; //Below knee 169 | if (inputdb >= aLowKnee) { 170 | if (inputdb <= aHighKnee) { 171 | //Knee transition 172 | float knee = inputdb - aLowKnee; 173 | attdb = aKneeRatio * knee * knee * aTwoKneeWidth; 174 | } else { 175 | //Above knee 176 | attdb = compThreshold + ((inputdb - compThreshold) * compRatio) - inputdb; 177 | } 178 | } 179 | if (attdb <= compdb) compdb = (aCompAttack * compdb) + (aOneMinusCompAttack * attdb); 180 | else compdb = (aCompRelease * compdb) + (aOneMinusCompRelease * attdb); 181 | } else compdb = MAX_DB; 182 | 183 | //Brickwall Limiter 184 | if (limiterEnabled) { 185 | float outdb = inputdb + compdb + makeupdb; 186 | if (outdb >= limitThreshold) limitdb = (aLimitAttack * limitdb) + (aOneMinusLimitAttack * (limitThreshold - outdb)); 187 | else limitdb *= aLimitRelease; 188 | } else limitdb = MAX_DB; 189 | 190 | //Compute linear gain 191 | float totalGain = compdb + makeupdb + limitdb + phingain; 192 | 193 | float multiplier = dbToUnit(totalGain); 194 | int16_t result = sample * multiplier; 195 | block->data[i] = result; 196 | //Apply gain to block 197 | } 198 | 199 | //Transmit & release 200 | transmit(block); 201 | release(block); 202 | } 203 | 204 | #endif -------------------------------------------------------------------------------- /src/effect_dynamics.h: -------------------------------------------------------------------------------- 1 | /* Audio Library for Teensy 3.X 2 | Dynamics Processor (Gate, Compressor & Limiter) 3 | Copyright (c) 2018, Marc Paquette (marc@dacsystemes.com) 4 | Based on analyse_rms, effect_envelope & mixer objects by Paul Stoffregen 5 | 6 | Development of this audio library was funded by PJRC.COM, LLC by sales of 7 | Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop 8 | open source software by purchasing Teensy or other PJRC products. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice, development funding notice, and this permission 18 | notice shall be included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | */ 28 | 29 | #ifndef effect_dynamics_h_ 30 | #define effect_dynamics_h_ 31 | 32 | #if !defined(KINETISL) 33 | 34 | #include "Arduino.h" 35 | #include "AudioStream.h" 36 | 37 | #define MIN_DB -110.0f 38 | #define MAX_DB 0.0f 39 | 40 | #define MIN_T 0.03f //Roughly 1 block 41 | #define MAX_T 4.00f 42 | 43 | #define RATIO_OFF 1.0f 44 | #define RATIO_INFINITY 60.0f 45 | 46 | class AudioEffectDynamics : public AudioStream { 47 | public: 48 | AudioEffectDynamics(void) 49 | : AudioStream(1, inputQueueArray) { 50 | 51 | compression(); 52 | limit(); 53 | autoMakeupGain(); 54 | compdb = MIN_DB; 55 | limitdb = MIN_DB; 56 | } 57 | 58 | //Sets the compression parameters. 59 | //threshold & kneeWidth are in db(FS) 60 | //attack and release are in seconds 61 | //ratio is expressed as x:1 i.e. 1 for no compression, 60 for brickwall limiting 62 | //Set kneeWidth to 0 for hard knee 63 | void compression(float threshold = -4.0f, float attack = MIN_T, float release = 0.2f, float ratio = 2.0f, float kneeWidth = 0.0f, float ingain = 0.0f) { 64 | phingain = ingain; 65 | compEnabled = threshold < MAX_DB; 66 | 67 | compThreshold = constrain(threshold, MIN_DB, MAX_DB); 68 | float compAttackTime = constrain(attack, MIN_T, MAX_T); 69 | float compReleaseTime = constrain(release, MIN_T, MAX_T); 70 | compRatio = 1.0f / constrain(abs(ratio), RATIO_OFF, RATIO_INFINITY); 71 | float compKneeWidth = constrain(abs(kneeWidth), 0.0f, 32.0f); 72 | computeMakeupGain(); 73 | 74 | aCompAttack = timeToAlpha(compAttackTime); 75 | aOneMinusCompAttack = 1.0f - aCompAttack; 76 | aCompRelease = timeToAlpha(compReleaseTime); 77 | aOneMinusCompRelease = 1.0f - aCompRelease; 78 | aHalfKneeWidth = compKneeWidth / 2.0f; 79 | aTwoKneeWidth = 1.0f / (compKneeWidth * 2.0f); 80 | aKneeRatio = compRatio - 1.0f; 81 | aLowKnee = compThreshold - aHalfKneeWidth; 82 | aHighKnee = compThreshold + aHalfKneeWidth; 83 | } 84 | 85 | //Sets the hard limiter parameters 86 | //threshold is in dbFS 87 | //attack & release are in seconds 88 | void limit(float threshold = -6.0f, float attack = MIN_T, float release = MIN_T) { 89 | 90 | limiterEnabled = threshold < MAX_DB; 91 | 92 | limitThreshold = constrain(threshold, MIN_DB, MAX_DB); 93 | float limitAttackTime = constrain(attack, MIN_T, MAX_T); 94 | float limitReleaseTime = constrain(release, MIN_T, MAX_T); 95 | 96 | computeMakeupGain(); 97 | 98 | aLimitAttack = timeToAlpha(limitAttackTime); 99 | aOneMinusLimitAttack = 1.0f - aLimitAttack; 100 | aLimitRelease = timeToAlpha(limitReleaseTime); 101 | } 102 | 103 | //Enables automatic makeup gain setting 104 | //headroom is in dbFS 105 | void autoMakeupGain(float headroom = 1.0f) { 106 | mgAutoEnabled = true; 107 | mgHeadroom = constrain(headroom, 0.0f, 60.0f); 108 | computeMakeupGain(); 109 | } 110 | 111 | //Sets a fixed makeup gain value. 112 | //gain is in dbFS 113 | void makeupGain(float gain = 0.0f) { 114 | mgAutoEnabled = false; 115 | makeupdb = constrain(gain, -12.0f, 24.0f); 116 | } 117 | 118 | private: 119 | audio_block_t *inputQueueArray[1]; 120 | 121 | float phingain; 122 | 123 | bool compEnabled = false; 124 | float compThreshold; 125 | float compRatio; 126 | float compdb; 127 | 128 | bool limiterEnabled = false; 129 | float limitThreshold; 130 | float limitdb; 131 | 132 | bool mgAutoEnabled; 133 | float mgHeadroom; 134 | float makeupdb; 135 | 136 | float aHalfKneeWidth; 137 | float aTwoKneeWidth; 138 | float aKneeRatio; 139 | float aLowKnee; 140 | float aHighKnee; 141 | float aCompAttack; 142 | float aOneMinusCompAttack; 143 | float aCompRelease; 144 | float aOneMinusCompRelease; 145 | float aLimitAttack; 146 | float aOneMinusLimitAttack; 147 | float aLimitRelease; 148 | //const static unsigned int sampleBufferSize = AUDIO_SAMPLE_RATE / 10; // number of samples to use for running RMS calulation = 1/10th of a second 149 | //u_int64_t sumOfSamplesSquared = 0; 150 | //uint32_t samplesSquared[sampleBufferSize] = {0}; 151 | uint16_t sampleIndex = 0; 152 | 153 | void computeMakeupGain() { 154 | if (mgAutoEnabled) { 155 | makeupdb = -compThreshold + (compThreshold * compRatio) + limitThreshold - mgHeadroom; 156 | } 157 | } 158 | 159 | //Computes smoothing time constants for a 10% to 90% change 160 | float timeToAlpha(float time) { 161 | return expf(-0.9542f / (((float)AUDIO_SAMPLE_RATE_EXACT / (float)AUDIO_BLOCK_SAMPLES) * time)); 162 | } 163 | 164 | virtual void update(void); 165 | }; 166 | #endif 167 | 168 | #endif -------------------------------------------------------------------------------- /src/effect_ensemble.h: -------------------------------------------------------------------------------- 1 | /* Audio Library for Teensy 3.X 2 | Portions Copyright (c) 2019, Alexander Davis info@matrixwide.com 3 | Portions Copyright (c) 2021, Vince R. Pearson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice, development funding notice, and this permission 13 | notice shall be included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | */ 23 | 24 | // V. Pearson: Implemented delay buffer interpolation with floating point fractional indices and using a larger table 25 | // to reduce audible delay sweep quantization noise. 26 | // The fractional indices are stored in flash memory instead of being calculated at runtime and stored in RAM. 27 | // #define LARGE_ENSEMBLE_LFO_TABLE uses a larger lfo table with only delay buffer interpolation. 28 | // This is more precise and is slightly faster but takes more flash memory. 29 | // With it undefined, lfo table and delay buffer interpolation are implemented with some extra processing and a smaller lfo table. 30 | 31 | #define LARGE_ENSEMBLE_LFO_TABLE 32 | 33 | #ifndef effect_ensemble_h_ 34 | #define effect_ensemble_h_ 35 | 36 | #include 37 | #include "AudioStream.h" 38 | #define ENSEMBLE_BUFFER_SIZE 1024 39 | // to put a channel 90 degrees out of LFO phase for stereo spread 40 | #define PHASE_90 367 41 | 42 | // LFO wavetable parameters 43 | #ifdef LARGE_ENSEMBLE_LFO_TABLE 44 | #define LFO_SAMPLES (1470*4) // number of samples in tablle 45 | #define LFO_SIZE (1470*4) // number of effective values (no interpolation so same as table size). 46 | #define COUNTS_PER_LFO (200/4) // Adjust for larger table so the rate is the same. 47 | #else 48 | #define LFO_SAMPLES 1470 // number of samples in table 49 | #define LFO_SIZE (1470*4) // number of effective values interpolated from table 50 | #define COUNTS_PER_LFO (200/4) // Adjust for larger effective table so the rate is the same. 51 | #endif 52 | 53 | #define LFO_RANGE 110 54 | 55 | class AudioEffectEnsemble : public AudioStream 56 | { 57 | public: 58 | AudioEffectEnsemble(void); 59 | virtual void update(void); 60 | void lfoRate(float rate); 61 | 62 | private: 63 | audio_block_t *inputQueueArray[1]; 64 | // buffers 65 | int16_t delayBuffer[ENSEMBLE_BUFFER_SIZE]; 66 | 67 | // LFO wavetable 68 | const static float PROGMEM lfoTable[]; 69 | 70 | // input index 71 | int16_t inIndex; 72 | // output indexes 73 | // default to csenter of buffer 74 | int16_t outIndex1; 75 | int16_t outIndex2; 76 | int16_t outIndex3; 77 | int16_t outIndex4; 78 | int16_t outIndex5; 79 | int16_t outIndex6; 80 | // lfo index 81 | // seprated by thirds to approximate 120 degree phase relationship 82 | int16_t lfoIndex1; 83 | int16_t lfoIndex2; 84 | int16_t lfoIndex3; 85 | int16_t lfoIndex4; 86 | int16_t lfoIndex5; 87 | int16_t lfoIndex6; 88 | // lfo rate counter 89 | int16_t lfoCount; 90 | // output index offset 91 | float offset1; 92 | float offset2; 93 | float offset3; 94 | float offset4; 95 | float offset5; 96 | float offset6; 97 | //Default countsPerLfo 98 | int countsPerLfo = COUNTS_PER_LFO; 99 | int16_t interpBuffer(float findex); 100 | 101 | #ifndef LARGE_ENSEMBLE_LFO_TABLE 102 | float lfoLookup(int16_t lfoIndex); 103 | #endif 104 | 105 | }; 106 | 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /src/effect_platervbstereo.cpp: -------------------------------------------------------------------------------- 1 | /* Stereo plate reverb for Teensy 4 2 | * 3 | * Author: Piotr Zapart 4 | * www.hexefx.com 5 | * 6 | * Copyright (c) 2020 by Piotr Zapart 7 | * 8 | * Development of this audio library was funded by PJRC.COM, LLC by sales of 9 | * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop 10 | * open source software by purchasing Teensy or other PJRC products. 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice, development funding notice, and this permission 20 | * notice shall be included in all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 | * THE SOFTWARE. 29 | */ 30 | 31 | 32 | #include 33 | #include "effect_platervbstereo.h" 34 | 35 | #define INP_ALLP_COEFF (0.65f) // default input allpass coeff 36 | #define LOOP_ALLOP_COEFF (0.65f) // default loop allpass coeff 37 | 38 | #define HI_LOSS_FREQ (0.3f) // scaled center freq for the treble loss filter 39 | // #define HI_LOSS_FREQ_MAX (0.08f) 40 | #define LO_LOSS_FREQ (0.06f) // scaled center freq for the bass loss filter 41 | 42 | #define LFO_AMPL_BITS (5) // 2^LFO_AMPL_BITS will be the LFO amplitude 43 | #define LFO_AMPL ((1<>1) // read offset = half the amplitude 45 | #define LFO_FRAC_BITS (16 - LFO_AMPL_BITS) // fractional part used for linear interpolation 46 | #define LFO_FRAC_MASK ((1< 16 161 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 162 | #endif 163 | #if AUDIO_BLOCK_SAMPLES > 32 164 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 165 | #endif 166 | #if AUDIO_BLOCK_SAMPLES > 48 167 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 168 | #endif 169 | #if AUDIO_BLOCK_SAMPLES > 64 170 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 171 | #endif 172 | #if AUDIO_BLOCK_SAMPLES > 80 173 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 174 | #endif 175 | #if AUDIO_BLOCK_SAMPLES > 96 176 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 177 | #endif 178 | #if AUDIO_BLOCK_SAMPLES > 112 179 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 180 | #endif 181 | } }; 182 | 183 | void AudioEffectPlateReverb::update() 184 | { 185 | const audio_block_t *blockL, *blockR; 186 | 187 | #if defined(__ARM_ARCH_7EM__) 188 | audio_block_t *outblockL; 189 | audio_block_t *outblockR; 190 | int i; 191 | float32_t input, acc, temp1, temp2; 192 | uint16_t temp16; 193 | float32_t rv_time; 194 | 195 | // for LFOs: 196 | int16_t lfo1_out_sin, lfo1_out_cos, lfo2_out_sin, lfo2_out_cos; 197 | int32_t y0, y1; 198 | int64_t y; 199 | uint32_t idx; 200 | static bool cleanup_done = false; 201 | // handle bypass, 1st call will clean the buffers to avoid continuing the previous reverb tail 202 | if (bypass) 203 | { 204 | if (!cleanup_done) 205 | { 206 | memset(in_allp1_bufL, 0, sizeof(in_allp1_bufL)); 207 | memset(in_allp2_bufL, 0, sizeof(in_allp2_bufL)); 208 | memset(in_allp3_bufL, 0, sizeof(in_allp3_bufL)); 209 | memset(in_allp4_bufL, 0, sizeof(in_allp4_bufL)); 210 | memset(in_allp1_bufR, 0, sizeof(in_allp1_bufR)); 211 | memset(in_allp2_bufR, 0, sizeof(in_allp2_bufR)); 212 | memset(in_allp3_bufR, 0, sizeof(in_allp3_bufR)); 213 | memset(in_allp4_bufR, 0, sizeof(in_allp4_bufR)); 214 | memset(lp_allp1_buf, 0, sizeof(lp_allp1_buf)); 215 | memset(lp_allp2_buf, 0, sizeof(lp_allp2_buf)); 216 | memset(lp_allp3_buf, 0, sizeof(lp_allp3_buf)); 217 | memset(lp_allp4_buf, 0, sizeof(lp_allp4_buf)); 218 | memset(lp_dly1_buf, 0, sizeof(lp_dly1_buf)); 219 | memset(lp_dly2_buf, 0, sizeof(lp_dly2_buf)); 220 | memset(lp_dly3_buf, 0, sizeof(lp_dly3_buf)); 221 | memset(lp_dly4_buf, 0, sizeof(lp_dly4_buf)); 222 | 223 | cleanup_done = true; 224 | } 225 | blockL = receiveReadOnly(0); 226 | blockR = receiveReadOnly(1); 227 | if (!blockL) blockL = &zeroblock; 228 | if (!blockR) blockR = &zeroblock; 229 | transmit((audio_block_t *)blockL,0); 230 | transmit((audio_block_t *)blockR,1); 231 | if (blockL != &zeroblock) release((audio_block_t *)blockL); 232 | if (blockR != &zeroblock) release((audio_block_t *)blockR); 233 | 234 | return; 235 | } 236 | cleanup_done = false; 237 | 238 | 239 | blockL = receiveReadOnly(0); 240 | blockR = receiveReadOnly(1); 241 | outblockL = allocate(); 242 | outblockR = allocate(); 243 | if (!outblockL || !outblockR) { 244 | if (outblockL) release(outblockL); 245 | if (outblockR) release(outblockR); 246 | if (blockL) release((audio_block_t *)blockL); 247 | if (blockR) release((audio_block_t *)blockR); 248 | return; 249 | } 250 | 251 | if (!blockL) blockL = &zeroblock; 252 | if (!blockR) blockR = &zeroblock; 253 | 254 | // convert data to float32 255 | arm_q15_to_float((q15_t *)blockL->data, input_blockL, AUDIO_BLOCK_SAMPLES); 256 | arm_q15_to_float((q15_t *)blockR->data, input_blockR, AUDIO_BLOCK_SAMPLES); 257 | 258 | rv_time = rv_time_k; 259 | 260 | for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) 261 | { 262 | // do the LFOs 263 | lfo1_phase_acc += lfo1_adder; 264 | idx = lfo1_phase_acc >> 24; // 8bit lookup table address 265 | y0 = AudioWaveformSine[idx]; 266 | y1 = AudioWaveformSine[idx+1]; 267 | idx = lfo1_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part 268 | y = (int64_t)y0 * (0x00FFFFFF - idx); 269 | y += (int64_t)y1 * idx; 270 | lfo1_out_sin = (int32_t) (y >> (32-8)); // 16bit output 271 | idx = ((lfo1_phase_acc >> 24)+64) & 0xFF; 272 | y0 = AudioWaveformSine[idx]; 273 | y1 = AudioWaveformSine[idx + 1]; 274 | y = (int64_t)y0 * (0x00FFFFFF - idx); 275 | y += (int64_t)y1 * idx; 276 | lfo1_out_cos = (int32_t) (y >> (32-8)); // 16bit output 277 | 278 | lfo2_phase_acc += lfo2_adder; 279 | idx = lfo2_phase_acc >> 24; // 8bit lookup table address 280 | y0 = AudioWaveformSine[idx]; 281 | y1 = AudioWaveformSine[idx+1]; 282 | idx = lfo2_phase_acc & 0x00FFFFFF; // lower 24 bit = fractional part 283 | y = (int64_t)y0 * (0x00FFFFFF - idx); 284 | y += (int64_t)y1 * idx; 285 | lfo2_out_sin = (int32_t) (y >> (32-8)); //32-8->output 16bit, 286 | idx = ((lfo2_phase_acc >> 24)+64) & 0xFF; 287 | y0 = AudioWaveformSine[idx]; 288 | y1 = AudioWaveformSine[idx + 1]; 289 | y = (int64_t)y0 * (0x00FFFFFF - idx); 290 | y += (int64_t)y1 * idx; 291 | lfo2_out_cos = (int32_t) (y >> (32-8)); // 16bit output 292 | 293 | input = input_blockL[i] * input_attn; 294 | // chained input allpasses, channel L 295 | acc = in_allp1_bufL[in_allp1_idxL] + input * in_allp_k; 296 | in_allp1_bufL[in_allp1_idxL] = input - in_allp_k * acc; 297 | input = acc; 298 | if (++in_allp1_idxL >= sizeof(in_allp1_bufL)/sizeof(float32_t)) in_allp1_idxL = 0; 299 | 300 | acc = in_allp2_bufL[in_allp2_idxL] + input * in_allp_k; 301 | in_allp2_bufL[in_allp2_idxL] = input - in_allp_k * acc; 302 | input = acc; 303 | if (++in_allp2_idxL >= sizeof(in_allp2_bufL)/sizeof(float32_t)) in_allp2_idxL = 0; 304 | 305 | acc = in_allp3_bufL[in_allp3_idxL] + input * in_allp_k; 306 | in_allp3_bufL[in_allp3_idxL] = input - in_allp_k * acc; 307 | input = acc; 308 | if (++in_allp3_idxL >= sizeof(in_allp3_bufL)/sizeof(float32_t)) in_allp3_idxL = 0; 309 | 310 | acc = in_allp4_bufL[in_allp4_idxL] + input * in_allp_k; 311 | in_allp4_bufL[in_allp4_idxL] = input - in_allp_k * acc; 312 | in_allp_out_L = acc; 313 | if (++in_allp4_idxL >= sizeof(in_allp4_bufL)/sizeof(float32_t)) in_allp4_idxL = 0; 314 | 315 | input = input_blockR[i] * input_attn; 316 | 317 | // chained input allpasses, channel R 318 | acc = in_allp1_bufR[in_allp1_idxR] + input * in_allp_k; 319 | in_allp1_bufR[in_allp1_idxR] = input - in_allp_k * acc; 320 | input = acc; 321 | if (++in_allp1_idxR >= sizeof(in_allp1_bufR)/sizeof(float32_t)) in_allp1_idxR = 0; 322 | 323 | acc = in_allp2_bufR[in_allp2_idxR] + input * in_allp_k; 324 | in_allp2_bufR[in_allp2_idxR] = input - in_allp_k * acc; 325 | input = acc; 326 | if (++in_allp2_idxR >= sizeof(in_allp2_bufR)/sizeof(float32_t)) in_allp2_idxR = 0; 327 | 328 | acc = in_allp3_bufR[in_allp3_idxR] + input * in_allp_k; 329 | in_allp3_bufR[in_allp3_idxR] = input - in_allp_k * acc; 330 | input = acc; 331 | if (++in_allp3_idxR >= sizeof(in_allp3_bufR)/sizeof(float32_t)) in_allp3_idxR = 0; 332 | 333 | acc = in_allp4_bufR[in_allp4_idxR] + input * in_allp_k; 334 | in_allp4_bufR[in_allp4_idxR] = input - in_allp_k * acc; 335 | in_allp_out_R = acc; 336 | if (++in_allp4_idxR >= sizeof(in_allp4_bufR)/sizeof(float32_t)) in_allp4_idxR = 0; 337 | 338 | // input allpases done, start loop allpases 339 | input = lp_allp_out + in_allp_out_R; 340 | acc = lp_allp1_buf[lp_allp1_idx] + input * loop_allp_k; // input is the lp allpass chain output 341 | lp_allp1_buf[lp_allp1_idx] = input - loop_allp_k * acc; 342 | input = acc; 343 | if (++lp_allp1_idx >= sizeof(lp_allp1_buf)/sizeof(float32_t)) lp_allp1_idx = 0; 344 | 345 | acc = lp_dly1_buf[lp_dly1_idx]; // read the end of the delay 346 | lp_dly1_buf[lp_dly1_idx] = input; // write new sample 347 | input = acc; 348 | if (++lp_dly1_idx >= sizeof(lp_dly1_buf)/sizeof(float32_t)) lp_dly1_idx = 0; // update index 349 | 350 | // hi/lo shelving filter 351 | temp1 = input - lpf1; 352 | lpf1 += temp1 * lp_lowpass_f; 353 | temp2 = input - lpf1; 354 | temp1 = lpf1 - hpf1; 355 | hpf1 += temp1 * lp_hipass_f; 356 | acc = lpf1 + temp2*lp_hidamp_k + hpf1*lp_lodamp_k; 357 | acc = acc * rv_time * rv_time_scaler; // scale by the reveb time 358 | 359 | input = acc + in_allp_out_L; 360 | 361 | acc = lp_allp2_buf[lp_allp2_idx] + input * loop_allp_k; 362 | lp_allp2_buf[lp_allp2_idx] = input - loop_allp_k * acc; 363 | input = acc; 364 | if (++lp_allp2_idx >= sizeof(lp_allp2_buf)/sizeof(float32_t)) lp_allp2_idx = 0; 365 | acc = lp_dly2_buf[lp_dly2_idx]; // read the end of the delay 366 | lp_dly2_buf[lp_dly2_idx] = input; // write new sample 367 | input = acc; 368 | if (++lp_dly2_idx >= sizeof(lp_dly2_buf)/sizeof(float32_t)) lp_dly2_idx = 0; // update index 369 | // hi/lo shelving filter 370 | temp1 = input - lpf2; 371 | lpf2 += temp1 * lp_lowpass_f; 372 | temp2 = input - lpf2; 373 | temp1 = lpf2 - hpf2; 374 | hpf2 += temp1 * lp_hipass_f; 375 | acc = lpf2 + temp2*lp_hidamp_k + hpf2*lp_lodamp_k; 376 | acc = acc * rv_time * rv_time_scaler; 377 | 378 | input = acc + in_allp_out_R; 379 | 380 | acc = lp_allp3_buf[lp_allp3_idx] + input * loop_allp_k; 381 | lp_allp3_buf[lp_allp3_idx] = input - loop_allp_k * acc; 382 | input = acc; 383 | if (++lp_allp3_idx >= sizeof(lp_allp3_buf)/sizeof(float32_t)) lp_allp3_idx = 0; 384 | acc = lp_dly3_buf[lp_dly3_idx]; // read the end of the delay 385 | lp_dly3_buf[lp_dly3_idx] = input; // write new sample 386 | input = acc; 387 | if (++lp_dly3_idx >= sizeof(lp_dly3_buf)/sizeof(float32_t)) lp_dly3_idx = 0; // update index 388 | // hi/lo shelving filter 389 | temp1 = input - lpf3; 390 | lpf3 += temp1 * lp_lowpass_f; 391 | temp2 = input - lpf3; 392 | temp1 = lpf3 - hpf3; 393 | hpf3 += temp1 * lp_hipass_f; 394 | acc = lpf3 + temp2*lp_hidamp_k + hpf3*lp_lodamp_k; 395 | acc = acc * rv_time * rv_time_scaler; 396 | 397 | input = acc + in_allp_out_L; 398 | 399 | acc = lp_allp4_buf[lp_allp4_idx] + input * loop_allp_k; 400 | lp_allp4_buf[lp_allp4_idx] = input - loop_allp_k * acc; 401 | input = acc; 402 | if (++lp_allp4_idx >= sizeof(lp_allp4_buf)/sizeof(float32_t)) lp_allp4_idx = 0; 403 | acc = lp_dly4_buf[lp_dly4_idx]; // read the end of the delay 404 | lp_dly4_buf[lp_dly4_idx] = input; // write new sample 405 | input = acc; 406 | if (++lp_dly4_idx >= sizeof(lp_dly4_buf)/sizeof(float32_t)) lp_dly4_idx= 0; // update index 407 | // hi/lo shelving filter 408 | temp1 = input - lpf4; 409 | lpf4 += temp1 * lp_lowpass_f; 410 | temp2 = input - lpf4; 411 | temp1 = lpf4 - hpf4; 412 | hpf4 += temp1 * lp_hipass_f; 413 | acc = lpf4 + temp2*lp_hidamp_k + hpf4*lp_lodamp_k; 414 | acc = acc * rv_time * rv_time_scaler; 415 | 416 | lp_allp_out = acc; 417 | 418 | // channel L: 419 | #ifdef TAP1_MODULATED 420 | temp16 = (lp_dly1_idx + lp_dly1_offset_L + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 421 | temp1 = lp_dly1_buf[temp16++]; // sample now 422 | if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0; 423 | temp2 = lp_dly1_buf[temp16]; // sample next 424 | input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 425 | acc = (temp1*(1.0f-input) + temp2*input)* 0.8f; 426 | #else 427 | temp16 = (lp_dly1_idx + lp_dly1_offset_L) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 428 | acc = lp_dly1_buf[temp16]* 0.8f; 429 | #endif 430 | 431 | 432 | #ifdef TAP2_MODULATED 433 | temp16 = (lp_dly2_idx + lp_dly2_offset_L + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 434 | temp1 = lp_dly2_buf[temp16++]; 435 | if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0; 436 | temp2 = lp_dly2_buf[temp16]; 437 | input = (float32_t)(lfo1_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 438 | acc += (temp1*(1.0f-input) + temp2*input)* 0.7f; 439 | #else 440 | temp16 = (lp_dly2_idx + lp_dly2_offset_L) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 441 | acc += (temp1*(1.0f-input) + temp2*input)* 0.6f; 442 | #endif 443 | 444 | temp16 = (lp_dly3_idx + lp_dly3_offset_L + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t)); 445 | temp1 = lp_dly3_buf[temp16++]; 446 | if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0; 447 | temp2 = lp_dly3_buf[temp16]; 448 | input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 449 | acc += (temp1*(1.0f-input) + temp2*input)* 0.6f; 450 | 451 | temp16 = (lp_dly4_idx + lp_dly4_offset_L + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t)); 452 | temp1 = lp_dly4_buf[temp16++]; 453 | if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0; 454 | temp2 = lp_dly4_buf[temp16]; 455 | input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 456 | acc += (temp1*(1.0f-input) + temp2*input)* 0.5f; 457 | 458 | // Master lowpass filter 459 | temp1 = acc - master_lowpass_l; 460 | master_lowpass_l += temp1 * master_lowpass_f; 461 | 462 | outblockL->data[i] =(int16_t)(master_lowpass_l * 32767.0f); //sat16(output * 30, 0); 463 | 464 | // Channel R 465 | #ifdef TAP1_MODULATED 466 | temp16 = (lp_dly1_idx + lp_dly1_offset_R + (lfo2_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 467 | temp1 = lp_dly1_buf[temp16++]; // sample now 468 | if (temp16 >= sizeof(lp_dly1_buf)/sizeof(float32_t)) temp16 = 0; 469 | temp2 = lp_dly1_buf[temp16]; // sample next 470 | input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 471 | 472 | acc = (temp1*(1.0f-input) + temp2*input)* 0.8f; 473 | #else 474 | temp16 = (lp_dly1_idx + lp_dly1_offset_R) % (sizeof(lp_dly1_buf)/sizeof(float32_t)); 475 | acc = lp_dly1_buf[temp16] * 0.8f; 476 | #endif 477 | #ifdef TAP2_MODULATED 478 | temp16 = (lp_dly2_idx + lp_dly2_offset_R + (lfo1_out_cos>>LFO_FRAC_BITS)) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 479 | temp1 = lp_dly2_buf[temp16++]; 480 | if (temp16 >= sizeof(lp_dly2_buf)/sizeof(float32_t)) temp16 = 0; 481 | temp2 = lp_dly2_buf[temp16]; 482 | input = (float32_t)(lfo1_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 483 | acc += (temp1*(1.0f-input) + temp2*input)* 0.7f; 484 | #else 485 | temp16 = (lp_dly2_idx + lp_dly2_offset_R) % (sizeof(lp_dly2_buf)/sizeof(float32_t)); 486 | acc += (temp1*(1.0f-input) + temp2*input)* 0.7f; 487 | #endif 488 | temp16 = (lp_dly3_idx + lp_dly3_offset_R + (lfo2_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly3_buf)/sizeof(float32_t)); 489 | temp1 = lp_dly3_buf[temp16++]; 490 | if (temp16 >= sizeof(lp_dly3_buf)/sizeof(float32_t)) temp16 = 0; 491 | temp2 = lp_dly3_buf[temp16]; 492 | input = (float32_t)(lfo2_out_sin & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 493 | acc += (temp1*(1.0f-input) + temp2*input)* 0.6f; 494 | 495 | temp16 = (lp_dly4_idx + lp_dly4_offset_R + (lfo1_out_sin>>LFO_FRAC_BITS)) % (sizeof(lp_dly4_buf)/sizeof(float32_t)); 496 | temp1 = lp_dly4_buf[temp16++]; 497 | if (temp16 >= sizeof(lp_dly4_buf)/sizeof(float32_t)) temp16 = 0; 498 | temp2 = lp_dly4_buf[temp16]; 499 | input = (float32_t)(lfo2_out_cos & LFO_FRAC_MASK) / ((float32_t)LFO_FRAC_MASK); // interp. k 500 | acc += (temp1*(1.0f-input) + temp2*input)* 0.5f; 501 | 502 | // Master lowpass filter 503 | temp1 = acc - master_lowpass_r; 504 | master_lowpass_r += temp1 * master_lowpass_f; 505 | outblockR->data[i] =(int16_t)(master_lowpass_r * 32767.0f); 506 | 507 | } 508 | transmit(outblockL, 0); 509 | transmit(outblockR, 1); 510 | release(outblockL); 511 | release(outblockR); 512 | if (blockL != &zeroblock) release((audio_block_t *)blockL); 513 | if (blockR != &zeroblock) release((audio_block_t *)blockR); 514 | 515 | #elif defined(KINETISL) 516 | blockL = receiveReadOnly(0); 517 | if (blockL) release(blockL); 518 | blockR = receiveReadOnly(1); 519 | if (blockR) release(blockR); 520 | #endif 521 | } 522 | -------------------------------------------------------------------------------- /src/effect_platervbstereo.h: -------------------------------------------------------------------------------- 1 | /* Stereo plate reverb for Teensy 4 2 | * 3 | * Author: Piotr Zapart 4 | * www.hexefx.com 5 | * 6 | * Copyright (c) 2020 by Piotr Zapart 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | /*** 28 | * Algorithm based on plate reverbs developed for SpinSemi FV-1 DSP chip 29 | * 30 | * Allpass + modulated delay line based lush plate reverb 31 | * 32 | * Input parameters are float in range 0.0 to 1.0: 33 | * 34 | * size - reverb time 35 | * hidamp - hi frequency loss in the reverb tail 36 | * lodamp - low frequency loss in the reverb tail 37 | * lowpass - output/master lowpass filter, useful for darkening the reverb sound 38 | * diffusion - lower settings will make the reverb tail more "echoey", optimal value 0.65 39 | * 40 | */ 41 | 42 | 43 | #ifndef _EFFECT_PLATERVBSTEREO_H 44 | #define _EFFECT_PLATERVBSTEREO_H 45 | 46 | #include 47 | #include "Audio.h" 48 | #include "AudioStream.h" 49 | #include "arm_math.h" 50 | 51 | 52 | // if uncommented will place all the buffers in the DMAMEM section ofd the memory 53 | // works with single instance of the reverb only 54 | //#define REVERB_USE_DMAMEM 55 | 56 | /*** 57 | * Loop delay modulation: comment/uncomment to switch sin/cos 58 | * modulation for the 1st or 2nd tap, 3rd tap is always modulated 59 | * more modulation means more chorus type sounding reverb tail 60 | */ 61 | //#define TAP1_MODULATED 62 | #define TAP2_MODULATED 63 | 64 | class AudioEffectPlateReverb : public AudioStream 65 | { 66 | public: 67 | AudioEffectPlateReverb(); 68 | virtual void update(); 69 | 70 | void size(float n) 71 | { 72 | n = constrain(n, 0.0f, 1.0f); 73 | n = map (n, 0.0f, 1.0f, 0.2f, rv_time_k_max); 74 | float32_t attn = map(n, 0.0f, rv_time_k_max, 0.5f, 0.25f); 75 | __disable_irq(); 76 | rv_time_k = n; 77 | input_attn = attn; 78 | __enable_irq(); 79 | } 80 | 81 | void hidamp(float n) 82 | { 83 | n = constrain(n, 0.0f, 1.0f); 84 | __disable_irq(); 85 | lp_hidamp_k = 1.0f - n; 86 | __enable_irq(); 87 | } 88 | 89 | void lodamp(float n) 90 | { 91 | n = constrain(n, 0.0f, 1.0f); 92 | __disable_irq(); 93 | lp_lodamp_k = -n; 94 | rv_time_scaler = 1.0f - n * 0.12f; // limit the max reverb time, otherwise it will clip 95 | __enable_irq(); 96 | } 97 | 98 | void lowpass(float n) 99 | { 100 | n = constrain(n, 0.0f, 1.0f); 101 | n = map(n*n*n, 0.0f, 1.0f, 0.05f, 1.0f); 102 | master_lowpass_f = n; 103 | } 104 | 105 | void diffusion(float n) 106 | { 107 | n = constrain(n, 0.0f, 1.0f); 108 | n = map(n, 0.0f, 1.0f, 0.005f, 0.65f); 109 | __disable_irq(); 110 | in_allp_k = n; 111 | loop_allp_k = n; 112 | __enable_irq(); 113 | } 114 | 115 | float32_t get_size(void) {return rv_time_k;} 116 | bool get_bypass(void) {return bypass;} 117 | void set_bypass(bool state) {bypass = state;}; 118 | void tgl_bypass(void) {bypass ^=1;} 119 | private: 120 | bool bypass = false; 121 | audio_block_t *inputQueueArray[2]; 122 | #ifndef REVERB_USE_DMAMEM 123 | float32_t input_blockL[AUDIO_BLOCK_SAMPLES]; 124 | float32_t input_blockR[AUDIO_BLOCK_SAMPLES]; 125 | #endif 126 | float32_t input_attn; 127 | 128 | float32_t in_allp_k; // input allpass coeff 129 | #ifndef REVERB_USE_DMAMEM 130 | float32_t in_allp1_bufL[224]; // input allpass buffers 131 | float32_t in_allp2_bufL[420]; 132 | float32_t in_allp3_bufL[856]; 133 | float32_t in_allp4_bufL[1089]; 134 | #endif 135 | uint16_t in_allp1_idxL; 136 | uint16_t in_allp2_idxL; 137 | uint16_t in_allp3_idxL; 138 | uint16_t in_allp4_idxL; 139 | float32_t in_allp_out_L; // L allpass chain output 140 | #ifndef REVERB_USE_DMAMEM 141 | float32_t in_allp1_bufR[156]; // input allpass buffers 142 | float32_t in_allp2_bufR[520]; 143 | float32_t in_allp3_bufR[956]; 144 | float32_t in_allp4_bufR[1289]; 145 | #endif 146 | uint16_t in_allp1_idxR; 147 | uint16_t in_allp2_idxR; 148 | uint16_t in_allp3_idxR; 149 | uint16_t in_allp4_idxR; 150 | float32_t in_allp_out_R; // R allpass chain output 151 | #ifndef REVERB_USE_DMAMEM 152 | float32_t lp_allp1_buf[2303]; // loop allpass buffers 153 | float32_t lp_allp2_buf[2905]; 154 | float32_t lp_allp3_buf[3175]; 155 | float32_t lp_allp4_buf[2398]; 156 | #endif 157 | uint16_t lp_allp1_idx; 158 | uint16_t lp_allp2_idx; 159 | uint16_t lp_allp3_idx; 160 | uint16_t lp_allp4_idx; 161 | float32_t loop_allp_k; // loop allpass coeff 162 | float32_t lp_allp_out; 163 | #ifndef REVERB_USE_DMAMEM 164 | float32_t lp_dly1_buf[3423]; 165 | float32_t lp_dly2_buf[4589]; 166 | float32_t lp_dly3_buf[4365]; 167 | float32_t lp_dly4_buf[3698]; 168 | #endif 169 | uint16_t lp_dly1_idx; 170 | uint16_t lp_dly2_idx; 171 | uint16_t lp_dly3_idx; 172 | uint16_t lp_dly4_idx; 173 | 174 | const uint16_t lp_dly1_offset_L = 201; // delay line tap offets 175 | const uint16_t lp_dly2_offset_L = 145; 176 | const uint16_t lp_dly3_offset_L = 1897; 177 | const uint16_t lp_dly4_offset_L = 280; 178 | 179 | const uint16_t lp_dly1_offset_R = 1897; 180 | const uint16_t lp_dly2_offset_R = 1245; 181 | const uint16_t lp_dly3_offset_R = 487; 182 | const uint16_t lp_dly4_offset_R = 780; 183 | 184 | float32_t lp_hidamp_k; // loop high band damping coeff 185 | float32_t lp_lodamp_k; // loop low baand damping coeff 186 | 187 | float32_t lpf1; // lowpass filters 188 | float32_t lpf2; 189 | float32_t lpf3; 190 | float32_t lpf4; 191 | 192 | float32_t hpf1; // highpass filters 193 | float32_t hpf2; 194 | float32_t hpf3; 195 | float32_t hpf4; 196 | 197 | float32_t lp_lowpass_f; // loop lowpass scaled frequency 198 | float32_t lp_hipass_f; // loop highpass scaled frequency 199 | 200 | float32_t master_lowpass_f; 201 | float32_t master_lowpass_l; 202 | float32_t master_lowpass_r; 203 | 204 | const float32_t rv_time_k_max = 0.95f; 205 | float32_t rv_time_k; // reverb time coeff 206 | float32_t rv_time_scaler; // with high lodamp settings lower the max reverb time to avoid clipping 207 | 208 | uint32_t lfo1_phase_acc; // LFO 1 209 | uint32_t lfo1_adder; 210 | 211 | uint32_t lfo2_phase_acc; // LFO 2 212 | uint32_t lfo2_adder; 213 | }; 214 | 215 | #endif // _EFFECT_PLATEREV_H 216 | -------------------------------------------------------------------------------- /src/samples/AudioSampleBd_linn.h: -------------------------------------------------------------------------------- 1 | // Audio data converted from audio file by wav2sketch_js 2 | #include "Arduino.h" 3 | extern const unsigned int AudioSampleBd_linn[2241]; -------------------------------------------------------------------------------- /src/samples/AudioSampleHh_rytmmpc60_2.h: -------------------------------------------------------------------------------- 1 | // Audio data converted from audio file by wav2sketch_js 2 | #include "Arduino.h" 3 | extern const unsigned int AudioSampleHh_rytmmpc60_2[2657]; -------------------------------------------------------------------------------- /src/samples/AudioSampleHho_rytmmpc60_3.h: -------------------------------------------------------------------------------- 1 | // Audio data converted from audio file by wav2sketch_js 2 | #include "Arduino.h" 3 | extern const unsigned int AudioSampleHho_rytmmpc60_3[8929]; -------------------------------------------------------------------------------- /src/samples/AudioSampleSd_linn.cpp: -------------------------------------------------------------------------------- 1 | // Audio data converted from audio file by wav2sketch_js 2 | 3 | #include "AudioSampleSd_linn.h" 4 | 5 | // Converted from SD_LINN.wav, using 44100 Hz, 16 bit u-law encoding 6 | PROGMEM 7 | const unsigned int AudioSampleSd_linn[1569] = { 8 | 0x01001818,0x3f229690,0x62636a62,0x66646261,0x65626366,0x64636465,0x63646665,0x61626363, 9 | 0x5e606262,0x5f626060,0x5d5f4912,0xc5d73056,0x4d5e6150,0xe9d8b443,0xe8e8f0f0,0xfbf8f2ee, 10 | 0xf1f2f4f7,0xf9f5f3f1,0xf5f8fbfb,0xf3fafaf8,0xf9fcfaf5,0xfaf7f5f6,0xf4f6f3f3,0xf4f3f3f3, 11 | 0xe8e7edf4,0xeff1f0ea,0xe9e6dee7,0xe9eaeae7,0xe3e4e2e4,0xdfe1e1e2,0xd1d9dad8,0xb0bfc7cd, 12 | 0x3c341492,0x3f383637,0x62615d52,0x63646463,0x6c656160,0x70737472,0x6966656a,0x77716b69, 13 | 0x74777979,0x7a767272,0x6a727678,0x74686867,0x66666c78,0x78756e66,0x6e777b7b,0x786b5f65, 14 | 0x7073797b,0x79787371,0x6e696f78,0x73767370,0x7a797975,0x76797a7a,0x78777776,0x7b7a7a79, 15 | 0x6670777b,0x78736864,0x79787879,0x74767878,0x66697073,0x605f6163,0x62636464,0x55555b60, 16 | 0x565d615a,0x53585756,0x444e5857,0x3a352b3a,0xb488222b,0xced0cbc3,0xd2d0cfcb,0xdde2ded6, 17 | 0xe5e4e4e0,0xe5e4e4e5,0xeceaeae8,0xeeefeeed,0xf2f1f0ed,0xf3f4f4f3,0xf6f5f4f3,0xfbf9f8f8, 18 | 0xfbfafafa,0xfbfcfcfc,0xfdfdfcfb,0xfdfefdfd,0xfcfdfefd,0xf5f8fafb,0xf7f6f8f7,0xfdfcfbf9, 19 | 0xfcfcfcfc,0xfbfcfcfc,0xfcfcfcfc,0xfaf8f9fc,0xfbfbfcfb,0xfbfbfbfb,0xfbfbfafa,0xf7f3f8fa, 20 | 0xf6f9fafb,0xf8f3f2f3,0xf1f1eff4,0xf0f2f4f2,0xeff0efee,0xefe8ebed,0xe8e3e4f1,0xe6eaeae8, 21 | 0xe0e0e0e3,0xd4d9dee0,0xcacecbcd,0xc5c6cac9,0x3a4840c0,0x4938262b,0x4e48464c,0x575d5c53, 22 | 0x6261615f,0x67686564,0x79746964,0x6b57737b,0x7b797976,0x687a7f7c,0x7f7e7e76,0x7e7f7d7f, 23 | 0x7e7d7b7a,0x7a7e7e7f,0x7d7d7c79,0x7d797a7d,0x797c7c7d,0x7c7c7b78,0x7b7b7c7c,0x7a787779, 24 | 0x797a7a7b,0x79757576,0x79756d73,0x69616777,0x656e7777,0x6e716663,0x62636563,0x52606162, 25 | 0x5351534e,0x48424655,0xad234046,0x391cd0c6,0xdcd6c9b2,0xd1d5d3d6,0xe4ded7d1,0xe0d5dce9, 26 | 0xebe7e3e1,0xe7e9efee,0xe7e8eaeb,0xf1f1f1ed,0xeef0f1f1,0xfafbf6ec,0xfaf1f0f3,0xfefbf4fc, 27 | 0xf4f9fcfc,0xfbfef3f0,0xfcfaf9f7,0xfef9fbfe,0xf7fffdfe,0xfdfefdf3,0xfdfdfdfd,0xfdfdfbfc, 28 | 0xf6f9fdfc,0xfbfcfcfc,0xfcfcfcfc,0xf4f8fcfb,0xf6f9fbf9,0xf6f3f0f2,0xf1f3f6f8,0xeef0f0f0, 29 | 0xe4dee2e8,0xe1edf1ed,0xe1cccad3,0xd6ca3dd8,0xb6c1cfd3,0xd1d3d9d3,0x5a4b00c6,0x5d575a62, 30 | 0x5747244a,0x40637b6f,0x514e515c,0x666a6860,0x5c626563,0x686c634c,0x68707270,0x69747b6f, 31 | 0x7c776e69,0x746a7178,0x7376787a,0x7b7d7871,0x7d7b7b7b,0x7374797d,0x777a7b7a,0x7a7b7e7a, 32 | 0x74717377,0x7d7c7d7c,0x73677178,0x7b7c7b7d,0x71777a7b,0x73797571,0x74726965,0x6e44616e, 33 | 0x5e666973,0x5d676652,0x5f5c5f5e,0x6155cc4c,0xab3c5254,0x2553a4c5,0xd8c7b9be,0xdebad0e6, 34 | 0xd8e1e2e0,0xe4dac7ce,0xe2e6e9e7,0xeef1e6e1,0xe7e6e8e7,0xeff5f2e7,0xf1ebecec,0xeeeff3f4, 35 | 0xebf3f7f7,0xfbf9f1eb,0xf5f7fbfa,0xf4f5f6f6,0xfcf9f6f4,0xf5f2f2fc,0xfbfaf7f5,0xf6f8fdfd, 36 | 0xfafaf8f8,0xfbfbf6f4,0xf9f6f8fa,0xf5f9fbfc,0xf4f4f5f5,0xf8fafaf7,0xfbfaf6f5,0xf5eaf1f7, 37 | 0xeff2f4f8,0xf0f8f8ed,0xf1f4f4f3,0xeef3f8f2,0xe5e2e3e9,0xe9e2e5e6,0xececeeee,0xe3e2e8f0, 38 | 0xd3e1e1e2,0x83ccdecd,0xd4c8bab2,0x911f2fc0,0x418aaeb2,0x40c74360,0x2558534f,0x5d533fcb, 39 | 0x67686662,0x4934405c,0x635f5c58,0x67787465,0x69525c5e,0x53707a78,0x665c5b58,0x78784e60, 40 | 0x63636867,0x65637679,0x3f5e6063,0x6a6c5ed1,0x62626569,0x66636062,0x6a696867,0x5c5b6166, 41 | 0x7061605d,0x58556a7a,0x7d796f5d,0x43294a74,0x6366645a,0x4d295761,0x78786661,0x65636263, 42 | 0x384e5a63,0x5d636549,0x38565f61,0x5c60492d,0x4f41364d,0x394f4e47,0xccadb110,0x3936aad4, 43 | 0xd1d0be28,0xdfe2e1d9,0xcbc1c3d4,0xd9ccd5d8,0xe7dee0de,0xdbdae3ef,0xe8ebe6e0,0xeeeae8e7, 44 | 0xb6e2f0ef,0xf6f0f4ef,0xebf4f7f9,0xecf2eed6,0xf6e7e7ea,0xf8f9f7fb,0xf4f2f2f4,0xf4faf7f4, 45 | 0xf5f2f0f1,0xf7f9f9f7,0xf7fbfaf9,0xf1e9e2f1,0xfbfaf6f2,0xebeaf5fa,0xecf0f0f0,0xf1f7efdc, 46 | 0xeff1f1f1,0xe7e7e9eb,0xe9e7e6e6,0xe4e9ebeb,0xe1d3cdda,0xc7e2ecec,0xe2e23b3e,0xddd9d4d3, 47 | 0xd231bdd7,0xd8dde0de,0xbeb8c3d2,0x364ed8e3,0xcccaae11,0xc6b9b4c3,0xb803b2bf,0x9148552f, 48 | 0x603ac6c0,0x3c424756,0x63625549,0x7169625c,0x4551606b,0x63666856,0xaa485761,0x6f747564, 49 | 0x68676868,0x5f6b6c68,0x7b6a645f,0x6f68647c,0x766f6b6b,0x676c7679,0x7a766b68,0x75717072, 50 | 0x71767576,0x7476766d,0x726d6b70,0x67707c7a,0x78736868,0x5a5f636c,0x7671635f,0x5e646d75, 51 | 0x61656461,0x5d5a575a,0x35505a5c,0x5e5e5950,0xb03f545a,0x2901b7c4,0x9c902a2e,0xb7d6e1cc, 52 | 0xdcb44026,0xccb8dbe2,0xe0e3deda,0xe2e6dfc6,0xe0e5e5e4,0xf2eee4df,0xe7e6eaef,0xece3dde1, 53 | 0xf0f3f5f2,0xe6ddeaf4,0xf9f9f2eb,0xf3efeff5,0xe8f5f6f4,0xf3f7f4ed,0xfaf1e7ed,0xf5f5f9fc, 54 | 0xf4faf6f5,0xf2f6f6ec,0xf4eef0f2,0xf5f1e3ef,0xebf0f4f4,0xf1f5f8f5,0xf2ede5e9,0xe3e9f0f3, 55 | 0xe8f2e8de,0xeae4e0e0,0xe7ecefec,0xd2d4dfe4,0xe0dad6d3,0xdde3e5e3,0xb9d4cad0,0x2a333430, 56 | 0x3645432e,0x92caddd3,0x5d666454,0x65644bbe,0xbfd85b68,0x5f646457,0x4445515b,0x68696558, 57 | 0x6a655764,0x62635953,0x675f6363,0x4d45536a,0x646d6553,0x6b6a6261,0x5b5a6066,0x67616060, 58 | 0x62626f6d,0x63686a68,0x63666361,0x6260575e,0x5d616363,0x62625853,0x70696061,0x184a6370, 59 | 0x687254c1,0x62666762,0x57c2c655,0x4f556262,0x625e4247,0x59575656,0x364c565a,0x554e4026, 60 | 0xbf405358,0x5c53459b,0xe6df5259,0xc0b2484f,0x5450d8dc,0xb323414a,0xced8d5c7,0xdacfb1b6, 61 | 0xe2d6c6dd,0xdce1e1dc,0xca4449ba,0xe4e4e2e2,0xe3d0d7e1,0xe3ddd5e0,0x24d0e4e5,0xe4e3e5dd, 62 | 0xe1e1d0dc,0xe6e3e0da,0xeee4e9eb,0xcbbcc6e7,0xe0f3f5de,0xdcdde1e3,0xe1ddd4d7,0xe5e8e7e2, 63 | 0xded4cdd8,0xe6e8e6e4,0xd9d1d6e2,0xe3ded8d6,0xe4e3dee3,0x16b9d2de,0xe0e4e6d9,0xd2dbdfdb, 64 | 0xe5d6c0b5,0x1ac5d1e0,0xe3e3dbac,0x5110cbdc,0xd5d0d9b3,0xb9d1d6d9,0xc2304b49,0x34d1d4d2, 65 | 0x37414956,0xd8cbb52f,0xb5c64cb9,0xb8c6b738,0x505e635c,0x9cb01f38,0x613fc132,0x2f576060, 66 | 0x5944dbd1,0x4b585c55,0x4b4ac8cc,0x43535552,0x403a23a7,0x4d484242,0x8d4b5b55,0x4739acbd, 67 | 0x51cc1444,0x3743485f,0x502a1035,0x4d29b820,0x625a2c53,0x3238404d,0x41495e57,0x61615a4b, 68 | 0x5936445a,0x5a666764,0x62676652,0x584b374b,0x43556b67,0x645f5349,0x645f6569,0x62606163, 69 | 0x60656965,0x65636463,0x5a586063,0x59585c60,0x626a655f,0x4f4f565b,0x40515651,0x635f5a51, 70 | 0xa6cdc354,0x41505453,0x1a932937,0xa0b0a681,0x3fdddc37,0xbe3a4140,0xced8dcd4,0xd9d0c0bb, 71 | 0xe0d1d1d5,0xc3bad5e1,0xdfdce1e4,0xe7e4e2e0,0xdad2d3e4,0xe1e6e6e2,0xebf1f1ea,0xe4e2e2e4, 72 | 0xe4e7e6e4,0xece5e4e3,0xeee9e7f0,0xe8ebeded,0xe1e1e5e7,0xedeae6e4,0xe6e7ebef,0xe8e5e4e4, 73 | 0xe9e3e1ea,0xe0e4e5e7,0xe0e4e6e2,0xd5ceced6,0xe2e3e1db,0xd5e0e1e1,0xc2d0ccb0,0xd5c4c0c2, 74 | 0xcfd9e0de,0x42493cb8,0xd3d2b337,0x4220c7d0,0x40464541,0x3ad6c899,0x40545a5c,0x57abc218, 75 | 0xa225284c,0x665c3b80,0x10b35467,0x5864533a,0x52473a31,0x61595453,0x585e6666,0x64615859, 76 | 0x5462615f,0x6b6b6054,0x5c332262,0x606a6661,0x6872796d,0x5d504a5d,0x64657170,0x65676262, 77 | 0x6668665e,0x5b626466,0x6562615a,0x59656969,0x6163706d,0x61615e5d,0x60605f60,0x5f3a4a57, 78 | 0x5e606066,0x54525458,0x564f484e,0x913c565a,0xaa494f44,0x284f389d,0x013041bb,0xd0963232, 79 | 0x869402c4,0x0d109a98,0xd6e1d227,0xc0c0cbd1,0xc2ced3c6,0xd6d5d3cc,0xded9b2c7,0xdbdadbd9, 80 | 0xdecadce3,0xe0e7e6e4,0xe4dcd2cd,0xe8e2e2e3,0xe5e3e7e9,0xcbe2eae8,0xebece8e0,0xe1e2e6e8, 81 | 0xe2e8e9e3,0xe3ece9e5,0xeae2d2d6,0xe6eaedec,0xdfe2e2e3,0xe2e0dada,0xe0c0c9e2,0xdbe3e4e4, 82 | 0xe0dcd3d4,0xd7ced2db,0xd6d8d2d3,0x93a1cad1,0xc0baccd3,0xced4c9c1,0x474530b3,0x3fa82143, 83 | 0xa5acc5a0,0x59513704,0xa5c83759,0x545a4a35,0x5a544a47,0x51404c57,0x31045456,0x56616255, 84 | 0x585e524b,0x5c615b58,0x5e50424d,0x445c6162,0x5b606351,0x52494a56,0x5d5f615d,0x45425158, 85 | 0x605a5655,0x465f6162,0x58595c43,0x5d5a5859,0x4d48535a,0x58575452,0x51494f57,0x57575553, 86 | 0x585d5e58,0x22224051,0x494d4436,0x605e524a,0x43122e4d,0x544b4b48,0xb5034856,0x204e4622, 87 | 0x5149503d,0xa0415354,0x3c5c45c1,0x3613a5a0,0x932c393d,0xaebfbfb1,0x434c5341,0xb2b39c33, 88 | 0xc1ae3e41,0xbad5d4d0,0xc2993d49,0x48bac5c8,0xd3d5c940,0xa73326c1,0xceccd3cf,0xc2c7c7cc, 89 | 0xc7cdc8b5,0xc4cecbc9,0xd0cdd1c5,0xcad6d9d5,0xd4e0c4b0,0xdacaa3b9,0xd1bcd2e1,0xbac9ccd0, 90 | 0xccd6dcc4,0xdad4d1d0,0xc7c3b1cc,0xd7dad7d0,0xd4d6d1ce,0xc03bc2d0,0xcbc9c8d7,0xc8b8b9c4, 91 | 0xc1c9d8d5,0xe0dacfc7,0xbccccdd2,0xb4544122,0x10c9dae2,0x09c69c3b,0x98323c39,0xced4d2be, 92 | 0x4343b0cc,0x20961938,0xc3cdc335,0x2d1daec0,0x92293835,0x4290b6aa,0x9b284b4f,0xaca4b9b0, 93 | 0x39503fc5,0x47cdc4a5,0xb2284658,0x4c594cbb,0x1a1ab520,0x44403022,0x4d51473a,0x3dca2344, 94 | 0x53453056,0x204a5252,0x504e338d,0x2a053544,0x42435355,0x404b4241,0x86475229,0x5a584c2f, 95 | 0x4a45a04b,0x504c4b47,0x53595b58,0x52404147,0x5a54545e,0x45354d57,0x595d5f55,0x5c555257, 96 | 0x5650404a,0x555a615e,0x54545456,0x4d515355,0x55595d52,0x56535254,0x52515456,0x424f5454, 97 | 0x53564d2a,0x4b524d50,0x4e53554a,0x50383648,0x27405255,0x40321610,0x12b52b4a,0x87ae3533, 98 | 0x21242c34,0xd01f3020,0xb4bfa8c8,0xc29f2594,0xc8bdcdd2,0xc8bdcfce,0xccd6dbd6,0xd6d3cac5, 99 | 0xc6c7cdd4,0xdbdad5ce,0xd3d0d0d4,0xd8e1dbd8,0xdcdcd5bb,0xd4d1d9de,0xdadbd9d7,0xded6d4d9, 100 | 0xd8d4d8dc,0xd3dbe4e0,0xd7d9d8d5,0xdcded7d4,0xd7d3d0d7,0xc7d5d9d9,0xd4d3d7d2,0xdbddd8d6, 101 | 0xc0c3c7cf,0xd1d6ccc3,0xb6b6bec5,0xb5bdbcb6,0xd0d2bfb1,0x913d3eb8,0x455098cc,0x16cb8a39, 102 | 0x382a3951,0x335d5641,0x453c2f9e,0x3e424647,0x514f463f,0x53535252,0x46434951,0x585e584e, 103 | 0x5049424f,0x444f5251,0x5a574c42,0x423e595a,0x575e605d,0x535e5654,0x5d606153,0x4b3d4557, 104 | 0x565d6561,0x5a53444e,0x605e5b59,0x5b4a595f,0x4a525b62,0x3e57564e,0x53505248,0x5c5a5755, 105 | 0x4b535b5f,0x4a4d4243,0x5659584a,0x4d3f4853,0x46433c48,0x2f414b48,0x2b26444a,0x3f513f34, 106 | 0x243b36c0,0xa1252224,0x9f9800a3,0xcec4b3ab,0xb9b5abbb,0xabb0c2c0,0x94b3bcbb,0xcacdb703, 107 | 0xb4b6c0c4,0xc2c4c1b7,0xc4bed3d3,0xabb9cdc9,0xd0d3d0c5,0xc427b4c7,0xcccdcad2,0xd2c9c7ca, 108 | 0xd3d1d1d2,0xb5c2d1d2,0xd2d0ceca,0xcad2d3d3,0xc8d5d9c9,0xe0cdc0c2,0xced3d4dd,0xccc2c2c7, 109 | 0xc7d1dada,0xdbd6b0af,0xc4c6ccd6,0xc51e96ba,0xbcc2c0c8,0xccbfa2b5,0x9b2c23b4,0x4318ccc4, 110 | 0xafc0b041,0x1f42309a,0x414b543e,0x4f48403c,0x37414348,0x4d450926,0x3b3f4446,0x52594938, 111 | 0x4b50524e,0x4d4b4847,0x50482834,0x37485554,0x473d3439,0x48515553,0x4a484944,0x5153514d, 112 | 0x4041474e,0x51545447,0x42414952,0x41545049,0x32424706,0x474c401f,0x49389e30,0x47565c54, 113 | 0x2e262134,0x4145443d,0x3a343136,0x2e1d3440,0x2d35413b,0x40403930,0x322b3138,0x3c41413b, 114 | 0x2f141530,0xa53e3f38,0x282534a6,0x41444236,0x44492e20,0xa396293c,0x32403991,0x37138f17, 115 | 0x41423b40,0xa3bcb135,0x302a514b,0x4549423c,0x1f88911a,0x40504536,0x81911410,0x9e302f14, 116 | 0x46484733,0x2d9eb137,0x32383526,0x30329e08,0x39352b23,0x20b6933a,0xc0b1383a,0x9d2022bb, 117 | 0x23383318,0x423fc0b5,0xa6ab8e26,0xad3b2aa2,0x93a1a3b6,0xb4b5b098,0x1c1788a7,0x9d8ea580, 118 | 0xb0282eaf,0x20a2c0c3,0x3216bb92,0xbec6bd1e,0x1f32338f,0x28a3ccb7,0xa9bcbeb6,0xb9b12c2b, 119 | 0xa5bbc5c1,0xcec8b881,0x199ab8c8,0xb9c6c9aa,0xb0a88e9b,0x3437b8bf,0xc5c1b488,0x24afc1c8, 120 | 0xc8c1b600,0xb1b7bdc9,0x41413689,0x2299be89,0x9c011d2a,0x4d3297b1,0xbac3c735,0x2434339d, 121 | 0xb3a92134,0x42443b91,0x93971935,0x3035301d,0x1515252a,0x1b454720,0x3a3a4846,0x3a484942, 122 | 0x443814a5,0x4b4e5150,0x39424848,0x42352b30,0x4a50524f,0x534d2942,0x4f51493e,0x4b43474c, 123 | 0x51504c50,0x45404951,0x534e4342,0x38475354,0x4f474241,0x494b5554,0x4948464a,0x5546494d, 124 | 0x4f4f4651,0x4d43404c,0x4c504b46,0x52543a43,0x4943403e,0x343f4d51,0x4647493e,0x35424746, 125 | 0x4738181c,0xb0265653,0x222a2d30,0x37483587,0x21b6c8b2,0x17b09b36,0x23240f84,0xb9bca604, 126 | 0xa6aa9f08,0xc9b9b0a8,0xc1cad0d0,0xbaaea6b2,0xbdc0c7c5,0xc5c5c8c0,0xb7b4c4d0,0xd0d3d2c8, 127 | 0xc1c3c7ca,0xd1d0c9c1,0xc3c5c6cd,0xd2d4d1c8,0xc7c4bcc1,0xc4b2d2d0,0xc9c4c5d2,0xc7b7cbd3, 128 | 0xcbcfcdcb,0xbba6a6c3,0xbbbebfbe,0xbec0c5c1,0xa4aeb6c0,0x2e3288ad,0xb6b9af03,0x28c1c6a6, 129 | 0x2c434540,0x3d352810,0xbda62b3b,0x47381fae,0x41505150,0x524c35ac,0x2239444f,0x3b404b32, 130 | 0x494d4e4c,0x4a334246,0x40444d52,0x4b504b42,0x51464445,0x514f4b54,0x3c40444a,0x54525147, 131 | 0x41465054,0x3e384248,0x4f525047,0x493b3144,0x4c4d4d4b,0x47464347,0x3f414346,0x47444443, 132 | 0x40404246,0x363a4040,0x31424846,0x4a4a3a2c,0x841d323b,0x47474538,0x98a61c3d,0x402f2834, 133 | 0x0827313a,0x02334229,0xadb2af9f,0x1141381b,0x1690a0b4,0x9d0a1e21,0xa7b0b1ac,0x2d24ae9f, 134 | 0xc8c8c6ba,0xbccd22a2,0xb20a172e,0xac80c1ce,0xbaaba1a2,0xcfc3abc2,0x82a5b3c0,0xc2b9b3a0, 135 | 0xb6bbc2c6,0xbfb5c0c3,0x2a0fa4b9,0xc1af1c2d,0xc3c1c3c2,0xc4b3438d,0xa1b5c0bf,0xa496aaaf, 136 | 0x208e9ca5,0x9eb6af2e,0x01312e10,0x4535c1b5,0xb4882f35,0x029b3a33,0x413f3122,0x251b2e3d, 137 | 0x36363029,0x3742493e,0x45393337,0x3d332236,0x2f213c40,0x414b5150,0x41423f3b,0x5148323f, 138 | 0x38343b45,0x48454944,0x2a35444a,0x40514d40,0x4c3f3b35,0x423b3f51,0x3e434342,0x4140413e, 139 | 0x14253840,0x40484539,0x41303436,0x41383b4a,0xbb223740,0x494c3eac,0xa4a98f30,0x34824142, 140 | 0x091e2f38,0xaf25310e,0x37373310,0x3017ba13,0x281e0a84,0x1db22936,0x912c3136,0x16b6bdb4, 141 | 0x3f312216,0xb5bbb237,0x8207829f,0xb01b3530,0x9ea9b5bc,0x29b3b29a,0x922d2f32,0xaa8729a6, 142 | 0x29282311,0x1c169c08,0x04031418,0x84981b2a,0x19221c10,0x33291c0c,0x2f363837,0x282a2125, 143 | 0x3e3b3327,0x34372019,0x322f3033,0x403d3938,0x22343b40,0x42423e2c,0x132c383f,0x4a403a2f, 144 | 0x3840464b,0x3f3c3837,0x19383f41,0x4b4b4527,0x9a253a44,0x4042402d,0x33353939,0x353e3c34, 145 | 0x48413d37,0x9a053947,0x3b3f3c33,0x3424393c,0x3032383e,0x1f27373e,0x232b221c,0x323a3b24, 146 | 0x33272a30,0x919cb381,0x36322304,0x0aa2042d,0x31242118,0xb3c0b932,0x2d2f0cad,0x99853030, 147 | 0x93868686,0xa7ada396,0x1c1092a0,0x198a8520,0xb601171c,0xa6989eb7,0x2baebab2,0xa3a2b5aa, 148 | 0xb6aea3a0,0x21371ebc,0x24b6a780,0xb7b8b12f,0xb69996af,0x93022595,0xb0bcb5ab,0x17213539, 149 | 0x9da4a69b,0xaf912383,0x3a2199a9,0x2d372c35,0x26a1ac8a,0x292b3c40,0x392f2522,0x3c414241, 150 | 0x43373639,0x30333a42,0x43403933,0x41454545,0x43423e3f,0x40464843,0x514a4642,0x4445414e, 151 | 0x403a3a3e,0x45404a4b,0x444b4a49,0x48453e34,0x39334046,0x444c5247,0x45393540,0x363e4749, 152 | 0x43403132,0x40252b3f,0x39354143,0x3e424340,0x32242535,0x0b194645,0x38393027,0x232b1e10, 153 | 0x27362c26,0xa0a18f94,0x2a3f3484,0xb2b2ab8f,0x870686ab,0x9f1e168c,0xa092b5b1,0x9cb0b4b3, 154 | 0xbdaa9009,0xa5a18fb5,0xbbb7b2ab,0xb8b6b4b5,0xaab3b8b9,0xb8bbb6a4,0xc2b5b7b7,0xb7a530b5, 155 | 0xbeb0adb7,0xa8abbbc0,0xc1c6b9b2,0xb2b8ad11,0xa219a0b0,0xb6b2abb0,0x8db0b8b9,0xb0abaea3, 156 | 0x140eabaf,0x9da9b2b2,0xaba61d1d,0x011b21a1,0x39312512,0x201ca220,0x33312623,0x3530201b, 157 | 0x1923383b,0x33353425,0x25403d37,0x3b434a3b,0x43331432,0x3f413734,0x402f373a,0x3f2b314a, 158 | 0x3c4a4843,0x42414138,0x36414544,0x4645423c,0x383f4345,0x44454135,0x42384043,0x41454345, 159 | 0x43188735,0x2e334548,0x35313430,0x333c4141,0x3a342120,0x2f0f9533,0x2a383b35,0x18252223, 160 | 0x2406a190,0x21840f22,0x9ea12027,0xa6203116,0x23169fac,0xb0afaf8a,0x1c139aaa,0x801ca6b0, 161 | 0xb4baa89a,0xa9b0b0a0,0x959495a4,0x08a2c0b3,0xa5aaa598,0x9813a7b2,0xaab1b1aa,0xa8b7b6a2, 162 | 0xb49b8894,0x1d81a5b6,0xafac9a1b,0x929194a4,0xa0b1ada3,0xa4b19731,0x06a8a0a1,0xa3812125, 163 | 0x8c93a3ab,0x27261f10,0x1d131922,0x2514222f,0x23812e2f,0x282d2d32,0x41211523,0x33301936, 164 | 0x2b343e37,0x32353a3a,0x3c392a2a,0x33393c3c,0x3f312929,0x393a3940,0x3a3a3a39,0x37373738, 165 | 0x31323939,0x3d3e3b35,0x422d343b,0x21313843,0x373d3818,0x33373b3c,0x3c3c3231,0x2c2d3032, 166 | 0x34323331,0x24282b33,0x3e312524,0x2a31343a,0x211b2d27,0x282e3437,0x2e31120a,0x17282c29, 167 | 0x4196ab8c,0x25220a33,0xa78b2c2a,0x2a251392,0x9ba21426,0x161d1907,0xaaa79b0b,0x9a9690a1, 168 | 0x89029098,0x98a2acab,0x8711281a,0xabb1b09a,0x13a8a4a2,0x99a1a989,0x95030e90,0xa3a49c92, 169 | 0x232f1e8f,0xa39b9198,0x172585a3,0x10889183,0x87212618,0x1a132929,0x930f3629,0x322d2625, 170 | 0x14353635,0x32383d1d,0x34312d2d,0x3b3b3734,0x3a352f37,0x38353538,0x3e333f3f,0x343d4143, 171 | 0x443e3225,0x3c3a323d,0x3137413f,0x4445433c,0x403d313b,0x40434440,0x44363338,0x34393e44, 172 | 0x37414437,0x3a3c3d3d,0x393a3637,0x3f3f3d37,0x373d3d3e,0x292a3033,0x383e3c2f,0x30302e31, 173 | 0x35373030,0x2f353429,0x2a372418,0x221c1f1d,0xa11f2b24,0x2c252310,0x86213130,0x1b809996, 174 | 0x91a72830,0x861b221f,0x221c849e,0x9598a085,0x9f928d8f,0x0e8fa3a9,0xa0a91c23,0x9d8d0406, 175 | 0x9f1b8ba9,0x0b040b92,0xaea1180a,0xa0990604,0x2629a9ac,0x81979087,0xb0212922,0x048382ac, 176 | 0x0f838e80,0x04090f10,0x1d2c130a,0x170894a3,0x0eb00d2c,0x2734322d,0x3013988a,0x332d2323, 177 | 0x2a1e9129,0x2d28282b,0x2d2b3737,0x34333030,0x322b2e34,0x37373634,0x312f3335,0x3737393b, 178 | 0x393e3936,0x35353634,0x3b3c3937,0x32343a3c,0x3a3d3f35,0x3b3d3d3d,0x34233136,0x3938373e, 179 | 0x3d373738,0x332e2334,0x2a353c36,0x2d283135,0x31353431,0x29292929,0x301e242a,0x2c251128, 180 | 0x9d1a302e,0x2e2f2c1e,0x1a970220,0x0aa08a32,0x13302b1d,0x1e0e8b95,0xa099801f,0x9da19190, 181 | 0x111c1d88,0x94a0a089,0xa8ab0315,0xa2821199,0x02a3adae,0xa3958c01,0x92a7b4ad,0xacaca597, 182 | 0xa19c0c95,0xa4aaaaa6,0x98959193,0xaca8a39d,0xa2011da6,0x9a98948e,0x93a30b84,0x840a1119, 183 | 0x0723049f,0x15191c10,0x1e130e10,0x2b2b2b26,0x32321b23,0x2a292b2a,0x2d36332e,0x34292624, 184 | 0x3331333b,0x32313132,0x32333e38,0x31343739,0x3a3f3832,0x37343535,0x3b33343d,0x3a363537, 185 | 0x3433343a,0x33363737,0x393a3c39,0x31333537,0x37363330,0x30323436,0x33353430,0x35323131, 186 | 0x3335282e,0x2c282c2f,0x26223137,0x292d2b28,0x2a1e0922,0x161b1c21,0x12102421,0x18202020, 187 | 0x18168a91,0x8d030e15,0x84a6ab99,0xa08c0a0b,0x920692a0,0x939ea5a8,0xafb28120,0xa0929ca6, 188 | 0xa7a4a7ad,0x8ba3adae,0xa5aaac94,0xaca9a4a0,0xa6a3a7aa,0xa9aaa9a9,0xa6a8a7a5,0xaaa5a3a4, 189 | 0x94949da9,0xaaa8a49f,0x0a979fa6,0xada89a10,0x849ca2a6,0x1097a096,0x0090911d,0x1d130883, 190 | 0x97838115,0x2a23049a,0x18100412,0x1b222020,0x1b252814,0x1c222525,0x32322f24,0x22252b2f, 191 | 0x2c223032,0x32353331,0x3037372c,0x38343331,0x3a362835,0x37363736,0x373b3836,0x34313233, 192 | 0x39373738,0x3c383737,0x3836373c,0x3136393a,0x373d3d37,0x36323131,0x39303640,0x34333436, 193 | 0x30303034,0x35353432,0x33312930,0x1e232f32,0x2d302f2b,0x12152128,0x1f232318,0x2318191d, 194 | 0x1c1aa284,0x11131411,0x850e848b,0x07878a8d,0x9c978b0f,0x8493989d,0xa09f9d8f,0x999b9ea0, 195 | 0xa2a89681,0x8d95999d,0xa3afab84,0x979ea3a5,0xa9aa9d97,0xaaa49e9e,0x91049fa9,0xa8aaa4a0, 196 | 0x8d9ea1a0,0xa2a29f96,0xa4a299a0,0x898f9399,0x0197a7a3,0x9c92000f,0x011981a3,0x20060183, 197 | 0x05969c20,0x1c1c1b12,0x232b1917,0x27232016,0x1d242728,0x30302c24,0x28282a2d,0x2e2c2b29, 198 | 0x37352126,0x2e303233,0x332d3335,0x31313233,0x302e3032,0x32373735,0x34363431,0x36343130, 199 | 0x33333233,0x35343333,0x31313235,0x35343331,0x36332631,0x32313132,0x31333333,0x30312f2f, 200 | 0x2c323027,0x302a2a2a,0x2a262430,0x25292b2a,0x2327302d,0x27242121,0x20242526,0x00000000, 201 | 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, 202 | 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, 203 | 0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000, 204 | 0x00000000, 205 | }; -------------------------------------------------------------------------------- /src/samples/AudioSampleSd_linn.h: -------------------------------------------------------------------------------- 1 | // Audio data converted from audio file by wav2sketch_js 2 | #include "Arduino.h" 3 | extern const unsigned int AudioSampleSd_linn[1569]; -------------------------------------------------------------------------------- /src/samples/AudioSampleTom_linn.h: -------------------------------------------------------------------------------- 1 | // Audio data converted from audio file by wav2sketch_js 2 | #include "Arduino.h" 3 | extern const unsigned int AudioSampleTom_linn[4513]; -------------------------------------------------------------------------------- /src/samples/AudioSampleTomh_linn.h: -------------------------------------------------------------------------------- 1 | // Audio data converted from audio file by wav2sketch_js 2 | #include "Arduino.h" 3 | extern const unsigned int AudioSampleTomh_linn[3265]; -------------------------------------------------------------------------------- /src/samples/AudioSampleTomhh_linn.h: -------------------------------------------------------------------------------- 1 | // Audio data converted from audio file by wav2sketch_js 2 | #include "Arduino.h" 3 | extern const unsigned int AudioSampleTomhh_linn[2593]; -------------------------------------------------------------------------------- /src/samples/AudioSampleToml_linn.h: -------------------------------------------------------------------------------- 1 | // Audio data converted from audio file by wav2sketch_js 2 | #include "Arduino.h" 3 | extern const unsigned int AudioSampleToml_linn[6849]; -------------------------------------------------------------------------------- /track.cpp: -------------------------------------------------------------------------------- 1 | #include "track.h" 2 | #include "midiFunctions.h" 3 | 4 | Track::Track(uint8_t trackId, String trackName, uint8_t trackColor) 5 | { 6 | // constructor code here 7 | name = trackName; 8 | _trackId = trackId; 9 | _nrEvents = NR_TRACK_EVENTS; 10 | _cuedEventIndex = 0; 11 | _midiChannel = trackId; 12 | _defaultNoteLengthTicks = 8 ; //(4 / 96 = <1/16) 13 | _defaultVelocity = 127; 14 | 15 | _noteOn_cb = nullptr; 16 | _noteOff_cb = nullptr; 17 | outputId = 0; 18 | trackRespondToTranspose = false; 19 | trackMuted = false; 20 | _tickFlag = 0; 21 | _cuedMuteFlag = false; 22 | _cuedMuteStatus = false; 23 | 24 | _prescalerCounter = 0; 25 | 26 | for (int patternId = 0; patternId < NR_PATTERNS; patternId++) 27 | { 28 | for (int i = 0; i < _nrEvents;i++) 29 | { 30 | _patterns[patternId].trackEvents[i] = empty_trackEvent; 31 | } 32 | _patterns[patternId].patternId = patternId; 33 | _patterns[patternId].patternLengthTicks = 8 * RESOLUTION16TH - 1; //RESOLUTION - 1; // 4 * 1/4th, (previously set to _trackLengthTicks) 34 | _patterns[patternId].patternStatus = PATTERN_EMPTY; 35 | _patterns[patternId].patternSpeed = PATTERN_SPEED_x1; 36 | } 37 | 38 | for (int i = 0; i < NR_PLAYED_EVENTS;i++) { _playedEvents[i] = empty_trackEvent; } 39 | 40 | _currentTrackTick = 0; 41 | _currentPattern = 0; 42 | _cuedPattern = 255; // 255 = no pattern cued 43 | color = trackColor; 44 | transpose = 0; 45 | lowerRow = 0; // which UI row to set when switching to this track. 0 for drums and higher for midi 46 | } 47 | 48 | uint8_t Track::getId() { return _trackId; } 49 | 50 | void Track::tickTrack() // sent from main sequencer at defined tick rate 51 | { 52 | _tickFlag = _tickPrescaler(); 53 | _currentTrackTick = _currentTrackTick + _tickFlag; 54 | 55 | if (_currentTrackTick > _patterns[_currentPattern].patternLengthTicks) //_trackLengthTicks 56 | { 57 | _currentTrackTick = 0; 58 | loopResetTrack(); 59 | _tickFlag = 0; 60 | } 61 | if (!trackMuted) _triggerEvents(); 62 | _handleAutoNoteOff(); 63 | } 64 | 65 | uint16_t Track::_tickPrescaler() 66 | { 67 | switch (_patterns[_currentPattern].patternSpeed) 68 | { 69 | case PATTERN_SPEED_x1: return 1; 70 | 71 | case PATTERN_SPEED_x2: return 2; 72 | 73 | case PATTERN_SPEED_x1_4: 74 | _prescalerCounter++; 75 | if (_prescalerCounter >= 4) 76 | { 77 | _prescalerCounter = 0; 78 | return 1; 79 | } 80 | else return 0; 81 | 82 | case PATTERN_SPEED_x1_3: 83 | _prescalerCounter++; 84 | if (_prescalerCounter >= 3) 85 | { 86 | _prescalerCounter = 0; 87 | return 1; 88 | } 89 | else return 0; 90 | 91 | case PATTERN_SPEED_x1_2: 92 | _prescalerCounter++; 93 | if (_prescalerCounter >= 2) 94 | { 95 | _prescalerCounter = 0; 96 | return 1; 97 | } 98 | else return 0; 99 | 100 | default: return 0; 101 | } 102 | } 103 | 104 | void Track::loopResetTrack() 105 | { 106 | _cuedEventIndex = 0; 107 | _currentTrackTick = 0; 108 | _prescalerCounter = 0; 109 | // check if a pattern change is cued here? 110 | if (_cuedPattern != 255) 111 | { 112 | _currentPattern = _cuedPattern; 113 | _cuedPattern = 255; 114 | _compactEventArray(); 115 | } 116 | if (_cuedMuteFlag) 117 | { 118 | trackMuted = _cuedMuteStatus; 119 | _cuedMuteFlag = false; 120 | } 121 | //printEventArray(); 122 | } 123 | 124 | void Track::cueMuteStatus(bool status) 125 | { 126 | _cuedMuteStatus = status; 127 | _cuedMuteFlag = true; 128 | } 129 | 130 | uint8_t Track::getMuteStatus() 131 | { 132 | if ( (trackMuted == true) && (_cuedMuteFlag == false) ) return MUTE_ON; 133 | if ( (trackMuted == false) && (_cuedMuteFlag == true) && (_cuedMuteStatus == true) ) return MUTE_ON_CUED; 134 | if ( (trackMuted == true) && (_cuedMuteFlag == true) && (_cuedMuteStatus == false) ) return MUTE_OFF_CUED; 135 | return MUTE_OFF; 136 | } 137 | 138 | void Track::_triggerEvents() 139 | { 140 | // SEND ALL EVENTS with tick <= currentTick. Set status played. Copy event to "played" buffer 141 | uint16_t index = _cuedEventIndex; 142 | while((_patterns[_currentPattern].trackEvents[index].tick <= _currentTrackTick) && (_patterns[_currentPattern].trackEvents[index].header != NO_EVENT) && (index < _nrEvents)) 143 | { 144 | if(_patterns[_currentPattern].trackEvents[index].header == EVENT_MIDI_SLEEPING) _patterns[_currentPattern].trackEvents[index].header = EVENT_MIDI_PLAYED; 145 | if(_noteOn_cb && (_patterns[_currentPattern].trackEvents[index].noteValue + transpose < 128)) 146 | { 147 | //Serial.println("called midicb"); 148 | if (_midiChannel == MIDICH_NOTEVALUE) 149 | { 150 | uint8_t noteValue = _patterns[_currentPattern].trackEvents[index].noteValue + transpose; 151 | uint8_t channel = constrain(noteValue, 1, 16); 152 | _noteOn_cb(channel, _patterns[_currentPattern].trackEvents[index].noteValue + transpose, _patterns[_currentPattern].trackEvents[index].noteVelocity); 153 | _copyToPlayedBuffer(_patterns[_currentPattern].trackEvents[index]); 154 | } 155 | else 156 | { 157 | _noteOn_cb(_midiChannel, _patterns[_currentPattern].trackEvents[index].noteValue + transpose, _patterns[_currentPattern].trackEvents[index].noteVelocity); 158 | _copyToPlayedBuffer(_patterns[_currentPattern].trackEvents[index]); 159 | } 160 | } 161 | index = index + 1; 162 | _updateCuedEventIndex(); 163 | } 164 | } 165 | 166 | void Track::_copyToPlayedBuffer(trackEvent event) 167 | { 168 | // note: store actual noteValue with transpose in played buffer 169 | event.noteValue = event.noteValue + transpose; 170 | uint8_t index = 0; 171 | while(_playedEvents[index].header != NO_EVENT && index < NR_PLAYED_EVENTS) index = index + 1; 172 | _playedEvents[index] = event; 173 | } 174 | 175 | void Track::_handleAutoNoteOff() 176 | { 177 | // if length == 0 send note off 178 | // otherwise length = length - 1 tick * prescaler 179 | for (uint8_t index = 0; index < NR_PLAYED_EVENTS; index++) 180 | { 181 | if((_playedEvents[index].header == EVENT_MIDI_PLAYED) && (_playedEvents[index].noteLength == 0)) 182 | { 183 | if(_noteOff_cb) _noteOff_cb(_midiChannel,_playedEvents[index].noteValue, 0); 184 | _playedEvents[index] = empty_trackEvent; 185 | } 186 | else if (_playedEvents[index].header == EVENT_MIDI_PLAYED) 187 | { 188 | _playedEvents[index].noteLength = _playedEvents[index].noteLength - _tickFlag; 189 | } 190 | } 191 | } 192 | 193 | void Track::flushPlayedBuffer() 194 | { 195 | for (uint8_t index = 0; index < NR_PLAYED_EVENTS; index++) 196 | { 197 | if(_playedEvents[index].header == EVENT_MIDI_PLAYED) 198 | { 199 | if(_noteOff_cb) _noteOff_cb(_midiChannel,_playedEvents[index].noteValue, _playedEvents[index].noteVelocity); 200 | _playedEvents[index] = empty_trackEvent; 201 | } 202 | } 203 | } 204 | 205 | uint16_t Track::getCurrentTrackTick() { return _currentTrackTick;} 206 | void Track::setPatternLengthBeats(uint16_t lengthBeats) { _patterns[_currentPattern].patternLengthTicks = lengthBeats * RESOLUTION - 1; } 207 | uint16_t Track::getPatternLengthBeats() { return ( (_patterns[_currentPattern].patternLengthTicks + 1) / RESOLUTION); } 208 | 209 | uint16_t Track::getPatternLengthColumns(uint16_t ticksPerColumn) {return ( (_patterns[_currentPattern].patternLengthTicks + 1) / RESOLUTION16TH); } 210 | void Track::setPatternLengthColumns(uint16_t length, uint16_t ticksPerColumn) { _patterns[_currentPattern].patternLengthTicks = length * RESOLUTION16TH - 1; } 211 | uint16_t Track::getPatternLengthColumns_indexed(uint8_t patternId, uint16_t ticksPerColumn) {return ( (_patterns[patternId].patternLengthTicks + 1) / RESOLUTION16TH); } 212 | 213 | void Track::setPatternSpeed(uint8_t patternId, uint8_t speed) { _patterns[patternId].patternSpeed = speed; } 214 | uint8_t Track::getPatternSpeed(uint8_t patternId) { return _patterns[_currentPattern].patternSpeed; } 215 | 216 | uint16_t Track::getCurrentBeat() { return _currentTrackTick / RESOLUTION;} 217 | uint16_t Track::getCurrentColumn(uint16_t ticksPerColumn) { return _currentTrackTick / ticksPerColumn; } 218 | uint16_t Track::getTrackDefaultNoteLengthTicks() { return _defaultNoteLengthTicks; } 219 | void Track::setTrackDefaultNoteLengthTicks(uint16_t noteLength) { _defaultNoteLengthTicks = noteLength; } 220 | uint8_t Track::getTrackDefaultVelocity() { return _defaultVelocity; } 221 | 222 | void Track::_updateCuedEventIndex() 223 | { 224 | if (_cuedEventIndex < _nrEvents - 1) _cuedEventIndex = _cuedEventIndex +1; 225 | } 226 | 227 | void Track::addEvent(uint16_t tick, uint8_t noteValue, uint8_t noteVelocity, uint8_t noteLength, bool audit) 228 | { 229 | if (_nextFreeEventId < _nrEvents) 230 | { 231 | _patterns[_currentPattern].trackEvents[_nextFreeEventId].header = EVENT_MIDI_SLEEPING; 232 | _patterns[_currentPattern].trackEvents[_nextFreeEventId].tick = tick; 233 | _patterns[_currentPattern].trackEvents[_nextFreeEventId].noteValue = noteValue; 234 | _patterns[_currentPattern].trackEvents[_nextFreeEventId].noteVelocity = noteVelocity; 235 | _patterns[_currentPattern].trackEvents[_nextFreeEventId].noteLength = noteLength; 236 | } 237 | if(audit) _auditEvent(_patterns[_currentPattern].trackEvents[_nextFreeEventId]); 238 | if (tick < _currentTrackTick) _updateCuedEventIndex(); 239 | _compactEventArray(); 240 | _patterns[_currentPattern].patternStatus = PATTERN_FILLED; 241 | } 242 | 243 | void Track::_auditEvent(trackEvent auditEvent) 244 | { 245 | // shall ONLY be called if sequencer is not running! 246 | if(_noteOn_cb && (auditEvent.noteValue + transpose < 128)) _noteOn_cb(_midiChannel, auditEvent.noteValue + transpose, auditEvent.noteVelocity); 247 | delay(50); // crude 248 | if(_noteOff_cb) _noteOff_cb(_midiChannel, auditEvent.noteValue + transpose, auditEvent.noteVelocity); 249 | } 250 | 251 | void Track::removeEvents(uint16_t tickStart, uint16_t tickEnd, uint8_t noteValue) 252 | { 253 | // find events within tick interval and delete them 254 | for (uint8_t index = 0; index < _nrEvents; index++) 255 | { 256 | if((_patterns[_currentPattern].trackEvents[index].tick >= tickStart) && (_patterns[_currentPattern].trackEvents[index].tick <= tickEnd) && (_patterns[_currentPattern].trackEvents[index].noteValue == noteValue)) 257 | { 258 | if(_patterns[_currentPattern].trackEvents[index].tick < _currentTrackTick) 259 | { 260 | // removed event before current time, reduce cued index 261 | _cuedEventIndex = _cuedEventIndex - 1; 262 | } 263 | _patterns[_currentPattern].trackEvents[index] = empty_trackEvent; 264 | } 265 | } 266 | 267 | _compactEventArray(); 268 | //printEventArray(16); 269 | } 270 | 271 | void Track::clearPattern(uint8_t patternId) 272 | { 273 | for (uint8_t index = 0; index < _nrEvents; index++) 274 | { 275 | _patterns[_currentPattern].trackEvents[index] = empty_trackEvent; 276 | } 277 | } 278 | 279 | void Track::clearTrack() 280 | { 281 | for (int patternId = 0; patternId < NR_PATTERNS; patternId++) 282 | { 283 | clearPattern(patternId); 284 | } 285 | } 286 | 287 | uint16_t Track::getEventsInTickInterval(uint16_t tickStart, uint16_t tickEnd, uint8_t noteValue) 288 | { 289 | uint16_t matchingEventCounter = 0; 290 | for (uint8_t index = 0; index < _nrEvents; index++) 291 | { 292 | if((_patterns[_currentPattern].trackEvents[index].tick >= tickStart) && (_patterns[_currentPattern].trackEvents[index].tick <= tickEnd) && (_patterns[_currentPattern].trackEvents[index].noteValue == noteValue) && (_patterns[_currentPattern].trackEvents[index].header != NO_EVENT)) 293 | { 294 | matchingEventCounter = matchingEventCounter + 1; 295 | } 296 | } 297 | return matchingEventCounter; 298 | } 299 | 300 | int16_t Track::getEventId(uint16_t tick, uint8_t note) 301 | { 302 | uint8_t index = 0; 303 | while (index < _nrEvents) 304 | { 305 | if ( (_patterns[_currentPattern].trackEvents[index].tick == tick) 306 | && (_patterns[_currentPattern].trackEvents[index].noteValue == note) 307 | && (_patterns[_currentPattern].trackEvents[index].header != NO_EVENT) ) return index; 308 | index++; 309 | } 310 | //printEventArray(16); 311 | return -1; 312 | } 313 | 314 | uint16_t Track::getEventsInTickNoteInterval(uint16_t tickStart, uint16_t tickEnd, uint8_t noteStart, uint8_t noteEnd) 315 | { 316 | uint16_t matchingEventCounter = 0; 317 | for (uint8_t index = 0; index < _nrEvents; index++) 318 | { 319 | if((_patterns[_currentPattern].trackEvents[index].tick >= tickStart) && (_patterns[_currentPattern].trackEvents[index].tick <= tickEnd) && (_patterns[_currentPattern].trackEvents[index].noteValue >= noteStart) && (_patterns[_currentPattern].trackEvents[index].noteValue <= noteEnd) && (_patterns[_currentPattern].trackEvents[index].header != NO_EVENT)) 320 | { 321 | matchingEventCounter = matchingEventCounter + 1; 322 | } 323 | } 324 | return matchingEventCounter; 325 | } 326 | 327 | void Track::_compactEventArray() 328 | { 329 | qsort(_patterns[_currentPattern].trackEvents, _nrEvents, sizeof(trackEvent), compareTwoEvents); 330 | int index = 0; 331 | while(_patterns[_currentPattern].trackEvents[index].header != NO_EVENT && index < _nrEvents) 332 | { 333 | index = index +1; 334 | } 335 | _nextFreeEventId = index; 336 | } 337 | 338 | void Track::setHandleNoteOn(MIDIcallback cb) { _noteOn_cb = cb; } 339 | void Track::setHandleNoteOff(MIDIcallback cb) { _noteOff_cb = cb; } 340 | 341 | TrackEvent Track::getEvent(uint8_t patternId, uint8_t eventId) { return _patterns[patternId].trackEvents[eventId]; } 342 | 343 | void Track::setEvent(uint8_t patternId, uint8_t eventId, TrackEvent event) { _patterns[patternId].trackEvents[eventId] = event; } 344 | 345 | Pattern Track::getPattern(uint8_t patternId) { return _patterns[patternId];} 346 | 347 | void Track::setPattern(uint8_t patternId, Pattern inputPattern) { _patterns[patternId] = inputPattern; } 348 | 349 | void Track::setPatternId(uint8_t patternId) 350 | { 351 | _currentPattern = patternId; 352 | _patterns[_currentPattern].patternStatus = PATTERN_ACTIVE; 353 | } 354 | 355 | void Track::cuePatternId(uint8_t patternId) 356 | { 357 | _cuedPattern = patternId; 358 | _patterns[_cuedPattern].patternStatus = PATTERN_CUED; 359 | } 360 | 361 | uint8_t Track::getActivePatternId() 362 | { 363 | return _currentPattern; 364 | } 365 | 366 | uint8_t Track::getPatternStatus(uint8_t patternId) 367 | { 368 | // if empty return empty 369 | // if active return active 370 | // return _patterns[_currentPattern].patternStatus; 371 | 372 | //if (_patterns[_currentPattern].patternStatus == PATTERN_EMPTY) return PATTERN_EMPTY; 373 | if (_cuedPattern == patternId) return PATTERN_CUED; 374 | if (_currentPattern == patternId) return PATTERN_ACTIVE; 375 | else return PATTERN_FILLED; 376 | } 377 | 378 | uint8_t Track::getTrackChannel() { return _midiChannel; } 379 | void Track::setTrackChannel(uint8_t channel) { _midiChannel = channel; } 380 | 381 | // debug array printouts 382 | void Track::printEventArray(uint8_t lastIndex) 383 | { 384 | Serial.print("Track: "); 385 | Serial.println(_trackId); 386 | 387 | Serial.print("_nrEvents: "); 388 | Serial.println(_nrEvents); 389 | 390 | Serial.print("_cuedEventIndex: "); 391 | Serial.println(_cuedEventIndex); 392 | 393 | Serial.print("Headers: "); 394 | for (uint8_t i = 0; i < lastIndex; i++) 395 | { 396 | Serial.print(_patterns[_currentPattern].trackEvents[i].header); 397 | Serial.print(", "); 398 | } 399 | Serial.println("END"); 400 | 401 | Serial.print("Ticks: "); 402 | for (uint8_t i = 0; i < lastIndex; i++) 403 | { 404 | Serial.print(_patterns[_currentPattern].trackEvents[i].tick); 405 | Serial.print(", "); 406 | } 407 | Serial.println("END"); 408 | 409 | Serial.print("Notes: "); 410 | for (uint8_t i = 0; i < lastIndex; i++) 411 | { 412 | Serial.print(_patterns[_currentPattern].trackEvents[i].noteValue); 413 | Serial.print(", "); 414 | } 415 | Serial.println("END"); 416 | 417 | Serial.print("Lengths: "); 418 | for (uint8_t i = 0; i < lastIndex; i++) 419 | { 420 | Serial.print(_patterns[_currentPattern].trackEvents[i].noteLength); 421 | Serial.print(", "); 422 | } 423 | Serial.println("END"); 424 | } 425 | 426 | void Track::printPlayedArray() 427 | { 428 | Serial.print("Track: "); 429 | Serial.println(_trackId); 430 | Serial.print("Headers: "); 431 | for (uint8_t i = 0; i < NR_PLAYED_EVENTS; i++) 432 | { 433 | Serial.print(_playedEvents[i].header); 434 | Serial.print(", "); 435 | } 436 | Serial.println("END"); 437 | 438 | Serial.print("Ticks: "); 439 | for (uint8_t i = 0; i < NR_PLAYED_EVENTS; i++) 440 | { 441 | Serial.print(_playedEvents[i].tick); 442 | Serial.print(", "); 443 | } 444 | Serial.println("END"); 445 | Serial.print("Notes: "); 446 | for (uint8_t i = 0; i < NR_PLAYED_EVENTS; i++) 447 | { 448 | Serial.print(_playedEvents[i].noteValue); 449 | Serial.print(", "); 450 | } 451 | Serial.println("END"); 452 | 453 | Serial.print("Lengths: "); 454 | for (uint8_t i = 0; i < NR_PLAYED_EVENTS; i++) 455 | { 456 | Serial.print(_playedEvents[i].noteLength); 457 | Serial.print(", "); 458 | } 459 | Serial.println("END"); 460 | } 461 | 462 | // compare functions for qsort 463 | int compareTwoEvents(const void *s1, const void *s2) 464 | { 465 | struct trackEvent *e1 = (struct trackEvent *)s1; 466 | struct trackEvent *e2 = (struct trackEvent *)s2; 467 | // Active events first in array 468 | if(e1->header == NO_EVENT && e2->header != NO_EVENT) return 1; 469 | if(e1->header != NO_EVENT && e2->header == NO_EVENT) return -1; 470 | // Then sort by tick 471 | if(e1->tick < e2->tick) return -1; 472 | if(e1->tick > e2->tick) return 1; 473 | return 0; 474 | } 475 | -------------------------------------------------------------------------------- /track.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Arduino.h" 4 | 5 | #define NO_EVENT 0 6 | #define EVENT_MIDI_PLAYED 1 7 | #define EVENT_MIDI_SLEEPING 2 8 | #define EVENT_CC_PLAYED 3 9 | #define EVENT_CC_SLEEPING 4 10 | #define EVENT_MIDI_NEW 5 11 | 12 | #define PATTERN_EMPTY 0 13 | #define PATTERN_FILLED 1 14 | #define PATTERN_ACTIVE 2 15 | #define PATTERN_CUED 3 16 | 17 | #define MUTE_OFF 0 18 | #define MUTE_ON 1 19 | #define MUTE_ON_CUED 2 20 | #define MUTE_OFF_CUED 3 21 | 22 | #define NR_TRACK_EVENTS 128 23 | #define NR_PLAYED_EVENTS 16 24 | #define RESOLUTION 24 // ticks per beat (1/4th) 25 | #define RESOLUTION16TH 6 // ticks per 1/16th --> ticks per index 26 | 27 | #define NR_TRACKS 7 28 | #define NR_PATTERNS 8 29 | 30 | #define MIDICH_NOTEVALUE 17 31 | 32 | #define PATTERN_SPEED_x1_4 0 33 | #define PATTERN_SPEED_x1_3 1 34 | #define PATTERN_SPEED_x1_2 2 35 | #define PATTERN_SPEED_x1 3 36 | #define PATTERN_SPEED_x2 4 37 | 38 | const uint8_t scale_major[8] = {0, 2, 4, 5, 7, 9, 11, 12}; 39 | const uint8_t scale_minor[8] = {0, 2, 3, 5, 7, 8, 10, 12}; 40 | 41 | struct trackEvent{ 42 | uint8_t header; 43 | uint16_t tick; 44 | uint8_t noteValue; 45 | uint8_t noteVelocity; 46 | uint16_t noteLength; 47 | }; 48 | 49 | typedef struct trackEvent TrackEvent; 50 | 51 | const trackEvent empty_trackEvent = {0,0,0,0,0}; 52 | 53 | int compareTwoEvents(const void *s1, const void *s2); 54 | 55 | struct pattern { 56 | uint8_t patternId; 57 | uint16_t patternLengthTicks; 58 | uint8_t patternStatus; 59 | uint8_t patternSpeed; 60 | TrackEvent trackEvents[NR_TRACK_EVENTS]; 61 | }; 62 | 63 | typedef struct pattern Pattern; 64 | 65 | typedef void (*MIDIcallback)(uint8_t channel, uint8_t note, uint8_t velocity); 66 | 67 | class Track { 68 | 69 | private: 70 | uint8_t _trackId; 71 | uint16_t _currentTrackTick; 72 | uint8_t _nrEvents; 73 | uint8_t _nextFreeEventId; 74 | Pattern _patterns[NR_PATTERNS]; 75 | TrackEvent _playedEvents[NR_PLAYED_EVENTS]; 76 | uint8_t _cuedEventIndex; 77 | uint8_t _currentPattern; 78 | uint8_t _cuedPattern; 79 | uint8_t _prescalerCounter; 80 | 81 | uint8_t _tickFlag; 82 | bool _cuedMuteFlag; 83 | bool _cuedMuteStatus; 84 | 85 | // Track configuration 86 | 87 | uint8_t _midiChannel; 88 | uint8_t _defaultNoteLengthTicks; 89 | uint8_t _defaultVelocity; 90 | 91 | MIDIcallback _noteOn_cb; 92 | MIDIcallback _noteOff_cb; 93 | 94 | void _compactEventArray(); 95 | void _updateCuedEventIndex(); 96 | void _triggerEvents(); 97 | void _handleAutoNoteOff(); 98 | void _copyToPlayedBuffer(trackEvent event); 99 | void _auditEvent(trackEvent event); 100 | uint16_t _tickPrescaler(); 101 | 102 | 103 | public: 104 | Track(uint8_t trackId, String trackName, uint8_t trackColor); 105 | String name; 106 | uint8_t outputId; 107 | uint8_t color; 108 | int transpose; 109 | uint8_t trackRespondToTranspose; 110 | bool trackMuted; 111 | uint8_t lowerRow; 112 | 113 | //void update(); 114 | 115 | uint8_t getId(); 116 | uint16_t getEventTick(uint8_t eventId); 117 | uint16_t getCurrentTrackTick(); 118 | 119 | void setPatternLengthBeats(uint16_t lengthBeats); 120 | uint16_t getPatternLengthBeats(); 121 | uint16_t getPatternLengthColumns(uint16_t ticksPerColumn); 122 | uint16_t getPatternLengthColumns_indexed(uint8_t patternId, uint16_t ticksPerColumn); 123 | void setPatternLengthColumns(uint16_t length, uint16_t ticksPerColumn); 124 | uint16_t getTrackDefaultNoteLengthTicks(); 125 | void setTrackDefaultNoteLengthTicks(uint16_t noteLength); 126 | uint8_t getTrackDefaultVelocity(); 127 | uint16_t getCurrentBeat(); 128 | uint16_t getCurrentColumn(uint16_t ticksPerColumn); 129 | 130 | void addEvent(uint16_t tick, uint8_t noteValue, uint8_t noteVelocity, uint8_t noteLength, bool audit); 131 | void removeEvents(uint16_t tickStart, uint16_t tickEnd, uint8_t noteValue); 132 | uint16_t getEventsInTickInterval(uint16_t tickStart, uint16_t tickEnd, uint8_t noteValue); 133 | uint16_t getEventsInTickNoteInterval(uint16_t tickStart, uint16_t tickEnd, uint8_t noteStart, uint8_t noteEnd); 134 | int16_t getEventId(uint16_t tick, uint8_t note); 135 | uint8_t getPatternStatus(uint8_t patternId); 136 | 137 | void clearPattern(uint8_t patternId); 138 | void clearTrack(); 139 | 140 | void setPatternId(uint8_t patternId); 141 | void cuePatternId(uint8_t patternId); 142 | uint8_t getActivePatternId(); 143 | 144 | void cueMuteStatus(bool status); 145 | uint8_t getMuteStatus(); 146 | 147 | TrackEvent getEvent(uint8_t patternId, uint8_t eventId); 148 | void setEvent(uint8_t patternId, uint8_t eventId, TrackEvent event); 149 | 150 | Pattern getPattern(uint8_t patternId); 151 | void setPattern(uint8_t patternId, Pattern inputPattern); 152 | 153 | void setPatternSpeed(uint8_t patternId, uint8_t speed); 154 | uint8_t getPatternSpeed(uint8_t patternId); 155 | 156 | void tickTrack(); // sent from main sequencer at defined tick rate 1/96th 157 | 158 | void loopResetTrack(); 159 | void flushPlayedBuffer(); 160 | void printEventArray(uint8_t lastIndex); 161 | void printPlayedArray(); 162 | 163 | void setHandleNoteOn(MIDIcallback cb); 164 | void setHandleNoteOff(MIDIcallback cb); 165 | 166 | uint8_t getTrackChannel(); 167 | void setTrackChannel(uint8_t channel); 168 | 169 | }; 170 | -------------------------------------------------------------------------------- /ui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "track.h" 4 | #include "midiFunctions.h" 5 | #include "sequencer.h" 6 | #include 7 | #include // from ILI9341_t3 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "sdMgr.h" 13 | 14 | #define SCREEN_YRES 240 15 | #define SCREEN_XRES 320 16 | #define BUTTON_HEIGHT 26 17 | #define BUTTON_WIDTH 150 18 | #define BUTTON_PADDING 5 19 | #define VAR_NAME_WIDTH 100 20 | #define VAR_VALUE_WIDTH 155 21 | #define MAIN_AREA_HEIGHT 170 22 | #define MAIN_AREA_WIDTH SCREEN_XRES 23 | 24 | #define PAGEINDICATOR_HEIGHT 30 25 | #define PAGEINDICATOR_WIDTH 100 26 | #define HEADER_X PAGEINDICATOR_WIDTH + 1 27 | #define HEADER_Y 0 28 | #define HEADER_WIDTH SCREEN_XRES - PAGEINDICATOR_WIDTH 29 | #define HEADER_HEIGHT PAGEINDICATOR_HEIGHT 30 | #define HEADER_TEXT_X HEADER_X + 5 31 | #define HEADER_OFFSET_X 33 32 | #define HEADER_VALUE_OFFSET_X 35 33 | 34 | #define MAIN_BG_COLOR ILI9341_NAVY 35 | #define PAGEINDICATOR_COLOR ILI9341_BLUE 36 | 37 | #define LPCOPY_XPOS 5 38 | #define LPCOPY_YPOS 200 39 | #define LPCOPY_WIDTH SCREEN_XRES 40 | #define LPCOPY_HEIGHT 40 41 | #define LPCOPY_BOXDIM 4 42 | #define LPCOPY_BOX_OUTLINE_COLOR ILI9341_BLACK 43 | #define LPCOPY_BOX_SELECTED_COLOR ILI9341_LIGHTGREY 44 | 45 | #define TFT_DC 9 46 | #define TFT_CS 10 47 | // MOSI=11, MISO=12, SCK=13 48 | 49 | #define MCP23017_ADDR 0x20 50 | 51 | #define ROT2_B 41 // GPIO_A17, J2 pin 7 52 | #define ROT2_A 40 // GPIO_A16, J2 pin 8 53 | #define ROT2_SW 2 // GPIO_D2, J2 pin 10 54 | // GND, J2 pin 9 55 | 56 | #define ROT1_B 3 // GPIO_D3, J2 pin 11 57 | #define ROT1_A 4 // GPIO_D4, J2 pin 13 58 | #define ROT1_SW 15 // GPIO_A1, J2 pin 14 59 | // GND, J2 pin 12 60 | 61 | #define PAGE_SONG 0 62 | #define PAGE_PATTERN 1 63 | #define PAGE_EVENT 2 64 | #define PAGE_TRACK 3 65 | #define PAGE_FILE 4 66 | #define PAGE_LISTDEVICES 5 67 | #define PAGE_SCENE 6 68 | #define PAGE_TOOLS 7 69 | #define PAGE_SAVE 8 70 | #define PAGE_SONG2 9 71 | 72 | #define PATTERN_LENGTH 0 73 | #define PATTERN_SPEED 1 74 | #define PATTERN_EVENTLENGTHDEF 2 75 | #define TRACK_OUTPUT 3 76 | #define SONG_BPM 4 77 | #define TRACK_CHANNEL 5 78 | #define EVENT_LENGTH 6 79 | #define TRACK_TRANSPOSESTATUS 7 80 | #define PATTERN_NR 8 81 | #define SCENE_NR 9 82 | #define SCENE_COLOR 10 83 | #define TOOLS_SELECTION 11 84 | #define TOOLS_ACTION 12 85 | #define TOOLS_TRANSPOSE 13 86 | #define MIDI_CLOCK 14 87 | #define SONG_PLAYMODE 15 88 | 89 | #define NR_PARAMETERS 11 90 | #define MAX_CHILDREN 8 91 | #define PAGE_NAV 0 92 | #define PAGE_PAR 1 93 | 94 | #define W_NONE 0 95 | #define W_LIST 1 96 | #define W_POT 2 97 | #define W_SLIDER 3 98 | 99 | #define DISPLAYMODE_SEQUENCER 0 100 | #define DISPLAYMODE_PATTERNSELECT 1 101 | 102 | #define LPMODE_SCENE 0 103 | #define LPMODE_PATTERN 1 104 | #define LPMODE_SONG 2 105 | #define LPMODE_KEYBOARD 3 106 | 107 | #define KEY_VAR_UI_PAGE 0 108 | #define KEY_VAR_LP_PAGE 1 109 | #define KEY_VAR_TRACK_NR 2 110 | #define KEY_VAR_PATTERN_NR 3 111 | #define KEY_VAR_SCENE_NR 4 112 | #define KEY_VAR_ARRPOS_NR 5 113 | 114 | #define NR_KEY_VARS 6 115 | 116 | extern uint8_t LPdisplayMode; 117 | extern uint8_t currentFileNr; 118 | 119 | typedef float (* FPgetFloat)(); 120 | typedef void (* FPsetFloat)(float); 121 | typedef String (* FPgetString)(uint8_t); 122 | 123 | struct page 124 | { 125 | uint8_t pageType; 126 | char name[9]; 127 | uint8_t nrChildren; 128 | uint8_t children[MAX_CHILDREN]; 129 | }; 130 | 131 | struct parameters 132 | { 133 | char name[9]; 134 | float minValue; 135 | float maxValue; 136 | float multiplier; 137 | uint8_t displayPrecision; 138 | FPgetFloat getFunction = nullptr; 139 | FPsetFloat setFunction = nullptr; 140 | FPgetString enumFunction = nullptr; 141 | }; 142 | 143 | void setupUI(); 144 | void showStartupScreen(); 145 | 146 | void updateUI(); 147 | void updateDisplayUI(); 148 | void updateLaunchpadUI(); 149 | 150 | void signalKeyVariableChange(); 151 | void updateHeader(bool firstCall); 152 | 153 | void clearMainArea(); 154 | void drawMenuButton(uint8_t index, String text, bool selected); 155 | 156 | void updateParameterPage(const uint8_t * parameterArray, const uint8_t nrParameters, bool firstCall, bool forceVariableUpdate); 157 | void drawParameterRow(uint8_t index, uint8_t parameter, bool selected); 158 | void updateParameterRow(uint8_t index, uint8_t parameter); 159 | 160 | float getValue(uint8_t parameter); 161 | void updateValue(uint8_t parameter, float value); 162 | 163 | void handleSpecialPages(bool firstCall); 164 | void displayDevicePage(bool firstCall); 165 | void displayFilePage(bool firstCall); 166 | void displayFileNr(uint8_t fileNr); 167 | void displayFileName(uint8_t fileNr); 168 | void displayFileName_strEdit(char * name, uint8_t markedIndex); 169 | void displayLoadSave(); 170 | void displaySavePage(bool firstCall); 171 | 172 | void displaySongPage2(bool firstCall); 173 | 174 | void displayEncoderButtonHint(uint8_t encoder, String hint, uint16_t color); 175 | void displayToolsPage(bool firstCall); 176 | 177 | void LPcopy_update(bool firstCall); 178 | void LPcopy_clearArea(); 179 | void LPcopy_drawBackground(uint8_t nrPages); 180 | void LPcopy_setSelectedPage(); 181 | void LPcopy_updateSingleEvent_converter(uint8_t note, uint8_t padState); 182 | void LPcopy_updateSingleEvent(uint8_t page, uint8_t column, uint8_t row, uint8_t LPcolor); 183 | void LPcopy_updateAllEvents(); 184 | void LPcopy_updateColumn(uint8_t column); 185 | void LPcopy_setStepIndicator(); 186 | void LPcopy_setColumnColor(uint8_t page, uint8_t column, uint8_t LPcolor); 187 | 188 | void LPinit(); 189 | void LPsetStepIndicator(); 190 | void LPpageIncrease(); 191 | void LPpageDecrease(); 192 | void LPscrollUp(); 193 | void LPscrollDown(); 194 | void LPsetPageFromTrackData(); 195 | void LPsetColumnFromTrackData(uint8_t padColumn); 196 | void setLPsongMode(); 197 | void setLPsceneMode(); 198 | void setLPpatternMode(); 199 | void LPsetTrackButtonsSceneMode(bool forceUpdate); 200 | void LPsetSceneButtonsSceneMode(bool forceUpdate); 201 | void LPsetSceneButtonsSongMode(bool forceUpdate); 202 | void LPsetArrEventStatus(uint8_t arrId, bool current); 203 | void LPtoggleMute(uint8_t control); 204 | void LPsetPageFromSceneData(bool forceUpdate); 205 | void LPsetTrackRowFromSceneData(uint8_t trackId, bool forceUpdate); 206 | void LPsetPageFromSongData(bool forceUpdate); 207 | 208 | uint16_t lpColor2tftColor(uint8_t lpColor); 209 | 210 | int16_t getEncoderDirection(uint8_t encoderNr); 211 | uint8_t updateButton(uint8_t buttonNr); 212 | void initMcp(); 213 | void updateMcp(); 214 | 215 | #define TFT_RED ILI9341_RED 216 | #define TFT_BLUE ILI9341_BLUE 217 | #define TFT_GREEN ILI9341_GREEN 218 | #define TFT_PURPLE ILI9341_PURPLE 219 | #define TFT_CYAN ILI9341_CYAN 220 | #define TFT_ORANGE ILI9341_ORANGE 221 | #define TFT_PINK ILI9341_PINK 222 | #define TFT_GHOST 0x1111 223 | #define TFT_DARKBLUE ILI9341_NAVY 224 | #define TFT_YELLOW ILI9341_YELLOW 225 | #define TFT_OFF 0x31A6 226 | 227 | //#define ILI9341_BLACK 0x0000 /* 0, 0, 0 */ 228 | //#define ILI9341_NAVY 0x000F /* 0, 0, 128 */ 229 | //#define ILI9341_DARKGREEN 0x03E0 /* 0, 128, 0 */ 230 | //#define ILI9341_DARKCYAN 0x03EF /* 0, 128, 128 */ 231 | //#define ILI9341_MAROON 0x7800 /* 128, 0, 0 */ 232 | //#define ILI9341_PURPLE 0x780F /* 128, 0, 128 */ 233 | //#define ILI9341_OLIVE 0x7BE0 /* 128, 128, 0 */ 234 | //#define ILI9341_LIGHTGREY 0xC618 /* 192, 192, 192 */ 235 | //#define ILI9341_DARKGREY 0x7BEF /* 128, 128, 128 */ 236 | //#define ILI9341_BLUE 0x001F /* 0, 0, 255 */ 237 | //#define ILI9341_GREEN 0x07E0 /* 0, 255, 0 */ 238 | //#define ILI9341_CYAN 0x07FF /* 0, 255, 255 */ 239 | //#define ILI9341_RED 0xF800 /* 255, 0, 0 */ 240 | //#define ILI9341_MAGENTA 0xF81F /* 255, 0, 255 */ 241 | //#define ILI9341_YELLOW 0xFFE0 /* 255, 255, 0 */ 242 | //#define ILI9341_WHITE 0xFFFF /* 255, 255, 255 */ 243 | //#define ILI9341_ORANGE 0xFD20 /* 255, 165, 0 */ 244 | //#define ILI9341_GREENYELLOW 0xAFE5 /* 173, 255, 47 */ 245 | //#define ILI9341_PINK 0xF81F 246 | --------------------------------------------------------------------------------