diff --git a/sketch/sketch.ino b/sketch/sketch.ino index 69ee235..c525e2a 100644 --- a/sketch/sketch.ino +++ b/sketch/sketch.ino @@ -22,17 +22,24 @@ float ABS_VOL = 0.5f; float decay = 0.99998f; Adafruit_SSD1306 display(OLED_W, OLED_H, &Wire); // ----- Menu Functions - int sound = 0; //Default voice - String sounds[6] = {"Saw+Sine", "Sine", "Saw", "Triangle", "Square", "Noise"}; - int sound_count = sizeof(sounds)/sizeof(sounds[0]); +int sound = 0; //Default voice +String sounds[6] = { "Saw+Sine", "Sine", "Saw", "Triangle", "Square", "Noise" }; +int sound_count = sizeof(sounds) / sizeof(sounds[0]); - // Modes - int mode = 0; - String mode_names[4] = {"Vol", "Sou", "Oct", "Env"}; - int mode_count = sizeof(mode_names)/sizeof(mode_names[0]); +// Modes +int mode = 0; +String mode_names[7] = { "Vol", "Sou", "Oct", "Env", "Arp", "ArpΔ", "Arp"}; +int mode_count = sizeof(mode_names) / sizeof(mode_names[0]); - // Octave - int octave = 2; +// Octave +int octave = 2; + +// Arpeggio +//int arp_pattern[5] = {1 ,3 ,5 ,3 ,1 }; +int arp_pattern[5] = {0 ,4 ,7 ,3 ,1 }; +int arpeggio = 1; // 0 = Off, 1 = +int arp_notes = 3; // Now many notes to arpeggiate +int arp_delay = 150; // 300 ms // ---- Audio ---- @@ -42,14 +49,14 @@ constexpr uint8_t CHANNELS = 1; constexpr uint8_t BITS = 16; // ----- Reverb ----- -constexpr int reverb_time = 200; // 200 ms. -constexpr int reverb_samples = (int)(reverb_time/1000*SAMPLE_RATE); +constexpr int reverb_time = 200; // 200 ms. +constexpr int reverb_samples = (int)(reverb_time / 1000 * SAMPLE_RATE); float reverbqueue[reverb_samples] = {}; float reverb_fade = 0.3f; // ---- Matrix ---- -const int X_PINS[4] = {8, 9, 10, 7}; -const int Y_PINS[4] = {19, 20, 21, 22}; +const int X_PINS[4] = { 8, 9, 10, 7 }; +const int Y_PINS[4] = { 19, 20, 21, 22 }; // ---- Synth ---- constexpr int MAX_VOICES = 200; @@ -58,10 +65,10 @@ constexpr int MAX_VOICES = 200; NOTES ========================================================= */ -const char* noteNames[12] = { - "C","C#","D","D#", - "E","F","F#","G", - "G#","A","A#","B" +const char *noteNames[12] = { + "C", "C#", "D", "D#", + "E", "F", "F#", "G", + "G#", "A", "A#", "B" }; float noteFreq[12]; @@ -80,10 +87,13 @@ void initNotes() { struct Voice { bool active = false; float freq = 0; + float startFreq = 0; float phase1 = 0; float phase2 = 0; float env = 0; float amp = 0; + long arp_time = 0; + int arp_position = 0; }; Voice voices[MAX_VOICES]; @@ -92,8 +102,11 @@ void noteOn(float freq) { for (auto &v : voices) { if (!v.active) { v.active = true; - v.freq = freq*(funcKey ? 1.25f : 1.0f); + v.freq = freq; + v.startFreq = freq; v.env = VOL; + v.arp_time = millis(); + v.arp_position = 0; v.phase1 = 0.5f; v.phase2 = 0.5f; return; @@ -103,7 +116,7 @@ void noteOn(float freq) { void noteOff(float freq) { for (auto &v : voices) { - if (v.active && fabs(v.freq - freq) < 0.01f) { + if (v.active && fabs(v.startFreq - freq) < 0.01f) { v.active = false; } } @@ -112,51 +125,56 @@ void noteOff(float freq) { float sineWave(float phase) { // Convert phase (0-1) to radians (0-2π) float radians = phase * 2.0f * 3.14156f; - + // Generate sine value (-1 to 1) float sineValue = sin(radians); - + // Map from (-1 to 1) to (0 to 1) float output = (sineValue + 1.0f) / 2.0f; - + return output; } -float handleReverb(float val){ - for(int i = 0; i= 4) { @@ -177,8 +197,12 @@ public: return frame[index++]; } - int peek() override { return -1; } - size_t write(uint8_t) override { return 0; } + int peek() override { + return -1; + } + size_t write(uint8_t) override { + return 0; + } private: void generate() { @@ -188,11 +212,18 @@ private: if (!v.active) continue; float osc = 0.0f; + + if (arpeggio>0 && (millis()-v.arp_time)>arp_delay){ + v.arp_time = millis(); + v.arp_position++; + v.arp_position = v.arp_position % arp_notes; + v.freq = transposeSemitones(v.startFreq, arp_pattern[v.arp_position]); + } // Defined voices - switch(sound){ + switch (sound) { // Saw+Sine case 0: - osc = (sineWave(v.phase1)+sineWave(v.phase2)+wavetableInt[(int)floor(1024.0f*v.phase2)])/3.0f; + osc = (sineWave(v.phase1) + sineWave(v.phase2) + wavetableInt[(int)floor(1024.0f * v.phase2)]) / 3.0f; break; // Sine Wave case 1: @@ -204,20 +235,20 @@ private: break; // Triangle Wave case 3: - osc = abs((v.phase1*2)-1.0f); + osc = abs((v.phase1 * 2) - 1.0f); break; // Square Wave case 4: - osc = (v.phase1>0.5f); + osc = (v.phase1 > 0.5f); break; - // Noise - case 5: - osc = sineWave(v.phase1)+(random(-1000,1000)/2000.0f); + // Noise + case 5: + osc = sineWave(v.phase1) + (random(-1000, 1000) / 2000.0f); break; } - + v.phase1 += v.freq / SAMPLE_RATE; - v.phase2 += (v.freq *1.0f * (1.25f + detune)) / SAMPLE_RATE; + v.phase2 += (v.freq * 1.0f * (1.25f + detune)) / SAMPLE_RATE; //v.phase2 += (v.freq *1.25f * (1.0f + detune)) / SAMPLE_RATE; if (v.phase1 >= 1) v.phase1 -= 1; if (v.phase2 >= 1) v.phase2 -= 1; @@ -228,10 +259,13 @@ private: mix += osc * v.amp; } + // Handle reverb + mix = (handleReverb(mix) + mix) / 2.0f; + lp += cutoff * (mix - lp); float out = lp * 0.4f; - //out = (handleReverb(out)+out)/2.0f; + int r = (delayIndex - 480 + DELAY_SAMPLES) % DELAY_SAMPLES; float ch = out + delayL[r] * 0.6f; @@ -256,7 +290,7 @@ private: ========================================================= */ I2SStream i2s; -JunoStream synth; +SynthStream synth; StreamCopy copier(i2s, synth); /* ========================================================= @@ -269,7 +303,7 @@ uint16_t scanMatrix() { for (int x = 0; x < 4; x++) { digitalWrite(X_PINS[x], HIGH); - delayMicroseconds(5); + delayMicroseconds(10); for (int y = 0; y < 4; y++) { if (digitalRead(Y_PINS[y])) { @@ -288,61 +322,62 @@ uint16_t keys = 0; uint16_t changed = keys ^ lastKeys; // Handle key scanning and menus -void handleInputs(){ +void handleInputs() { lastKeys = keys; keys = scanMatrix(); changed = keys ^ lastKeys; for (int i = 0; i < 16; i++) { if (changed & (1 << i)) { if (i < 12) { - if ((keys & (1 << i))){ + if ((keys & (1 << i))) { noteOn(noteFreq[i]); - }else{ + } else { noteOff(noteFreq[i]); } } else { funcKey = (keys & (1 << 12)); // If Enter key pressed - if(keys & (1 << 14)){ - mode = mode+1; + if (keys & (1 << 14)) { + mode = mode + 1; mode = mode % mode_count; } // If Left key is pressed - if(keys & (1 << 13)){ - switch(mode){ + if (keys & (1 << 13)) { + switch (mode) { case 0: // Volume - if(VOL > 0.1f){ VOL-=0.1f; } break; - case 1: // Sound - sound = sound - 1; - if( sound < 0 ){sound = sound_count-1;} + if (VOL > 0.1f) { VOL -= 0.1f; } break; - case 2: // Octave - if(octave>1){ octave = octave-1;} + case 1: // Sound + sound = sound - 1; + if (sound < 0) { sound = sound_count - 1; } + break; + case 2: // Octave + if (octave > 1) { octave = octave - 1; } initNotes(); break; - case 3: // Envelope - // TODO + case 3: // Envelope + // TODO break; } - + // If Right key is pressed - }else if((keys & (1 << 15))){ - switch(mode){ - case 0: // Volume - if(VOL<1.0f){VOL+=0.1f;} - break; - case 1: // Sound - sound = sound + 1; - sound = sound % sound_count; - break; - case 2: // Octave - if(octave<8){octave = octave + 1;} - initNotes(); - break; - case 3: // Envelope - break; - } + } else if ((keys & (1 << 15))) { + switch (mode) { + case 0: // Volume + if (VOL < 1.0f) { VOL += 0.1f; } + break; + case 1: // Sound + sound = sound + 1; + sound = sound % sound_count; + break; + case 2: // Octave + if (octave < 8) { octave = octave + 1; } + initNotes(); + break; + case 3: // Envelope + break; } + } //menuButton(i-12); } } @@ -376,18 +411,18 @@ void drawPads(uint16_t keys) { } void drawScope() { - int mid = 45; // middle of scope on Y position - int height = 20; + int mid = 45; // middle of scope on Y position + int height = 20; float maxVal = 0.0f; float threshold = 0.002f; float scale = 1.0f; for (int x = 0; x < 127; x++) { int i1 = (synth.scopeIndex + x) & 127; - if(synth.scope[i1]>threshold && synth.scope[i1] > maxVal){ + if (synth.scope[i1] > threshold && synth.scope[i1] > maxVal) { maxVal = synth.scope[i1]; } } - scale = 1.0f/maxVal; + scale = 1.0f / maxVal; for (int x = 0; x < 127; x++) { int i1 = (synth.scopeIndex + x) & 127; int i2 = (i1 + 1) & 127; @@ -396,8 +431,7 @@ void drawScope() { mid - synth.scope[i1] * scale * height, x + 1, mid - synth.scope[i2] * scale * height, - SSD1306_WHITE - ); + SSD1306_WHITE); } } @@ -414,26 +448,24 @@ void drawVoices() { } void drawVolumebar() { - display.fillRect(0,60,(128*VOL), 64, SSD1306_WHITE); + display.fillRect(0, 60, (128 * VOL), 64, SSD1306_WHITE); } // Draws the current Synthesizer sound preset -void drawInfo(){ +void drawInfo() { // Show current Sound preset display.setTextSize(1); display.setTextColor(SSD1306_WHITE); - display.setCursor(0,6); + display.setCursor(0, 6); display.print(sounds[sound]); // Show current menu mode - display.setCursor(64,6); + display.setCursor(64, 6); display.print("Set:"); - display.setCursor(100,6); + display.setCursor(100, 6); display.print(mode_names[mode]); - display.setCursor(90,18); - display.print("Oct:"+String(octave)); - - + display.setCursor(90, 18); + display.print("Oct:" + String(octave)); } void updateOLED(uint16_t keys) { @@ -455,12 +487,12 @@ void setup() { for (int i = 0; i < 4; i++) { pinMode(X_PINS[i], OUTPUT); digitalWrite(X_PINS[i], LOW); - pinMode(Y_PINS[i], INPUT); // external pulldowns + pinMode(Y_PINS[i], INPUT); // external pulldowns } // Set up Display - Wire.setSDA(4); // GP4 (pin 6) - Wire.setSCL(5); // GP5 (pin 7) + Wire.setSDA(4); // GP4 (pin 6) + Wire.setSCL(5); // GP5 (pin 7) Wire.begin(); display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR); display.clearDisplay(); @@ -487,7 +519,7 @@ void loop() { } // Seocnd core does inputs + display + leds -void loop1(){ +void loop1() { handleInputs(); if (millis() - lastUI > 30) {