mirror of
https://github.com/erik-toth/audio-synth.git
synced 2026-03-12 13:17:42 +00:00
Refactor code structure for improved readability and maintainability, imporved matrix read, improved SB button read
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
/*
|
||||
* Example Code Three - Dual Channel Sequencer
|
||||
* TODO:
|
||||
- add predefined sequence of voltage (e.g. for usage as startup sound)
|
||||
- implement INFO and MISC pins form file FIRMWARE_DEF.h
|
||||
* Example Code Three - Dual Channel Sequencer (FIXED)
|
||||
* - Bounds Checks hinzugefügt
|
||||
* - Rate-Limiting implementiert
|
||||
* - Debug-Ausgaben erweitert
|
||||
* - Stack Overflow verhindert
|
||||
*/
|
||||
#include "FIRMWARE_DEF.h"
|
||||
#include "FIRMWARE.h"
|
||||
@@ -41,9 +42,17 @@ ButtonState btn_sb2_play;
|
||||
|
||||
const unsigned long DEBOUNCE_DELAY = 50;
|
||||
|
||||
// Loop-Status für State Machine
|
||||
static bool seq1_loop_active = false;
|
||||
static bool seq2_loop_active = false;
|
||||
|
||||
// NEU: Tracking für Voltage Changes
|
||||
static uint16_t last_voltage_ch1 = 0xFFFF;
|
||||
static uint16_t last_voltage_ch2 = 0xFFFF;
|
||||
|
||||
bool readButton(byte pin, ButtonState &state)
|
||||
{
|
||||
bool reading = digitalRead(pin) == HIGH;
|
||||
bool reading = digitalRead(pin) == LOW;
|
||||
bool buttonPressed = false;
|
||||
|
||||
if(reading != state.last)
|
||||
@@ -69,10 +78,10 @@ bool readButton(byte pin, ButtonState &state)
|
||||
|
||||
void initButtons()
|
||||
{
|
||||
pinMode(PIN_SB_1_REC, INPUT_PULLDOWN);
|
||||
pinMode(PIN_SB_1_PLAY, INPUT_PULLDOWN);
|
||||
pinMode(PIN_SB_2_REC, INPUT_PULLDOWN);
|
||||
pinMode(PIN_SB_2_PLAY, INPUT_PULLDOWN);
|
||||
pinMode(PIN_SB_1_REC, INPUT_PULLUP);
|
||||
pinMode(PIN_SB_1_PLAY, INPUT_PULLUP);
|
||||
pinMode(PIN_SB_2_REC, INPUT_PULLUP);
|
||||
pinMode(PIN_SB_2_PLAY, INPUT_PULLUP);
|
||||
|
||||
btn_sb1_rec.current = false;
|
||||
btn_sb1_rec.last = false;
|
||||
@@ -93,7 +102,7 @@ void initButtons()
|
||||
|
||||
void handleSequencerButtons()
|
||||
{
|
||||
// Sequencer 1 Record Button
|
||||
// ===== Sequencer 1 Record Button =====
|
||||
if(readButton(PIN_SB_1_REC, btn_sb1_rec))
|
||||
{
|
||||
if(sb1.isRecording())
|
||||
@@ -106,58 +115,39 @@ void handleSequencerButtons()
|
||||
{
|
||||
if(sb1.isPlaying()) sb1.stopPlay();
|
||||
sb1.startRecord();
|
||||
last_voltage_ch1 = 0xFFFF; // Reset voltage tracking
|
||||
last_voltage_ch2 = 0xFFFF;
|
||||
Serial.printf("\n\r[SEQ1] Recording started (2 channels)...");
|
||||
}
|
||||
}
|
||||
|
||||
// Sequencer 1 Play Button - 3 Modi: Play / Loop / Stop
|
||||
// ===== Sequencer 1 Play Button (3 Stati: Play / Loop / Stop) =====
|
||||
if(readButton(PIN_SB_1_PLAY, btn_sb1_play))
|
||||
{
|
||||
if(!sb1.isPlaying())
|
||||
{
|
||||
// Nicht am Spielen -> Starte Playback (ohne Loop)
|
||||
if(sb1.isRecording()) sb1.stopRecord();
|
||||
sb1.setLoop(false);
|
||||
seq1_loop_active = false;
|
||||
sb1.startPlay();
|
||||
Serial.printf("\n\r[SEQ1] Playback started (single)\n\r\tSteps: %i, Duartion: %ims", sb1.getStepCount(), sb1.getTotalDuration());
|
||||
Serial.printf("\n\r[SEQ1] Playback started (single)\n\r\tSteps: %i, Duration: %ims",
|
||||
sb1.getStepCount(), sb1.getTotalDuration());
|
||||
}
|
||||
else if(!seq1_loop_active)
|
||||
{
|
||||
sb1.setLoop(true);
|
||||
seq1_loop_active = true;
|
||||
Serial.printf("\n\r[SEQ1] Loop activated");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Am Spielen -> Prüfe Loop-Status
|
||||
if(!sb1.isPlaying()) // Falls schon gestoppt
|
||||
{
|
||||
// Starte neu
|
||||
sb1.setLoop(false);
|
||||
sb1.startPlay();
|
||||
Serial.printf("\n\r[SEQ1] Playback started (single)");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ist am Spielen - ermittle ob Loop aktiv ist
|
||||
// Wir testen das indirekt: Wenn ein Sequencer am Ende angekommen ist
|
||||
// und noch spielt, dann muss Loop aktiv sein
|
||||
// Alternative: Wir tracken den Loop-Status selbst
|
||||
static bool seq1_loop_active = false;
|
||||
|
||||
if(!seq1_loop_active)
|
||||
{
|
||||
// 2. Klick: Loop aktivieren
|
||||
sb1.setLoop(true);
|
||||
seq1_loop_active = true;
|
||||
Serial.printf("\n\r[SEQ1] Loop activated");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 3. Klick: Stop
|
||||
sb1.stopPlay();
|
||||
seq1_loop_active = false;
|
||||
Serial.printf("\n\r[SEQ1] Playback stopped");
|
||||
}
|
||||
}
|
||||
sb1.stopPlay();
|
||||
seq1_loop_active = false;
|
||||
Serial.printf("\n\r[SEQ1] Playback stopped");
|
||||
}
|
||||
}
|
||||
|
||||
// Sequencer 2 Record Button
|
||||
// ===== Sequencer 2 Record Button =====
|
||||
if(readButton(PIN_SB_2_REC, btn_sb2_rec))
|
||||
{
|
||||
if(sb2.isRecording())
|
||||
@@ -170,40 +160,35 @@ void handleSequencerButtons()
|
||||
{
|
||||
if(sb2.isPlaying()) sb2.stopPlay();
|
||||
sb2.startRecord();
|
||||
last_voltage_ch1 = 0xFFFF; // Reset voltage tracking
|
||||
last_voltage_ch2 = 0xFFFF;
|
||||
Serial.printf("\n\r[SEQ2] Recording started (2 channels)...");
|
||||
}
|
||||
}
|
||||
|
||||
// Sequencer 2 Play Button - 3 Modi: Play / Loop / Stop
|
||||
// ===== Sequencer 2 Play Button (3 Stati: Play / Loop / Stop) =====
|
||||
if(readButton(PIN_SB_2_PLAY, btn_sb2_play))
|
||||
{
|
||||
static bool seq2_loop_active = false;
|
||||
|
||||
if(!sb2.isPlaying())
|
||||
{
|
||||
// Nicht am Spielen -> Starte Playback (ohne Loop)
|
||||
if(sb2.isRecording()) sb2.stopRecord();
|
||||
sb2.setLoop(false);
|
||||
seq2_loop_active = false;
|
||||
sb2.startPlay();
|
||||
Serial.printf("\n\r[SEQ2] Playback started (single)");
|
||||
Serial.printf("\n\r[SEQ2] Playback started (single)\n\r\tSteps: %i, Duration: %ims",
|
||||
sb2.getStepCount(), sb2.getTotalDuration());
|
||||
}
|
||||
else if(!seq2_loop_active)
|
||||
{
|
||||
sb2.setLoop(true);
|
||||
seq2_loop_active = true;
|
||||
Serial.printf("\n\r[SEQ2] Loop activated");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!seq2_loop_active)
|
||||
{
|
||||
// 2. Klick: Loop aktivieren
|
||||
sb2.setLoop(true);
|
||||
seq2_loop_active = true;
|
||||
Serial.printf("\n\r[SEQ2] Loop activated");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 3. Klick: Stop
|
||||
sb2.stopPlay();
|
||||
seq2_loop_active = false;
|
||||
Serial.printf("\n\r[SEQ2] Playback stopped");
|
||||
}
|
||||
sb2.stopPlay();
|
||||
seq2_loop_active = false;
|
||||
Serial.printf("\n\r[SEQ2] Playback stopped");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,8 +196,28 @@ void handleSequencerButtons()
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(BAUDRATE);
|
||||
delay(2000);
|
||||
Serial.printf("\n\r=== FIXED VERSION v2 ===");
|
||||
Serial.printf("\n\rSerial OK!");
|
||||
|
||||
keyboard.begin();
|
||||
cv.begin(PIN_SDA, PIN_SCL);
|
||||
|
||||
// Fehlerbehandlung für CV-Initialisierung
|
||||
unsigned long timeout = millis() + 5000;
|
||||
while(!cv.begin(PIN_SDA, PIN_SCL))
|
||||
{
|
||||
Serial.printf("\n\r[ERROR] CV initialization failed. Retrying...");
|
||||
delay(500);
|
||||
|
||||
if(millis() > timeout)
|
||||
{
|
||||
Serial.printf("\n\r[FATAL] CV initialization timeout! Check I2C connection.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Serial.printf("\n\r[OK] CV initialized");
|
||||
|
||||
initButtons();
|
||||
|
||||
sb1.setLoop(false);
|
||||
@@ -227,11 +232,47 @@ void setup()
|
||||
Serial.printf("\n\r 3rd click: Stop");
|
||||
Serial.printf("\n\r PIN_SB_2_REC: SEQ2 Record Start/Stop (CH1+CH2)");
|
||||
Serial.printf("\n\r PIN_SB_2_PLAY: SEQ2 Play Mode (same as SEQ1)");
|
||||
Serial.printf("\n\r");
|
||||
Serial.printf("\n\rFIXES:");
|
||||
Serial.printf("\n\r - Bounds checks in all array accesses");
|
||||
Serial.printf("\n\r - Rate limiting (5ms) for addStep()");
|
||||
Serial.printf("\n\r - Only call addStep() on voltage change");
|
||||
Serial.printf("\n\r - Stack overflow prevention");
|
||||
Serial.printf("\n\r==============================================\n\r");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// ===== DEBUG HEARTBEAT =====
|
||||
static unsigned long lastDebugPrint = 0;
|
||||
static unsigned long loopCounter = 0;
|
||||
|
||||
loopCounter++;
|
||||
|
||||
// Debug-Ausgabe alle 5 Sekunden
|
||||
if(millis() - lastDebugPrint > 5000)
|
||||
{
|
||||
Serial.printf("\n\r[HEARTBEAT] Loop count: %lu", loopCounter);
|
||||
Serial.printf("\n\r[DEBUG] SB1: Rec=%d, Play=%d, Steps=%d",
|
||||
sb1.isRecording(), sb1.isPlaying(), sb1.getStepCount());
|
||||
Serial.printf("\n\r[DEBUG] SB2: Rec=%d, Play=%d, Steps=%d",
|
||||
sb2.isRecording(), sb2.isPlaying(), sb2.getStepCount());
|
||||
Serial.printf("\n\r[DEBUG] Free heap: %lu bytes", ESP.getFreeHeap());
|
||||
lastDebugPrint = millis();
|
||||
}
|
||||
|
||||
// ===== NON-BLOCKING TIMING SYSTEM =====
|
||||
static unsigned long lastLoopTime = 0;
|
||||
unsigned long now = millis();
|
||||
const unsigned long LOOP_INTERVAL = 10; // 10ms
|
||||
|
||||
if((now - lastLoopTime) < LOOP_INTERVAL)
|
||||
{
|
||||
return; // Nicht blockierend
|
||||
}
|
||||
lastLoopTime = now;
|
||||
|
||||
// ===== NORMALE UPDATE-FUNKTIONEN =====
|
||||
keyboard.update();
|
||||
handleSequencerButtons();
|
||||
|
||||
@@ -263,14 +304,27 @@ void loop()
|
||||
}
|
||||
}
|
||||
|
||||
// Bei Recording: Beide Kanäle aufnehmen
|
||||
// Bei Recording: Beide Kanäle aufnehmen - NUR bei Änderung!
|
||||
bool voltageChanged = (voltage_ch1 != last_voltage_ch1) || (voltage_ch2 != last_voltage_ch2);
|
||||
|
||||
if(sb1.isRecording())
|
||||
{
|
||||
sb1.addStep(voltage_ch1, voltage_ch2);
|
||||
if(voltageChanged)
|
||||
{
|
||||
sb1.addStep(voltage_ch1, voltage_ch2);
|
||||
last_voltage_ch1 = voltage_ch1;
|
||||
last_voltage_ch2 = voltage_ch2;
|
||||
}
|
||||
}
|
||||
|
||||
if(sb2.isRecording())
|
||||
{
|
||||
sb2.addStep(voltage_ch1, voltage_ch2);
|
||||
if(voltageChanged)
|
||||
{
|
||||
sb2.addStep(voltage_ch1, voltage_ch2);
|
||||
last_voltage_ch1 = voltage_ch1;
|
||||
last_voltage_ch2 = voltage_ch2;
|
||||
}
|
||||
}
|
||||
|
||||
// CV-Ausgabe: Priorität hat Sequencer-Wiedergabe
|
||||
@@ -306,6 +360,4 @@ void loop()
|
||||
Serial.printf("\n\r[SEQ2] Final: Steps: %i, Duration: %ims",
|
||||
sb2.getStepCount(), sb2.getTotalDuration());
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
Reference in New Issue
Block a user