Components and supplies
Arduino GIGA R1 WiFi
Olimex MIDI Shield
Arduino® GIGA Display Shield
Apps and platforms
Arduino IDE 2.2.1
Project description
Code
M7 core
cpp
Upload this to the M7 core
1/* 2 * Wavetable Synthesizer 3 * 4 * Copyright (c) Guy Bartell 2024 5 * 6 * This code is licensed under the Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0). 7 * 8 * You are free to: 9 * - Share: Copy and redistribute the material in any medium or format. 10 * - Adapt: Remix, transform, and build upon the material. 11 * 12 * Under the following terms: 13 * - Attribution: You must give appropriate credit, provide a link to the license, 14 * and indicate if changes were made. You may do so in any reasonable manner, 15 * but not in any way that suggests the licensor endorses you or your use. 16 * - NonCommercial: You may not use the material for commercial purposes. 17 * 18 * No additional restrictions: You may not apply legal terms or technological 19 * measures that legally restrict others from doing anything the license permits. 20 * 21 * Full license text can be found at: 22 * https://creativecommons.org/licenses/by-nc/4.0/ 23 * 24 * Third-party libraries and components included in this software are licensed 25 * under their respective licenses, as listed in the LICENSE file. 26 * 27 * For commercial use or licensing inquiries, please contact guybartell.ext@gmail.com. 28 */ 29 30// DAC & MIDI (M7 core) 31 32#include <Arduino_AdvancedAnalog.h> // DAC 33#include "wavetables.h" // Definitions for wavetables 34#include <MIDI.h> 35#include <RPC.h> 36 37MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI); 38 39AdvancedDAC dac1(A13); 40 41unsigned long time_now = millis(); // for timer function, seed for random 42uint16_t* lut = sine_wave; // Initialize LUT with the sine wave LUT 43 44uint16_t temp_lut[512]; // Temporary LUT for editing current waveform 45float volume = 1.0; // Volume level, 0.0 (mute) to 1.0 (max) 46 47// ADSR parameters 48float attack_time = 0.0025; // seconds 49float decay_time = 0.0025; // seconds 50float sustain_level = 1.0; // sustain level (0.0 to 1.0) 51float release_time = 0.0025; // seconds 52 53float semitone_range = 2.0; 54 55bool velocityMode = 1; // Velocity sensitive by default 56bool randomModeM7 = 0; // Controls whether curve (0) or block (1) interpolation 57 58int pitch_bend = 0; // Global pitch bend value (-8192 to 8191) 59 60// array of look-up tables (LUTs) 61const uint16_t* wave_luts[4] = { sine_wave, square_wave, sawtooth_wave, organ_pipe_wave }; 62 63// size of LUT for DAC 64static size_t lut_size = sizeof(sine_wave) / sizeof(sine_wave[0]); 65 66const float sample_rate = 192000.0; 67 68// Lower MIDI latency (5ms) although only one DAC can be used 69const int n_samples = 512; // Define n_samples here 70const int n_buffers = 4; // Define n_buffers here 71 72// ADSR envelope stages 73enum ADSRStage { ATTACK, DECAY, SUSTAIN, RELEASE, OFF }; 74 75// Structure for each voice 76struct Voice { 77 float frequency; 78 float phase_accumulator; 79 float phase_increment; 80 ADSRStage stage; 81 float adsr_level; 82 int velocity; 83 bool active; 84}; 85 86#define NUM_VOICES 16 87Voice voices[NUM_VOICES]; 88 89void startDAC(int dacMode){ 90 if (dacMode){ 91 dac1.begin(AN_RESOLUTION_12, sample_rate, n_samples, n_buffers); 92 } 93 else if (!dacMode){ 94 dac1.stop(); 95 } 96} 97// Control whether velocity sensitive or not (currently not implemented) 98void setVelocityMode(int velocityModeIn){ 99 if (velocityModeIn == 1){ 100 velocityMode = 1; 101 } 102 else { 103 velocityMode = 0; 104 } 105} 106// Receive pitch bend from MIDI keyboard 107void handlePitchBend(byte channel, int bendValue) { 108 pitch_bend = bendValue; 109} 110 111// Update pitch bend range from M4 112void setPitchBend(int range) { 113 semitone_range = range; 114} 115 116// Update MIDI channel from M4 117void setMidiChannel(int channel) { 118 if (channel == 0){ 119 MIDI.begin(MIDI_CHANNEL_OMNI); 120 } 121 else { 122 MIDI.begin(channel); 123 } 124} 125 126// Convert pitch bend to range to be used by voices 127float calculatePitchBendFactor(int bendValue) { 128 return pow(2.0, (bendValue / 8192.0) * semitone_range / 12.0); 129} 130 131 132// Set up ADSR values for each stage based on sample rate 133float adsr_increment[4] = { 134 1.0 / (attack_time * sample_rate), 135 -1.0 / (decay_time * sample_rate) * (1.0 - sustain_level), 136 0.0, 137 -1.0 / (release_time * sample_rate) 138}; 139 140// Convert frequency to phase increment for reading from LUT 141void setVoiceFrequency(Voice& voice, float frequency) { 142 voice.frequency = frequency; 143 voice.phase_increment = (frequency * lut_size) / sample_rate; 144} 145 146// Update ADSR 147void updateADSR(Voice& voice) { 148 switch (voice.stage) { 149 case ATTACK: 150 voice.adsr_level += adsr_increment[ATTACK]; 151 if (voice.adsr_level >= 1.0) { 152 voice.adsr_level = 1.0; 153 voice.stage = DECAY; 154 } 155 break; 156 case DECAY: 157 voice.adsr_level += adsr_increment[DECAY]; 158 if (voice.adsr_level <= sustain_level) { 159 voice.adsr_level = sustain_level; 160 voice.stage = SUSTAIN; 161 } 162 break; 163 case RELEASE: 164 voice.adsr_level += adsr_increment[RELEASE]; 165 if (voice.adsr_level <= 0.0) { 166 voice.adsr_level = 0.0; 167 voice.stage = OFF; 168 voice.active = false; 169 } 170 break; 171 default: 172 break; 173 } 174} 175 176// Map MIDI note to frequency 177float noteToFreq(int note) { 178 float a = 440.0; 179 return (a / 32) * pow(2, ((note - 9) / 12.0)); 180} 181 182// Process and mix all voices 183void updateDAC(AdvancedDAC& dac_out) { 184 if (dac_out.available()) { 185 SampleBuffer buf = dac_out.dequeue(); 186 187 float pitch_bend_factor = calculatePitchBendFactor(pitch_bend); 188 189 for (size_t i = 0; i < buf.size(); i++) { 190 float sample_sum = 0; 191 192 // Process each voice 193 for (int v = 0; v < NUM_VOICES; v++) { 194 if (voices[v].active) { 195 size_t index = static_cast<size_t>(voices[v].phase_accumulator) % lut_size; 196 float sample = temp_lut[index] * volume * (voices[v].velocity / 127.0) * voices[v].adsr_level; 197 sample_sum += sample / NUM_VOICES; 198 float adjusted_increment = voices[v].frequency * pitch_bend_factor * lut_size / sample_rate; 199 voices[v].phase_accumulator += adjusted_increment; 200 201 if (voices[v].phase_accumulator >= lut_size) { 202 voices[v].phase_accumulator -= lut_size; 203 } 204 updateADSR(voices[v]); 205 } 206 } 207 208 buf[i] = sample_sum; // Write mixed sample to buffer 209 } 210 211 dac_out.write(buf); 212 } 213} 214 215// Function to turn off all voices 216void panic() { 217 for (int v = 0; v < NUM_VOICES; v++) { 218 if (voices[v].active) { 219 voices[v].stage = RELEASE; 220 } 221 } 222} 223 224// MIDI Note On handler 225void handleNoteOn(byte channel, byte pitch, byte velocity) { 226 float newFrequency = noteToFreq(pitch); // MIDI note to frequency 227 228 // Check for an inactive voice 229 for (int v = 0; v < NUM_VOICES; v++) { 230 if (!voices[v].active) { 231 voices[v].active = true; 232 voices[v].stage = ATTACK; 233 voices[v].adsr_level = 0.0; 234 if (velocityMode) { 235 voices[v].velocity = velocity; 236 } else { 237 voices[v].velocity = 127; 238 } 239 setVoiceFrequency(voices[v], newFrequency); 240 return; // Exit after assigning a voice 241 } 242 } 243} 244 245// MIDI Note Off handler 246void handleNoteOff(byte channel, byte pitch, byte velocity) { 247 float frequency = noteToFreq(pitch); 248 for (int v = 0; v < NUM_VOICES; v++) { 249 if (voices[v].active && voices[v].frequency == frequency) { 250 voices[v].stage = RELEASE; 251 } 252 } 253} 254 255// Update volume from M4 256void setVolume(float vol) { 257 volume = vol; 258} 259 260// Update attack from M4 261void setAttack(float att){ 262 attack_time = att; 263 adsr_increment[ATTACK] = 1.0 / (attack_time * sample_rate); 264} 265 266// Update decay from M4 267void setDecay(float dec){ 268 decay_time = dec; 269 adsr_increment[DECAY] = -1.0 / (decay_time * sample_rate) * (1.0 - sustain_level); 270} 271 272// Update sustain from M4 273void setSustain(float sus){ 274 sustain_level = sus; 275 adsr_increment[DECAY] = -1.0 / (decay_time * sample_rate) * (1.0 - sustain_level); 276} 277// Update release from M4 278void setRelease(float rel){ 279 release_time = rel; 280 adsr_increment[RELEASE] = -1.0 / (release_time * sample_rate); 281} 282 283// Set whether random function interpolates curves (0) or blocks (1) 284void setRandomMode(bool mode){ 285 randomModeM7 = mode; 286} 287 288// Load LUT into temp_lut 289void loadLUT(int index) { 290 memcpy(temp_lut, wave_luts[index], 512 * sizeof(uint16_t)); 291} 292 293void updateLUT(int lutIndex, int newValue){ 294 temp_lut[lutIndex] = newValue; // Update temporary LUT 295} 296 297// Retrieves a single element from temp_lut 298int getTempLutElement(int index) { 299 if (index >= 0 && index < lut_size) { 300 return temp_lut[index]; 301 } else { 302 return 0; // Return a default value if index is out of bounds 303 } 304} 305 306// Retrieves a single element from temp_lut 307int getWavLutElement(int wavetable, int index) { 308 if (index >= 0 && index < lut_size) { 309 return wave_luts[wavetable][index]; 310 } else { 311 return 0; // Return a default value if index is out of bounds 312 } 313} 314// Clamp function to prevent spike/folding issue with cubic spline 315int clampToRange(int value, int min, int max) { 316 return (value < min) ? min : (value > max) ? max : value; 317} 318 319// Generates a LUT of interpolated random values 320void generateRandomArray(int step_size) { 321 randomSeed(time_now); 322 323 if (randomModeM7 == 1){ // 'Block' shape function 324 // Fills start of every step with a random value 325 for (int i = 0; i < lut_size; i += step_size) { 326 int randomValue = random(0, 4096); // 12 bit values, full range 327 temp_lut[i] = randomValue; 328 329 // Fills indexes between steps with same value as that of the preceding step 330 for (int j = i; j < i + step_size && j < lut_size; j++) { 331 temp_lut[j] = randomValue; 332 } 333 } 334} 335 336else { // 'Curve' shape function 337 338 // Fills start of every step with a random value 339 for (int i = 0; i < lut_size; i += step_size) { 340 temp_lut[i] = random(256, 3840); // 12 bit values, limited range to prevent wrapping 341 } 342 343 // Cubic interpolation between anchor points 344 for (int i = 0; i < lut_size - step_size; i += step_size) { 345 // Control points for cubic interpolation 346 int p0 = (i >= step_size) ? temp_lut[i - step_size] : temp_lut[i]; // Previous anchor point (or duplicate of the first if out of bounds) 347 int p1 = temp_lut[i]; // Start anchor point 348 int p2 = temp_lut[i + step_size]; // End anchor point 349 int p3 = (i + 2 * step_size < lut_size) ? temp_lut[i + 2 * step_size] : p2; // Next anchor point (or duplicate of the last if out of bounds) 350 351 352 // Interpolate between p1 and p2, cubic hermite spline 353 for (int j = 1; j < step_size; j++) { 354 float t = j / float(step_size); // Normalized position between points (0 to 1) 355 356 // Cubic hermite spline formula 357 float a0 = -0.5 * p0 + 1.5 * p1 - 1.5 * p2 + 0.5 * p3; 358 float a1 = p0 - 2.5 * p1 + 2.0 * p2 - 0.5 * p3; 359 float a2 = -0.5 * p0 + 0.5 * p2; 360 float a3 = p1; 361 362 // Calculate interpolated value 363 int interpolatedValue = (int)(a0 * t * t * t + a1 * t * t + a2 * t + a3); 364 interpolatedValue = clampToRange(interpolatedValue, 0, 4095); // Prevent wrapping in case earlier limited range doesn't solve this 365 366 temp_lut[i + j] = interpolatedValue; 367 } 368 } 369 370 // Handle the last section 371 int lastStep = ((lut_size / step_size) - 1) * step_size; 372 // Step 2: Perform cubic interpolation between anchor points 373 for (int i = lastStep; i < lut_size; i += step_size) { 374 // Control points for cubic interpolation 375 int p0 = (i >= step_size) ? temp_lut[i - step_size] : temp_lut[i]; // Previous anchor point (or duplicate the first if out of bounds) 376 int p1 = temp_lut[lastStep]; // Start anchor point 377 int p2 = temp_lut[0]; // End anchor point 378 int p3 = (i + 2 * step_size < lut_size) ? temp_lut[i + 2 * step_size] : p2; // Next anchor point (or duplicate the last if out of bounds) 379 380 // Interpolate between p1 and p2, cubic hermite spline 381 for (int j = 1; j < step_size; j++) { 382 float t = j / float(step_size); // Normalized position between points (0 to 1) 383 384 // Cubic hermite spline formula 385 float a0 = -0.5 * p0 + 1.5 * p1 - 1.5 * p2 + 0.5 * p3; 386 float a1 = p0 - 2.5 * p1 + 2.0 * p2 - 0.5 * p3; 387 float a2 = -0.5 * p0 + 0.5 * p2; 388 float a3 = p1; 389 390 // Calculate interpolated value 391 int interpolatedValue = (int)(a0 * t * t * t + a1 * t * t + a2 * t + a3); 392 interpolatedValue = clampToRange(interpolatedValue, 0, 4095); 393 394 temp_lut[i + j] = interpolatedValue; 395 } 396 } 397 } 398} 399 400 401// Generates a LUT with all zero values 402void clearArray() { 403 for (int i = 0; i < lut_size; i++) { 404 temp_lut[i] = 0; 405 } 406} 407 408void setup() { 409 Serial.begin(115200); 410 sleep_manager_lock_deep_sleep(); // Ignore deepsleep mode in MBed core, without this deep sleep is enabled after a few minutes and midi input is delayed 411 412 // Midi handling 413 MIDI.setHandleNoteOn(handleNoteOn); 414 MIDI.setHandleNoteOff(handleNoteOff); 415 MIDI.setHandlePitchBend(handlePitchBend); 416 MIDI.begin(MIDI_CHANNEL_OMNI); 417 418 if (!dac1.begin(AN_RESOLUTION_12, sample_rate, n_samples, n_buffers)) { 419 Serial.println("Failed to start DAC1 !"); 420 while (1); 421 } 422// RPC functions 423 RPC.begin(); 424 RPC.bind("setVolume", setVolume); 425 RPC.bind("loadLUT", loadLUT); 426 RPC.bind("generateRandomArray", generateRandomArray); 427 RPC.bind("clearArray", clearArray); 428 RPC.bind("updateLUT", updateLUT); 429 RPC.bind("getTempLutElement", getTempLutElement); 430 RPC.bind("startDAC", startDAC); 431 RPC.bind("getWavLutElement", getWavLutElement); 432 RPC.bind("setAttack", setAttack); 433 RPC.bind("setDecay", setDecay); 434 RPC.bind("setSustain", setSustain); 435 RPC.bind("setRelease", setRelease); 436 RPC.bind("setRandomMode", setRandomMode); 437 RPC.bind("setVelocityMode", setVelocityMode); 438 RPC.bind("panic", panic); 439 RPC.bind("setPitchBend", setPitchBend); 440 RPC.bind("setMidiChannel", setMidiChannel); 441 442 loadLUT(0); // Load sine wave by default 443} 444 445void loop() { 446 MIDI.read(); 447 updateDAC(dac1); 448}
Wavetables header file
cpp
Make sure this is in the M7 folder
1#ifndef WAVETABLES_H 2#define WAVETABLES_H 3#include <Arduino.h> // Included to make uint16_t available 4 5// wavetable definitions 6extern uint16_t sine_wave[512]; 7extern uint16_t square_wave[512]; 8extern uint16_t sawtooth_wave[512]; 9extern uint16_t organ_pipe_wave[512]; 10extern uint16_t christmas_tree[512]; 11 12#endif
Wavetables source file
cpp
Make sure this is in the M7 folder
1#include "wavetables.h" 2#include <Arduino.h> // Included to make uint16_t available 3 4uint16_t sine_wave[512] = { 5 0x7FF, 0x818, 0x831, 0x84A, 0x863, 0x87D, 0x896, 0x8AF, 0x8C8, 0x8E1, 0x8FA, 0x913, 6 0x92B, 0x944, 0x95D, 0x976, 0x98E, 0x9A7, 0x9C0, 0x9D8, 0x9F1, 0xA09, 0xA21, 0xA39, 7 0xA51, 0xA69, 0xA81, 0xA99, 0xAB1, 0xAC8, 0xAE0, 0xAF7, 0xB0F, 0xB26, 0xB3D, 0xB54, 8 0xB6A, 0xB81, 0xB98, 0xBAE, 0xBC4, 0xBDA, 0xBF0, 0xC06, 0xC1C, 0xC31, 0xC46, 0xC5C, 9 0xC71, 0xC85, 0xC9A, 0xCAE, 0xCC3, 0xCD7, 0xCEB, 0xCFE, 0xD12, 0xD25, 0xD38, 0xD4B, 10 0xD5E, 0xD71, 0xD83, 0xD95, 0xDA7, 0xDB8, 0xDCA, 0xDDB, 0xDEC, 0xDFD, 0xE0D, 0xE1E, 11 0xE2E, 0xE3E, 0xE4D, 0xE5C, 0xE6C, 0xE7A, 0xE89, 0xE97, 0xEA5, 0xEB3, 0xEC1, 0xECE, 12 0xEDB, 0xEE8, 0xEF5, 0xF01, 0xF0D, 0xF18, 0xF24, 0xF2F, 0xF3A, 0xF45, 0xF4F, 0xF59, 13 0xF63, 0xF6C, 0xF75, 0xF7E, 0xF87, 0xF8F, 0xF97, 0xF9F, 0xFA6, 0xFAD, 0xFB4, 0xFBB, 14 0xFC1, 0xFC7, 0xFCD, 0xFD2, 0xFD7, 0xFDC, 0xFE0, 0xFE4, 0xFE8, 0xFEC, 0xFEF, 0xFF2, 15 0xFF5, 0xFF7, 0xFF9, 0xFFB, 0xFFC, 0xFFD, 0xFFE, 0xFFE, 0xFFF, 0xFFE, 0xFFE, 0xFFD, 16 0xFFC, 0xFFB, 0xFF9, 0xFF7, 0xFF5, 0xFF2, 0xFEF, 0xFEC, 0xFE8, 0xFE4, 0xFE0, 0xFDC, 17 0xFD7, 0xFD2, 0xFCD, 0xFC7, 0xFC1, 0xFBB, 0xFB4, 0xFAD, 0xFA6, 0xF9F, 0xF97, 0xF8F, 18 0xF87, 0xF7E, 0xF75, 0xF6C, 0xF63, 0xF59, 0xF4F, 0xF45, 0xF3A, 0xF2F, 0xF24, 0xF18, 19 0xF0D, 0xF01, 0xEF5, 0xEE8, 0xEDB, 0xECE, 0xEC1, 0xEB3, 0xEA5, 0xE97, 0xE89, 0xE7A, 20 0xE6C, 0xE5C, 0xE4D, 0xE3E, 0xE2E, 0xE1E, 0xE0D, 0xDFD, 0xDEC, 0xDDB, 0xDCA, 0xDB8, 21 0xDA7, 0xD95, 0xD83, 0xD71, 0xD5E, 0xD4B, 0xD38, 0xD25, 0xD12, 0xCFE, 0xCEB, 0xCD7, 22 0xCC3, 0xCAE, 0xC9A, 0xC85, 0xC71, 0xC5C, 0xC46, 0xC31, 0xC1C, 0xC06, 0xBF0, 0xBDA, 23 0xBC4, 0xBAE, 0xB98, 0xB81, 0xB6A, 0xB54, 0xB3D, 0xB26, 0xB0F, 0xAF7, 0xAE0, 0xAC8, 24 0xAB1, 0xA99, 0xA81, 0xA69, 0xA51, 0xA39, 0xA21, 0xA09, 0x9F1, 0x9D8, 0x9C0, 0x9A7, 25 0x98E, 0x976, 0x95D, 0x944, 0x92B, 0x913, 0x8FA, 0x8E1, 0x8C8, 0x8AF, 0x896, 0x87D, 26 0x863, 0x84A, 0x831, 0x818, 0x7FF, 0x7E6, 0x7CD, 0x7B4, 0x79B, 0x781, 0x768, 0x74F, 27 0x736, 0x71D, 0x704, 0x6EB, 0x6D3, 0x6BA, 0x6A1, 0x688, 0x670, 0x657, 0x63E, 0x626, 28 0x60D, 0x5F5, 0x5DD, 0x5C5, 0x5AD, 0x595, 0x57D, 0x565, 0x54D, 0x536, 0x51E, 0x507, 29 0x4EF, 0x4D8, 0x4C1, 0x4AA, 0x494, 0x47D, 0x466, 0x450, 0x43A, 0x424, 0x40E, 0x3F8, 30 0x3E2, 0x3CD, 0x3B8, 0x3A2, 0x38D, 0x379, 0x364, 0x350, 0x33B, 0x327, 0x313, 0x300, 31 0x2EC, 0x2D9, 0x2C6, 0x2B3, 0x2A0, 0x28D, 0x27B, 0x269, 0x257, 0x246, 0x234, 0x223, 32 0x212, 0x201, 0x1F1, 0x1E0, 0x1D0, 0x1C0, 0x1B1, 0x1A2, 0x192, 0x184, 0x175, 0x167, 33 0x159, 0x14B, 0x13D, 0x130, 0x123, 0x116, 0x109, 0xFD, 0xF1, 0xE6, 0xDA, 0xCF, 34 0xC4, 0xB9, 0xAF, 0xA5, 0x9B, 0x92, 0x89, 0x80, 0x77, 0x6F, 0x67, 0x5F, 35 0x58, 0x51, 0x4A, 0x43, 0x3D, 0x37, 0x31, 0x2C, 0x27, 0x22, 0x1E, 0x1A, 36 0x16, 0x12, 0x0F, 0x0C, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01, 0x00, 0x00, 37 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x05, 0x07, 0x09, 0x0C, 0x0F, 0x12, 38 0x16, 0x1A, 0x1E, 0x22, 0x27, 0x2C, 0x31, 0x37, 0x3D, 0x43, 0x4A, 0x51, 39 0x58, 0x5F, 0x67, 0x6F, 0x77, 0x80, 0x89, 0x92, 0x9B, 0xA5, 0xAF, 0xB9, 40 0xC4, 0xCF, 0xDA, 0xE6, 0xF1, 0xFD, 0x109, 0x116, 0x123, 0x130, 0x13D, 0x14B, 41 0x159, 0x167, 0x175, 0x184, 0x192, 0x1A2, 0x1B1, 0x1C0, 0x1D0, 0x1E0, 0x1F1, 0x201, 42 0x212, 0x223, 0x234, 0x246, 0x257, 0x269, 0x27B, 0x28D, 0x2A0, 0x2B3, 0x2C6, 0x2D9, 43 0x2EC, 0x300, 0x313, 0x327, 0x33B, 0x350, 0x364, 0x379, 0x38D, 0x3A2, 0x3B8, 0x3CD, 44 0x3E2, 0x3F8, 0x40E, 0x424, 0x43A, 0x450, 0x466, 0x47D, 0x494, 0x4AA, 0x4C1, 0x4D8, 45 0x4EF, 0x507, 0x51E, 0x536, 0x54D, 0x565, 0x57D, 0x595, 0x5AD, 0x5C5, 0x5DD, 0x5F5, 46 0x60D, 0x626, 0x63E, 0x657, 0x670, 0x688, 0x6A1, 0x6BA, 0x6D3, 0x6EB, 0x704, 0x71D, 47 0x736, 0x74F, 0x768, 0x781, 0x79B, 0x7B4, 0x7CD, 0x7E6}; 48uint16_t square_wave[512] = { 49 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 50 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 51 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 52 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 53 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 54 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 55 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 56 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 57 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 58 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 59 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 60 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 61 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 62 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 63 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 64 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 65 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 66 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 67 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 68 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 69 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0xFFF, 70 0xFFF, 0xFFF, 0xFFF, 0xFFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 78 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 81 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 83 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 87 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 88 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 89 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 90 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 91 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 92 93uint16_t sawtooth_wave[512] = { 94 0x00, 0x07, 0x0F, 0x17, 0x1F, 0x27, 0x2F, 0x37, 0x3F, 0x47, 0x4F, 0x57, 95 0x5F, 0x67, 0x6F, 0x77, 0x7F, 0x87, 0x8F, 0x97, 0x9F, 0xA7, 0xAF, 0xB7, 96 0xBF, 0xC7, 0xCF, 0xD7, 0xDF, 0xE7, 0xEF, 0xF7, 0xFF, 0x107, 0x10F, 0x117, 97 0x11F, 0x127, 0x12F, 0x137, 0x13F, 0x147, 0x14F, 0x157, 0x15F, 0x167, 0x16F, 0x177, 98 0x17F, 0x187, 0x18F, 0x197, 0x19F, 0x1A7, 0x1AF, 0x1B7, 0x1BF, 0x1C7, 0x1CF, 0x1D7, 99 0x1DF, 0x1E7, 0x1EF, 0x1F7, 0x1FF, 0x207, 0x20F, 0x217, 0x21F, 0x227, 0x22F, 0x237, 100 0x23F, 0x247, 0x24F, 0x257, 0x25F, 0x267, 0x26F, 0x277, 0x27F, 0x287, 0x28F, 0x297, 101 0x29F, 0x2A7, 0x2AF, 0x2B7, 0x2BF, 0x2C7, 0x2CF, 0x2D7, 0x2DF, 0x2E7, 0x2EF, 0x2F7, 102 0x2FF, 0x307, 0x30F, 0x317, 0x31F, 0x327, 0x32F, 0x337, 0x33F, 0x347, 0x34F, 0x357, 103 0x35F, 0x367, 0x36F, 0x377, 0x37F, 0x387, 0x38F, 0x397, 0x39F, 0x3A7, 0x3AF, 0x3B7, 104 0x3BF, 0x3C7, 0x3CF, 0x3D7, 0x3DF, 0x3E7, 0x3EF, 0x3F7, 0x3FF, 0x407, 0x40F, 0x417, 105 0x41F, 0x427, 0x42F, 0x437, 0x43F, 0x447, 0x44F, 0x457, 0x45F, 0x467, 0x46F, 0x477, 106 0x47F, 0x487, 0x48F, 0x497, 0x49F, 0x4A7, 0x4AF, 0x4B7, 0x4BF, 0x4C7, 0x4CF, 0x4D7, 107 0x4DF, 0x4E7, 0x4EF, 0x4F7, 0x4FF, 0x507, 0x50F, 0x517, 0x51F, 0x527, 0x52F, 0x537, 108 0x53F, 0x547, 0x54F, 0x557, 0x55F, 0x567, 0x56F, 0x577, 0x57F, 0x587, 0x58F, 0x597, 109 0x59F, 0x5A7, 0x5AF, 0x5B7, 0x5BF, 0x5C7, 0x5CF, 0x5D7, 0x5DF, 0x5E7, 0x5EF, 0x5F7, 110 0x5FF, 0x607, 0x60F, 0x617, 0x61F, 0x627, 0x62F, 0x637, 0x63F, 0x647, 0x64F, 0x657, 111 0x65F, 0x667, 0x66F, 0x677, 0x67F, 0x687, 0x68F, 0x697, 0x69F, 0x6A7, 0x6AF, 0x6B7, 112 0x6BF, 0x6C7, 0x6CF, 0x6D7, 0x6DF, 0x6E7, 0x6EF, 0x6F7, 0x6FF, 0x707, 0x70F, 0x717, 113 0x71F, 0x727, 0x72F, 0x737, 0x73F, 0x747, 0x74F, 0x757, 0x75F, 0x767, 0x76F, 0x777, 114 0x77F, 0x787, 0x78F, 0x797, 0x79F, 0x7A7, 0x7AF, 0x7B7, 0x7BF, 0x7C7, 0x7CF, 0x7D7, 115 0x7DF, 0x7E7, 0x7EF, 0x7F7, 0x7FF, 0x807, 0x80F, 0x817, 0x81F, 0x827, 0x82F, 0x837, 116 0x83F, 0x847, 0x84F, 0x857, 0x85F, 0x867, 0x86F, 0x877, 0x87F, 0x887, 0x88F, 0x897, 117 0x89F, 0x8A7, 0x8AF, 0x8B7, 0x8BF, 0x8C7, 0x8CF, 0x8D7, 0x8DF, 0x8E7, 0x8EF, 0x8F7, 118 0x8FF, 0x907, 0x90F, 0x917, 0x91F, 0x927, 0x92F, 0x937, 0x93F, 0x947, 0x94F, 0x957, 119 0x95F, 0x967, 0x96F, 0x977, 0x97F, 0x987, 0x98F, 0x997, 0x99F, 0x9A7, 0x9AF, 0x9B7, 120 0x9BF, 0x9C7, 0x9CF, 0x9D7, 0x9DF, 0x9E7, 0x9EF, 0x9F7, 0x9FF, 0xA07, 0xA0F, 0xA17, 121 0xA1F, 0xA27, 0xA2F, 0xA37, 0xA3F, 0xA47, 0xA4F, 0xA57, 0xA5F, 0xA67, 0xA6F, 0xA77, 122 0xA7F, 0xA87, 0xA8F, 0xA97, 0xA9F, 0xAA7, 0xAAF, 0xAB7, 0xABF, 0xAC7, 0xACF, 0xAD7, 123 0xADF, 0xAE7, 0xAEF, 0xAF7, 0xAFF, 0xB07, 0xB0F, 0xB17, 0xB1F, 0xB27, 0xB2F, 0xB37, 124 0xB3F, 0xB47, 0xB4F, 0xB57, 0xB5F, 0xB67, 0xB6F, 0xB77, 0xB7F, 0xB87, 0xB8F, 0xB97, 125 0xB9F, 0xBA7, 0xBAF, 0xBB7, 0xBBF, 0xBC7, 0xBCF, 0xBD7, 0xBDF, 0xBE7, 0xBEF, 0xBF7, 126 0xBFF, 0xC07, 0xC0F, 0xC17, 0xC1F, 0xC27, 0xC2F, 0xC37, 0xC3F, 0xC47, 0xC4F, 0xC57, 127 0xC5F, 0xC67, 0xC6F, 0xC77, 0xC7F, 0xC87, 0xC8F, 0xC97, 0xC9F, 0xCA7, 0xCAF, 0xCB7, 128 0xCBF, 0xCC7, 0xCCF, 0xCD7, 0xCDF, 0xCE7, 0xCEF, 0xCF7, 0xCFF, 0xD07, 0xD0F, 0xD17, 129 0xD1F, 0xD27, 0xD2F, 0xD37, 0xD3F, 0xD47, 0xD4F, 0xD57, 0xD5F, 0xD67, 0xD6F, 0xD77, 130 0xD7F, 0xD87, 0xD8F, 0xD97, 0xD9F, 0xDA7, 0xDAF, 0xDB7, 0xDBF, 0xDC7, 0xDCF, 0xDD7, 131 0xDDF, 0xDE7, 0xDEF, 0xDF7, 0xDFF, 0xE07, 0xE0F, 0xE17, 0xE1F, 0xE27, 0xE2F, 0xE37, 132 0xE3F, 0xE47, 0xE4F, 0xE57, 0xE5F, 0xE67, 0xE6F, 0xE77, 0xE7F, 0xE87, 0xE8F, 0xE97, 133 0xE9F, 0xEA7, 0xEAF, 0xEB7, 0xEBF, 0xEC7, 0xECF, 0xED7, 0xEDF, 0xEE7, 0xEEF, 0xEF7, 134 0xEFF, 0xF07, 0xF0F, 0xF17, 0xF1F, 0xF27, 0xF2F, 0xF37, 0xF3F, 0xF47, 0xF4F, 0xF57, 135 0xF5F, 0xF67, 0xF6F, 0xF77, 0xF7F, 0xF87, 0xF8F, 0xF97, 0xF9F, 0xFA7, 0xFAF, 0xFB7, 136 0xFBF, 0xFC7, 0xFCF, 0xFD7, 0xFDF, 0xFE7, 0xFEF, 0xFF7}; 137 138uint16_t organ_pipe_wave[512] = { 139 0x7FF, 0x826, 0x84E, 0x875, 0x89D, 0x8C4, 0x8EB, 0x912, 0x939, 0x95F, 0x986, 0x9AC, 140 0x9D2, 0x9F7, 0xA1C, 0xA41, 0xA66, 0xA8A, 0xAAD, 0xAD1, 0xAF3, 0xB16, 0xB38, 0xB59, 141 0xB7A, 0xB9A, 0xBBA, 0xBD9, 0xBF7, 0xC15, 0xC32, 0xC4F, 0xC6B, 0xC86, 0xCA0, 0xCBA, 142 0xCD3, 0xCEC, 0xD03, 0xD1A, 0xD30, 0xD45, 0xD5A, 0xD6E, 0xD81, 0xD93, 0xDA4, 0xDB5, 143 0xDC4, 0xDD3, 0xDE1, 0xDEF, 0xDFB, 0xE07, 0xE12, 0xE1C, 0xE25, 0xE2D, 0xE35, 0xE3C, 144 0xE42, 0xE47, 0xE4C, 0xE4F, 0xE52, 0xE54, 0xE56, 0xE56, 0xE56, 0xE56, 0xE54, 0xE52, 145 0xE4F, 0xE4C, 0xE47, 0xE43, 0xE3D, 0xE37, 0xE30, 0xE29, 0xE21, 0xE19, 0xE10, 0xE07, 146 0xDFD, 0xDF2, 0xDE8, 0xDDC, 0xDD1, 0xDC4, 0xDB8, 0xDAB, 0xD9E, 0xD90, 0xD82, 0xD74, 147 0xD66, 0xD57, 0xD48, 0xD39, 0xD29, 0xD1A, 0xD0A, 0xCFA, 0xCEA, 0xCDA, 0xCCA, 0xCB9, 148 0xCA9, 0xC99, 0xC88, 0xC78, 0xC67, 0xC57, 0xC46, 0xC36, 0xC26, 0xC16, 0xC05, 0xBF5, 149 0xBE6, 0xBD6, 0xBC6, 0xBB7, 0xBA8, 0xB99, 0xB8A, 0xB7B, 0xB6D, 0xB5E, 0xB50, 0xB42, 150 0xB35, 0xB28, 0xB1B, 0xB0E, 0xB01, 0xAF5, 0xAE9, 0xADD, 0xAD2, 0xAC7, 0xABC, 0xAB2, 151 0xAA7, 0xA9E, 0xA94, 0xA8B, 0xA81, 0xA79, 0xA70, 0xA68, 0xA60, 0xA58, 0xA51, 0xA4A, 152 0xA43, 0xA3D, 0xA36, 0xA30, 0xA2A, 0xA25, 0xA1F, 0xA1A, 0xA15, 0xA11, 0xA0C, 0xA08, 153 0xA04, 0xA00, 0x9FC, 0x9F8, 0x9F5, 0x9F2, 0x9EE, 0x9EB, 0x9E8, 0x9E6, 0x9E3, 0x9E0, 154 0x9DD, 0x9DB, 0x9D8, 0x9D6, 0x9D4, 0x9D1, 0x9CF, 0x9CC, 0x9CA, 0x9C8, 0x9C5, 0x9C3, 155 0x9C0, 0x9BE, 0x9BB, 0x9B8, 0x9B5, 0x9B3, 0x9B0, 0x9AD, 0x9A9, 0x9A6, 0x9A3, 0x99F, 156 0x99C, 0x998, 0x994, 0x990, 0x98B, 0x987, 0x982, 0x97E, 0x979, 0x974, 0x96E, 0x969, 157 0x963, 0x95D, 0x957, 0x951, 0x94B, 0x944, 0x93D, 0x936, 0x92F, 0x928, 0x920, 0x919, 158 0x911, 0x909, 0x901, 0x8F8, 0x8F0, 0x8E7, 0x8DE, 0x8D5, 0x8CC, 0x8C3, 0x8B9, 0x8B0, 159 0x8A6, 0x89C, 0x892, 0x888, 0x87E, 0x874, 0x869, 0x85F, 0x854, 0x84A, 0x83F, 0x835, 160 0x82A, 0x81F, 0x815, 0x80A, 0x7FF, 0x7F4, 0x7E9, 0x7DF, 0x7D4, 0x7C9, 0x7BF, 0x7B4, 161 0x7AA, 0x79F, 0x795, 0x78A, 0x780, 0x776, 0x76C, 0x762, 0x758, 0x74E, 0x745, 0x73B, 162 0x732, 0x729, 0x720, 0x717, 0x70E, 0x706, 0x6FD, 0x6F5, 0x6ED, 0x6E5, 0x6DE, 0x6D6, 163 0x6CF, 0x6C8, 0x6C1, 0x6BA, 0x6B3, 0x6AD, 0x6A7, 0x6A1, 0x69B, 0x695, 0x690, 0x68A, 164 0x685, 0x680, 0x67C, 0x677, 0x673, 0x66E, 0x66A, 0x666, 0x662, 0x65F, 0x65B, 0x658, 165 0x655, 0x651, 0x64E, 0x64B, 0x649, 0x646, 0x643, 0x640, 0x63E, 0x63B, 0x639, 0x636, 166 0x634, 0x632, 0x62F, 0x62D, 0x62A, 0x628, 0x626, 0x623, 0x621, 0x61E, 0x61B, 0x618, 167 0x616, 0x613, 0x610, 0x60C, 0x609, 0x606, 0x602, 0x5FE, 0x5FA, 0x5F6, 0x5F2, 0x5ED, 168 0x5E9, 0x5E4, 0x5DF, 0x5D9, 0x5D4, 0x5CE, 0x5C8, 0x5C1, 0x5BB, 0x5B4, 0x5AD, 0x5A6, 169 0x59E, 0x596, 0x58E, 0x585, 0x57D, 0x573, 0x56A, 0x560, 0x557, 0x54C, 0x542, 0x537, 170 0x52C, 0x521, 0x515, 0x509, 0x4FD, 0x4F0, 0x4E3, 0x4D6, 0x4C9, 0x4BC, 0x4AE, 0x4A0, 171 0x491, 0x483, 0x474, 0x465, 0x456, 0x447, 0x438, 0x428, 0x418, 0x409, 0x3F9, 0x3E8, 172 0x3D8, 0x3C8, 0x3B8, 0x3A7, 0x397, 0x386, 0x376, 0x365, 0x355, 0x345, 0x334, 0x324, 173 0x314, 0x304, 0x2F4, 0x2E4, 0x2D5, 0x2C5, 0x2B6, 0x2A7, 0x298, 0x28A, 0x27C, 0x26E, 174 0x260, 0x253, 0x246, 0x23A, 0x22D, 0x222, 0x216, 0x20C, 0x201, 0x1F7, 0x1EE, 0x1E5, 175 0x1DD, 0x1D5, 0x1CE, 0x1C7, 0x1C1, 0x1BB, 0x1B7, 0x1B2, 0x1AF, 0x1AC, 0x1AA, 0x1A8, 176 0x1A8, 0x1A8, 0x1A8, 0x1AA, 0x1AC, 0x1AF, 0x1B2, 0x1B7, 0x1BC, 0x1C2, 0x1C9, 0x1D1, 177 0x1D9, 0x1E2, 0x1EC, 0x1F7, 0x203, 0x20F, 0x21D, 0x22B, 0x23A, 0x249, 0x25A, 0x26B, 178 0x27D, 0x290, 0x2A4, 0x2B9, 0x2CE, 0x2E4, 0x2FB, 0x312, 0x32B, 0x344, 0x35E, 0x378, 179 0x393, 0x3AF, 0x3CC, 0x3E9, 0x407, 0x425, 0x444, 0x464, 0x484, 0x4A5, 0x4C6, 0x4E8, 180 0x50B, 0x52D, 0x551, 0x574, 0x598, 0x5BD, 0x5E2, 0x607, 0x62C, 0x652, 0x678, 0x69F, 181 0x6C5, 0x6EC, 0x713, 0x73A, 0x761, 0x789, 0x7B0, 0x7D8};
M4 core
cpp
Upload this to the M4 core
1/* 2 * Wavetable Synthesizer 3 * 4 * Copyright (c) Guy Bartell 2024 5 * 6 * This code is licensed under the Creative Commons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0). 7 * 8 * You are free to: 9 * - Share: Copy and redistribute the material in any medium or format. 10 * - Adapt: Remix, transform, and build upon the material. 11 * 12 * Under the following terms: 13 * - Attribution: You must give appropriate credit, provide a link to the license, 14 * and indicate if changes were made. You may do so in any reasonable manner, 15 * but not in any way that suggests the licensor endorses you or your use. 16 * - NonCommercial: You may not use the material for commercial purposes. 17 * 18 * No additional restrictions: You may not apply legal terms or technological 19 * measures that legally restrict others from doing anything the license permits. 20 * 21 * Full license text can be found at: 22 * https://creativecommons.org/licenses/by-nc/4.0/ 23 * 24 * Third-party libraries and components included in this software are licensed 25 * under their respective licenses, as listed in the LICENSE file. 26 * 27 * For commercial use or licensing inquiries, please contact guybartell.ext@gmail.com. 28 */ 29 30 // DISPLAY (M4 core) 31 32#include "Arduino_GigaDisplay_GFX.h" // Display graphics 33#include "Arduino_GigaDisplayTouch.h" // Touch screen functionality 34#include <RPC.h> 35 36GigaDisplay_GFX display; 37Arduino_GigaDisplayTouch touchDetector; 38 39#define WHITE 0xffff 40#define BLACK 0x0000 41#define CYAN 0x2c70 42#define GREY 0x7430 43#define DISPLAY_HEIGHT 480 44#define DISPLAY_WIDTH 800 45 46unsigned long lastTouchHandler = 0; // Used for non-blocking delay 47unsigned long lastWaveformDraw = 0; // Variable non-blocking delay defined by step size (for drawWaveform) 48 49int step_size = 1; 50int threshold = 20; // time in milliseconds for button threshold 51int wavThreshold; // time in milliseconds for button threshold 52// bool on = 1; // to keep track of on/off state for on/off switch 53 54int downsample_factor = 512 / 64; // Used for rendering the wavetable button waveforms 55 56const float BIT_SCALER = 4095 / 255; // Used for scaling between 12Bit and 8Bit look-up tables(LUTs) for loading waveform and writing to LUT 57 58// Constants for button dimensions 59const int BUTTON_HEIGHT = 32; 60const int BUTTON_WIDTH = 64; 61 62// Coordinates for buttons and display 63const int LEFT_BOUNDARY = 144; 64const int WAVEFORM_DISPLAY_X = 112; 65const int BOTTOM_BAR_X = 40; 66const int TOP_BAR_X = 408; 67 68const int RND_X = BOTTOM_BAR_X; 69const int RND_Y = 499; 70const int CLR_X = TOP_BAR_X; 71const int CLR_Y = 38; 72 73const int ON_X = TOP_BAR_X; 74const int ON_Y = LEFT_BOUNDARY; 75const int VOL_X = TOP_BAR_X; 76const int VOL_Y = 254; 77const int DRAW_MODE_X = BOTTOM_BAR_X; 78const int DRAW_MODE_Y = 38; 79const int LEFT_BAR_X = 335; 80const int LEFT_BAR_Y = 38; 81const int RND_MODE_X = BOTTOM_BAR_X; 82const int RND_MODE_Y = 588; 83 84// Co-ordinates for envelope display 85const int ATTACK_X = 333; 86const int ATTACK_Y = 254; 87const int DECAY_X = ATTACK_X - 74; 88const int DECAY_Y = ATTACK_Y; 89const int SUSTAIN_X = DECAY_X - 74; 90const int SUSTAIN_Y = ATTACK_Y; 91const int RELEASE_X = SUSTAIN_X - 74; 92const int RELEASE_Y = ATTACK_Y; 93const int ATTACK_TEXT_X = ATTACK_X - 5; 94const int ATTACK_TEXT_Y = 143; 95const int DECAY_TEXT_X = ATTACK_TEXT_X - 74; 96const int DECAY_TEXT_Y = ATTACK_TEXT_Y; 97const int SUSTAIN_TEXT_X = DECAY_TEXT_X - 74; 98const int SUSTAIN_TEXT_Y = ATTACK_TEXT_Y; 99const int RELEASE_TEXT_X = SUSTAIN_TEXT_X - 74; 100const int RELEASE_TEXT_Y = ATTACK_TEXT_Y; 101const int VOL_TEXT_X = ATTACK_TEXT_X + 74; 102const int VOL_TEXT_Y = ATTACK_TEXT_Y; 103 104// Screen buttons co-ordinates 105const int WAV_X = 334; 106const int WAV_Y = 700; 107const int ENV_X = 260; 108const int ENV_Y = 700; 109const int MIDI_X = 186; 110const int MIDI_Y = 700; 111 112// Velocity sensitivity button co-ordinates 113const int VEL_X = WAVEFORM_DISPLAY_X; 114const int VEL_Y = 700; 115 116// Panic button co-ordinates 117const int PANIC_X = 186; 118const int PANIC_Y = SUSTAIN_Y; 119 120const int LUT_DISPLAY_BIT_DEPTH = 256; 121 122// ADSR ranges 123const float ATTACK_RANGE = 1.0; 124const float DECAY_RANGE = 1.0; 125const float SUSTAIN_RANGE = 1.0; 126const float RELEASE_RANGE = 1.0; 127 128// For updating M7 129float attack = 0.0025; // seconds 130float decay = 0.0025; // seconds 131float sustain = 1.0; // sustain level (0.0 to 1.0) 132float release = 0.0025; // seconds 133 134// For updating ADSR sliders 135int attackVal = 0; 136int decayVal = 0; 137int sustainVal = 400; 138int releaseVal = 0; 139 140// For updating pitch bend and midi channel in sliders & M7 141int pbRangeSlider = 50; 142int pbRange = 2; 143int midiChannelSlider = 0; 144int midiChannel = 0; 145 146// Toggle button handlers, not currently implemented 147// bool on = 1; 148// bool onOld = 0; 149// bool onState = 0; 150// bool onStateOld = 0; 151 152// For handling draw/erase button 153bool draw = 1; 154bool drawOld = 0; 155bool drawState = 0; 156bool drawStateOld = 0; 157bool drawMode = 1; // Controls whether drawing adds (1) or clears (0) waveform 158 159// For handling curve/block random generation 160bool rndMode = 0; 161bool rndModeOld = 1; 162bool rndModeState = 0; 163bool rndModeStateOld = 0; 164 165// For handling envelope button 166bool env = 0; 167bool envOld = 1; 168bool envState = 0; 169bool envStateOld = 0; 170 171// For handling wav button 172bool wav = 1; 173bool wavOld = 0; 174bool wavState = 0; 175bool wavStateOld = 0; 176 177// For handling velocity sensitivity button (not currently implemented) 178bool velocityMode = 1; 179bool velocityModeOld = 0; 180bool velocityModeState = 0; 181bool velocityModeStateOld = 0; 182 183int velocitySend = 1; 184 185// MIDI panic (all voices off) to update M7 186bool panic = 0; 187bool panicOld = 0; 188bool panicState = 0; 189bool panicStateOld = 0; 190 191// For handling MIDI button 192bool midi = 0; 193bool midiOld = 1; 194bool midiState = 0; 195bool midiStateOld = 0; 196 197// Blocks touchscreen while wav, env & midi screens are loading 198int screenLoading = 0; 199 200// For handling clear button 201bool clr = 0; 202bool clrOld = 0; 203 204// For handling random button 205bool rnd = 0; 206bool rndOld = 0; 207 208// array of random step sizes 209const char* steps[8] = { "512", "256", "128", "64", "32", "16", "8", "4" }; 210const float steps_values[8] = { 1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0 }; 211 212// array of MIDI channel values 213const char* CH_steps[17] = { "ALL", " 1", " 2", " 3", " 4", " 5", " 6", " 7", " 8", " 9", " 10", " 11", " 12", " 13", " 14", " 15", " 16" }; 214 215// array of pitch bend values 216const char* PB_steps[25] = { " 0", " +1", " +2", " +3", " +4", " +5", " +6", " +7", " +8", " +9", "+10", "+11", "+12", "+13", "+14", "+15", "+16", "+17", "+18", "+19", "+20", "+21", "+22", "+23", "+24" }; 217 218// array of LUT names 219const char* wavs[4] = { "sine_wave", "square_wave", "sawtooth_wave", "organ_pipe_wave" }; 220 221// number of elements in wavs array, for generating wavetable buttons 222static size_t wavs_size = sizeof(wavs) / sizeof(wavs[0]); 223 224// number of elements in steps array, for step size selector 225static size_t steps_size = sizeof(steps) / sizeof(steps[0]); 226 227int selectedButton = 0; // Tracks the currently selected wavetable button 228int selectedButtonOld = 0; // Tracks the previously selected wavetable button 229int selectedStep = 0; // Tracks the currently selected step button 230 231// Size of LUT 232static size_t lut_size = 512; 233 234// Function to map floats 235float fmap(float x, float in_min, float in_max, float out_min, float out_max) { 236 return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 237} 238// Function to map to common logarithm scale 239float logmap(float x, float in_min, float in_max, float out_min, float out_max) { 240 // Normalize input to 0.0 – 1.0 241 float normalized = (x - in_min) / (in_max - in_min); 242 normalized = max(0.0, min(1.0, normalized)); // Clamp between 0 and 1 243 244 // Logarithmic transformation 245 float logNormalized = log10(1 + 9 * normalized); 246 247 // Map back to output range 248 return (logNormalized * (out_max - out_min)) + out_min; 249} 250 251 252void gigaTouchHandler(uint8_t contacts, GDTpoint_t* points) { 253 if (!screenLoading) { // Only when screens have finished loading 254 // Reset all momentary buttons to 0 (no press) 255 clr = 0; 256 rnd = 0; 257 258 wavThreshold = calculateThreshold(step_size); 259 260 for (int i = 0; i < contacts; i++) { 261 buttonHandler(points[i].x, points[i].y); 262 volumeSlider(points[i].x, points[i].y); 263 randomStepSize(points[i].x, points[i].y); 264 // Only call drawWaveform if the global threshold has passed 265 if (millis() - lastWaveformDraw > wavThreshold) { 266 drawWaveform(points[i].x, points[i].y, step_size); 267 lastWaveformDraw = millis(); // Update the last execution time 268 } 269 ADSRSlider(points[i].x, points[i].y); 270 midiSlider(points[i].x, points[i].y); 271 } 272 lastTouchHandler = millis(); 273 } 274} 275 276// Non-blocking delay threshold dynamically updated from step-size 277int calculateThreshold(int stepSize) { 278 // Map step sizes to thresholds 279 if (stepSize <= 128 && stepSize >= 64) { 280 return 80; 281 } else if (stepSize <= 32 && stepSize >= 16) { 282 return 60; 283 } else if (stepSize < 16 && stepSize >= 8) { 284 return 40; 285 } else if (stepSize == 4) { 286 return 20; 287 } else if (stepSize == 2) { 288 return 5; 289 } else if (stepSize == 1) { 290 return 1; 291 } else { 292 return 1; // Default threshold 293 } 294} 295 296// Handles button presses 297void buttonHandler(int x, int y) { 298 // Normalise x & y to between 0.0 & 1.0 299 float normalisedWav_X = float(x - WAV_X) / BUTTON_HEIGHT; 300 float normalisedWav_Y = float(y - WAV_Y) / BUTTON_WIDTH; 301 float normalisedEnv_X = float(x - ENV_X) / BUTTON_HEIGHT; 302 float normalisedEnv_Y = float(y - ENV_Y) / BUTTON_WIDTH; 303 float normalisedMidi_X = float(x - MIDI_X) / BUTTON_HEIGHT; 304 float normalisedMidi_Y = float(y - MIDI_Y) / BUTTON_WIDTH; 305 // float normalisedOn_X = float(x - ON_X) / BUTTON_HEIGHT; 306 // float normalisedOn_Y = float(y - ON_Y) / BUTTON_WIDTH; 307 // float normalisedVelocity_X = float(x - VEL_X) / (BUTTON_HEIGHT); 308 // float normalisedVelocity_Y = float(y - VEL_Y) / BUTTON_WIDTH; 309 310 // Mark wav button as pressed if within bounds 311 if (normalisedWav_X >= 0.0 && normalisedWav_X <= 1.0 && normalisedWav_Y >= 0.0 && normalisedWav_Y <= 1.0) { 312 wav = 1; 313 } 314 // Mark envelope button as pressed if within bounds 315 if (normalisedEnv_X >= 0.0 && normalisedEnv_X <= 1.0 && normalisedEnv_Y >= 0.0 && normalisedEnv_Y <= 1.0) { 316 env = 1; 317 } 318 319 // Mark MIDI button as pressed if within bounds 320 if (normalisedMidi_X >= 0.0 && normalisedMidi_X <= 1.0 && normalisedMidi_Y >= 0.0 && normalisedMidi_Y <= 1.0) { 321 midi = 1; 322 } 323 324 // Mark on button as pressed if within bounds 325 // if (normalisedOn_X >= 0.0 && normalisedOn_X <= 1.0 && normalisedOn_Y >= 0.0 && normalisedOn_Y <= 1.0) { 326 // onState = 1; 327 // } 328 329 330 // Mark velocity mode button as pressed if within bounds 331 // if (normalisedVelocity_X >= 0.0 && normalisedVelocity_X <= 1.0 && normalisedVelocity_Y >= 0.0 && normalisedVelocity_Y <= 1.0) { 332 // velocityModeState = 1; 333 // } 334 335 if (wav) { // Only when waveform screen is displayed 336 337 // Normalise x & y to between 0.0 & 1.0 338 float normalisedDraw_X = float(x) / (BUTTON_HEIGHT + 52); 339 float normalisedDraw_Y = float(y - DRAW_MODE_Y) / BUTTON_WIDTH; 340 float normalisedRandomMode_X = float(x) / (BUTTON_HEIGHT + 52); 341 float normalisedRandomMode_Y = float(y - RND_MODE_Y) / BUTTON_WIDTH; 342 343 float normalisedClr_X = float(x - CLR_X) / BUTTON_HEIGHT; 344 float normalisedClr_Y = float(y - CLR_Y) / BUTTON_WIDTH; 345 float normalisedRnd_X = float(x) / 72; 346 float normalisedRnd_Y = float(y - RND_Y) / BUTTON_WIDTH; 347 348 // Mark clear button as pressed if within bounds 349 if (normalisedClr_X >= 0.0 && normalisedClr_X <= 1.0 && normalisedClr_Y >= 0.0 && normalisedClr_Y <= 1.0) { 350 clr = 1; 351 } 352 353 // Mark random button as pressed if within bounds 354 if (normalisedRnd_X >= 0.0 && normalisedRnd_X <= 1.0 && normalisedRnd_Y >= 0.0 && normalisedRnd_Y <= 1.0) { 355 rnd = 1; 356 } 357 358 // Mark draw mode button as pressed if within bounds 359 if (normalisedDraw_X >= 0.0 && normalisedDraw_X <= 1.0 && normalisedDraw_Y >= 0.0 && normalisedDraw_Y <= 1.0) { 360 drawState = 1; 361 } 362 363 // Mark random mode button as pressed if within bounds 364 if (normalisedRandomMode_X >= 0.0 && normalisedRandomMode_X <= 1.0 && normalisedRandomMode_Y >= 0.0 && normalisedRandomMode_Y <= 1.0) { 365 rndModeState = 1; 366 } 367 368 // Select waveform select button as pressed if within bounds 369 for (int i = 0; i < wavs_size; i++) { 370 if (x >= 0 && x <= 74 && y >= (LEFT_BOUNDARY + (89 * i)) && y <= (LEFT_BOUNDARY + (89 * i) + BUTTON_WIDTH + 2)) { 371 if (i != selectedButton) { 372 // Set new LUT and load it into the temporary LUT 373 selectedButton = i; 374 } 375 } 376 } 377 } 378 379 if (midi){ // Only when MIDI screen is displayed 380 float normalisedPanic_X = float(x - PANIC_X) / BUTTON_HEIGHT; 381 float normalisedPanic_Y = float(y - PANIC_Y) / BUTTON_WIDTH; 382 383 // Mark panic button as pressed if within bounds 384 if (normalisedPanic_X >= 0.0 && normalisedPanic_X <= 1.0 && normalisedPanic_Y >= 0.0 && normalisedPanic_Y <= 1.0) { 385 panic = 1; 386 } 387 } 388} 389 390// Select LUT from example LUTs in wavetable buttons 391void waveformSelection() { 392 if (selectedButton != selectedButtonOld && selectedButton != -1) { 393 screenLoading = 1; 394 RPC.call("loadLUT", selectedButton); // Load LUT from M7 395 // Redraw all buttons 396 generateWavetableButtons(); 397 // Update main waveform display 398 updateMainWaveformDisplay(); 399 } else if (selectedButton != selectedButtonOld && selectedButton == -1) { 400 generateWavetableButtons(); 401 } 402 selectedButtonOld = selectedButton; 403} 404 405 406// Handles buttons for step size for random & drawing functions 407void randomStepSize(int x, int y) { 408 for (int i = 0; i < steps_size; i++) { 409 if (x >= LEFT_BAR_X - (BUTTON_HEIGHT * i) && x <= (LEFT_BAR_X - (BUTTON_HEIGHT * i) + BUTTON_HEIGHT) && y >= LEFT_BAR_Y && y <= LEFT_BAR_Y + BUTTON_WIDTH && wav) { 410 if (i != selectedStep) { 411 selectedStep = i; 412 step_size = steps_values[i]; // Selects step size name 413 // Redraw all buttons 414 generateRandomStepButtons(); 415 } 416 } 417 } 418} 419 420// Draws 'random' button 421void drawRandom() { 422 if (rnd != rndOld) { 423 if (rnd == 1) { // momentary pressed 424 screenLoading = 1; 425 selectedButton = -1; 426 buttonOn(RND_X, RND_Y, 6, "RND"); 427 RPC.call("generateRandomArray", step_size); // Generate temp LUT in M7 428 generateWavetableButtons(); 429 updateMainWaveformDisplay(); 430 } else { // momentary released 431 buttonOff(RND_X, RND_Y, 6, "RND"); 432 } 433 // Update the old state of the momentary 434 rndOld = rnd; 435 } 436} 437 438// Draws 'clear' button 439void drawClear() { 440 if (clr != clrOld) { 441 if (clr == 1) { // momentary pressed 442 screenLoading = 1; 443 selectedButton = -1; 444 buttonOn(CLR_X, CLR_Y, 6, "CLR"); // Clear temp LUT in M7 445 RPC.call("clearArray"); 446 generateWavetableButtons(); 447 updateMainWaveformDisplay(); 448 } else { // momentary released 449 buttonOff(CLR_X, CLR_Y, 6, "CLR"); 450 } 451 // Update the old state of the momentary 452 clrOld = clr; 453 } 454} 455 456// Draws on/off button, currently not implemented 457// void drawOn() { 458// if (onState != onStateOld) { 459// if (onState == 1) { // momentary pressed 460// on = !on; 461// } 462// // Update the old state of the momentary 463// onStateOld = onState; 464// } 465// if (on != onOld) { 466// if (on == 1) { 467// buttonOn(ON_X, ON_Y, 14, "ON"); 468// // Starts DAC on 'ON' button press 469// // RPC.call("startDAC", 1); 470// // Serial.println("toggle on"); 471// } else if (on == 0) { 472// buttonOff(ON_X, ON_Y, 6, "OFF"); 473// // Starts DAC on 'OFF' button press 474// // RPC.call("startDAC", 0); 475// // Serial.println("toggle off"); 476// } 477// onOld = on; 478// } 479// } 480 481// Draws 'panic' button 482void drawPanic() { 483 if (panicState != panicStateOld) { 484 if (panicState == 1) { // momentary pressed 485 panic = !panic; 486 } 487 // Update the old state of the momentary 488 panicStateOld = panicState; 489 } 490 if (panic != panicOld) { 491 if (panic == 1) { 492 buttonOn(PANIC_X, PANIC_Y, 6, " ! "); 493 RPC.call("panic"); // Executes all voices off in M7 494 } else if (panic == 0) { 495 buttonOff(PANIC_X, PANIC_Y, 6, " ! "); 496 } 497 panicOld = panic; 498 } 499} 500 501// Generates and updates the envelope display 502void generateEnvelopeDisplay() { 503 504 display.fillRect(0, LEFT_BOUNDARY - 1, LEFT_BOUNDARY + LUT_DISPLAY_BIT_DEPTH, lut_size + 2, WHITE); // Clear previous display 505 display.fillRect(0, 0, DISPLAY_HEIGHT, 142, WHITE); // Clear left bar 506 507 display.drawRect(ATTACK_X, ATTACK_Y, BUTTON_HEIGHT + 2, 402, CYAN); 508 display.fillRect(ATTACK_X + 1, ATTACK_Y, BUTTON_HEIGHT, attackVal, CYAN); 509 display.drawRect(DECAY_X, DECAY_Y, BUTTON_HEIGHT + 2, 402, CYAN); 510 display.fillRect(DECAY_X + 1, DECAY_Y, BUTTON_HEIGHT, decayVal, CYAN); 511 display.drawRect(SUSTAIN_X, SUSTAIN_Y, BUTTON_HEIGHT + 2, 402, CYAN); 512 display.fillRect(SUSTAIN_X + 1, SUSTAIN_Y, BUTTON_HEIGHT, sustainVal, CYAN); 513 display.drawRect(RELEASE_X, RELEASE_Y, BUTTON_HEIGHT + 2, 402, CYAN); 514 display.fillRect(RELEASE_X + 1, RELEASE_Y, BUTTON_HEIGHT, releaseVal, CYAN); 515 labelText(ATTACK_TEXT_X, ATTACK_TEXT_Y, "ATT"); 516 labelText(DECAY_TEXT_X, DECAY_TEXT_Y, "DEC"); 517 labelText(SUSTAIN_TEXT_X, SUSTAIN_TEXT_Y, "SUS"); 518 labelText(RELEASE_TEXT_X, RELEASE_TEXT_Y, "REL"); 519 screenLoading = 0; // Update screen loading state once everything has loaded 520} 521 522// Generates and updates the MIDI display 523void generateMIDIDisplay() { 524 525 display.fillRect(0, LEFT_BOUNDARY - 1, LEFT_BOUNDARY + LUT_DISPLAY_BIT_DEPTH, lut_size + 2, WHITE); // Clear previous display 526 display.fillRect(0, 0, DISPLAY_HEIGHT, 142, WHITE); // Clear left bar 527 display.drawRect(ATTACK_X, ATTACK_Y, BUTTON_HEIGHT + 2, 402, CYAN); 528 display.fillRect(ATTACK_X + 1, ATTACK_Y, BUTTON_HEIGHT, pbRangeSlider, CYAN); 529 display.drawRect(DECAY_X, DECAY_Y, BUTTON_HEIGHT + 2, 402, CYAN); 530 display.fillRect(DECAY_X + 1, DECAY_Y, BUTTON_HEIGHT, midiChannelSlider, CYAN); 531 valuesText(ATTACK_TEXT_X, ATTACK_TEXT_Y + 52, pbRange, PB_steps); 532 valuesText(DECAY_TEXT_X, DECAY_TEXT_Y + 52, midiChannel, CH_steps); 533 labelText(ATTACK_TEXT_X, ATTACK_TEXT_Y, "PB:"); 534 labelText(DECAY_TEXT_X, DECAY_TEXT_Y, "CH:"); 535 labelText(SUSTAIN_TEXT_X, SUSTAIN_TEXT_Y, "PANIC"); 536 buttonOff(PANIC_X, PANIC_Y, 6, " ! "); 537 screenLoading = 0; // Update screen loading state once everything has loaded 538} 539 540// Draws wav screen select button 541void drawWav() { 542 if (wavState != wavStateOld) { 543 if (wavState == 1) { // momentary pressed 544 wav = !wav; 545 } 546 // Update the old state of the momentary 547 wavStateOld = wavState; 548 } 549 if (wav != wavOld) { 550 if (wav) { 551 screenLoading = 1; 552 buttonOn(WAV_X, WAV_Y, 6, "WAV"); 553 env = 0; 554 midi = 0; 555 drawEnv(); 556 drawMidi(); 557 generateRandomStepButtons(); 558 generateWavetableButtons(); 559 redrawDrawMode(); 560 display.fillRect(RND_X+16, RND_Y+64, 1, 25, CYAN); 561 redrawRandomMode(); 562 buttonOff(RND_X, RND_Y, 6, "RND"); 563 buttonOff(CLR_X, CLR_Y, 6, "CLR"); 564 updateMainWaveformDisplay(); 565 } else if (!wav) { 566 buttonOff(WAV_X, WAV_Y, 6, "WAV"); 567 } 568 wavOld = wav; 569 } 570} 571 572// Draws envelope screen select button 573void drawEnv() { 574 if (envState != envStateOld) { 575 if (envState == 1) { // momentary pressed 576 env = !env; 577 } 578 // Update the old state of the momentary 579 envStateOld = envState; 580 } 581 if (env != envOld) { 582 if (env == 1) { 583 screenLoading = 1; 584 buttonOn(ENV_X, ENV_Y, 6, "ENV"); 585 wav = 0; 586 midi = 0; 587 generateEnvelopeDisplay(); 588 } else if (env == 0) { 589 buttonOff(ENV_X, ENV_Y, 6, "ENV"); 590 } 591 envOld = env; 592 } 593} 594 595// Draws MIDI screen select button 596void drawMidi() { 597 if (midiState != midiStateOld) { 598 if (midiState == 1) { // momentary pressed 599 midi = !midi; 600 } 601 // Update the old state of the momentary 602 midiStateOld = midiState; 603 } 604 if (midi != midiOld) { 605 if (midi == 1) { 606 screenLoading = 1; 607 buttonOn(MIDI_X, MIDI_Y, 6, "MID"); 608 wav = 0; 609 env = 0; 610 generateMIDIDisplay(); 611 } else if (midi == 0) { 612 buttonOff(MIDI_X, MIDI_Y, 6, "MID"); 613 } 614 midiOld = midi; 615 } 616} 617// Draws velocity sensitivity mode button, currently not implemented 618// void drawVelocityMode() { 619// if (velocityModeState != velocityModeStateOld) { 620// if (velocityModeState == 1) { // momentary pressed 621// velocityMode = !velocityMode; 622// } 623// // Update the old state of the momentary 624// velocityModeStateOld = velocityModeState; 625// } 626// if (velocityMode != velocityModeOld) { 627// if (velocityMode == 1) { 628// buttonOn(VEL_X, VEL_Y, 6, "VEL"); 629// velocitySend = 1; 630 631// } else if (velocityMode == 0) { 632// buttonOff(VEL_X, VEL_Y, 6, "VEL"); 633// velocitySend = 0; 634// } 635// RPC.call("setVelocityMode", velocitySend); 636// velocityModeOld = velocityMode; 637// } 638// } 639 640// Draws draw/erase mode button 641void drawDrawMode() { 642 if (drawState != drawStateOld) { 643 if (drawState == 1) { // momentary pressed 644 draw = !draw; 645 } 646 // Update the old state of the momentary 647 drawStateOld = drawState; 648 } 649 if (draw != drawOld) { 650 if (draw == 1) { 651 buttonOn(DRAW_MODE_X, DRAW_MODE_Y, 6, "ADD"); 652 drawMode = 1; 653 654 } else if (draw == 0) { 655 buttonOff(DRAW_MODE_X, DRAW_MODE_Y, 6, "SUB"); 656 drawMode = 0; 657 } 658 drawOld = draw; 659 } 660} 661 662void redrawDrawMode(){ 663 if (draw == 1) { 664 buttonOn(DRAW_MODE_X, DRAW_MODE_Y, 6, "ADD"); 665 } else if (draw == 0) { 666 buttonOff(DRAW_MODE_X, DRAW_MODE_Y, 6, "SUB"); 667 } 668 } 669 670void redrawRandomMode() { 671 if (rndMode == 1) { 672 buttonOn(RND_MODE_X, RND_MODE_Y, 6, "SQR"); 673 } else if (rndMode == 0) { 674 buttonOff(RND_MODE_X, RND_MODE_Y, 6, "CUR"); 675 } 676} 677 678// Draws random mode button 679void drawRandomMode() { 680 if (rndModeState != rndModeStateOld) { 681 if (rndModeState == 1) { // momentary pressed 682 rndMode = !rndMode; 683 } 684 // Update the old state of the momentary 685 rndModeStateOld = rndModeState; 686 } 687 if (rndMode != rndModeOld) { 688 if (rndMode == 1) { 689 buttonOn(RND_MODE_X, RND_MODE_Y, 6, "SQR"); 690 } else if (rndMode == 0) { 691 buttonOff(RND_MODE_X, RND_MODE_Y, 6, "CUR"); 692 } 693 694 RPC.call("setRandomMode", rndMode); 695 696 rndModeOld = rndMode; 697 } 698} 699 700// Handle touch within the main waveform area 701void drawWaveform(int x, int y, int step_size) { 702 int step_size_multiplier = step_size; // Controls width of bars drawn 703 704 if (x >= WAVEFORM_DISPLAY_X && x <= WAVEFORM_DISPLAY_X + LUT_DISPLAY_BIT_DEPTH && y >= LEFT_BOUNDARY && y <= (LEFT_BOUNDARY + lut_size) && wav) { 705 706 int lutIndex = (y - LEFT_BOUNDARY); // Map Y to LUT index 707 int blockIndex = lutIndex / step_size_multiplier; // Determine which block the LUT index belongs to 708 int blockStart = blockIndex * step_size_multiplier; // Calculate the start of the block 709 int blockEnd = blockStart + step_size_multiplier; // Calculate the end of the block 710 int newValue; 711 712 // Ensure block indices stay within bounds 713 if (blockStart >= 0 && blockEnd <= lut_size) { 714 if (drawMode) { 715 newValue = map(x, WAVEFORM_DISPLAY_X, WAVEFORM_DISPLAY_X + LUT_DISPLAY_BIT_DEPTH, 0, 255); // Map X to new LUT value 716 } else { 717 newValue = 0; 718 } 719 720 // Update LUT values for the entire block and redraw it 721 for (int i = blockStart; i < blockEnd; ++i) { 722 int upscaledNewValue = newValue * BIT_SCALER; 723 RPC.call("updateLUT", i, upscaledNewValue); 724 } 725 726 // Draw the block on the display 727 for (int i = blockStart; i < blockEnd; ++i) { 728 int displayY = i + 144; // Map LUT index back to Y-coordinate on display 729 display.fillRect(WAVEFORM_DISPLAY_X, displayY, newValue, 1, CYAN); // Filled part 730 display.fillRect(WAVEFORM_DISPLAY_X + newValue, displayY, 255 - newValue, 1, WHITE); // Remaining part 731 } 732 } 733 selectedButton = -1; 734 } 735} 736 737// Handle touch within the volume slider 738void volumeSlider(int x, int y) { 739 if (x >= VOL_X && x <= VOL_X + BUTTON_HEIGHT && y >= VOL_Y && y <= VOL_Y + 401) { 740 display.fillRect(VOL_X + 1, VOL_Y + 1, BUTTON_HEIGHT, y - VOL_Y + 1, CYAN); 741 display.fillRect(VOL_X + 1, y + 1, BUTTON_HEIGHT, VOL_Y + 401 - y, WHITE); 742 float volume = fmap(float(y), VOL_Y, VOL_Y + 402, 0.0, 1.0); 743 RPC.call("setVolume", volume); 744 } 745} 746 747// Handle touch within the adsr sliders 748void ADSRSlider(int x, int y) { 749 if (env == 1) { 750 if (x >= ATTACK_X && x <= ATTACK_X + BUTTON_HEIGHT && y >= ATTACK_Y && y <= ATTACK_Y + 401) { 751 attackVal = y - ATTACK_Y + 1; 752 display.fillRect(ATTACK_X + 1, ATTACK_Y + 1, BUTTON_HEIGHT, attackVal, CYAN); 753 display.fillRect(ATTACK_X + 1, y + 1, BUTTON_HEIGHT, 400 - attackVal, WHITE); 754 attack = logmap(float(attackVal), 0, 400, 0.0025, ATTACK_RANGE); 755 RPC.call("setAttack", attack); 756 } 757 758 if (x >= DECAY_X && x <= DECAY_X + BUTTON_HEIGHT && y >= DECAY_Y && y <= DECAY_Y + 401) { 759 decayVal = y - DECAY_Y + 1; 760 display.fillRect(DECAY_X + 1, DECAY_Y + 1, BUTTON_HEIGHT, decayVal, CYAN); 761 display.fillRect(DECAY_X + 1, y + 1, BUTTON_HEIGHT, 400 - decayVal, WHITE); 762 decay = logmap(float(decayVal), 0, 400, 0.0025, DECAY_RANGE); 763 RPC.call("setDecay", decay); 764 } 765 766 if (x >= SUSTAIN_X && x <= SUSTAIN_X + BUTTON_HEIGHT && y >= SUSTAIN_Y && y <= SUSTAIN_Y + 401) { 767 sustainVal = y - SUSTAIN_Y + 1; 768 display.fillRect(SUSTAIN_X + 1, SUSTAIN_Y + 1, BUTTON_HEIGHT, sustainVal, CYAN); 769 display.fillRect(SUSTAIN_X + 1, y + 1, BUTTON_HEIGHT, 400 - sustainVal, WHITE); 770 sustain = fmap(float(sustainVal), 0, 400, 0.0, SUSTAIN_RANGE); 771 RPC.call("setSustain", sustain); 772 } 773 774 if (x >= RELEASE_X && x <= RELEASE_X + BUTTON_HEIGHT && y >= RELEASE_Y && y <= RELEASE_Y + 401) { 775 releaseVal = y - RELEASE_Y + 1; 776 display.fillRect(RELEASE_X + 1, RELEASE_Y + 1, BUTTON_HEIGHT, releaseVal, CYAN); 777 display.fillRect(RELEASE_X + 1, y + 1, BUTTON_HEIGHT, 400 - releaseVal, WHITE); 778 release = logmap(float(releaseVal), 0, 400, 0.0025, RELEASE_RANGE); 779 RPC.call("setRelease", release); 780 } 781 } 782} 783 784// Handle touch within the MIDI sliders 785void midiSlider(int x, int y) { 786 if (midi == 1) { 787 if (x >= ATTACK_X && x <= ATTACK_X + BUTTON_HEIGHT && y >= ATTACK_Y && y <= ATTACK_Y + 401) { 788 pbRangeSlider = y - ATTACK_Y + 1; 789 display.fillRect(ATTACK_X + 1, ATTACK_Y + 1, BUTTON_HEIGHT, pbRangeSlider, CYAN); 790 display.fillRect(ATTACK_X + 1, y + 1, BUTTON_HEIGHT, 400 - pbRangeSlider, WHITE); 791 pbRange = map(pbRangeSlider, 0, 400, 0, 24); 792 valuesText(ATTACK_TEXT_X, ATTACK_TEXT_Y + 52, pbRange, PB_steps); 793 RPC.call("setPitchBend", pbRange); // Update range in M7 794 } 795 796 if (x >= DECAY_X && x <= DECAY_X + BUTTON_HEIGHT && y >= DECAY_Y && y <= DECAY_Y + 401) { 797 midiChannelSlider = y - DECAY_Y + 1; 798 display.fillRect(DECAY_X + 1, DECAY_Y + 1, BUTTON_HEIGHT, midiChannelSlider, CYAN); 799 display.fillRect(DECAY_X + 1, y + 1, BUTTON_HEIGHT, 400 - midiChannelSlider, WHITE); 800 midiChannel = map(midiChannelSlider, 0, 400, 0, 16); 801 valuesText(DECAY_TEXT_X, DECAY_TEXT_Y + 52, midiChannel, CH_steps); 802 RPC.call("setMidiChannel", midiChannel); // Update channel in M7 803 } 804 } 805} 806 807// Generates and updates wavetable buttons state (rnd & clr buttons also update this) 808void generateWavetableButtons() { 809 for (int i = 0; i < wavs_size; i++) { 810 // Determine colour for outline and wavetable button 811 int colour = (i == selectedButton) ? CYAN : GREY; 812 // Draw button outline 813 display.drawRect(BOTTOM_BAR_X, (LEFT_BOUNDARY + (89 * i)), BUTTON_HEIGHT + 2, BUTTON_WIDTH + 2, colour); 814 // Draw waveform inside button 815 for (int j = 0; j < 64; j++) { 816 int downsampledIndex = j * downsample_factor; 817 auto downsampled_value = RPC.call("getWavLutElement", i, downsampledIndex).as<int>(); 818 display.fillRect((BOTTOM_BAR_X + 1), ((LEFT_BOUNDARY + 1) + (89 * i)) + j, downsampled_value / BIT_SCALER / 8, 1, colour); 819 } 820 } 821} 822 823// Updates random step size buttons state 824void generateRandomStepButtons() { 825 for (int i = 0; i < steps_size; i++) { 826 // Determine colour for outline and wavetable button 827 int stepBoxColour = (i == selectedStep) ? CYAN : WHITE; 828 int stepTextColour = (i == selectedStep) ? WHITE : CYAN; 829 int xOffset = 5; 830 // Draw button outline 831 display.drawRect((LEFT_BAR_X - (BUTTON_HEIGHT * i)), LEFT_BAR_Y, BUTTON_HEIGHT, BUTTON_WIDTH, CYAN); 832 display.fillRect((LEFT_BAR_X - (BUTTON_HEIGHT * i) + 1), LEFT_BAR_Y + 1, 30, BUTTON_WIDTH - 2, stepBoxColour); 833 // Draw button text 834 display.setRotation(1); // rotate display so that text is oriented correctly 835 int xText = LEFT_BAR_Y; 836 int yText = 480 - (LEFT_BAR_X - (BUTTON_HEIGHT * i)) - BUTTON_HEIGHT; 837 display.setCursor((xText + xOffset), yText + 5); // text position 838 display.setTextSize(3); // adjust text size 839 display.setTextColor(stepTextColour); // set text colour 840 display.print(steps[i]); // print text 841 display.setRotation(0); // reset screen orientation 842 } 843} 844 845// Generates and updates the main waveform display on loading of LUT, waveform drawing, random & clear buttons 846void updateMainWaveformDisplay() { 847 display.drawRect(WAVEFORM_DISPLAY_X - 1, LEFT_BOUNDARY - 1, 257, 514, CYAN); 848 display.fillRect(WAVEFORM_DISPLAY_X, LEFT_BOUNDARY, 255, lut_size, WHITE); // Clear previous display 849 850 for (int i = 0; i < lut_size; i++) { 851 852 // Retrieve each element of temp_lut from the server 853 auto lut_value = RPC.call("getTempLutElement", i).as<int>(); 854 int downscaledLutValue = lut_value / BIT_SCALER; 855 display.fillRect(WAVEFORM_DISPLAY_X, LEFT_BOUNDARY + i, downscaledLutValue, 1, CYAN); // Draw waveform 856 857 if (i == lut_size - 1) { 858 screenLoading = 0; 859 } 860 } 861} 862// Used to display ADSR legends 863void labelText(int x, int y, String text) { 864 display.setRotation(1); // rotate display so that text is oriented correctly 865 int newX = y; 866 int newY = 480 - x - BUTTON_HEIGHT; 867 display.setCursor(newX, newY); // text position 868 display.setTextSize(3); // adjust text size 869 display.setTextColor(CYAN); // set text colour 870 display.print(text); // print text 871 display.setRotation(0); // reset screen orientation 872} 873 874// Used to display MIDI legends 875void valuesText(int x, int y, int val, const char* arrayName[]) { 876 display.fillRect(x, y, 32, 59, WHITE); 877 display.setRotation(1); // rotate display so that text is oriented correctly 878 int newX = y; 879 int newY = 480 - x - BUTTON_HEIGHT; 880 display.setCursor(newX, newY); // text position 881 display.setTextSize(3); // adjust text size 882 display.setTextColor(CYAN); // set text colour 883 display.print(arrayName[val]); // print text 884 display.setRotation(0); // reset screen orientation 885} 886 887// Generates an 'on' button at specified coordinates, text position offset and with button text 888void buttonOn(int x, int y, int xOffset, String buttonText) { 889 display.fillRect(x, y, BUTTON_HEIGHT, BUTTON_WIDTH, CYAN); 890 display.setRotation(1); // rotate display so that text is oriented correctly 891 int xText = y; 892 int yText = 480 - x - BUTTON_HEIGHT; 893 display.setCursor((xText + xOffset), yText + 5); // text position 894 display.setTextSize(3); // adjust text size 895 display.setTextColor(WHITE); // set text colour 896 display.print(buttonText); // print text 897 display.setRotation(0); // reset screen orientation 898} 899 900// Generates an 'off' button at specified coordinates, text position offset and with button text 901void buttonOff(int x, int y, int xOffset, String buttonText) { 902 display.fillRect(x, y, BUTTON_HEIGHT, BUTTON_WIDTH, WHITE); // clear previous 'ON' filled rectangle 903 display.drawRect(x, y, BUTTON_HEIGHT, BUTTON_WIDTH, CYAN); 904 display.setRotation(1); 905 int xText = y; 906 int yText = 480 - x - BUTTON_HEIGHT; 907 display.setCursor((xText + xOffset), yText + 5); 908 display.setTextSize(3); 909 display.setTextColor(CYAN); 910 display.print(buttonText); 911 display.setRotation(0); 912} 913 914void setup() { 915 sleep_manager_lock_deep_sleep(); // Ignore deepsleep mode in MBed core, without this deep sleep is enabled after a few minutes and midi input is delayed 916 917 // Initialize RPC communication 918 RPC.begin(); 919 920 // Initialize touchscreen 921 touchDetector.begin(); 922 touchDetector.onDetect(gigaTouchHandler); 923 924 // Begin drawing display 925 display.begin(); 926 display.fillScreen(WHITE); 927 928 // Draws waveform boundary box 929 display.drawRect(WAVEFORM_DISPLAY_X - 1, LEFT_BOUNDARY - 1, 257, 514, CYAN); 930 931 // Draws volume bar and sets volume level to max 932 display.drawRect(VOL_X, VOL_Y, BUTTON_HEIGHT + 2, 403, CYAN); 933 display.fillRect(VOL_X + 1, VOL_Y + 1, BUTTON_HEIGHT, 401, CYAN); 934 935 // Draws volume label 936 labelText(VOL_TEXT_X, VOL_TEXT_Y, "VOL"); 937 938 display.fillRect(RND_X+16, RND_Y+64, 1, 25, CYAN); // line joining rnd & rnd mode 939 940} 941 942void loop() { 943 944 if (millis() - lastTouchHandler > threshold) { 945 clr = 0; 946 rnd = 0; 947 panic = 0; 948 // onState = 0; 949 drawState = 0; 950 rndModeState = 0; 951 velocityModeState = 0; 952 } 953 954 delay(10); 955 drawRandom(); 956 drawClear(); 957 drawPanic(); 958 // drawOn(); 959 drawDrawMode(); 960 // drawVelocityMode(); 961 drawRandomMode(); 962 drawEnv(); 963 drawWav(); 964 drawMidi(); 965 waveformSelection(); 966}
Comments
Only logged in users can leave comments
Guy Bartell
Posted by
•Arduino_Genuino
6
0
Arduino GIGA Wavetable Synthesiser | Arduino Project Hub