├── .gitignore ├── .gitattributes ├── LICENSE ├── mod1-turingmachine ├── scales.h ├── TuningHelper │ └── TuningHelper.ino └── mod1-turingmachine.ino ├── mod1-potrecorder └── mod1-potrecorder.ino ├── mod1-sampleandhold └── mod1-sampleandhold.ino ├── mod1-lorenz └── mod1-lorenz.ino ├── mod1-bezier-spike └── mod1-bezier-spike.ino ├── mod1_randomwalk └── mod1_randomwalk.ino ├── mod1-bernoulli └── mod1-bernoulli.ino ├── mod1-1D-wavetable └── mod1-1D-wavetable.ino ├── mod1_3ch_LFO └── mod1_3ch_LFO.ino ├── README.md ├── dual_ad_envelope └── dual_ad_envelope.ino └── mod1_LFO └── mod1_LFO.ino /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /01_gerber 3 | /mod1_AD_env -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Robert Heel 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 and this permission notice shall be included in all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /mod1-turingmachine/scales.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALES_H 2 | #define SCALES_H 3 | 4 | // measured PWM values for semitones (C to B over 3 octaves + C) 5 | const int tuningValues[] = { 6 | 0, 11, 15, 19, 23, 27, 31, 35, 40, 44, 48, 53, // Octave 1 7 | 57, 61, 65, 69, 73, 77, 82, 86, 90, 94, 99, 103, // Octave 2 8 | 107, 112, 116, 120, 124, 128, 132, 137, 141, 145, 149, 153, 9 | 157 // Extra high C (3 octaves + 1) 10 | }; 11 | const int totalNotes = sizeof(tuningValues) / sizeof(tuningValues[0]); 12 | 13 | // Index sets for scales, based on tuningValues[] 14 | // These represent note positions from 0–11 and are transposed up later 15 | 16 | // For all quantizers below: val = 0–(totalNotes-1) 17 | int quantizeToMajor(int val) { 18 | const int majorSteps[] = {0, 2, 4, 5, 7, 9, 11}; // C major scale degrees 19 | int octave = val / 12; 20 | int semitone = val % 12; 21 | 22 | for (int i = 0; i < 7; i++) { 23 | if (semitone <= majorSteps[i]) { 24 | return tuningValues[(octave * 12) + majorSteps[i]]; 25 | } 26 | } 27 | return tuningValues[(octave * 12) + 11]; // fallback to B 28 | } 29 | 30 | int quantizeToMinor(int val) { 31 | const int minorSteps[] = {0, 2, 3, 5, 7, 8, 10}; // C natural minor 32 | int octave = val / 12; 33 | int semitone = val % 12; 34 | 35 | for (int i = 0; i < 7; i++) { 36 | if (semitone <= minorSteps[i]) { 37 | return tuningValues[(octave * 12) + minorSteps[i]]; 38 | } 39 | } 40 | return tuningValues[(octave * 12) + 10]; 41 | } 42 | 43 | int quantizeToPhrygian(int val) { 44 | const int phrygianSteps[] = {0, 1, 3, 5, 7, 8, 10}; // Phrygian mode 45 | int octave = val / 12; 46 | int semitone = val % 12; 47 | 48 | for (int i = 0; i < 7; i++) { 49 | if (semitone <= phrygianSteps[i]) { 50 | return tuningValues[(octave * 12) + phrygianSteps[i]]; 51 | } 52 | } 53 | return tuningValues[(octave * 12) + 10]; 54 | } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /mod1-turingmachine/TuningHelper/TuningHelper.ino: -------------------------------------------------------------------------------- 1 | // Tuning Helper - Outputs fast PWM values to Pin F3 (D10) 2 | // Adjust and verify each note one by one using a tuner 3 | 4 | const int pwmOutPin = 10; // F3 / D10 5 | const int tuningValues[] = { 6 | 0, // C 0 7 | 11, // C# 1 8 | 15, // D 2 9 | 19, // D# 3 10 | 23, // E 4 11 | 27, // F 5 12 | 31, // F# 6 13 | 35, // G 7 14 | 40, // G# 8 15 | 44, // A 9 16 | 48, // A# 10 17 | 53, // B 11 18 | 19 | 57, // C 12 20 | 61, // C# 13 21 | 65, // D 14 22 | 69, // D# 15 23 | 73, // E 16 24 | 77, // F 17 25 | 82, // F# 18 26 | 86, // G 19 27 | 90, // G# 20 28 | 94, // A 21 29 | 99, // A# 22 30 | 103, // B 23 31 | 32 | 107, // C 24 33 | 112, // C# 25 34 | 116, // D 26 35 | 120, // D# 27 36 | 124, // E 28 37 | 128, // F 29 38 | 132, // F# 30 39 | 137, // G 31 40 | 141, // G# 32 41 | 145, // A 33 42 | 149, // A# 34 43 | 153, // B 35 44 | 45 | 157 // C 36 46 | 47 | }; 48 | 49 | void setup() { 50 | Serial.begin(9600); 51 | setupFastPWM(); 52 | 53 | // === COMMENT/UNCOMMENT ONE AT A TIME FOR TUNING === 54 | analogWriteQuantized(tuningValues[0]); // C 55 | //analogWriteQuantized(tuningValues[1]); // C# 56 | //analogWriteQuantized(tuningValues[2]); // D 57 | //analogWriteQuantized(tuningValues[3]); // D# 58 | //analogWriteQuantized(tuningValues[4]); // E 59 | // analogWriteQuantized(tuningValues[5]); // F 60 | //analogWriteQuantized(tuningValues[6]); // F# 61 | // analogWriteQuantized(tuningValues[7]); // G 62 | //analogWriteQuantized(tuningValues[8]); // G# 63 | // analogWriteQuantized(tuningValues[9]); // A 64 | // analogWriteQuantized(tuningValues[10]); // A# 65 | // analogWriteQuantized(tuningValues[11]); // B 66 | 67 | //analogWriteQuantized(tuningValues[12]); // C 68 | //analogWriteQuantized(tuningValues[13]); // C# 69 | //analogWriteQuantized(tuningValues[14]); // D 70 | //analogWriteQuantized(tuningValues[15]); // D# 71 | //analogWriteQuantized(tuningValues[16]); // E 72 | //analogWriteQuantized(tuningValues[17]); // F 73 | //analogWriteQuantized(tuningValues[18]); // F# 74 | //analogWriteQuantized(tuningValues[19]); // G 75 | //analogWriteQuantized(tuningValues[20]); // G# 76 | //analogWriteQuantized(tuningValues[21]); // A 77 | //analogWriteQuantized(tuningValues[22]); // A# 78 | //analogWriteQuantized(tuningValues[23]); // B 79 | 80 | //analogWriteQuantized(tuningValues[24]); // C 81 | //analogWriteQuantized(tuningValues[25]); // C# 82 | //analogWriteQuantized(tuningValues[26]); // D 83 | //analogWriteQuantized(tuningValues[27]); // D# 84 | //analogWriteQuantized(tuningValues[28]); // E 85 | //analogWriteQuantized(tuningValues[29]); // F 86 | //analogWriteQuantized(tuningValues[30]); // F# 87 | //analogWriteQuantized(tuningValues[31]); // G 88 | //analogWriteQuantized(tuningValues[32]); // G# 89 | //analogWriteQuantized(tuningValues[33]); // A 90 | //analogWriteQuantized(tuningValues[34]); // A# 91 | //analogWriteQuantized(tuningValues[35]); // B 92 | 93 | //analogWriteQuantized(tuningValues[36]); // C 94 | } 95 | 96 | void loop() { 97 | // Nothing here, static output for tuning 98 | } 99 | 100 | void setupFastPWM() { 101 | // --- F3 on D10 via Timer1 --- 102 | pinMode(pwmOutPin, OUTPUT); 103 | TCCR1A = _BV(COM1B1) | _BV(WGM10); // Fast PWM 8-bit, non-inverting on OCR1B 104 | TCCR1B = _BV(WGM12) | _BV(CS10); // No prescaler 105 | OCR1B = 0; 106 | } 107 | 108 | void analogWriteQuantized(uint8_t val) { 109 | OCR1B = val; // Writes directly to PWM on F3 (D10) 110 | } 111 | -------------------------------------------------------------------------------- /mod1-potrecorder/mod1-potrecorder.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Pot1 recorder firmware for Mod1 eurorack module designed by Hagiwo by Rob Heel. 3 | Pot1 Recorder with Fast PWM Output (100 Hz S&H style) max recording time 7.5 sec. 4 | You can record movement of pot1 while holding down the button. Once let go it will loop your recording. 5 | Pot2 is speed, from half to double speed. Pot3 is gain control. 6 | Speed CV offset via F1 CV in. Gain CV offset via F2 CV in. CV out on F4. 7 | */ 8 | 9 | 10 | #include 11 | 12 | #define POT1 A0 // Pot to be recorded 13 | #define POT2 A1 // Playback speed control 14 | #define POT3 A2 // Gain 15 | #define F1 A3 // Speed modifier (Voltage control input) 16 | #define F2 A4 // Gain CV 17 | #define BUTTON 4 // Recording button 18 | #define LED 3 // Output Led 19 | #define F4 11 // CV out 20 | 21 | #define TABLE_SIZE 768 // Max buffer (~75% of Nano SRAM) 22 | #define INTERVAL 10 // ms between samples (100 Hz) 23 | 24 | int values[TABLE_SIZE]; 25 | int index = 0; 26 | int recordedLength = 0; 27 | 28 | bool recording = false; 29 | bool playback = false; 30 | bool waitForButtonRelease = false; 31 | bool hasRecordedYet = false; 32 | 33 | unsigned long lastMillis = 0; 34 | 35 | void setup() { 36 | pinMode(BUTTON, INPUT_PULLUP); // Active LOW 37 | pinMode(LED, OUTPUT); 38 | pinMode(F4, OUTPUT); 39 | 40 | // Fast PWM on Pin 11 (OC2A) 41 | TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); 42 | TCCR2B = _BV(CS21); // Prescaler 8 = ~7.8kHz PWM 43 | OCR2A = 0; // Start with 0% duty 44 | } 45 | 46 | void loop() { 47 | bool buttonPressed = (digitalRead(BUTTON) == LOW); 48 | unsigned long now = millis(); 49 | 50 | // === Handle Preview Mode === 51 | if (!recording && !playback) { 52 | int previewVal = analogRead(POT1) / 4; 53 | OCR2A = previewVal; // Route Pot1 to output directly (F4) 54 | analogWrite(LED, previewVal); // LED mirrors preview signal 55 | } 56 | 57 | // === Handle Recording === 58 | if (recording && index < TABLE_SIZE) { 59 | if (now - lastMillis >= INTERVAL) { 60 | lastMillis = now; 61 | 62 | int value = analogRead(POT1) / 4; // Scale 0–1023 to 0–255 63 | values[index] = value; 64 | OCR2A = value; // Live output while recording 65 | analogWrite(LED, value); // LED mirrors signal 66 | recordedLength = index + 1; 67 | index++; 68 | } 69 | } 70 | 71 | // === Stop recording if full === 72 | if (recording && index >= TABLE_SIZE) { 73 | recording = false; 74 | playback = true; 75 | index = 0; 76 | waitForButtonRelease = true; 77 | lastMillis = now; 78 | } 79 | 80 | // === Start recording (on button press) 81 | if (buttonPressed && !recording && !waitForButtonRelease) { 82 | recording = true; 83 | playback = false; 84 | index = 0; 85 | recordedLength = 0; 86 | lastMillis = now; 87 | } 88 | 89 | // === If button released, allow new recordings again 90 | if (!buttonPressed && waitForButtonRelease) { 91 | waitForButtonRelease = false; 92 | } 93 | 94 | // === Stop recording if button is released early 95 | if (!buttonPressed && recording) { 96 | recording = false; 97 | playback = true; 98 | index = 0; 99 | lastMillis = now; 100 | } 101 | 102 | // === Playback loop 103 | if (playback) { 104 | // Get speed from POT2 105 | int baseInterval = map(analogRead(POT2), 0, 1023, 20, 5); // Playback speed range 20ms to 5ms 106 | // Get speed modifier from F1 (scaled from 0 to 5V input, mapped to 1.0 to 3.0 multiplier) 107 | int modOffset = map(analogRead(F1), 0, 1023, 0, 15); // F1: adds speed (shorter interval) 108 | 109 | // Calculate final interval by multiplying base speed with the speed modifier 110 | int dynamicInterval = baseInterval - modOffset; 111 | dynamicInterval = max(dynamicInterval, 1); // clamp to safe minimum (avoid 0 or negative) 112 | 113 | if (now - lastMillis >= dynamicInterval) { 114 | lastMillis = now; 115 | 116 | // Read Pot3 and F2 to calculate gain 117 | int gainPot = analogRead(POT3); // 0 - 1023 118 | int gainCV = analogRead(F2); // 0 - 1023 119 | // Convert both to float multipliers between 0.0 - 1.0 120 | float potGain = gainPot / 1023.0; 121 | float cvGain = gainCV / 1023.0; 122 | 123 | float totalGain = potGain + cvGain; // Final gain multiplier (0.0 - 1.0) 124 | totalGain = constrain(totalGain, 0.0, 1.0); // Ensure gain is within 0.0 - 1.0 125 | 126 | // Apply gain during playback output 127 | int outputValue = values[index]; 128 | 129 | float scaledOutput = outputValue * totalGain; 130 | scaledOutput = constrain(scaledOutput, 0.0, 255.0); // Constrain to 0-255 range 131 | OCR2A = (int)round(scaledOutput); // Convert back to int for the PWM output 132 | 133 | /* 134 | int scaledOutput = outputValue * totalGain; 135 | scaledOutput = constrain(scaledOutput, 0, 255); 136 | OCR2A = scaledOutput; 137 | */ 138 | analogWrite(LED, scaledOutput); // LED follows output 139 | 140 | index = (index + 1) % recordedLength; 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /mod1-sampleandhold/mod1-sampleandhold.ino: -------------------------------------------------------------------------------- 1 | /* 2 | // Sample and Hold with Slew for Hagiwos Mod1 by Rob Heel. 3 | Classical sample and hold function. Sample and hold output on F4, triggered by button or F1 trigger in. 4 | If nothing is patched into sample input F2 an internal sample source is used. 5 | Pot1 is a bias for the internal noise. In the middle position truly random, fully clockwise shifts towards higher values, 6 | fully counter clockwise shifts towards lower values. Pot3 is gain/level. 7 | Slewed output on F3 with Pot 2 controling time constant of the slew. 8 | 9 | Potentiometers 10 | Potentiometer 1 → A0 noise bias 11 | Potentiometer 2 → A1 time constant slew 12 | Potentiometer 3 → A2 gain 13 | Inputs/Outputs: 14 | F1 → A3 trigger in 15 | F2 → A4 sample input 16 | F3 → A5 slew output 17 | F4 → D11 sample and hold output 18 | LED → Pin 3 19 | Button → Pin 4 trigger 20 | 21 | */ 22 | 23 | 24 | #include 25 | 26 | // Pin Definitions 27 | #define BUTTON_PIN 4 28 | #define LED_PIN 3 29 | #define F1_PIN A3 30 | #define F2_PIN A4 31 | #define F3_PIN 10 // Slew Output (Fast PWM) 32 | #define F4_PIN 11 // PWM Output 33 | #define POT_GAIN A2 34 | #define POT_SLEW A1 35 | 36 | int heldValue = 0; 37 | bool buttonState = false; 38 | bool lastButtonState = false; 39 | int slewValue = 0; 40 | 41 | bool lastF1State = LOW; // Store previous state of F1 (for rising edge detection) 42 | 43 | void setup() { 44 | pinMode(BUTTON_PIN, INPUT_PULLUP); 45 | pinMode(LED_PIN, OUTPUT); 46 | pinMode(F1_PIN, INPUT); 47 | pinMode(F2_PIN, INPUT); 48 | pinMode(F3_PIN, OUTPUT); 49 | pinMode(F4_PIN, OUTPUT); 50 | 51 | // Set up PWM on D11 (F4) 52 | TCCR2A = _BV(COM2A1) | _BV(WGM20) | _BV(WGM21); // Fast PWM 53 | TCCR2B = _BV(CS21); // Prescaler 8 54 | 55 | // Set up PWM on D10 (F3) - Fast PWM 56 | //TCCR1A = _BV(COM1A1) | _BV(WGM10) | _BV(WGM11); // Fast PWM 57 | // TCCR1B = _BV(CS11); // Prescaler 8 58 | 59 | TCCR1A = _BV(COM1A1) | _BV(WGM10); // Enable PWM on OC1A (D10), WGM10 for 8-bit 60 | TCCR1B = _BV(WGM12) | _BV(CS11); // Fast PWM 8-bit, prescaler 8 61 | 62 | } 63 | 64 | void loop() { 65 | // Read Button State 66 | buttonState = digitalRead(BUTTON_PIN) == LOW; 67 | 68 | // Read External Trigger (F1) - Rising edge detection 69 | //bool currentF1State = analogRead(F1_PIN) > 512; // Consider above 2.5V as "high" 512 70 | bool currentF1State = digitalRead(F1_PIN); // digital read as trigger detect 71 | bool trigger = (currentF1State && !lastF1State); // Rising edge detected 72 | 73 | // Check Sample Input 74 | int sampleInput = analogRead(F2_PIN); 75 | bool externalSample = sampleInput > 20; // Threshold to detect cable plugged in 76 | 77 | // Gain Control 78 | float gain = analogRead(POT_GAIN) / 1023.0; 79 | 80 | // Slew Time Control 81 | int slewTime = map(analogRead(POT_SLEW), 0, 1023, 1, 50); // Adjust range as needed 82 | 83 | // Read Potentiometer 1 (A0) to influence random value distribution 84 | int pot1Value = analogRead(A0); 85 | int potBias = map(pot1Value, 0, 1023, -500, 500); // Map pot value to range of -500 to +500 86 | 87 | // New Sample if Button Pressed or Rising Edge Trigger 88 | if ((buttonState && !lastButtonState) || trigger) { 89 | digitalWrite(LED_PIN, HIGH); 90 | if (externalSample) { 91 | heldValue = sampleInput * gain; // Use external input 92 | } else { 93 | // Generate random value influenced by Potentiometer 1 94 | int randomValue = random(1024); 95 | int biasedValue = randomValue + potBias; 96 | 97 | // Clamp the biased value to the range 0 to 1023 98 | biasedValue = constrain(biasedValue, 0, 1023); 99 | 100 | heldValue = biasedValue * gain; 101 | } 102 | analogWrite(F4_PIN, heldValue / 4); // Convert 10-bit to 8-bit PWM 103 | delay(10); 104 | digitalWrite(LED_PIN, LOW); 105 | } 106 | 107 | 108 | // Slew Calculation 109 | static unsigned long slewStartTimeMicros = 0; 110 | static int startValue = 0; 111 | static int targetValue = 0; 112 | static int slewDurationMicros = 0; 113 | static bool slewing = false; 114 | 115 | static unsigned long lastStepTime = 0; 116 | static int steps = 1; 117 | static float stepSize = 0; 118 | static float intermediateValue = 0; 119 | 120 | // Slew time from Pot2 121 | int rawPot = analogRead(POT_SLEW); 122 | float curved = pow((float)rawPot / 1023.0, 2.5); // Optional: exponential response 123 | int currentSlewTime = constrain(curved * 1000, 10, 1000); // 10–1000ms 124 | int totalTimeMicros = currentSlewTime * 1000; 125 | 126 | // Start new slew 127 | if (heldValue != targetValue) { 128 | startValue = slewValue; 129 | targetValue = heldValue; 130 | slewDurationMicros = totalTimeMicros; 131 | steps = max(1, currentSlewTime * 2); // 2 steps per ms = 0.5ms per step 132 | stepSize = (float)(targetValue - startValue) / steps; 133 | intermediateValue = startValue; 134 | slewStartTimeMicros = micros(); 135 | lastStepTime = micros(); 136 | slewing = true; 137 | } 138 | 139 | if (slewing) { 140 | unsigned long now = micros(); 141 | if (now - lastStepTime >= 500) { // 0.5ms per step 142 | intermediateValue += stepSize; 143 | slewValue = round(intermediateValue); 144 | lastStepTime = now; 145 | 146 | if ((stepSize > 0 && slewValue >= targetValue) || 147 | (stepSize < 0 && slewValue <= targetValue)) { 148 | slewValue = targetValue; 149 | slewing = false; 150 | } 151 | } 152 | } 153 | 154 | // Output to F3 155 | analogWrite(F3_PIN, slewValue / 4); // 10-bit to 8-bit PWM 156 | 157 | 158 | // Save current F1 state for next loop 159 | lastF1State = currentF1State; 160 | 161 | // Save button state for next loop 162 | lastButtonState = buttonState; 163 | } 164 | -------------------------------------------------------------------------------- /mod1-lorenz/mod1-lorenz.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Lorenz Attractor CV Generator firmware by Rob Heel for Mod1 designed by Hagiwo. 3 | 4 | Lorenz Attractor is great for organic, non-repeating CV movement, chaotic but not random. 5 | This system inspired the popular 'butterfly effect' metaphor, where small changes can lead 6 | to dramatically different outcomes.” https://en.wikipedia.org/wiki/Lorenz_system 7 | 8 | Pots: 9 | A0 → Sigma (flow strength / controls how fast x and y try to equalize/ also mapped to stepSize) 10 | A1 → Rho (divergence / higher = stronger pull from center -> more chaos, from calm to chaos) 11 | A2 → Beta (damping / controls how sharply z grows or decays) 12 | 13 | Input: 14 | F1 (A3 / D17) → Trigger input (resets attractor on rising edge) 15 | 16 | Outputs: 17 | D9 (F2) → x axis (PWM CV) 18 | D10 (F3) → y axis (PWM CV) 19 | D11 (F4) → z axis (PWM CV) 20 | 21 | Button (D4) → toggle normal and slow mode 22 | LED (D3) → Blinks in stepsize 23 | */ 24 | 25 | 26 | #include 27 | #define EEPROM_ADDR 0 // Address to store slowMode 28 | 29 | const int potSigmaPin = A0; 30 | const int potRhoPin = A1; 31 | const int potBetaPin = A2; 32 | 33 | const int triggerPin = A3; // F1 input 34 | const int pinF2 = 9; // OC1A 35 | const int pinF3 = 10; // OC1B 36 | const int pinF4 = 11; // OC2A (Timer2) 37 | 38 | const int ledPin = 3; 39 | const int buttonPin = 4; 40 | 41 | // trigger-in state 42 | bool lastTriggerState = LOW; 43 | 44 | // slow and normal mode 45 | bool slowMode = false; 46 | bool lastButtonState = HIGH; // Because INPUT_PULLUP 47 | unsigned long lastDebounceTime = 0; 48 | unsigned long debounceDelay = 50; 49 | 50 | // Lorenz variables 51 | float x = 0.1, y = 0.0, z = 0.0; 52 | float dt = 0.01; 53 | 54 | void setup() { 55 | Serial.begin(9600); // for debug open the serial port at 9600 bps: 56 | // Setup input pins 57 | pinMode(potSigmaPin, INPUT); 58 | pinMode(potRhoPin, INPUT); 59 | pinMode(potBetaPin, INPUT); 60 | 61 | pinMode(ledPin, OUTPUT); 62 | pinMode(buttonPin, INPUT_PULLUP); 63 | // trigger-in to reset attractor 64 | pinMode(triggerPin, INPUT); // or INPUT_PULLUP if signal is open when idle 65 | 66 | // Setup PWM pins as outputs 67 | pinMode(pinF2, OUTPUT); 68 | pinMode(pinF3, OUTPUT); 69 | pinMode(pinF4, OUTPUT); 70 | 71 | // --- Timer1 (16-bit) PWM Setup for F2 (D9) & F3 (D10) --- 72 | // Fast PWM, 8-bit 73 | TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10); 74 | TCCR1B = _BV(WGM12) | _BV(CS10); // No prescale, ~31.4kHz PWM 75 | 76 | // --- Timer2 (8-bit) PWM Setup for F4 (D11) --- 77 | // Fast PWM on OC2A (D11), prescaler 8 (~7.8kHz) 78 | TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); 79 | TCCR2B = _BV(CS21); 80 | 81 | // Initialize OCR registers to mid-value 82 | OCR1A = 128; 83 | OCR1B = 128; 84 | OCR2A = 128; 85 | } 86 | 87 | unsigned long lastBlinkTime = 0; 88 | bool ledState = false; 89 | 90 | void loop() { 91 | // Read current button state 92 | bool buttonState = digitalRead(buttonPin); 93 | 94 | // Debounce handling 95 | if (buttonState != lastButtonState) { 96 | lastDebounceTime = millis(); // reset debounce timer 97 | } 98 | 99 | if ((millis() - lastDebounceTime) > debounceDelay) { 100 | // Button state is stable here 101 | static bool lastStableState = HIGH; 102 | 103 | if (buttonState != lastStableState) { 104 | lastStableState = buttonState; 105 | 106 | if (buttonState == LOW) { // Button pressed (assuming active low) 107 | slowMode = !slowMode; 108 | EEPROM.write(EEPROM_ADDR, slowMode ? 1 : 0); 109 | Serial.print("Slow mode: "); 110 | Serial.println(slowMode ? "ON" : "OFF"); 111 | } 112 | } 113 | } 114 | 115 | lastButtonState = buttonState; 116 | 117 | // input trigger rising edge to reset attractor 118 | bool currentTrigger = digitalRead(triggerPin); 119 | if (currentTrigger == HIGH && lastTriggerState == LOW) { 120 | // Rising edge detected — reset attractor 121 | x = 0.1; 122 | y = 0.0; 123 | z = 0.0; 124 | } 125 | lastTriggerState = currentTrigger; 126 | 127 | // Read pots and map to Lorenz parameters 128 | float sigma = map(analogRead(potSigmaPin), 0, 1023, slowMode ? 1 : 5, slowMode ? 10 : 20); 129 | float rho = map(analogRead(potRhoPin), 0, 1023, 20, 50); 130 | float beta = map(analogRead(potBetaPin), 0, 1023, 1, 4); 131 | 132 | // Map Sigma Pin also to Stepsize in discrete steps 133 | int potVal = analogRead(potSigmaPin); 134 | if (slowMode) { 135 | if (potVal < 341) dt = 0.0001; 136 | else if (potVal < 682) dt = 0.0005; 137 | else dt = 0.001; 138 | } else { 139 | if (potVal < 341) dt = 0.001; 140 | else if (potVal < 682) dt = 0.005; 141 | else dt = 0.01; 142 | } 143 | 144 | // Lorenz attractor differential equations 145 | float dx = sigma * (y - x); 146 | float dy = x * (rho - z) - y; 147 | float dz = x * y - beta * z; 148 | 149 | // Euler integration 150 | x += dx * dt; 151 | y += dy * dt; 152 | z += dz * dt; 153 | 154 | // Map Lorenz outputs to PWM range 155 | int pwmX = constrain(mapFloat(x, -30, 30, 0, 255), 0, 255); 156 | int pwmY = constrain(mapFloat(y, -30, 30, 0, 255), 0, 255); 157 | int pwmZ = constrain(mapFloat(z, 0, 50, 0, 255), 0, 255); 158 | 159 | // Output PWM signals 160 | OCR1A = pwmX; // F2 - D9 161 | OCR1B = pwmY; // F3 - D10 162 | OCR2A = pwmZ; // F4 - D11 163 | 164 | // LED blink on D3 165 | unsigned long blinkInterval = map(dt * 1000000, 50, 10000, 500, 50); // microseconds to milliseconds 166 | blinkInterval = constrain(blinkInterval, 50, 500); // clamp to a sane range 167 | 168 | unsigned long now = millis(); 169 | if (now - lastBlinkTime > blinkInterval) { 170 | ledState = !ledState; 171 | digitalWrite(ledPin, ledState); 172 | lastBlinkTime = now; 173 | } 174 | 175 | // No delay for responsiveness 176 | } 177 | 178 | 179 | // Helper function to map float like Arduino's map() 180 | float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) { 181 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 182 | } 183 | 184 | -------------------------------------------------------------------------------- /mod1-bezier-spike/mod1-bezier-spike.ino: -------------------------------------------------------------------------------- 1 | // This code was originally written by HAGIWO and released under CC0 2 | /* Adapted for Mod1 and added a second Spike/Jitter Bezier mode, accesible via button by Rob Heel 3 | Potentiometers 4 | Potentiometer 1 → A0 freq 5 | Potentiometer 2 → A1 curve 6 | Potentiometer 3 → A2 dev 7 | Inputs/Outputs: 8 | F1 → A3 freq CV in 9 | F2 → A4 curve CV in 10 | F3 → A5 dev CV in 11 | F4 → D11 output 12 | LED → Pin 3 13 | Button → Pin 4 switch modes 14 | 15 | */ 16 | 17 | #include //for fast PWM 18 | int i = 0; 19 | int start_val = 0; // Bezier Curve Starting Point 20 | int end_val = 255; // Bezier Curve end Point 21 | float old_wait = 0; 22 | float wait = 0; // Bezier curve x-axis (time) 23 | float bz_val = 0; // Bezier curve y-axis (voltage) 24 | int dev, level, curve, freq; 25 | long timer = 0; // Time tracking for micros 26 | long timer1 = 0; // Time tracking for analog read interval 27 | float x[256]; // Bezier Curve Calculation Tables 28 | 29 | int devpot = 0; 30 | int devCV = 0; 31 | 32 | int freq_rnd = 501; 33 | int freq_dev = 40; 34 | int chance[32] = { 5, 12, 21, 33, 48, 67, 90, 118, 151, 189, 232, 279, 331, 386, 443, 501, 559, 616, 671, 723, 770, 813, 851, 884, 912, 935, 954, 969, 981, 990, 997, 1000 }; // normal distribution table 35 | int freq_err[32] = { 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 22, 24, 26, 28, 30, 33, 36, 40, 46, 52, 58, 64, 70, 76, 82, 90, 98, 110, 122, 136, 148 }; // Frequency Variation 36 | 37 | // Add a new mode variable for Bezier and SPIKE Bezier 38 | enum Mode { 39 | BEZIER_MODE, 40 | SPIKE_BEZIER_MODE 41 | }; 42 | Mode currentMode = BEZIER_MODE; // Start with Bezier Mode 43 | 44 | // Variables for the spike and jitter effect 45 | int spike_hold_counter = 0; // Counter to hold the spike for a few cycles 46 | int spike_value = 0; // The intensity of the spike (random value) 47 | 48 | void setup() { 49 | for (int j = 0; j < 255; j++) { // Preparation of Bezier Curve Calculation Tables 50 | x[j] = j * 0.003921; // j/255 51 | } 52 | 53 | pinMode(11, OUTPUT); // CV output 54 | pinMode(3, OUTPUT); // LED output 55 | pinMode(4, INPUT_PULLUP); // Button input (Pin 4) 56 | timer = micros(); 57 | timer1 = millis(); 58 | 59 | // pin pwm setting 60 | TCCR1B &= B11111000; 61 | TCCR1B |= B00000001; 62 | delay(50); 63 | } 64 | 65 | void loop() { 66 | // Toggle modes with the button 67 | if (digitalRead(4) == LOW) { 68 | delay(200); // debounce delay 69 | currentMode = (Mode)((currentMode + 1) % 2); // Toggle between 2 modes 70 | } 71 | 72 | if (timer1 + 50 < millis()) { 73 | freq = min(511, (analogRead(0) / 2 + analogRead(3) / 2)) * freq_dev; 74 | curve = min(255, (analogRead(1) / 4 + analogRead(4) / 4)); 75 | level = 1023 / 4; 76 | timer1 = millis(); 77 | } 78 | 79 | if (timer + (wait - old_wait) <= micros()) { 80 | old_wait = wait; 81 | i++; 82 | 83 | if (i >= 255) { // Recalculation of target voltage values 84 | i = 0; 85 | start_val = end_val; 86 | end_val = random(0, 255); 87 | change_freq_error(); 88 | } 89 | 90 | switch (currentMode) { 91 | case BEZIER_MODE: 92 | // Standard Bezier calculation 93 | wait = 3 * pow((1 - x[i]), 2) * x[i] * curve + 3 * (1 - x[i]) * pow(x[i], 2) * (255 - curve) + pow(x[i], 3) * 255; 94 | wait = 1 + wait * freq * 2; 95 | bz_val = pow((1 - x[i]), 3) * start_val + 3 * pow((1 - x[i]), 2) * x[i] * start_val + 3 * (1 - x[i]) * pow(x[i], 2) * end_val + pow(x[i], 3) * end_val; 96 | break; 97 | 98 | case SPIKE_BEZIER_MODE: 99 | // Basic Bezier curve calculations 100 | wait = 3 * pow((1 - x[i]), 2) * x[i] * curve + 3 * (1 - x[i]) * pow(x[i], 2) * (255 - curve) + pow(x[i], 3) * 255; 101 | wait = 1 + wait * freq * 2; // Frequency influence 102 | 103 | // Basic Bezier transition for the value 104 | bz_val = pow((1 - x[i]), 3) * start_val + 3 * pow((1 - x[i]), 2) * x[i] * start_val + 3 * (1 - x[i]) * pow(x[i], 2) * end_val + pow(x[i], 3) * end_val; 105 | 106 | // Spike trigger: 5% chance to trigger a spike 107 | if (random(0, 100) < 5 && spike_hold_counter == 0) { 108 | // Start the spike hold, random spike size 109 | spike_hold_counter = random(5, 20); // Hold spike for 5 to 20 cycles 110 | spike_value = random(-50, 50); // Randomize spike intensity 111 | } 112 | 113 | // If spike is active, hold the spike for a few cycles 114 | if (spike_hold_counter > 0) { 115 | bz_val += spike_value; // Apply spike value 116 | spike_hold_counter--; // Decrease the counter, spike will fade out 117 | } 118 | 119 | // Add jitter effect with 15% chance 120 | if (random(0, 100) < 15) { // 15% chance for jitter effect 121 | bz_val += random(-20, 20); // Small random jitter in the output 122 | } 123 | 124 | // Smooth the output to avoid very harsh jumps 125 | bz_val = constrain(bz_val, 0, 255); // Keep the value within bounds 126 | 127 | // Set the output voltage (scaled to level) 128 | analogWrite(11, bz_val * level / 255); 129 | 130 | // Change the values when a full cycle is completed 131 | if (i >= 255) { 132 | i = 0; 133 | start_val = end_val; 134 | end_val = random(0, 255); 135 | change_freq_error(); // Keep the frequency variation 136 | } 137 | break; 138 | } 139 | 140 | timer = micros(); 141 | PWM_OUT(); // Output the PWM signal 142 | } 143 | } // end void loop 144 | 145 | void change_freq_error() { // Frequency variation is obtained from the standard deviation table 146 | 147 | devpot = map(analogRead(2), 0, 1023, 0, 500); 148 | devCV = map(analogRead(4), 0, 1023, 0, 500); 149 | // Combine both values and map back to range 0-500 150 | int dev = map(devpot + devCV, 0, 1000, 0, 500); 151 | freq_rnd = random(500 - dev, 500 + dev); 152 | for (int k = 0; k < 32; k++) { 153 | if (freq_rnd >= chance[k] && freq_rnd < chance[k + 1]) { 154 | freq_dev = freq_err[k]; 155 | } 156 | } 157 | } 158 | 159 | void PWM_OUT() { // PWM output 160 | int output_value = bz_val * level / 255; 161 | analogWrite(11, bz_val * level / 255); 162 | analogWrite(3, output_value); // LED output - mirrors the CV intensity 163 | } 164 | -------------------------------------------------------------------------------- /mod1_randomwalk/mod1_randomwalk.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Random Walk with Gravity Mode + Lagged Output for Hagiwos MOD1 module by Rob Heel. 3 | Button toggles between classic Random Walk mode and Gravity Mode. 4 | F2 outputs a lagged/delayed version of F4 for ambient crossfading effects. 5 | 6 | Hardware Configuration: 7 | - Potentiometer 1 (Rate) → A0 8 | - Potentiometer 2 (Bias/Offset) → A1 9 | - Potentiometer 3 (ChaosDepth) → A2 10 | 11 | - LED Indicator → Pin 3 (OCR2B) 12 | - Push Button → Pin 4 13 | 14 | Inputs/Outputs: 15 | - F1 A3 CV input (controls Lag Amount - how closely F2 follows F4) 16 | - F2 D9 Lagged Output (slower following version of F4) 17 | - F3 A5 CV input (adds to ChaosDepth) 18 | - F4 D11 Random Walk Output (main) 19 | 20 | Ultra Slow Random walk CV module code 21 | - Locked to ultra-slow frequency range for gentle random walks 22 | - F2 creates a lagged version that slowly follows F4 23 | 24 | Behavior: 25 | - Pot1 controls frequency (how often new random steps occur). 26 | - Pot2 (Bias/Offset): Controls an offset applied to the walk output, shifting it up or down. 27 | - Pot3 (ChaosDepth): Controls how large each random step can be. F3 CV input affects ChaosDepth. 28 | Low values make the walk drift subtly, high values allow bigger, wilder jumps between steps. 29 | 30 | - F1 CV input controls Lag Amount - how closely F2 follows F4. Perfect for LFO modulation! 31 | No CV (0V) = F2 is almost independent (wide, evolving textures) 32 | High CV (5V) = F2 follows F4 very closely (tight relationship) 33 | 34 | - Button toggles between classic Random Walk mode and Gravity Mode. 35 | Gravity Mode pulls the output slowly back to 0 over time. 36 | - F2 outputs a lagged version - creates beautiful dancing relationships with F4. 37 | */ 38 | 39 | #include 40 | #include 41 | 42 | // ---------------- Global Variables and Constants ---------------- 43 | static const unsigned int TABLE_SIZE = 1024; 44 | static const unsigned long UPDATE_INTERVAL_US = 400; // ~2500Hz LFO update rate 45 | 46 | uint8_t waveTable[TABLE_SIZE]; 47 | 48 | // Phase and value tracking 49 | float walkPhase = 0.0; 50 | float laggedPhase = 0.0; // Lagged version that slowly follows walkPhase 51 | 52 | // Chaos parameters 53 | float chaosDepth = 0.0; 54 | float rate = 0.001f; // Default ultra slow rate 55 | float bias = 0.0f; // Bias offset 56 | 57 | // Lag parameters 58 | float baseLagAmount = 0.9995f; // Base lag amount - almost independent when no CV 59 | float lagAmount = 0.9995f; // Current lag amount (calculated each loop) 60 | 61 | // Mode toggle 62 | bool gravityMode = false; // Default to classic random walk 63 | 64 | void setup() { 65 | // Set up pins 66 | pinMode(11, OUTPUT); // F4 - Random Walk Output (main) 67 | pinMode(9, OUTPUT); // F2 - Lagged Output 68 | pinMode(3, OUTPUT); // LED Indicator 69 | pinMode(4, INPUT_PULLUP); // Button input 70 | 71 | configurePWM(); 72 | 73 | // Initialize lagged phase to match walk phase 74 | laggedPhase = walkPhase; 75 | } 76 | 77 | void loop() { 78 | // Read potentiometer values and CV inputs 79 | rate = readFrequency(A0); // Rate controlled only by pot now 80 | chaosDepth = (analogRead(A2) / 1023.0f) + (analogRead(A5) / 1023.0f); // ChaosDepth modulated by F3 CV input 81 | chaosDepth = constrain(chaosDepth, 0.0f, 1.0f); // Ensure chaos stays within 0-1 82 | 83 | bias = (analogRead(A1) / 1023.0f) * 0.8f - 0.4f; // Pot2 as bias control (-0.4 to +0.4 offset) 84 | 85 | // F1 CV input controls lag amount - INVERTED for intuitive behavior 86 | float lagCV = analogRead(A3) / 1023.0f; // Read F1 CV input (0.0 to 1.0) 87 | lagAmount = baseLagAmount - (lagCV * 0.015f); // Range from 0.9995 (independent) to 0.9845 (tight following) 88 | lagAmount = constrain(lagAmount, 0.98f, 0.9995f); // Safety limits 89 | 90 | // Check for button press to toggle mode 91 | if (digitalRead(4) == LOW) { 92 | gravityMode = !gravityMode; 93 | delay(200); // Basic debounce 94 | } 95 | 96 | // Random walk update 97 | if (gravityMode) { 98 | updateGravityWalk(walkPhase, rate, chaosDepth); 99 | } else { 100 | updateRandomWalk(walkPhase, rate, chaosDepth); 101 | } 102 | 103 | // Update lagged output - now dynamically controlled by F1 CV! 104 | updateLaggedOutput(walkPhase, laggedPhase, lagAmount); 105 | 106 | // Apply bias to both outputs 107 | int walkStepVal = (int)((walkPhase + bias) * 255.0f); 108 | walkStepVal = constrain(walkStepVal, 0, 255); 109 | 110 | int laggedStepVal = (int)((laggedPhase + bias) * 255.0f); 111 | laggedStepVal = constrain(laggedStepVal, 0, 255); 112 | 113 | analogWrite(11, walkStepVal); // F4 - Main Random Walk output 114 | analogWrite(9, laggedStepVal); // F2 - Lagged output 115 | OCR2B = walkStepVal; // LED brightness reflects main output 116 | } 117 | 118 | // Set up PWM registers 119 | void configurePWM() { 120 | TCCR1A = 0; TCCR1B = 0; 121 | TCCR1A |= (1 << WGM10) | (1 << COM1A1) | (1 << COM1B1); 122 | TCCR1B |= (1 << WGM12) | (1 << CS10); 123 | 124 | TCCR2A = 0; TCCR2B = 0; 125 | TCCR2A |= (1 << WGM20) | (1 << WGM21) | (1 << COM2A1) | (1 << COM2B1); 126 | TCCR2B |= (1 << CS20); 127 | } 128 | 129 | // Classic random walk behavior for F4 output 130 | void updateRandomWalk(float &phase, float rate, float depth) { 131 | float randomStep = (random(-100, 100) / 100.0f) * depth; 132 | phase += randomStep * rate; 133 | 134 | if (phase < 0.0f) phase = 0.0f; 135 | if (phase > 1.0f) phase = 1.0f; 136 | } 137 | 138 | // Gravity mode — random walk that slowly falls back to 0 139 | void updateGravityWalk(float &phase, float rate, float depth) { 140 | float randomStep = (random(-100, 100) / 100.0f) * depth; 141 | phase += randomStep * rate; 142 | 143 | // Introduce gravity pull towards 0 144 | phase *= 0.99f; // 0.995f → Gentle pull (slow decay) 0.98f → Medium pull (faster drift to zero) 0.9f → Strong pull (snaps back quickly) 145 | 146 | if (phase < 0.0f) phase = 0.0f; 147 | if (phase > 1.0f) phase = 1.0f; 148 | } 149 | 150 | // Update lagged output - now with dynamic lag control! 151 | void updateLaggedOutput(float mainPhase, float &laggedPhase, float currentLagAmount) { 152 | // Exponential smoothing with CV-controlled lag amount 153 | // Higher lag = slower following (F2 drifts independently) - DEFAULT with no CV 154 | // Lower lag = faster following (F2 stays close to F4) - when CV is applied 155 | 156 | laggedPhase = (laggedPhase * currentLagAmount) + (mainPhase * (1.0f - currentLagAmount)); 157 | 158 | // Perfect: Patch nothing = wide independent textures 159 | // Patch LFO = dynamic relationship from independent to tight coupling! 160 | } 161 | 162 | // Read frequency from Pot1 (A0) 163 | float readFrequency(int analogPin) { 164 | int rawVal = analogRead(analogPin); 165 | float fMin = 0.001f; // Locked to ultra slow mode 166 | float fMax = 0.1f; 167 | return fMin * powf(fMax / fMin, rawVal / 1023.0f); // Exponential scaling 168 | } -------------------------------------------------------------------------------- /mod1-turingmachine/mod1-turingmachine.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Turing Machine / KLEE-style Sequencer (array version) 3 | Description: 4 | - F1 (A3/D17): Trigger input (rising edge detection) 5 | - F2: CV in offset for pot1 6 | - F3 : Quantized CV output via fast PWM (0–5V) 7 | - F4 (D11): CV output via fast PWM (0–5V) 8 | - Pot1 (A0): Randomness vs lock (sequencing knob) 9 | - Pot2 (A1): Loop length selector (2,3,4,5,6,8,12,16) 10 | - Pot3 (A2): Gain control (output scaling 0–5V) 11 | */ 12 | 13 | #include "scales.h" 14 | #define BUTTON_PIN 4 15 | 16 | typedef int (*QuantizeFunction)(int); 17 | QuantizeFunction quantizers[] = { 18 | quantizeToMajor, 19 | quantizeToMinor, 20 | quantizeToPhrygian 21 | }; 22 | 23 | #define NUM_SCALES (sizeof(quantizers) / sizeof(quantizers[0])) // define numbers of scales in scales.h 24 | 25 | enum ScaleType { 26 | MAJOR, 27 | MINOR, 28 | PHRYGIAN 29 | }; 30 | 31 | 32 | int currentScaleIndex = 0; 33 | unsigned long lastDebounceTime = 0; 34 | const unsigned long debounceDelay = 200; 35 | bool lastButtonState = HIGH; 36 | 37 | const int triggerPin = 17; // F1 (A3 / D17) 38 | const int cvOutPin = 11; // F4 (D11) 39 | const int ledPin = 3; // LED for CV feedback 40 | 41 | const int pot1Pin = A0; // Randomness vs lock 42 | const int pot2Pin = A1; // Loop length 43 | const int pot3Pin = A2; // Gain 44 | 45 | bool shiftRegister[16] = { 0 }; // 16-step binary sequence 46 | 47 | const int loopOptions[] = { 2, 3, 4, 5, 6, 8, 12, 16 }; 48 | const int numLoopOptions = sizeof(loopOptions) / sizeof(loopOptions[0]); 49 | 50 | bool prevClockState = LOW; 51 | 52 | void setup() { 53 | Serial.begin(9600); 54 | pinMode(triggerPin, INPUT); 55 | pinMode(cvOutPin, OUTPUT); 56 | pinMode(ledPin, OUTPUT); 57 | pinMode(BUTTON_PIN, INPUT_PULLUP); 58 | 59 | setupFastPWM(); 60 | analogWriteFast(0); 61 | analogWrite(ledPin, 0); 62 | randomSeed(analogRead(A7)); // Safe analog pin 63 | } 64 | 65 | void loop() { 66 | checkScaleButton(); 67 | bool currClockState = digitalRead(triggerPin); 68 | if (currClockState == HIGH && prevClockState == LOW) { 69 | stepSequencer(); 70 | } 71 | prevClockState = currClockState; 72 | } 73 | 74 | void stepSequencer() { 75 | // Read pots 76 | int pot1Hardware = analogRead(pot1Pin); 77 | Serial.print("POT1: "); 78 | Serial.print(pot1Hardware); 79 | int pot1cvInput = analogRead(A4); 80 | pot1cvInput = map(pot1cvInput, 0, 900, 0, 1023); // remap for full range behavior 81 | 82 | Serial.print(" | CV IN: "); 83 | Serial.print(pot1cvInput); 84 | int pot1 = pot1Hardware + pot1cvInput; // sum pot1 and CV in F2 85 | pot1 = constrain(pot1, 0, 1023); // Make sure we stay in bounds 86 | Serial.print(" | : "); 87 | Serial.print(pot1); 88 | 89 | int pot2 = analogRead(pot2Pin); // loop length 90 | int pot3 = analogRead(pot3Pin); // gain 91 | 92 | // Map pot2 to loop length 93 | //int loopIndex = constrain(map(pot2, 0, 1023, 0, numLoopOptions - 1), 0, numLoopOptions - 1); 94 | //int loopLength = loopOptions[loopIndex]; 95 | int loopLength; 96 | if (pot1 > 400 && pot1 < 630) { // fullyRandom range 97 | loopLength = 8; // fixed loop for free-run randomness 98 | } else { 99 | int loopIndex = constrain(map(pot2, 0, 1023, 0, numLoopOptions - 1), 0, numLoopOptions - 1); 100 | loopLength = loopOptions[loopIndex]; 101 | } 102 | 103 | 104 | // Map pot3 to gain factor 105 | float gain = pot3 / 1023.0; 106 | 107 | // Determine behavior from pot1 108 | bool fullyRandom = false; 109 | bool locked = false; 110 | bool doubleLock = false; 111 | bool slip = false; 112 | 113 | if (pot1 < 40) { 114 | doubleLock = true; 115 | } else if (pot1 < 400) { 116 | slip = true; 117 | } else if (pot1 < 630) { 118 | fullyRandom = true; 119 | } else if (pot1 < 950) { 120 | slip = true; 121 | } else { 122 | locked = true; 123 | } 124 | 125 | // Decide next bit 126 | bool newBit = 0; 127 | if (fullyRandom) { 128 | newBit = random(2); 129 | } else if (locked) { 130 | newBit = shiftRegister[loopLength - 1]; 131 | } else if (doubleLock) { 132 | int index = (loopLength * 2) - 1; 133 | if (index < 16) { 134 | newBit = shiftRegister[index]; 135 | } else { 136 | newBit = shiftRegister[15]; // fallback 137 | } 138 | /* 139 | } else if (slip) { 140 | newBit = (random(100) < 10) ? random(2) : shiftRegister[loopLength - 1]; // FIXED 10% chance of slipping 141 | } 142 | */ 143 | } else if (slip) { 144 | int slipChance = 10; 145 | 146 | if (pot1 < 400) { 147 | // Slip range from 40 to 400 → increase chance from 10% to 50% 148 | slipChance = map(pot1, 40, 400, 10, 50); 149 | } else if (pot1 >= 630 && pot1 < 970) { 150 | // Slip range from 630 to 970 → decrease chance from 50% to 10% 151 | slipChance = map(pot1, 630, 950, 50, 10); 152 | } 153 | 154 | newBit = (random(100) < slipChance) ? random(2) : shiftRegister[loopLength - 1]; 155 | } 156 | 157 | 158 | 159 | // Shift values right 160 | for (int i = 15; i > 0; i--) { 161 | shiftRegister[i] = shiftRegister[i - 1]; 162 | } 163 | shiftRegister[0] = newBit; 164 | 165 | // Convert top 8 bits to number 166 | uint16_t bits = 0; 167 | for (int i = 0; i < 8; i++) { 168 | bits |= shiftRegister[i] << (7 - i); // Always use top 8 bits 169 | } 170 | 171 | // Map to 0–255 for fast PWM 172 | int cvValue = bits; // Already 8-bit range 173 | int scaledValue = cvValue * gain; 174 | 175 | analogWriteFast(scaledValue); // CV Out on D11 F4 176 | analogWrite(3, scaledValue); // LED brightness on Pin 3 177 | quantized(); // Quantized CV Out on D10 (F3) 178 | 179 | // Debugging 180 | Serial.print("CV: "); 181 | Serial.print(scaledValue); 182 | Serial.print(" | Loop: "); 183 | Serial.print(loopLength); 184 | Serial.print(" | Mode: "); 185 | if (fullyRandom) Serial.println("Random"); 186 | else if (locked) Serial.println("Locked"); 187 | else if (doubleLock) Serial.println("Double Lock"); 188 | else if (slip) Serial.println("Slip"); 189 | } 190 | 191 | 192 | void checkScaleButton() { 193 | bool buttonState = digitalRead(BUTTON_PIN); 194 | if (buttonState == LOW && lastButtonState == HIGH) { 195 | unsigned long now = millis(); 196 | if (now - lastDebounceTime > debounceDelay) { 197 | currentScaleIndex = (currentScaleIndex + 1) % NUM_SCALES; 198 | Serial.print("Switched to scale: "); 199 | Serial.println(currentScaleIndex); 200 | lastDebounceTime = now; 201 | } 202 | } 203 | lastButtonState = buttonState; 204 | } 205 | 206 | 207 | void quantized() { 208 | int pot3 = analogRead(pot3Pin); 209 | float gain = pot3 / 1023.0; 210 | 211 | // Use fixed 8-bit value (same as stepSequencer) 212 | int value = 0; 213 | for (int i = 0; i < 8; i++) { 214 | value |= (shiftRegister[i] << (7 - i)); 215 | } 216 | 217 | // Apply gain and quantize 218 | int maxNoteIndex = totalNotes - 1; 219 | int rawNote = map(value, 0, 255, 0, maxNoteIndex); 220 | int scaledNote = rawNote * gain; 221 | scaledNote = constrain(scaledNote, 0, maxNoteIndex); 222 | 223 | int quantizedVal = quantizers[currentScaleIndex](scaledNote); 224 | analogWriteQuantized(quantizedVal); 225 | } 226 | 227 | 228 | // Fast PWM on Timer1 for Pin D10 (F3) 229 | void setupFastPWM() { 230 | // --- F4 on D11 via Timer2 --- 231 | TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); // Fast PWM on OC2A 232 | TCCR2B = _BV(CS20); // No prescaler 233 | OCR2A = 0; 234 | 235 | // --- F3 on D10 via Timer1 --- 236 | pinMode(10, OUTPUT); 237 | TCCR1A = _BV(COM1B1) | _BV(WGM10); // Fast PWM 8-bit, non-inverting on OCR1B 238 | TCCR1B = _BV(WGM12) | _BV(CS10); // No prescaler 239 | OCR1B = 0; 240 | } 241 | 242 | void analogWriteFast(uint8_t val) { 243 | OCR2A = val; 244 | } 245 | 246 | void analogWriteQuantized(uint8_t val) { 247 | OCR1B = val; // Writes to F3 248 | } 249 | -------------------------------------------------------------------------------- /mod1-bernoulli/mod1-bernoulli.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Enhanced Bernoulli Gate and Loop Sequencer with Glitch Bursts for Hagiwos Mod1 for Arduino Nano by Rob Heel. 3 | ------------------------------------------------------ 4 | This code implements a Bernoulli gate function with an optional step sequence mode and Glitch Bursts. 5 | The mode is controlled by Pot2. Behavior: 6 | 7 | Pot1 : Bernoulli Gate on output F3/ F4. Pot 1 sets probability which output is used from trigger in on F1. F2 is CV in for pot1. 8 | 9 | Pot2 (A1) Modes: 10 | - Fully Counterclockwise (0-20%): Bernoulli Mode (random coin toss) 11 | - 20-40%: 4-Step Sequence 12 | - 40-60%: 8-Step Sequence 13 | - 60-80%: 16-Step Sequence 14 | - 80-100%: 32-Step Sequence 15 | 16 | Pot3 (A2) - Miss Probability and Glitch Burst: 17 | - Fully Counterclockwise: No missed triggers, no glitch bursts 18 | - Turn Clockwise: Up to 35% of triggers are missed. Linear behavior 0-35%. 19 | - 80-100%: Glitch Bursts introduced (random rapid trigger bursts) 20 | 21 | The sequence can be regenerated by pressing the button. 22 | 23 | Pin Configuration: 24 | ------------------ 25 | Potentiometer 1 (Probability) - A0 26 | Potentiometer 2 (Mode/Length) - A1 27 | Potentiometer 3 (Miss Probability/Glitch) - A2 28 | Trigger Input - A3 (F1) 29 | CV Input (0-5V) - A4 (F2) 30 | Output 1 (F3) - D10 31 | Output 2 (F4) - D11 32 | LED - D3 33 | Button - D4 34 | */ 35 | 36 | const int trigInputPin = A3; // Trigger Input Pin 37 | const int cvInputPin = A4; // CV Input Pin (F2) 38 | const int potPin = A0; // Potentiometer 1 (Probability) - Input Pin 39 | const int lengthPotPin = A1; // Potentiometer 2 (Sequence Length/Mode) - Input Pin 40 | const int missPotPin = A2; // Potentiometer 3 (Miss Probability/Glitch) - Input Pin 41 | const int outputPinF3 = 10; // Output Pin F3 42 | const int outputPinF4 = 11; // Output Pin F4 43 | const int ledPin = 3; // LED Pin 44 | const int buttonPin = 4; // Button Pin to trigger sequence generation 45 | const unsigned long pulseDuration = 20; // Pulse Duration for LED and Output High 46 | 47 | int probability = 512; // Probability value based on Pot 1 48 | int combinedProbability = 512; // Combined probability with CV input 49 | int cvValue = 0; // CV input value 50 | bool trigState = LOW; // Current state of trigger input 51 | bool lastTrigState = LOW; // Last state of trigger input (to detect rising edge) 52 | unsigned long pulseStartTime = 0; // Time when the pulse starts 53 | bool pulseActive = false; // Flag for pulse activation 54 | int outputPin = outputPinF3; // Default output pin (F3) 55 | 56 | int sequenceLength = 0; // Length of the sequence (based on Pot2) 57 | int sequenceStep = 0; // Current step in the sequence 58 | int sequence[32]; // Array to store the sequence of outputs 59 | bool inSequenceMode = false; // Flag for sequence mode 60 | 61 | unsigned long lastTriggerTime = 0; // Last time a trigger event occurred 62 | 63 | // Function to generate a new sequence based on Pot2 (length) and Pot1 (probability) 64 | void generateSequence(int length) { 65 | for (int i = 0; i < length; i++) { 66 | int coinToss = random(0, 1023); 67 | // Based on coin toss, decide if F3 or F4 will be used for this sequence step 68 | if (coinToss < combinedProbability) { 69 | sequence[i] = outputPinF3; 70 | } else { 71 | sequence[i] = outputPinF4; 72 | } 73 | } 74 | } 75 | 76 | // Setup function to initialize all pins and random seed 77 | void setup() { 78 | pinMode(trigInputPin, INPUT); // Trigger input pin 79 | pinMode(cvInputPin, INPUT); // CV input pin (F2) 80 | pinMode(potPin, INPUT); // Potentiometer 1 (Probability) 81 | pinMode(lengthPotPin, INPUT); // Potentiometer 2 (Sequence Length/Mode) 82 | pinMode(missPotPin, INPUT); // Potentiometer 3 (Miss Probability/Glitch) 83 | pinMode(outputPinF3, OUTPUT); // Output pin F3 84 | pinMode(outputPinF4, OUTPUT); // Output pin F4 85 | pinMode(ledPin, OUTPUT); // LED output pin 86 | pinMode(buttonPin, INPUT_PULLUP); // Button pin with pull-up resistor 87 | 88 | randomSeed(analogRead(A7)); // Initialize random seed from unused pin A7 89 | digitalWrite(ledPin, LOW); // Ensure LED starts off 90 | } 91 | 92 | // Function to generate a glitch burst on the outputs 93 | // A glitch burst is a rapid series of outputs (F3 and F4) with random delays 94 | void glitchBurst(unsigned long interval) { 95 | int burstLength = random(3, 10); 96 | unsigned long burstDelay = interval / random(2, 5); // Fraction of the trigger interval 97 | for (int i = 0; i < burstLength; i++) { 98 | // Choose between F3 and F4 based on the probability (Pot 1) 99 | int coinToss = random(0, 1023); // Coin toss based on Pot 1 probability 100 | int output = (coinToss < combinedProbability) ? outputPinF4 : outputPinF3; 101 | 102 | digitalWrite(output, HIGH); 103 | delay(burstDelay); 104 | digitalWrite(output, LOW); 105 | delay(burstDelay); 106 | } 107 | } 108 | 109 | 110 | 111 | // Main loop function 112 | void loop() { 113 | trigState = digitalRead(trigInputPin); // Read the trigger input 114 | 115 | // Determine sequence length and mode based on Pot2 (lengthPotPin) 116 | int lengthValue = analogRead(lengthPotPin); 117 | if (lengthValue < 200) { 118 | sequenceLength = 0; 119 | inSequenceMode = false; 120 | } else if (lengthValue < 400) { 121 | sequenceLength = 4; 122 | inSequenceMode = true; 123 | } else if (lengthValue < 600) { 124 | sequenceLength = 8; 125 | inSequenceMode = true; 126 | } else if (lengthValue < 800) { 127 | sequenceLength = 16; 128 | inSequenceMode = true; 129 | } else { 130 | sequenceLength = 32; 131 | inSequenceMode = true; 132 | } 133 | 134 | // Generate sequence when the button is pressed 135 | if (digitalRead(buttonPin) == LOW) { 136 | generateSequence(sequenceLength); 137 | sequenceStep = 0; 138 | } 139 | 140 | // Check for rising edge of trigger (i.e., when trigger goes from LOW to HIGH) 141 | if (trigState == HIGH && lastTrigState == LOW) { 142 | unsigned long currentTime = millis(); 143 | unsigned long interval = currentTime - lastTriggerTime; // Time since last trigger 144 | lastTriggerTime = currentTime; // Update last trigger time 145 | 146 | // Read Pot1 (Probability) and adjust based on CV input (F2) 147 | probability = analogRead(potPin); 148 | if (inSequenceMode) { 149 | probability = 1023 - probability; // Reverse probability for sequence mode 150 | } 151 | cvValue = analogRead(cvInputPin); 152 | combinedProbability = constrain(probability + cvValue, 0, 1023); // Combine values 153 | 154 | // Miss Probability: Read Pot3 (missPotPin) to determine chance of missing trigger 155 | int missProbability = map(analogRead(missPotPin), 0, 1023, 0, 350); 156 | if (random(0, 1000) < missProbability) { 157 | lastTrigState = trigState; 158 | return; // Skip if trigger is missed 159 | } 160 | 161 | // Glitch Burst Probability: Determine chance of a glitch burst occurring 162 | int glitchThreshold = map(analogRead(missPotPin), 820, 1023, 0, 20); 163 | if (random(0, 100) < glitchThreshold) { 164 | glitchBurst(interval); // Call glitchBurst function to create random glitch bursts 165 | } 166 | 167 | // Handle sequence mode or Bernoulli mode 168 | int coinToss; 169 | if (inSequenceMode) { 170 | coinToss = random(0, 1023); 171 | outputPin = sequence[sequenceStep]; // Use pre-generated sequence 172 | sequenceStep = (sequenceStep + 1) % sequenceLength; // Move to next step in sequence 173 | } else { 174 | coinToss = random(0, 1023); 175 | outputPin = (coinToss < combinedProbability) ? outputPinF4 : outputPinF3; 176 | } 177 | 178 | // Activate output and LED 179 | digitalWrite(outputPin, HIGH); 180 | digitalWrite(ledPin, HIGH); 181 | pulseStartTime = millis(); 182 | pulseActive = true; 183 | } 184 | 185 | // Manage pulse duration: Turn off outputs and LED after pulseDuration 186 | if (pulseActive && millis() - pulseStartTime >= pulseDuration) { 187 | digitalWrite(outputPin, LOW); 188 | digitalWrite(ledPin, LOW); 189 | pulseActive = false; 190 | } 191 | 192 | lastTrigState = trigState; // Update last trigger state for next loop iteration 193 | } 194 | -------------------------------------------------------------------------------- /mod1-1D-wavetable/mod1-1D-wavetable.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Procedurally Generated Triple Wavetable LFO 3 | 4 | Triple wavetable / terrain LFO with CV speed modulation input and probabilistic SloMo mode. 5 | Firmware idea by Rob Heel for Mod1 eurorack module by Hagiwo. 6 | 7 | On each button press, three independent wavetables (“terrains”) are generated. 8 | Pot C controls the number of “knots” (points) in each waveform. Individual outs on F2, F3, F4. 9 | 10 | Waveform generation is semi-random, following musical constraints: 11 | - Starts and ends at the same value for seamless looping. 12 | - Contains at least one zero crossing. 13 | - Nonlinear knot spacing. 14 | - After a spike, a longer rest region follows. 15 | - One curved segment per waveform (Bézier) 16 | 17 | Wavetables reading has a defined detune in speed: 18 | speed1 * 0.9 -> F2; speed2 * 1.0 -> F3; speed3 * 1.1 -> F4; 19 | 20 | Random slow-mo events that scale with tempo - probability set via Potb. 21 | SloMo randomly and independently slows down the playback speed of each terrain waveform for a short, 22 | speed-dependent duration — creating natural, unsynced pauses or “breaths” in their motion. 23 | 24 | CV input (0 to 5 Volts) on F1 (A3) for speed offset (adds 0–1 Hz to base speed). 25 | 26 | Pots: 27 | A0 -> Base speed (0.01–5 Hz) 28 | A1 -> SloMo probability 29 | A2 -> Knots (3..12) 30 | 31 | Button: 32 | D4 -> generate new set of waveforms 33 | 34 | LED: 35 | D3 -> blink during generation 36 | 37 | Outputs: 38 | F2 (D9) : Terrain 1 39 | F3 (D10) : Terrain 2 40 | F4 (D11) : Terrain 3 41 | 42 | Inputs: 43 | F1 (A3) : CV input for speed offset (adds 0–1 Hz to base speed) 44 | */ 45 | 46 | #define TABLE_SIZE 256 47 | uint16_t terrain1[TABLE_SIZE]; 48 | uint16_t terrain2[TABLE_SIZE]; 49 | uint16_t terrain3[TABLE_SIZE]; 50 | 51 | float phase1 = 0.0, phase2 = 0.0, phase3 = 0.0; 52 | float lastPwm1 = 128.0, lastPwm2 = 128.0, lastPwm3 = 128.0; 53 | 54 | // --- Uncomment to enable serial debug --- 55 | // #define DEBUG_SERIAL 56 | 57 | // --- Pins --- 58 | const int potA = A0; // speed 59 | const int potB = A1; // SloMo probability 60 | const int potC = A2; // knots 61 | const int cvIn = A3; // F1 speed modulation CV in 62 | const int buttonPin = 4; 63 | const int ledPin = 3; 64 | 65 | // --- Debounce --- 66 | bool lastButtonState = HIGH; 67 | unsigned long lastDebounce = 0; 68 | const unsigned long debounceDelay = 50; 69 | bool blinking = false; 70 | unsigned long blinkStart = 0; 71 | const unsigned long blinkDuration = 200; 72 | 73 | // --- Utils --- 74 | float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) { 75 | return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; 76 | } 77 | 78 | // ----------------------------------------------------------------------------- 79 | // setup() 80 | // ----------------------------------------------------------------------------- 81 | void setup() { 82 | pinMode(potA, INPUT); 83 | pinMode(potB, INPUT); 84 | pinMode(potC, INPUT); 85 | pinMode(cvIn, INPUT); 86 | pinMode(buttonPin, INPUT_PULLUP); 87 | pinMode(ledPin, OUTPUT); 88 | 89 | // PWM outs 90 | pinMode(9, OUTPUT); // F2 91 | pinMode(10, OUTPUT); // F3 92 | pinMode(11, OUTPUT); // F4 93 | 94 | // --- Timer1 (16-bit) PWM Setup for D10 (F3) --- 95 | TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10); 96 | TCCR1B = _BV(WGM12) | _BV(CS10); // ~31.4 kHz PWM, no prescale 97 | 98 | // --- Timer2 (8-bit) PWM Setup for D11 (F4) --- 99 | TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20); 100 | TCCR2B = _BV(CS21); // prescale 8 → ~7.8 kHz PWM 101 | 102 | OCR1A = 128; 103 | OCR1B = 128; 104 | OCR2A = 128; 105 | 106 | randomSeed(analogRead(A7)); 107 | 108 | #ifdef DEBUG_SERIAL 109 | Serial.begin(115200); 110 | Serial.println("Dual Terrain LFO Ready"); 111 | #endif 112 | 113 | generateTerrains(); // initial generation 114 | } 115 | 116 | // ----------------------------------------------------------------------------- 117 | // generateTerrain() helper for one table 118 | // ----------------------------------------------------------------------------- 119 | void generateSingleTerrain(uint16_t* table, int knots, float heightScale) { 120 | float knotVals[24]; 121 | for (int k = 0; k < knots; ++k) { 122 | float val = (random(0, 1000) / 1000.0f) * heightScale; 123 | knotVals[k] = val; 124 | } 125 | knotVals[random(0, knots)] = 0.0f; 126 | knotVals[random(0, knots)] = heightScale; 127 | 128 | float knotPos[24]; 129 | float total = 0.0f; 130 | for (int k = 0; k < knots; ++k) { 131 | float step = random(50, 200) / 1000.0f; 132 | total += step; 133 | knotPos[k] = total; 134 | } 135 | for (int k = 0; k < knots; ++k) knotPos[k] /= total; 136 | 137 | knotVals[knots - 1] = knotVals[0]; 138 | knotPos[knots - 1] = 1.0f; 139 | 140 | int curvedSegment = random(0, knots - 1); 141 | float curvature = random(-80, 80) / 100.0f; 142 | 143 | for (int i = 0; i < TABLE_SIZE; ++i) { 144 | float t = (float)i / (TABLE_SIZE - 1); 145 | int k0 = 0; 146 | while (k0 < knots - 1 && t > knotPos[k0 + 1]) k0++; 147 | int k1 = min(k0 + 1, knots - 1); 148 | float frac = (t - knotPos[k0]) / (knotPos[k1] - knotPos[k0]); 149 | 150 | float v; 151 | if (k0 == curvedSegment) { 152 | float v0 = knotVals[k0]; 153 | float v1 = knotVals[k1]; 154 | float vMid = (v0 + v1) / 2.0f + curvature; 155 | vMid = constrain(vMid, 0.0f, 1.0f); 156 | float oneMinusT = 1.0f - frac; 157 | v = oneMinusT * oneMinusT * v0 158 | + 2.0f * oneMinusT * frac * vMid 159 | + frac * frac * v1; 160 | } else { 161 | v = knotVals[k0] * (1.0f - frac) + knotVals[k1] * frac; 162 | } 163 | 164 | table[i] = (uint16_t)(constrain(v, 0.0f, 1.0f) * 65535.0f); 165 | } 166 | } 167 | 168 | // ----------------------------------------------------------------------------- 169 | // generateTerrains() – at once 170 | // ----------------------------------------------------------------------------- 171 | void generateTerrains() { 172 | digitalWrite(ledPin, HIGH); 173 | blinking = true; 174 | blinkStart = millis(); 175 | 176 | int rawC = analogRead(potC); 177 | int knots = mapFloat(rawC, 0, 1023, 3, 12); 178 | float heightScale = 1.0; 179 | 180 | /* 181 | randomSeed(analogRead(A7)); 182 | generateSingleTerrain(terrain1, knots, heightScale); 183 | 184 | randomSeed(micros()); 185 | generateSingleTerrain(terrain2, knots, heightScale); 186 | 187 | generateSingleTerrain(terrain3, knots, heightScale); 188 | */ 189 | 190 | randomSeed(analogRead(A7)); 191 | generateSingleTerrain(terrain1, knots, heightScale); 192 | 193 | randomSeed(analogRead(A6)); 194 | generateSingleTerrain(terrain2, knots, heightScale); 195 | 196 | randomSeed(micros()); 197 | generateSingleTerrain(terrain3, knots, heightScale); 198 | 199 | phase1 = phase2 = phase3 = 0.0; 200 | 201 | #ifdef DEBUG_SERIAL 202 | Serial.println("New terrains generated"); 203 | #endif 204 | } 205 | 206 | // ----------------------------------------------------------------------------- 207 | // loop() 208 | // ----------------------------------------------------------------------------- 209 | void loop() { 210 | // --- button --- 211 | int reading = digitalRead(buttonPin); 212 | if (reading != lastButtonState) lastDebounce = millis(); 213 | if ((millis() - lastDebounce) > debounceDelay) { 214 | static int lastStableState = HIGH; 215 | if (reading != lastStableState) { 216 | lastStableState = reading; 217 | if (reading == LOW) generateTerrains(); 218 | } 219 | } 220 | lastButtonState = reading; 221 | 222 | if (blinking && millis() - blinkStart > blinkDuration) { 223 | digitalWrite(ledPin, LOW); 224 | blinking = false; 225 | } 226 | 227 | // --- base speed from pot --- 228 | float speedCtrl = analogRead(potA) / 1023.0; 229 | //float baseHz = 0.01 * pow(500.0, speedCtrl); // 0.01..5 Hz exponential 230 | float baseHz = 0.01 * pow(300.0, speedCtrl); // 0.01 .. ~3 Hz 231 | 232 | // --- CV modulation --- 233 | int rawCV = analogRead(cvIn); 234 | float cvHz = mapFloat(rawCV, 0, 1023, 0.0, 1.0); // 0–1 Hz 235 | float tableHz = baseHz + cvHz; // combined 236 | 237 | // --- clamp --- 238 | if (tableHz < 0.0) tableHz = 0.0; // safety cap 239 | if (tableHz > 10.0) tableHz = 10.0; // safety cap 240 | 241 | // --- time step --- 242 | static unsigned long lastTime = 0; 243 | unsigned long now = micros(); 244 | float dt = (now - lastTime) / 1e6; 245 | if (dt <= 0) dt = 0.001; 246 | lastTime = now; 247 | 248 | 249 | // --- PotB controls slowdown probability/intensity --- 250 | float intensity = analogRead(potB) / 1023.0; // 0.0 .. 1.0 251 | 252 | // --- fixed detune between the three outputs --- 253 | float speed1 = tableHz * 0.9; // F2 254 | float speed2 = tableHz * 1.0; // F3 255 | float speed3 = tableHz * 1.1; // F4 256 | 257 | // --- SlowMo struct for independent slowdown events --- 258 | struct SlowMo { 259 | bool active; 260 | float slowFactor; // e.g., 0.2 = 5× slower 261 | float duration; // milliseconds 262 | unsigned long startTime; 263 | }; 264 | static SlowMo slow1, slow2, slow3; 265 | 266 | // Helper lambda to maybe trigger one slowdown 267 | auto maybeTriggerSlowmo = [&](SlowMo &s) { 268 | // PotB controls probability of triggering 269 | if (!s.active && random(10000) < intensity * 5) { 270 | s.active = true; // intensity * 5 -> 0.05 % chance 271 | s.slowFactor = random(10, 50) / 100.0; // 0.1–0.5x speed 272 | float durBase = 800 + random(200, 3000); // 0.8–3 s // or base 2000 + random(1000, 6000) 273 | s.duration = durBase / (tableHz + 0.1); // slower base speed → longer event 274 | s.startTime = millis(); 275 | } 276 | if (s.active && (millis() - s.startTime > s.duration)) { 277 | s.active = false; // back to normal speed 278 | } 279 | }; 280 | 281 | // Evaluate possible slowdowns for each waveform 282 | maybeTriggerSlowmo(slow1); 283 | maybeTriggerSlowmo(slow2); 284 | maybeTriggerSlowmo(slow3); 285 | 286 | // --- Apply slowdown factors if active --- 287 | float actualSpeed1 = slow1.active ? speed1 * slow1.slowFactor : speed1; 288 | float actualSpeed2 = slow2.active ? speed2 * slow2.slowFactor : speed2; 289 | float actualSpeed3 = slow3.active ? speed3 * slow3.slowFactor : speed3; 290 | 291 | // --- Advance phases --- 292 | phase1 += actualSpeed1 * TABLE_SIZE * dt; 293 | phase2 += actualSpeed2 * TABLE_SIZE * dt; 294 | phase3 += actualSpeed3 * TABLE_SIZE * dt; 295 | 296 | // Wrap around 297 | if (phase1 >= TABLE_SIZE) phase1 -= TABLE_SIZE; 298 | if (phase2 >= TABLE_SIZE) phase2 -= TABLE_SIZE; 299 | if (phase3 >= TABLE_SIZE) phase3 -= TABLE_SIZE; 300 | 301 | // --- interpolation helper --- 302 | auto interpTable = [](uint16_t* tbl, float phase) { 303 | int i0 = (int)phase; 304 | int i1 = i0 + 1; 305 | if (i1 >= TABLE_SIZE) i1 = 0; 306 | float frac = phase - (float)i0; 307 | int32_t delta = (int32_t)tbl[i1] - (int32_t)tbl[i0]; 308 | int32_t tmp = (int32_t)tbl[i0] + (int32_t)(delta * frac); 309 | tmp = constrain(tmp, 0, 65535); 310 | return (uint16_t)tmp; 311 | }; 312 | 313 | // --- interpolate --- 314 | uint16_t interp1 = interpTable(terrain1, phase1); 315 | uint16_t interp2 = interpTable(terrain2, phase2); 316 | uint16_t interp3 = interpTable(terrain3, phase3); 317 | 318 | int pwm1 = (interp1 + 128) >> 8; 319 | int pwm2 = (interp2 + 128) >> 8; 320 | int pwm3 = (interp3 + 128) >> 8; 321 | 322 | lastPwm1 = lastPwm1 * 0.9 + pwm1 * 0.1; 323 | lastPwm2 = lastPwm2 * 0.9 + pwm2 * 0.1; 324 | lastPwm3 = lastPwm3 * 0.9 + pwm3 * 0.1; 325 | 326 | // --- write to PWM registers --- 327 | OCR1A = (uint8_t)lastPwm1; // F2 328 | OCR1B = (uint8_t)lastPwm2; // F3 329 | OCR2A = (uint8_t)lastPwm3; // F4 330 | 331 | #ifdef DEBUG_SERIAL 332 | static unsigned long lastPrint = 0; 333 | if (millis() - lastPrint > 100) { 334 | Serial.print("CVHz="); Serial.print(cvHz, 2); 335 | Serial.print(" BaseHz="); Serial.print(baseHz, 2); 336 | Serial.print(" TotalHz="); Serial.println(tableHz, 2); 337 | lastPrint = millis(); 338 | } 339 | #endif 340 | } 341 | -------------------------------------------------------------------------------- /mod1_3ch_LFO/mod1_3ch_LFO.ino: -------------------------------------------------------------------------------- 1 | /* 2 | HAGIWO MOD1 3ch LFO Ver1.0. 3ch output LFO. 3 | Adapted by Rob Heel. Added a fourth waveform: Random slope. 4 | 5 | --Pin assign--- 6 | POT1 A0 LFO1 frequency 7 | POT2 A1 LFO2 frequency 8 | POT3 A2 LFO3 frequency 9 | F1 A3 frequency CV in (apply all ch LFO freq) 10 | F2 A4 LFO1 output D9 11 | F3 A5 LFO2 output D10 12 | F4 D11 LFO3 output D11 13 | BUTTON change waveform 14 | LED LFO1 output 15 | EEPROM Saves select waveform 16 | 17 | This program generates independent LFO signals (D9, D10, D11) and an LED indicator (D3) at ~62.5kHz PWM. 18 | Each LFO has a selectable waveform (Triangle, Square, Sine, Random Slope) chosen by a push button on D4 (INPUT_PULLUP). 19 | The frequencies of the LFOs are controlled by three pots on A0, A1, A2, plus an offset on A3. 20 | Now the maximum frequency is changed from 2Hz to 5Hz. (0.02 ~ 5Hz range) 21 | Whenever the waveform changes, its type is saved to EEPROM and upon startup, the saved waveform type is restored from EEPROM. 22 | */ 23 | 24 | #include // Include EEPROM library for read/write 25 | 26 | // ---------------- Global Variables and Constants ---------------- 27 | static const unsigned int TABLE_SIZE = 1024; // 1024 points in one waveform cycle 28 | static const unsigned long UPDATE_INTERVAL_US = 400; // ~2500Hz LFO update rate 29 | static const unsigned long debounceDelay = 50UL; // 50ms for switch debounce 30 | 31 | // Single wave table (8-bit resolution) 32 | uint8_t waveTable[TABLE_SIZE]; 33 | 34 | // Current wave type: 0=Triangle, 1=Square, 2=Sine, 3=Random Slope 35 | int waveType = 0; 36 | 37 | // Phase indexes for each LFO 38 | float lfoIndex1 = 0.0; 39 | float lfoIndex2 = 0.0; 40 | float lfoIndex3 = 0.0; 41 | 42 | // LFO frequencies for each channel 43 | float lfoFreq1 = 0.02; 44 | float lfoFreq2 = 0.02; 45 | float lfoFreq3 = 0.02; 46 | 47 | // Timing variables for LFO update 48 | unsigned long previousMicros = 0; 49 | 50 | // Debounce variables 51 | int lastButtonState = HIGH; // HIGH means not pressed (pull-up) 52 | unsigned long buttonPreviousMillis = 0; // Last time we acknowledged a button press 53 | 54 | // Random Slope variables 55 | float currentSlopeValue1 = 0; 56 | float targetSlopeValue1 = 0; 57 | float slopeStep1 = 0; 58 | unsigned long previousMillis1 = 0; 59 | 60 | float currentSlopeValue2 = 0; 61 | float targetSlopeValue2 = 0; 62 | float slopeStep2 = 0; 63 | unsigned long previousMillis2 = 0; 64 | 65 | float currentSlopeValue3 = 0; 66 | float targetSlopeValue3 = 0; 67 | float slopeStep3 = 0; 68 | unsigned long previousMillis3 = 0; 69 | 70 | // ---------------- Function Prototypes ---------------- 71 | void configurePWM(); // Set up Timer1 and Timer2 for ~62.5kHz PWM 72 | void createWaveTable(int type); // Re-generate wave table for the chosen wave type 73 | void createTriangleTable(); 74 | void createSquareTable(); 75 | void createSineTable(); 76 | void updateRandomSlope(float ¤tMillis, float &previousMillis, float ¤tSlopeValue, float &targetSlopeValue, float &slopeStep, float frequency); 77 | float readFrequency(int analogPin); // Maps analog input (0~1023) to 0.02~5.0Hz 78 | float readFrequencyOffset(int analogPin); // Maps analog input (0~1023) to 0.02~5.0Hz 79 | void updateLFO(float &phaseIndex, float freq); 80 | void handleButtonInput(); // Debounced button reading using millis() 81 | 82 | // ------------------------------------------------------ 83 | void setup() { 84 | // Configure I/O pins 85 | pinMode(9, OUTPUT); // LFO1 (OCR1A) 86 | pinMode(10, OUTPUT); // LFO2 (OCR1B) 87 | pinMode(11, OUTPUT); // LFO3 (OCR2A) 88 | pinMode(3, OUTPUT); // LED indicator (OCR2B) 89 | pinMode(4, INPUT_PULLUP); // Push button (pull-up); press => LOW 90 | 91 | // Configure Timer1 & Timer2 to ~62.5kHz PWM 92 | configurePWM(); 93 | 94 | // Read waveType from EEPROM (address 0) 95 | int storedWaveType = EEPROM.read(0); // Range could be 0~255 96 | // Validate the stored value (must be 0,1,2,3); if invalid, fallback to 0 97 | if (storedWaveType < 0 || storedWaveType > 3) { 98 | storedWaveType = 0; 99 | } 100 | waveType = storedWaveType; 101 | 102 | // Create the initial wave table based on EEPROM 103 | createWaveTable(waveType); 104 | } 105 | 106 | // ------------------------------------------------------ 107 | void loop() { 108 | // Handle push button input (debounce) to change the waveform 109 | handleButtonInput(); 110 | 111 | // Update LFO signals at ~2500Hz 112 | unsigned long currentMicros = micros(); 113 | if (currentMicros - previousMicros >= UPDATE_INTERVAL_US) { 114 | previousMicros = currentMicros; 115 | 116 | // Read pot values for each LFO base frequency 117 | float baseFreq1 = readFrequency(A0); 118 | float baseFreq2 = readFrequency(A1); 119 | float baseFreq3 = readFrequency(A2); 120 | 121 | // Read offset from A3 (0~5V => 0.02~5.0Hz) 122 | float freqOffset = readFrequencyOffset(A3); 123 | 124 | // Combine them: baseFreq + offset 125 | lfoFreq1 = baseFreq1 + freqOffset; 126 | lfoFreq2 = baseFreq2 + freqOffset; 127 | lfoFreq3 = baseFreq3 + freqOffset; 128 | 129 | if (waveType == 3) { // Random Slope 130 | unsigned long currentMillis = millis(); 131 | updateRandomSlope(currentMillis, previousMillis1, currentSlopeValue1, targetSlopeValue1, slopeStep1, lfoFreq1); 132 | updateRandomSlope(currentMillis, previousMillis2, currentSlopeValue2, targetSlopeValue2, slopeStep2, lfoFreq2); 133 | updateRandomSlope(currentMillis, previousMillis3, currentSlopeValue3, targetSlopeValue3, slopeStep3, lfoFreq3); 134 | 135 | // Set duty cycles directly via OCR registers 136 | OCR1A = currentSlopeValue1; 137 | OCR1B = currentSlopeValue2; 138 | OCR2A = currentSlopeValue3; 139 | OCR2B = currentSlopeValue1; // LED shows LFO1's output 140 | } else { 141 | // Update phase indexes 142 | updateLFO(lfoIndex1, lfoFreq1); 143 | updateLFO(lfoIndex2, lfoFreq2); 144 | updateLFO(lfoIndex3, lfoFreq3); 145 | 146 | // Get table positions (integer) from phase indexes 147 | int tablePos1 = (int)lfoIndex1 % TABLE_SIZE; 148 | int tablePos2 = (int)lfoIndex2 % TABLE_SIZE; 149 | int tablePos3 = (int)lfoIndex3 % TABLE_SIZE; 150 | 151 | // Read waveTable for each LFO 152 | uint8_t outputVal1 = waveTable[tablePos1]; 153 | uint8_t outputVal2 = waveTable[tablePos2]; 154 | uint8_t outputVal3 = waveTable[tablePos3]; 155 | 156 | // Set duty cycles directly via OCR registers 157 | // LFO1 -> OCR1A (Pin 9) 158 | // LFO2 -> OCR1B (Pin 10) 159 | // LFO3 -> OCR2A (Pin 11) 160 | // LED indicator (same as LFO1) -> OCR2B (Pin 3) 161 | 162 | OCR1A = outputVal1; 163 | OCR1B = outputVal2; 164 | OCR2A = outputVal3; 165 | OCR2B = outputVal1; // LED shows LFO1's output 166 | } 167 | } 168 | } 169 | 170 | // ------------------------------------------------------ 171 | // Configure Timer1 (16-bit) and Timer2 (8-bit) for ~62.5kHz PWM 172 | void configurePWM() { 173 | // ---- Timer1 setup (Pins 9=OCR1A, 10=OCR1B) ---- 174 | // Fast PWM 8-bit mode: WGM10=1, WGM11=0, WGM12=1, WGM13=0 175 | // Non-inverting for OCR1A, OCR1B: COM1A1=1, COM1B1=1 176 | // No prescaler: CS10=1 177 | TCCR1A = 0; 178 | TCCR1B = 0; 179 | TCCR1A |= (1 << WGM10) | (1 << COM1A1) | (1 << COM1B1); 180 | TCCR1B |= (1 << WGM12) | (1 << CS10); 181 | 182 | // ---- Timer2 setup (Pins 3=OCR2B, 11=OCR2A) ---- 183 | // Fast PWM mode (0xFF): WGM20=1, WGM21=1, WGM22=0 184 | // Non-inverting for OCR2A, OCR2B: COM2A1=1, COM2B1=1 185 | // No prescaler: CS20=1 186 | TCCR2A = 0; 187 | TCCR2B = 0; 188 | TCCR2A |= (1 << WGM20) | (1 << WGM21) | (1 << COM2A1) | (1 << COM2B1); 189 | TCCR2B |= (1 << CS20); 190 | } 191 | 192 | // ------------------------------------------------------ 193 | // Debounced button input using millis() 194 | void handleButtonInput() { 195 | // Read current button state (LOW when pressed) 196 | int reading = digitalRead(4); 197 | unsigned long currentMillis = millis(); 198 | 199 | // Check if enough time has passed to confirm a new button event 200 | if (currentMillis - buttonPreviousMillis > debounceDelay) { 201 | // Detect transition from HIGH to LOW (button press) 202 | if (reading == LOW && lastButtonState == HIGH) { 203 | // Cycle waveType: 0->1->2->3->0->... 204 | waveType = (waveType + 1) % 4; 205 | 206 | // Regenerate the wave table for the new wave type 207 | createWaveTable(waveType); 208 | 209 | // Save the new waveType to EEPROM 210 | EEPROM.write(0, waveType); 211 | 212 | // Update the timestamp of this confirmed press 213 | buttonPreviousMillis = currentMillis; 214 | } 215 | } 216 | // Update for next iteration 217 | lastButtonState = reading; 218 | } 219 | 220 | // ------------------------------------------------------ 221 | // Create wave table based on waveType 222 | void createWaveTable(int type) { 223 | switch (type) { 224 | case 0: // Triangle 225 | createTriangleTable(); 226 | break; 227 | case 1: // Square 228 | createSquareTable(); 229 | break; 230 | case 2: // Sine 231 | createSineTable(); 232 | break; 233 | default: // Random Slope (no wave table needed) 234 | break; 235 | } 236 | } 237 | 238 | // ------------------------------------------------------ 239 | // Create a triangle wave in waveTable (8-bit range 0~255) 240 | void createTriangleTable() { 241 | // First half rising 0->255, second half falling 255->0 242 | for (int i = 0; i < TABLE_SIZE; i++) { 243 | if (i < (TABLE_SIZE / 2)) { 244 | float val = (255.0f * i) / (TABLE_SIZE / 2); 245 | waveTable[i] = (uint8_t)val; 246 | } else { 247 | int j = i - (TABLE_SIZE / 2); 248 | float val = 255.0f - (255.0f * j) / (TABLE_SIZE / 2); 249 | waveTable[i] = (uint8_t)val; 250 | } 251 | } 252 | } 253 | 254 | // ------------------------------------------------------ 255 | // Create a square wave in waveTable (8-bit range 0 or 255) 256 | void createSquareTable() { 257 | // First half of cycle = 0, second half = 255 258 | for (int i = 0; i < TABLE_SIZE; i++) { 259 | if (i < (TABLE_SIZE / 2)) { 260 | waveTable[i] = 0; 261 | } else { 262 | waveTable[i] = 255; 263 | } 264 | } 265 | } 266 | 267 | // ------------------------------------------------------ 268 | // Create a sine wave in waveTable (8-bit range 0~255) 269 | void createSineTable() { 270 | for (int i = 0; i < TABLE_SIZE; i++) { 271 | // Angle from 0 to 2*pi 272 | float angle = 2.0f * 3.14159265359f * ((float)i / (float)TABLE_SIZE); 273 | // Map sin() from -1~+1 to 0~255 274 | float sineVal = (sin(angle) + 1.0f) * 127.5f; 275 | if (sineVal < 0.0f) sineVal = 0.0f; 276 | if (sineVal > 255.0f) sineVal = 255.0f; 277 | waveTable[i] = (uint8_t)sineVal; 278 | } 279 | } 280 | 281 | // ------------------------------------------------------ 282 | // Map analog input (0~1023) to 0.02~5.0Hz 283 | float readFrequency(int analogPin) { 284 | int rawVal = analogRead(analogPin); 285 | float fMin = 0.02f; 286 | float fMax = 5.0f; // Changed upper limit to 5.0Hz 287 | float freq = fMin + (fMax - fMin) * (rawVal / 1023.0f); 288 | return freq; 289 | } 290 | 291 | // ------------------------------------------------------ 292 | // Map analog input (0~1023) to 0.02~5.0Hz (offset) 293 | float readFrequencyOffset(int analogPin) { 294 | int rawVal = analogRead(analogPin); 295 | float fMin = 0.02f; 296 | float fMax = 5.0f; // Changed upper limit to 5.0Hz 297 | float offset = fMin + (fMax - fMin) * (rawVal / 1023.0f); 298 | return offset; 299 | } 300 | 301 | // ------------------------------------------------------ 302 | // Update LFO phase index based on frequency 303 | void updateLFO(float &phaseIndex, float freq) { 304 | // ~2500 updates per second => increment = freq * (TABLE_SIZE / 2500) 305 | float increment = freq * ((float)TABLE_SIZE / 2500.0f); 306 | phaseIndex += increment; 307 | // The table access uses % TABLE_SIZE on int cast, so no clamp needed here 308 | } 309 | 310 | // ------------------------------------------------------ 311 | // Update Random Slope waveform 312 | void updateRandomSlope(unsigned long currentMillis, unsigned long &previousMillis, float ¤tSlopeValue, float &targetSlopeValue, float &slopeStep, float frequency) { 313 | if (currentMillis - previousMillis >= (1000.0 / frequency)) { 314 | previousMillis = currentMillis; 315 | targetSlopeValue = random(0, 256); // Generate a random value between 0 and 255 316 | slopeStep = (targetSlopeValue - currentSlopeValue) / (1000.0 / frequency); 317 | } 318 | 319 | currentSlopeValue += slopeStep; 320 | 321 | // Keep within bounds 322 | if ((slopeStep > 0 && currentSlopeValue >= targetSlopeValue) || 323 | (slopeStep < 0 && currentSlopeValue <= targetSlopeValue)) { 324 | currentSlopeValue = targetSlopeValue; 325 | } 326 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **Hagiwo MOD1 firmwares** 2 | 3 | Alternative firmwares for Hagiwos Mod1 Arduino based Module. 4 | https://note.com/solder_state/n/nc05d8e8fd311 5 | 6 | Mod1 is an Arduino Nano based eurorack module designed by Hagiwo, that can be utilized with own code. 7 | Here you will find some slightly changed or completely new code ideas for this module. 8 | 9 | 10 | ![mod1](https://assets.st-note.com/production/uploads/images/166671260/rectangle_large_type_2_74d04b7593d4c5aa3a08d021646da297.jpeg) 11 | 12 | # Current firmwares 13 | - [Mod1 LFO](#mod1-lfo) 14 | - [3chan LFO](#3chan-lfo) 15 | - [randomwalk](#randomwalk) 16 | - [bezier curve with spike/ jitter mode](#bezier-curve-with-spike-jitter-mode) 17 | - [sample and hold with slew](#sample-and-hold-with-slew) 18 | - [Enhanced Bernoulli Gate and Loop Sequencer with glitch bursts](#enhanced-bernoulli-gate-and-loop-sequencer-with-glitch-bursts) 19 | - [pot1 recorder](#pot1-recorder) 20 | - [Turing machine/ Klee style sequencer with quantized/ unquantized CV out](#turing-machine-klee-style-sequencer-with-quantized-and-unquantized-cv-out) 21 | - [Lorenz System](#lorenz-system) 22 | - [Dual AD envelope](#Dual-AD-envelope) 23 | - [Procedurally Generated Triple Wavetable LFO](#Procedurally-Generated-Triple-Wavetable-LFO) 24 | 25 | # Mod1 LFO 26 | Multi waveform LFO.\ 27 | Instead of SawRevWave a random slope is used as 5th waveform. 28 | SawRevWave is still in the code if you want to return to Hagiwos original code.\ 29 | Selectable waveforms: Sine, Triangle, Square, Saw, Random Slope, MaxTable waveforms. 30 | 31 | - Pot1 → frequency 32 | - Pot2 → waveform select 33 | - Pot3 → output level 34 | - F1 → frequency CV in 35 | - F2 → waveform CV in 36 | - F3 → output level CV in 37 | - F4 → output 38 | - BUTTON → change frequency range 39 | - LED → output 40 | - EEPROM → Saves the frequency range when the button is pressed 41 | 42 | # 3chan LFO 43 | Three channel LFO.\ 44 | Added a fourth waveform: Random slope.\ 45 | Selectable waveforms: Triangle, Square, Sine, Random Slope chosen by a push button on D4. 46 | 47 | - Pot1 → LFO1 frequency 48 | - Pot2 → LFO2 frequency 49 | - Pot3 → LFO3 frequency 50 | - F1 → frequency CV in (apply to all ch LFO freq) 51 | - F2 → LFO1 output 52 | - F3 → LFO2 output 53 | - F4 → LFO3 output 54 | - BUTTON → change waveform 55 | - LED → LFO1 output 56 | - EEPROM → Saves select waveform 57 | 58 | # randomwalk 59 | Random Walk with Gravity Mode + Lagged Output\ 60 | Button toggles between classic Random Walk mode and Gravity Mode.\ 61 | Gravity Mode pulls the output slowly back to 0 over time.\ 62 | F2 outputs a lagged/delayed version of F4, great for ambient crossfading effects.\ 63 | The lagged version creates beautiful dancing relationships with F4, when modulated via CV in. 64 | 65 | Slow, evolving CV signals, offering both chaotic and stabilized output behaviors. 66 | 67 | Rate, Bias/Offset, ChaosDepth can be controlled via pots. 68 | 69 | Classic Random Walk Mode: Generates smooth, random CV output with adjustable step size.\ 70 | Gravity Mode: Adds a gradual pull back to zero, creating a drifting, self-centering effect. 71 | 72 | - Pot1 → Rate / Control the frequency of random steps. 73 | - Pot2 → Bias/Offset /Shifts the output up or down. 74 | - Pot3 → ChaosDepth (step size of walk) / Adjust the step size from subtle drifts to wild jumps. 75 | - Button → switch mode 76 | - F1 → CV input controls Lag Amount - how closely F2 follows F4. Perfect for LFO modulation! 77 | - F2 → CV output/ Lagged CV out 78 | - F3 → CV input / adds to ChaosDepth 79 | - F4 → Random Walk Output 80 | 81 | # bezier curve with spike jitter mode 82 | Bezier curve random CV generator by Hagiwo adapted for Mod1 and added a second Spike/Jitter Bezier mode, accesible via button. 83 | 84 | Smooth random CV source with additional spike and jitter mode. 85 | 86 | Spike, jitter probability and spike length can be fine tuned in code. I think i settled for nicely erratic not too random values in the spike mode. 87 | 88 | - Pot1 → freq 89 | - Pot2 → curve 90 | - Pot3 → dev 91 | - F1 → freq CV in 92 | - F2 → curve CV in 93 | - F3 → dev CV in 94 | - F4 → output 95 | - LED → Pin 3 96 | - Button → switch modes 97 | 98 | 99 | # sample and hold with slew 100 | Classical sample and hold function. Sample and hold output on F4, triggered by button or F1 trigger input.\ 101 | If nothing is patched into sample input F2 an internal sample source is used. 102 | 103 | Pot1 is a bias for the internal noise.\ 104 | In the middle position truly random, fully clockwise shifts towards higher values, fully counter clockwise shifts towards lower values. 105 | 106 | Pot3 is gain/level. 107 | 108 | Sample and hold output on F4\ 109 | Slewed output on F3 with Pot 2 controling time constant of the slew, up to 1000 ms slew. 110 | 111 | - Pot1 → A0 noise bias 112 | - Pot2 → A1 time constant slew 113 | - Pot3 → A2 gain 114 | - F1 → A3 trigger in 115 | - F2 → A4 sample input 116 | - F3 → A5 slew output 117 | - F4 → D11 sample and hold output 118 | - LED → Pin 3 119 | - Button → Pin 4 trigger 120 | 121 | # Enhanced Bernoulli Gate and Loop Sequencer with glitch bursts 122 | This firmware combines a probabilistic Bernoulli gate with a step sequencer, providing flexible trigger manipulation and glitchy bursts for 123 | randomness, controlled probabilities, and rhythmic chaos. 124 | 125 | Route incoming triggers to either of the two outputs. The decision is random, with a controllable amount of randomness/ probability (pot1 + CV input). 126 | 127 | In Step Sequence Modes, pressing the button generates a new randomized sequence. 4-Step, 8-Step, 32-Step Sequence. 128 | 129 | With pot3 you can introduce Miss Probability and Glitch Bursts. 130 | 131 | 132 | This combination of probability control, step sequencing, trigger skipping, and glitch bursts makes the module versatile for generative rhythms, random variations, and chaotic textures. 133 | 134 | Controls:\ 135 | Pot 1 Probability\ 136 | Sets the probability of switching between the two outputs (F3 and F4).\ 137 | Fully counterclockwise: Always output F3.\ 138 | Fully clockwise: Always output F4.\ 139 | Intermediate positions introduce a random chance of either output based on the pot setting. 140 | 141 | Pot 2 Mode/Length\ 142 | Selects between Bernoulli Mode and Step Sequence Modes:\ 143 | 0-20% (CCW): Bernoulli Mode\ 144 | 20-40%: 4-Step Sequence\ 145 | 40-60%: 8-Step Sequence\ 146 | 60-80%: 16-Step Sequence\ 147 | 80-100% (CW): 32-Step Sequence\ 148 | In Step Sequence Modes, pressing the button generates a new randomized sequence. 149 | 150 | Pot 3 Miss Probability and Glitch Burst\ 151 | Sets the probability of skipping triggers:\ 152 | Fully counterclockwise: No triggers are missed.\ 153 | Turn clockwise: Up to 35% of triggers are missed. Linear 0-35%.\ 154 | 80-100% (CW): Introduces Glitch Bursts, where rapid chaotic trigger sequences are generated occasionally. 155 | 156 | Inputs and Outputs:\ 157 | F1 Trigger Input\ 158 | Incoming trigger signal to be processed through the Bernoulli gate or sequencer. 159 | 160 | F2 CV Input\ 161 | Modulates the output probability in conjunction with Pot 1, allowing CV control. 162 | 163 | F3 Output 1\ 164 | One of the two output trigger channels, chosen based on the current probability setting. 165 | 166 | F4 Output 2\ 167 | The second output trigger channel, acting as a counterpart to F3. 168 | 169 | Button\ 170 | Pressing the button in Sequence Mode regenerates the step sequence with a new randomized pattern. 171 | In Bernoulli Mode, the button has no effect. 172 | 173 | LED Indicator\ 174 | Flashes on every valid trigger output (either F3 or F4). 175 | 176 | 177 | # pot1 recorder 178 | 179 | Pot1 Recorder with Fast PWM Output (100 Hz S&H style) max recording time 7.5 sec.\ 180 | S&H style recording and playback in order to squeeze out a useful recording time of the nano’s small SRAM. 181 | 182 | You can record movement of pot1 while holding down the button. Once let go it will loop the recording. 183 | 184 | Pot2 is speed, from half to double speed.\ 185 | Pot3 is gain control. 186 | 187 | Speed CV offset via F1 CV in.\ 188 | Gain CV offset via F2 CV in. 189 | 190 | CV out on F4. 191 | 192 | - Pot1 → Pot to record 193 | - Pot2 → Speed control of recording 194 | - Pot3 → Output gain 195 | - F1 → Speed offset CV in 0-5 V 196 | - F2 → Gain offset CV in 0-5 V 197 | - F3 → / 198 | - F4 → Output 199 | - LED → Visualize Output 200 | - Button → Recording button 201 | 202 | # Turing Machine Klee-style sequencer with quantized and unquantized CV out 203 | 204 | This firmware turns your Mod1 into a Turing Machine / Klee-style sequencer with quantized and unquantized CV out. 205 | 206 | It generates evolving or locked step sequences using a shift register, modulated by a randomness knob and/ or CV with slip, lock, and random modes. 207 | 208 | Loop length and output range are controllable via pots. 209 | 210 | Musical scales are selectable via a button for the quantized output. 211 | 212 | Great for random, looped and evolving melodies and patterns. 213 | 214 | Pot1 → Main sequencing pot (random/ slip/ lock)\ 215 | At noon, the sequences are random.\ 216 | At 5 o'clock, it locks into a repeating sequence.\ 217 | At 7 o'clock, it double locks into a repeating sequence twice as long as the 'length' setting.\ 218 | At 3 o'clock or 9 o'clock, it slips; looping but occasionally changing notes. 219 | 220 | Pot2 → Loop length\ 221 | Pot3 → Range/ gain\ 222 | F1 → Trigger in\ 223 | F2 → CV in offset for pot1\ 224 | F3 → Quantized CV output\ 225 | F4 → CV output\ 226 | BUTTON → change scale for quantized output\ 227 | 228 | 229 | # Lorenz System 230 | 231 | Lorenz Attractors are great for organic, non-repeating CV movement, chaotic but not random. 232 | This system inspired the popular 'butterfly effect' metaphor, where small changes can lead 233 | to dramatically different outcomes.” 234 | 235 | Pots:\ 236 | A0 → Sigma (flow strength / controls how fast x and y try to equalize/ also mapped to stepSize\ 237 | A1 → Rho (divergence / higher = stronger pull from center -> more chaos, from calm to chaos)\ 238 | A2 → Beta (damping / controls how sharply z grows or decays) 239 | 240 | Input:\ 241 | F1 (A3 / D17) → Trigger input (resets attractor on rising edge) 242 | 243 | Outputs:\ 244 | D9 (F2) → x axis (PWM CV)\ 245 | D10 (F3) → y axis (PWM CV)\ 246 | D11 (F4) → z axis (PWM CV) 247 | 248 | Button (D4) → toggle normal and slow mode\ 249 | LED (D3) → Blinks in stepsize 250 | 251 | 252 | 253 | # Dual AD envelope 254 | 255 | Two independent Attack–Decay envelopes with shared attack and release knobs.\ 256 | Per-envelope random timing variation via Pot3 (A2):\ 257 |  • Fully CCW → no variation\ 258 |  • Fully CW → max random deviation per trigger\ 259 | Variations are stronger at shorter attack/release settings, lighter at longer ones. 260 | 261 | Envelope 1 is also manually triggerable via push button.\ 262 | LED indicates envelope 1 level. 263 | 264 | Outputs are fast PWM (16-bit for ENV1/ENV2, 8-bit LED).\ 265 | Designed for Eurorack/modular trigger input and CV envelope output. 266 | 267 | - Pot1 → attack 268 | - Pot2 → decay 269 | - Pot3 → variation amount 270 | - F1 → trigger in envelope1 271 | - F2 → envelope1 out 272 | - F3 → output level CV in 273 | - F4 → trigger in envelope2 274 | - BUTTON → trigger envelope1 275 | - LED → output envelope1 276 | 277 | 278 | # Procedurally Generated Triple Wavetable LFO 279 | 280 | Triple wavetable / terrain LFO with CV speed modulation input and probabilistic SloMo mode. 281 | 282 | On each button press, three independent wavetables (“terrains”) are generated.\ 283 | Pot C controls the number of “knots” (points) in each waveform. Individual outs on F2, F3, F4. 284 | 285 | Waveform generation is semi-random, following musical constraints: 286 | - Starts and ends at the same value for seamless looping. 287 | - Contains at least one zero crossing. 288 | - Nonlinear knot spacing. 289 | - After a spike, a longer rest region follows. 290 | - One curved segment per waveform (Bézier) 291 | 292 | 293 | 294 | ![mod1-wavetable_terrain](https://github.com/user-attachments/assets/1d06856a-d64a-48b7-a007-e2a16f4279a4) 295 | 296 | 297 | 298 | Wavetables reading has a defined detune in speed:\ 299 | speed1 * 0.9 -> F2; speed2 * 1.0 -> F3; speed3 * 1.1 -> F4; 300 | 301 | Random slow-mo events that scale with tempo - probability set via Potb.\ 302 | SloMo randomly and independently slows down the playback speed of each terrain waveform for a short,\ 303 | speed-dependent duration — creating natural, unsynced pauses or “breaths” in their motion. 304 | 305 | CV input (0 to 5 Volts) on F1 (A3) for speed offset (adds 0–1 Hz to base speed). 306 | 307 | Pots:\ 308 | A0 -> Base speed (0.01–5 Hz)\ 309 | A1 -> SloMo probability\ 310 | A2 -> Knots (3..12) 311 | 312 | Button:\ 313 | D4 -> generate new set of waveforms 314 | 315 | LED:\ 316 | D3 -> blink during generation 317 | 318 | Outputs:\ 319 | F2 (D9) : Terrain 1\ 320 | F3 (D10) : Terrain 2\ 321 | F4 (D11) : Terrain 3 322 | 323 | Inputs:\ 324 | F1 (A3) : CV input for speed offset (adds 0–1 Hz to base speed) 325 | 326 | 327 | 328 | 329 | # mod1 general Hardware Configuration 330 | Potentiometers 331 | - Pot1 → A0 332 | - Pot2 → A1 333 | - Pot3 → A2 334 | 335 | Inputs/Outputs: 336 | - F1 → A3/ D17 in/output 337 | - F2 → A4/ D9 in/output 338 | - F3 → A5/ D10 in/output 339 | - F4 → D11 in/output 340 | 341 | - LED → Pin 3 342 | - Button → Pin 4 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | -------------------------------------------------------------------------------- /dual_ad_envelope/dual_ad_envelope.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Dual AD envelope for Mod1 module designed by HAGIWO, adapted by Rob Heel. 3 | 4 | Two independent Attack–Decay envelopes with shared attack and release knobs. 5 | Per-envelope random timing variation via Pot3 (A2): 6 |  • Fully CCW → no variation 7 |  • Fully CW → max random deviation per trigger 8 | Variations are stronger at shorter attack/release settings, lighter at longer ones. 9 | 10 | Envelope 1 is also manually triggerable via push button. 11 | LED indicates envelope 1 level. 12 | 13 | Outputs are fast PWM (16-bit for ENV1/ENV2, 8-bit LED). 14 | Designed for Eurorack/modular trigger input and CV envelope output. 15 | 16 | --Pin assign--- 17 | POT1 A0 Attack time 18 | POT2 A1 Release Time 19 | POT3 A2 Variation amount 20 | 21 | F1 D17 Trigger1 IN 22 | F2 D9 envelope1 out 23 | 24 | F3 D10 Trigger2 IN 25 | F4 D11 envelope2 out 26 | BUTTON Trigger envelope1 27 | LED output envelope1 28 | EEPROM N/A 29 | 30 | */ 31 | #define TABLE_SIZE 1024 32 | #define Brightness 160 //0 - 255 33 | 34 | const static PROGMEM byte Curve[1024] = { 35 | 255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224, 224, 223, 222, 221, 220, 219, 218, 217, 217, 216, 215, 214, 213, 212, 211, 211, 210, 209, 208, 207, 206, 206, 205, 204, 203, 202, 202, 201, 200, 199, 198, 198, 197, 196, 195, 194, 194, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 177, 176, 175, 174, 174, 173, 172, 172, 171, 170, 170, 169, 168, 168, 167, 166, 166, 165, 164, 164, 163, 162, 162, 161, 160, 160, 159, 158, 158, 157, 157, 156, 155, 155, 154, 153, 153, 152, 152, 151, 150, 150, 149, 149, 148, 147, 147, 146, 146, 145, 144, 144, 143, 143, 142, 142, 141, 140, 140, 139, 139, 138, 138, 137, 136, 136, 135, 135, 134, 134, 133, 133, 132, 132, 131, 131, 130, 129, 129, 128, 128, 127, 127, 126, 126, 125, 125, 124, 124, 123, 123, 122, 122, 121, 121, 120, 120, 119, 119, 118, 118, 117, 117, 117, 116, 116, 115, 115, 114, 114, 113, 113, 112, 112, 111, 111, 111, 110, 110, 109, 109, 108, 108, 107, 107, 107, 106, 106, 105, 105, 104, 104, 104, 103, 103, 102, 102, 101, 101, 101, 100, 100, 99, 99, 99, 98, 98, 97, 97, 97, 96, 96, 95, 95, 95, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91, 90, 90, 90, 89, 89, 89, 88, 88, 88, 87, 87, 86, 86, 86, 85, 85, 85, 84, 84, 84, 83, 83, 83, 82, 82, 82, 81, 81, 81, 80, 80, 80, 79, 79, 79, 78, 78, 78, 77, 77, 77, 76, 76, 76, 75, 75, 75, 74, 74, 74, 74, 73, 73, 73, 72, 72, 72, 71, 71, 71, 71, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 60, 60, 60, 60, 59, 59, 59, 59, 58, 58, 58, 58, 57, 57, 57, 57, 56, 56, 56, 56, 56, 55, 55, 55, 55, 54, 54, 54, 54, 53, 53, 53, 53, 53, 52, 52, 52, 52, 51, 51, 51, 51, 51, 50, 50, 50, 50, 49, 49, 49, 49, 49, 48, 48, 48, 48, 48, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 43, 43, 43, 43, 43, 43, 42, 42, 42, 42, 42, 41, 41, 41, 41, 41, 41, 40, 40, 40, 40, 40, 40, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, 38, 37, 37, 37, 37, 37, 37, 37, 36, 36, 36, 36, 36, 36, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 33, 33, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 29, 29, 29, 29, 29, 29, 29, 29, 28, 28, 28, 28, 28, 28, 28, 28, 27, 27, 27, 27, 27, 27, 27, 27, 26, 26, 26, 26, 26, 26, 26, 26, 25, 25, 25, 25, 25, 25, 25, 25, 25, 24, 24, 24, 24, 24, 24, 24, 24, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 36 | }; 37 | 38 | const static PROGMEM int PotAdjust[1024] = { 39 | 0, 5, 10, 15, 20, 25, 30, 35, 39, 44, 49, 54, 59, 63, 68, 73, 77, 82, 87, 91, 96, 100, 105, 109, 114, 118, 123, 127, 132, 136, 140, 145, 149, 153, 158, 162, 166, 170, 174, 179, 183, 187, 191, 195, 199, 203, 207, 211, 215, 219, 223, 227, 231, 235, 239, 243, 246, 250, 254, 258, 262, 265, 269, 273, 276, 280, 284, 287, 291, 295, 298, 302, 305, 309, 312, 316, 319, 323, 326, 330, 333, 336, 340, 343, 347, 350, 353, 356, 360, 363, 366, 369, 373, 376, 379, 382, 385, 389, 392, 395, 398, 401, 404, 407, 410, 413, 416, 419, 422, 425, 428, 431, 434, 437, 440, 443, 445, 448, 451, 454, 457, 459, 462, 465, 468, 471, 473, 476, 479, 481, 484, 487, 489, 492, 495, 497, 500, 502, 505, 507, 510, 513, 515, 518, 520, 523, 525, 527, 530, 532, 535, 537, 540, 542, 544, 547, 549, 551, 554, 556, 558, 561, 563, 565, 568, 570, 572, 574, 576, 579, 581, 583, 585, 587, 590, 592, 594, 596, 598, 600, 602, 604, 606, 608, 611, 613, 615, 617, 619, 621, 623, 625, 627, 629, 631, 632, 634, 636, 638, 640, 642, 644, 646, 648, 650, 651, 653, 655, 657, 659, 661, 662, 664, 666, 668, 669, 671, 673, 675, 676, 678, 680, 682, 683, 685, 687, 688, 690, 692, 693, 695, 697, 698, 700, 701, 703, 705, 706, 708, 709, 711, 712, 714, 716, 717, 719, 720, 722, 723, 725, 726, 728, 729, 731, 732, 733, 735, 736, 738, 739, 741, 742, 743, 745, 746, 748, 749, 750, 752, 753, 754, 756, 757, 758, 760, 761, 762, 764, 765, 766, 767, 769, 770, 771, 773, 774, 775, 776, 778, 779, 780, 781, 782, 784, 785, 786, 787, 788, 790, 791, 792, 793, 794, 795, 797, 798, 799, 800, 801, 802, 803, 804, 805, 807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, 826, 827, 828, 829, 830, 831, 832, 833, 834, 835, 836, 837, 838, 839, 840, 841, 842, 843, 843, 844, 845, 846, 847, 848, 849, 850, 851, 851, 852, 853, 854, 855, 856, 857, 857, 858, 859, 860, 861, 862, 862, 863, 864, 865, 866, 867, 867, 868, 869, 870, 870, 871, 872, 873, 874, 874, 875, 876, 877, 877, 878, 879, 880, 880, 881, 882, 882, 883, 884, 885, 885, 886, 887, 887, 888, 889, 889, 890, 891, 892, 892, 893, 894, 894, 895, 895, 896, 897, 897, 898, 899, 899, 900, 901, 901, 902, 903, 903, 904, 904, 905, 906, 906, 907, 907, 908, 909, 909, 910, 910, 911, 912, 912, 913, 913, 914, 914, 915, 916, 916, 917, 917, 918, 918, 919, 919, 920, 920, 921, 921, 922, 923, 923, 924, 924, 925, 925, 926, 926, 927, 927, 928, 928, 929, 929, 930, 930, 931, 931, 932, 932, 933, 933, 933, 934, 934, 935, 935, 936, 936, 937, 937, 938, 938, 939, 939, 939, 940, 940, 941, 941, 942, 942, 942, 943, 943, 944, 944, 945, 945, 945, 946, 946, 947, 947, 947, 948, 948, 949, 949, 949, 950, 950, 951, 951, 951, 952, 952, 953, 953, 953, 954, 954, 954, 955, 955, 955, 956, 956, 957, 957, 957, 958, 958, 958, 959, 959, 959, 960, 960, 960, 961, 961, 961, 962, 962, 962, 963, 963, 963, 964, 964, 964, 965, 965, 965, 966, 966, 966, 967, 967, 967, 967, 968, 968, 968, 969, 969, 969, 970, 970, 970, 970, 971, 971, 971, 972, 972, 972, 972, 973, 973, 973, 974, 974, 974, 974, 975, 975, 975, 975, 976, 976, 976, 977, 977, 977, 977, 978, 978, 978, 978, 979, 979, 979, 979, 980, 980, 980, 980, 981, 981, 981, 981, 981, 982, 982, 982, 982, 983, 983, 983, 983, 984, 984, 984, 984, 984, 985, 985, 985, 985, 986, 986, 986, 986, 986, 987, 987, 987, 987, 987, 988, 988, 988, 988, 988, 989, 989, 989, 989, 989, 990, 990, 990, 990, 990, 991, 991, 991, 991, 991, 992, 992, 992, 992, 992, 993, 993, 993, 993, 993, 993, 994, 994, 994, 994, 994, 994, 995, 995, 995, 995, 995, 996, 996, 996, 996, 996, 996, 997, 997, 997, 997, 997, 997, 997, 998, 998, 998, 998, 998, 998, 999, 999, 999, 999, 999, 999, 999, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1002, 1002, 1002, 1002, 1002, 1002, 1002, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1003, 1004, 1004, 1004, 1004, 1004, 1004, 1004, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1005, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1006, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1007, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1008, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1009, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1010, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1011, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1012, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1013, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1014, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1015, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1016, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1017, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1018, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1019, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1020, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1021, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1022, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023, 1023 40 | }; 41 | 42 | 43 | 44 | float waveIndex1 = 0, waveIndex2 = 0; 45 | int outputValue1 = 0, outputValue2 = 0; 46 | int lastout1 = 0, lastout2 = 0; 47 | unsigned long previousMillis = 0, currentMillis = 0; 48 | bool lastTrig1 = HIGH, lastTrig2 = HIGH; 49 | int state1 = 0, state2 = 0; 50 | bool EoC1 = 0, EoC2 = 0; 51 | int EoCcount1 = 0, EoCcount2 = 0; 52 | int atkTime = 0, relTime = 0; 53 | int atkTime1 = 0, relTime1 = 0; 54 | int atkTime2 = 0, relTime2 = 0; 55 | 56 | int lastButton = HIGH; 57 | int lastTrig1Pin = HIGH; 58 | 59 | void setup() { 60 | Serial.begin(9600); 61 | pinMode(4, INPUT_PULLUP); // button 62 | pinMode(17, INPUT); // trigger1 63 | pinMode(10, INPUT); // trigger2 64 | pinMode(3, OUTPUT); // LED 65 | pinMode(9, OUTPUT); // env1 out 66 | pinMode(11, OUTPUT); // env2 out 67 | 68 | // Fast PWM setup 69 | TCCR2A = (1 << WGM21) | (1 << WGM20) | (1 << COM2B1); 70 | TCCR2B = (1 << CS20); 71 | TCCR1A = (1 << WGM11) | (1 << COM1A1); 72 | TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); 73 | ICR1 = 255; 74 | } 75 | 76 | void loop() { 77 | currentMillis = millis(); 78 | 79 | // Read pots 80 | int pot1 = analogRead(A0); 81 | int pot2 = analogRead(A1); 82 | int variationPot = analogRead(A2); 83 | 84 | atkTime = 1024 - pgm_read_word(&PotAdjust[pot1]); 85 | relTime = 1024 - pgm_read_word(&PotAdjust[pot2]); 86 | 87 | // Normalized pot values 88 | float atkNorm = pot1 / 1023.0; 89 | float relNorm = pot2 / 1023.0; 90 | float varNorm = variationPot / 1023.0; 91 | 92 | // Scaled variation range (0.2 to 0.8) 93 | // To shift the range to 0.1–0.8, replace 0.6 + 0.2 with 0.7 + 0.1 94 | float atkVarScale = (1.0 - atkNorm) * 0.6 + 0.2; 95 | float relVarScale = (1.0 - relNorm) * 0.6 + 0.2; 96 | float atkVarAmount = varNorm * atkVarScale; 97 | float relVarAmount = varNorm * relVarScale; 98 | 99 | // Trigger reads with edge detection 100 | int buttonReading = digitalRead(4); 101 | int trig1Reading = digitalRead(17); 102 | int trig2Reading = digitalRead(10); 103 | 104 | bool triggerEnvelope1 = (lastButton == HIGH && buttonReading == LOW) || 105 | (lastTrig1Pin == HIGH && trig1Reading == LOW); 106 | bool triggerEnvelope2 = (lastTrig2 == HIGH && trig2Reading == LOW); 107 | 108 | lastButton = buttonReading; 109 | lastTrig1Pin = trig1Reading; 110 | lastTrig2 = trig2Reading; 111 | 112 | if (triggerEnvelope1) { 113 | lastout1 = (state1 == 2) ? outputValue1 : 0; 114 | state1 = 1; 115 | waveIndex1 = 0; 116 | atkTime1 = atkTime + random(-1000, 1000) / 1000.0 * atkTime * atkVarAmount; 117 | relTime1 = relTime + random(-1000, 1000) / 1000.0 * relTime * relVarAmount; 118 | } 119 | if (triggerEnvelope2) { 120 | lastout2 = (state2 == 2) ? outputValue2 : 0; 121 | state2 = 1; 122 | waveIndex2 = 0; 123 | atkTime2 = atkTime + random(-1000, 1000) / 1000.0 * atkTime * atkVarAmount; 124 | relTime2 = relTime + random(-1000, 1000) / 1000.0 * relTime * relVarAmount; 125 | } 126 | 127 | if (currentMillis - previousMillis >= 1) { 128 | previousMillis = currentMillis; 129 | 130 | // ENV 1 131 | if (state1 == 1) { 132 | outputValue1 = map(pgm_read_byte(&Curve[(int)waveIndex1]), 0, 255, 255, lastout1); 133 | waveIndex1 += 0.05 * atkTime1 / 2; 134 | } else if (state1 == 2) { 135 | outputValue1 = map(pgm_read_byte(&Curve[(int)waveIndex1 - TABLE_SIZE]), 0, 255, 0, 255); 136 | waveIndex1 += 0.05 * relTime1 / 2; 137 | } 138 | if (waveIndex1 > TABLE_SIZE && waveIndex1 < 2 * TABLE_SIZE) state1 = 2; 139 | if (waveIndex1 >= 2 * TABLE_SIZE) { 140 | waveIndex1 = 0; 141 | state1 = 0; 142 | EoC1 = 1; 143 | } 144 | if (EoC1) { 145 | if (++EoCcount1 > 10) { 146 | EoCcount1 = 0; 147 | EoC1 = 0; 148 | } 149 | } 150 | analogWrite(9, outputValue1); 151 | analogWrite(3, outputValue1 * Brightness / 255); 152 | 153 | // ENV 2 154 | if (state2 == 1) { 155 | outputValue2 = map(pgm_read_byte(&Curve[(int)waveIndex2]), 0, 255, 255, lastout2); 156 | waveIndex2 += 0.05 * atkTime2 / 2; 157 | } else if (state2 == 2) { 158 | outputValue2 = map(pgm_read_byte(&Curve[(int)waveIndex2 - TABLE_SIZE]), 0, 255, 0, 255); 159 | waveIndex2 += 0.05 * relTime2 / 2; 160 | } 161 | if (waveIndex2 > TABLE_SIZE && waveIndex2 < 2 * TABLE_SIZE) state2 = 2; 162 | if (waveIndex2 >= 2 * TABLE_SIZE) { 163 | waveIndex2 = 0; 164 | state2 = 0; 165 | EoC2 = 1; 166 | } 167 | if (EoC2) { 168 | if (++EoCcount2 > 10) { 169 | EoCcount2 = 0; 170 | EoC2 = 0; 171 | } 172 | } 173 | analogWrite(11, outputValue2); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /mod1_LFO/mod1_LFO.ino: -------------------------------------------------------------------------------- 1 | /* 2 | HAGIWO MOD1 LFO Ver1.0 for Arduino Nano 3 | Slightly modified by Rob Heel, insted of SawRevWave a random slope is used as 5th waveform. 4 | Various CV inputs allow you to create chaotic CVs. 5 | 6 | --Pin assign--- 7 | POT1 A0 frequency 8 | POT2 A1 waveform select 9 | POT3 A2 output level 10 | F1 A3 frequency CV in 11 | F2 A4 waveform CV in 12 | F3 A5 output level CV in 13 | F4 D11 output 14 | BUTTON change frequency range 15 | LED output 16 | EEPROM Saves the frequency range when the button is pressed 17 | */ 18 | #include 19 | #define TABLE_SIZE 1024 20 | #define Brightness 64 21 | 22 | const byte PROGMEM SinTable[TABLE_SIZE] = { 23 | 127, 128, 129, 129, 130, 131, 132, 132, 133, 134, 135, 136, 136, 137, 138, 139, 139, 140, 141, 142, 143, 143, 144, 145, 146, 146, 147, 148, 149, 150, 150, 151, 152, 153, 153, 154, 155, 156, 156, 157, 158, 159, 159, 160, 161, 162, 163, 163, 164, 165, 166, 166, 167, 168, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 177, 177, 178, 179, 179, 180, 181, 182, 182, 183, 184, 184, 185, 186, 186, 187, 188, 188, 189, 190, 191, 191, 192, 193, 193, 194, 195, 195, 196, 197, 197, 198, 198, 199, 200, 200, 201, 202, 202, 203, 204, 204, 205, 205, 206, 207, 207, 208, 208, 209, 210, 210, 211, 211, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 223, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 233, 234, 234, 235, 235, 236, 236, 236, 237, 237, 238, 238, 238, 239, 239, 239, 240, 240, 241, 241, 241, 242, 242, 242, 243, 243, 243, 244, 244, 244, 244, 245, 245, 245, 246, 246, 246, 247, 247, 247, 247, 248, 248, 248, 248, 249, 249, 249, 249, 249, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 255, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 253, 253, 253, 253, 253, 253, 253, 253, 252, 252, 252, 252, 252, 252, 252, 251, 251, 251, 251, 251, 250, 250, 250, 250, 250, 249, 249, 249, 249, 249, 248, 248, 248, 248, 247, 247, 247, 247, 246, 246, 246, 245, 245, 245, 244, 244, 244, 244, 243, 243, 243, 242, 242, 242, 241, 241, 241, 240, 240, 239, 239, 239, 238, 238, 238, 237, 237, 236, 236, 236, 235, 235, 234, 234, 233, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 228, 228, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 221, 221, 220, 220, 219, 219, 218, 218, 217, 217, 216, 215, 215, 214, 214, 213, 213, 212, 211, 211, 210, 210, 209, 208, 208, 207, 207, 206, 205, 205, 204, 204, 203, 202, 202, 201, 200, 200, 199, 198, 198, 197, 197, 196, 195, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 186, 185, 184, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 177, 176, 175, 174, 174, 173, 172, 171, 171, 170, 169, 168, 168, 167, 166, 166, 165, 164, 163, 163, 162, 161, 160, 159, 159, 158, 157, 156, 156, 155, 154, 153, 153, 152, 151, 150, 150, 149, 148, 147, 146, 146, 145, 144, 143, 143, 142, 141, 140, 139, 139, 138, 137, 136, 136, 135, 134, 133, 132, 132, 131, 130, 129, 129, 128, 127, 126, 125, 125, 124, 123, 122, 122, 121, 120, 119, 118, 118, 117, 116, 115, 115, 114, 113, 112, 111, 111, 110, 109, 108, 108, 107, 106, 105, 104, 104, 103, 102, 101, 101, 100, 99, 98, 98, 97, 96, 95, 95, 94, 93, 92, 91, 91, 90, 89, 88, 88, 87, 86, 86, 85, 84, 83, 83, 82, 81, 80, 80, 79, 78, 77, 77, 76, 75, 75, 74, 73, 72, 72, 71, 70, 70, 69, 68, 68, 67, 66, 66, 65, 64, 63, 63, 62, 61, 61, 60, 59, 59, 58, 57, 57, 56, 56, 55, 54, 54, 53, 52, 52, 51, 50, 50, 49, 49, 48, 47, 47, 46, 46, 45, 44, 44, 43, 43, 42, 41, 41, 40, 40, 39, 39, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 26, 25, 25, 24, 24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 19, 19, 18, 18, 18, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 15, 15, 16, 16, 16, 17, 17, 18, 18, 18, 19, 19, 20, 20, 21, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 39, 39, 40, 40, 41, 41, 42, 43, 43, 44, 44, 45, 46, 46, 47, 47, 48, 49, 49, 50, 50, 51, 52, 52, 53, 54, 54, 55, 56, 56, 57, 57, 58, 59, 59, 60, 61, 61, 62, 63, 63, 64, 65, 66, 66, 67, 68, 68, 69, 70, 70, 71, 72, 72, 73, 74, 75, 75, 76, 77, 77, 78, 79, 80, 80, 81, 82, 83, 83, 84, 85, 86, 86, 87, 88, 88, 89, 90, 91, 91, 92, 93, 94, 95, 95, 96, 97, 98, 98, 99, 100, 101, 101, 102, 103, 104, 104, 105, 106, 107, 108, 108, 109, 110, 111, 111, 112, 113, 114, 115, 115, 116, 117, 118, 118, 119, 120, 121, 122, 122, 123, 124, 125, 125, 126 24 | }; 25 | const byte PROGMEM TriTable[TABLE_SIZE] = { 26 | 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 41, 41, 42, 42, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 48, 48, 49, 49, 50, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, 84, 85, 85, 86, 86, 87, 87, 88, 88, 89, 89, 90, 90, 91, 91, 92, 92, 93, 93, 94, 94, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 122, 122, 123, 123, 124, 124, 125, 125, 126, 126, 127, 127, 127, 128, 128, 129, 129, 130, 130, 131, 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, 136, 137, 137, 138, 138, 139, 139, 140, 140, 141, 141, 142, 142, 143, 143, 144, 144, 145, 145, 146, 146, 147, 147, 148, 148, 149, 149, 150, 150, 151, 151, 152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 157, 158, 158, 159, 159, 160, 160, 161, 161, 162, 162, 163, 163, 164, 164, 165, 165, 166, 166, 167, 167, 168, 168, 169, 169, 170, 170, 171, 171, 172, 172, 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, 178, 179, 179, 180, 180, 181, 181, 182, 182, 183, 183, 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, 189, 189, 190, 190, 191, 191, 192, 192, 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199, 199, 200, 200, 201, 201, 202, 202, 203, 203, 204, 204, 205, 205, 206, 206, 207, 207, 208, 208, 209, 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255, 254, 254, 253, 253, 252, 252, 251, 251, 250, 250, 249, 249, 248, 248, 247, 247, 246, 246, 245, 245, 244, 244, 243, 243, 242, 242, 241, 241, 240, 240, 239, 239, 238, 238, 237, 237, 236, 236, 235, 235, 234, 234, 233, 233, 232, 232, 231, 231, 230, 230, 229, 229, 228, 228, 227, 227, 226, 226, 225, 225, 224, 224, 223, 223, 222, 222, 221, 221, 220, 220, 219, 219, 218, 218, 217, 217, 216, 216, 215, 215, 214, 214, 213, 213, 212, 212, 211, 211, 210, 210, 209, 209, 208, 208, 207, 207, 206, 206, 205, 205, 204, 204, 203, 203, 202, 202, 201, 201, 200, 200, 199, 199, 198, 198, 197, 197, 196, 196, 195, 195, 194, 194, 193, 193, 192, 192, 191, 191, 190, 190, 189, 189, 188, 188, 187, 187, 186, 186, 185, 185, 184, 184, 183, 183, 182, 182, 181, 181, 180, 180, 179, 179, 178, 178, 177, 177, 176, 176, 175, 175, 174, 174, 173, 173, 172, 172, 171, 171, 170, 170, 169, 169, 168, 168, 167, 167, 166, 166, 165, 165, 164, 164, 163, 163, 162, 162, 161, 161, 160, 160, 159, 159, 158, 158, 157, 157, 156, 156, 155, 155, 154, 154, 153, 153, 152, 152, 151, 151, 150, 150, 149, 149, 148, 148, 147, 147, 146, 146, 145, 145, 144, 144, 143, 143, 142, 142, 141, 141, 140, 140, 139, 139, 138, 138, 137, 137, 136, 136, 135, 135, 134, 134, 133, 133, 132, 132, 131, 131, 130, 130, 129, 129, 128, 128, 127, 127, 127, 126, 126, 125, 125, 124, 124, 123, 123, 122, 122, 121, 121, 120, 120, 119, 119, 118, 118, 117, 117, 116, 116, 115, 115, 114, 114, 113, 113, 112, 112, 111, 111, 110, 110, 109, 109, 108, 108, 107, 107, 106, 106, 105, 105, 104, 104, 103, 103, 102, 102, 101, 101, 100, 100, 99, 99, 98, 98, 97, 97, 96, 96, 95, 95, 94, 94, 93, 93, 92, 92, 91, 91, 90, 90, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 83, 82, 82, 81, 81, 80, 80, 79, 79, 78, 78, 77, 77, 76, 76, 75, 75, 74, 74, 73, 73, 72, 72, 71, 71, 70, 70, 69, 69, 68, 68, 67, 67, 66, 66, 65, 65, 64, 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56, 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0 27 | }; 28 | const byte PROGMEM SquTable[TABLE_SIZE] = { 29 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 30 | }; 31 | const byte PROGMEM SawTable[TABLE_SIZE] = { 32 | 127, 127, 128, 128, 128, 128, 129, 129, 129, 129, 130, 130, 130, 130, 131, 131, 131, 131, 132, 132, 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 135, 135, 135, 135, 136, 136, 136, 136, 137, 137, 137, 137, 138, 138, 138, 138, 139, 139, 139, 139, 140, 140, 140, 140, 141, 141, 141, 141, 142, 142, 142, 142, 143, 143, 143, 143, 144, 144, 144, 144, 145, 145, 145, 145, 146, 146, 146, 146, 147, 147, 147, 147, 148, 148, 148, 148, 149, 149, 149, 149, 150, 150, 150, 150, 151, 151, 151, 151, 152, 152, 152, 152, 153, 153, 153, 153, 154, 154, 154, 154, 155, 155, 155, 155, 156, 156, 156, 156, 157, 157, 157, 157, 158, 158, 158, 158, 159, 159, 159, 159, 160, 160, 160, 160, 161, 161, 161, 161, 162, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 164, 165, 165, 165, 165, 166, 166, 166, 166, 167, 167, 167, 167, 168, 168, 168, 168, 169, 169, 169, 169, 170, 170, 170, 170, 170, 171, 171, 171, 171, 172, 172, 172, 172, 173, 173, 173, 173, 174, 174, 174, 174, 175, 175, 175, 175, 176, 176, 176, 176, 177, 177, 177, 177, 178, 178, 178, 178, 179, 179, 179, 179, 180, 180, 180, 180, 181, 181, 181, 181, 182, 182, 182, 182, 183, 183, 183, 183, 184, 184, 184, 184, 185, 185, 185, 185, 186, 186, 186, 186, 187, 187, 187, 187, 188, 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, 191, 191, 191, 192, 192, 192, 192, 193, 193, 193, 193, 194, 194, 194, 194, 195, 195, 195, 195, 196, 196, 196, 196, 197, 197, 197, 197, 198, 198, 198, 198, 199, 199, 199, 199, 200, 200, 200, 200, 201, 201, 201, 201, 202, 202, 202, 202, 203, 203, 203, 203, 204, 204, 204, 204, 205, 205, 205, 205, 206, 206, 206, 206, 207, 207, 207, 207, 208, 208, 208, 208, 209, 209, 209, 209, 210, 210, 210, 210, 211, 211, 211, 211, 212, 212, 212, 212, 213, 213, 213, 213, 214, 214, 214, 214, 215, 215, 215, 215, 216, 216, 216, 216, 217, 217, 217, 217, 218, 218, 218, 218, 219, 219, 219, 219, 220, 220, 220, 220, 221, 221, 221, 221, 222, 222, 222, 222, 223, 223, 223, 223, 224, 224, 224, 224, 225, 225, 225, 225, 226, 226, 226, 226, 227, 227, 227, 227, 228, 228, 228, 228, 229, 229, 229, 229, 230, 230, 230, 230, 231, 231, 231, 231, 232, 232, 232, 232, 233, 233, 233, 233, 234, 234, 234, 234, 235, 235, 235, 235, 236, 236, 236, 236, 237, 237, 237, 237, 238, 238, 238, 238, 239, 239, 239, 239, 240, 240, 240, 240, 241, 241, 241, 241, 242, 242, 242, 242, 243, 243, 243, 243, 244, 244, 244, 244, 245, 245, 245, 245, 246, 246, 246, 246, 247, 247, 247, 247, 248, 248, 248, 248, 249, 249, 249, 249, 250, 250, 250, 250, 251, 251, 251, 251, 252, 252, 252, 252, 253, 253, 253, 253, 254, 254, 254, 254, 255, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 82, 82, 82, 83, 83, 83, 83, 84, 84, 84, 84, 85, 85, 85, 85, 85, 86, 86, 86, 86, 87, 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90, 90, 91, 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 99, 99, 99, 99, 100, 100, 100, 100, 101, 101, 101, 101, 102, 102, 102, 102, 103, 103, 103, 103, 104, 104, 104, 104, 105, 105, 105, 105, 106, 106, 106, 106, 107, 107, 107, 107, 108, 108, 108, 108, 109, 109, 109, 109, 110, 110, 110, 110, 111, 111, 111, 111, 112, 112, 112, 112, 113, 113, 113, 113, 114, 114, 114, 114, 115, 115, 115, 115, 116, 116, 116, 116, 117, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 124, 125, 125, 125, 125, 126, 126, 126, 126, 127, 127 33 | }; 34 | // void updateRandomSlope is used instead of this table 35 | const byte PROGMEM SawRevWaveTable[TABLE_SIZE] = { 36 | 127, 127, 126, 126, 126, 126, 125, 125, 125, 125, 124, 124, 124, 124, 123, 123, 123, 123, 122, 122, 122, 122, 121, 121, 121, 121, 120, 120, 120, 120, 119, 119, 119, 119, 118, 118, 118, 118, 117, 117, 117, 117, 116, 116, 116, 116, 115, 115, 115, 115, 114, 114, 114, 114, 113, 113, 113, 113, 112, 112, 112, 112, 111, 111, 111, 111, 110, 110, 110, 110, 109, 109, 109, 109, 108, 108, 108, 108, 107, 107, 107, 107, 106, 106, 106, 106, 105, 105, 105, 105, 104, 104, 104, 104, 103, 103, 103, 103, 102, 102, 102, 102, 101, 101, 101, 101, 100, 100, 100, 100, 99, 99, 99, 99, 98, 98, 98, 98, 97, 97, 97, 97, 96, 96, 96, 96, 95, 95, 95, 95, 94, 94, 94, 94, 93, 93, 93, 93, 92, 92, 92, 92, 91, 91, 91, 91, 90, 90, 90, 90, 89, 89, 89, 89, 88, 88, 88, 88, 87, 87, 87, 87, 86, 86, 86, 86, 85, 85, 85, 85, 85, 84, 84, 84, 84, 83, 83, 83, 83, 82, 82, 82, 82, 81, 81, 81, 81, 80, 80, 80, 80, 79, 79, 79, 79, 78, 78, 78, 78, 77, 77, 77, 77, 76, 76, 76, 76, 75, 75, 75, 75, 74, 74, 74, 74, 73, 73, 73, 73, 72, 72, 72, 72, 71, 71, 71, 71, 70, 70, 70, 70, 69, 69, 69, 69, 68, 68, 68, 68, 67, 67, 67, 67, 66, 66, 66, 66, 65, 65, 65, 65, 64, 64, 64, 64, 63, 63, 63, 63, 62, 62, 62, 62, 61, 61, 61, 61, 60, 60, 60, 60, 59, 59, 59, 59, 58, 58, 58, 58, 57, 57, 57, 57, 56, 56, 56, 56, 55, 55, 55, 55, 54, 54, 54, 54, 53, 53, 53, 53, 52, 52, 52, 52, 51, 51, 51, 51, 50, 50, 50, 50, 49, 49, 49, 49, 48, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 45, 45, 45, 45, 44, 44, 44, 44, 43, 43, 43, 43, 42, 42, 42, 42, 41, 41, 41, 41, 40, 40, 40, 40, 39, 39, 39, 39, 38, 38, 38, 38, 37, 37, 37, 37, 36, 36, 36, 36, 35, 35, 35, 35, 34, 34, 34, 34, 33, 33, 33, 33, 32, 32, 32, 32, 31, 31, 31, 31, 30, 30, 30, 30, 29, 29, 29, 29, 28, 28, 28, 28, 27, 27, 27, 27, 26, 26, 26, 26, 25, 25, 25, 25, 24, 24, 24, 24, 23, 23, 23, 23, 22, 22, 22, 22, 21, 21, 21, 21, 20, 20, 20, 20, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 14, 13, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 255, 254, 254, 254, 254, 253, 253, 253, 253, 252, 252, 252, 252, 251, 251, 251, 251, 250, 250, 250, 250, 249, 249, 249, 249, 248, 248, 248, 248, 247, 247, 247, 247, 246, 246, 246, 246, 245, 245, 245, 245, 244, 244, 244, 244, 243, 243, 243, 243, 242, 242, 242, 242, 241, 241, 241, 241, 240, 240, 240, 240, 239, 239, 239, 239, 238, 238, 238, 238, 237, 237, 237, 237, 236, 236, 236, 236, 235, 235, 235, 235, 234, 234, 234, 234, 233, 233, 233, 233, 232, 232, 232, 232, 231, 231, 231, 231, 230, 230, 230, 230, 229, 229, 229, 229, 228, 228, 228, 228, 227, 227, 227, 227, 226, 226, 226, 226, 225, 225, 225, 225, 224, 224, 224, 224, 223, 223, 223, 223, 222, 222, 222, 222, 221, 221, 221, 221, 220, 220, 220, 220, 219, 219, 219, 219, 218, 218, 218, 218, 217, 217, 217, 217, 216, 216, 216, 216, 215, 215, 215, 215, 214, 214, 214, 214, 213, 213, 213, 213, 212, 212, 212, 212, 211, 211, 211, 211, 210, 210, 210, 210, 209, 209, 209, 209, 208, 208, 208, 208, 207, 207, 207, 207, 206, 206, 206, 206, 205, 205, 205, 205, 204, 204, 204, 204, 203, 203, 203, 203, 202, 202, 202, 202, 201, 201, 201, 201, 200, 200, 200, 200, 199, 199, 199, 199, 198, 198, 198, 198, 197, 197, 197, 197, 196, 196, 196, 196, 195, 195, 195, 195, 194, 194, 194, 194, 193, 193, 193, 193, 192, 192, 192, 192, 191, 191, 191, 191, 190, 190, 190, 190, 189, 189, 189, 189, 188, 188, 188, 188, 187, 187, 187, 187, 186, 186, 186, 186, 185, 185, 185, 185, 184, 184, 184, 184, 183, 183, 183, 183, 182, 182, 182, 182, 181, 181, 181, 181, 180, 180, 180, 180, 179, 179, 179, 179, 178, 178, 178, 178, 177, 177, 177, 177, 176, 176, 176, 176, 175, 175, 175, 175, 174, 174, 174, 174, 173, 173, 173, 173, 172, 172, 172, 172, 171, 171, 171, 171, 170, 170, 170, 170, 170, 169, 169, 169, 169, 168, 168, 168, 168, 167, 167, 167, 167, 166, 166, 166, 166, 165, 165, 165, 165, 164, 164, 164, 164, 163, 163, 163, 163, 162, 162, 162, 162, 161, 161, 161, 161, 160, 160, 160, 160, 159, 159, 159, 159, 158, 158, 158, 158, 157, 157, 157, 157, 156, 156, 156, 156, 155, 155, 155, 155, 154, 154, 154, 154, 153, 153, 153, 153, 152, 152, 152, 152, 151, 151, 151, 151, 150, 150, 150, 150, 149, 149, 149, 149, 148, 148, 148, 148, 147, 147, 147, 147, 146, 146, 146, 146, 145, 145, 145, 145, 144, 144, 144, 144, 143, 143, 143, 143, 142, 142, 142, 142, 141, 141, 141, 141, 140, 140, 140, 140, 139, 139, 139, 139, 138, 138, 138, 138, 137, 137, 137, 137, 136, 136, 136, 136, 135, 135, 135, 135, 134, 134, 134, 134, 133, 133, 133, 133, 132, 132, 132, 132, 131, 131, 131, 131, 130, 130, 130, 130, 129, 129, 129, 129, 128, 128, 128, 128, 127, 127 37 | }; 38 | const byte PROGMEM MaxTable[TABLE_SIZE] = { 39 | 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 40 | }; 41 | 42 | float lfoFrequency = 0.1; 43 | int waveHeight = 255; 44 | int waveForm = 0; 45 | float waveIndex = 0; 46 | long outputValue = 0; 47 | int outputLed = 0; 48 | int freqRange = 1; 49 | 50 | unsigned long previousMillis = 0; 51 | unsigned long currentMillis = 0; 52 | 53 | // Random slope waveform variables 54 | float currentSlopeValue = 0.0; 55 | float targetSlopeValue = 0.0; 56 | float slopeStep = 0.0; 57 | unsigned long randomSlopeMillis = 0; 58 | 59 | bool lastButtonState = HIGH; 60 | unsigned long buttonPreviousMillis = 0; 61 | const unsigned long debounceDelay = 50; 62 | 63 | void setup() { 64 | pinMode(4, INPUT_PULLUP); 65 | pinMode(3, OUTPUT); 66 | pinMode(11, OUTPUT); 67 | 68 | freqRange = EEPROM.read(0); 69 | 70 | TCCR2A = (1 << WGM21) | (1 << WGM20) | (1 << COM2B1); 71 | TCCR2B = (1 << CS20); 72 | TCCR1A = (1 << WGM11) | (1 << COM1A1); 73 | TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); 74 | ICR1 = 255; 75 | 76 | randomSeed(analogRead(A6)); 77 | } 78 | 79 | void loop() { 80 | currentMillis = millis(); 81 | 82 | int reading = digitalRead(4); 83 | if (currentMillis - buttonPreviousMillis > debounceDelay) { 84 | if (reading == LOW && lastButtonState == HIGH) { 85 | freqRange = (freqRange == 1) ? 10 : 1; 86 | EEPROM.write(0, freqRange); 87 | buttonPreviousMillis = currentMillis; 88 | } 89 | } 90 | lastButtonState = reading; 91 | 92 | int potValueA0 = analogRead(A0); 93 | int CVValueA3 = analogRead(A3); 94 | lfoFrequency = min(potValueA0 + CVValueA3, 1023) * 0.0015 * freqRange; 95 | 96 | int potValueA1 = analogRead(A1); 97 | int CVValueA4 = analogRead(A4); 98 | waveForm = min(potValueA1 + CVValueA4, 1023); 99 | 100 | int potValueA2 = analogRead(A2); 101 | int CVValueA5 = analogRead(A5); 102 | waveHeight = min(potValueA2 / 4 + CVValueA5 / 4, 255); 103 | 104 | if (currentMillis - previousMillis >= 1) { 105 | previousMillis = currentMillis; 106 | 107 | if (waveForm <= 102) { 108 | outputValue = pgm_read_byte(&SinTable[(int)waveIndex]) * waveHeight / 255; 109 | } else if (waveForm <= 308) { 110 | outputValue = pgm_read_byte(&TriTable[(int)waveIndex]) * waveHeight / 255; 111 | } else if (waveForm <= 514) { 112 | outputValue = pgm_read_byte(&SquTable[(int)waveIndex]) * waveHeight / 255; 113 | } else if (waveForm <= 720) { 114 | outputValue = pgm_read_byte(&SawTable[(int)waveIndex]) * waveHeight / 255; 115 | } else if (waveForm <= 924) { // Adjusted range for random slope 116 | updateRandomSlope(lfoFrequency); 117 | outputValue = currentSlopeValue * waveHeight / 255; 118 | } else { 119 | outputValue = pgm_read_byte(&MaxTable[(int)waveIndex]) * waveHeight / 255; 120 | } 121 | 122 | analogWrite(11, outputValue); 123 | analogWrite(3, (byte)outputValue * Brightness / 255); 124 | 125 | waveIndex = waveIndex + lfoFrequency + 0.01; 126 | if (waveIndex >= TABLE_SIZE) { 127 | waveIndex -= TABLE_SIZE; 128 | } 129 | } 130 | } 131 | 132 | // Improved random slope function 133 | void updateRandomSlope(float frequency) { 134 | if (currentMillis - randomSlopeMillis >= (1000.0 / frequency)) { 135 | randomSlopeMillis = currentMillis; 136 | targetSlopeValue = random(0, 256); 137 | slopeStep = (targetSlopeValue - currentSlopeValue) / (1000.0 / frequency); 138 | } 139 | 140 | currentSlopeValue += slopeStep; 141 | currentSlopeValue = constrain(currentSlopeValue, 0, 255); // Ensure within bounds 142 | 143 | if ((slopeStep > 0 && currentSlopeValue >= targetSlopeValue) || 144 | (slopeStep < 0 && currentSlopeValue <= targetSlopeValue)) { 145 | currentSlopeValue = targetSlopeValue; 146 | } 147 | } 148 | --------------------------------------------------------------------------------