Firmware MCU: Sequencer, erfassen und widergabe im Sequencerblock von beiden Channel, playback im single und loop modus, test OK

This commit is contained in:
2025-11-30 20:20:05 +01:00
parent ce4e6cb536
commit dac90a977b
38 changed files with 8028 additions and 3833 deletions

View File

@@ -1,19 +1,11 @@
/*
* Example Code Three
* with Sequencer
*
* Bedienung:
* - Keyboard-Tasten: CV-Ausgabe direkt oder Recording
* - PIN_SB_1_REC: Sequencer 1 Record Start/Stop
* - PIN_SB_1_PLAY: Sequencer 1 Play/Stop
* - PIN_SB_2_REC: Sequencer 2 Record Start/Stop
* - PIN_SB_2_PLAY: Sequencer 2 Play/Stop
* Example Code Three - Dual Channel Sequencer
*/
#include "FIRMWARE_DEF.h"
#include "FIRMWARE.h"
static byte pins_keyboard_row[N_KEYBOARD_ROW] = {PIN_K_R0, PIN_K_R1, PIN_K_R2, PIN_K_R3};
static byte pins_keyboard_col[N_KEYBOARD_COL] = {PIN_K_C0, PIN_K_C1, PIN_K_C2};
byte pins_keyboard_row[N_KEYBOARD_ROW] = {PIN_K_R0, PIN_K_R1, PIN_K_R2, PIN_K_R3};
byte pins_keyboard_col[N_KEYBOARD_COL] = {PIN_K_C0, PIN_K_C1, PIN_K_C2};
Keyboard keyboard(N_KEYBOARD_ROW, N_KEYBOARD_COL, pins_keyboard_row, pins_keyboard_col);
@@ -28,8 +20,9 @@ uint16_t keyToVoltage[N_KEYBOARD_ROW*N_KEYBOARD_COL] = { /* 83mV = 1/12V */
CV cv(&MCP4728, &Wire, N_CV_GATES, cvMap, keyToVoltage, N_KEYBOARD_ROW, N_KEYBOARD_COL);
SequencerBlock sb1(30000, 250); // 30 Sekunden max, 250ms Timeout
SequencerBlock sb2(30000, 250);
// Sequencer mit 30s max, 50ms Mindest-Step-Dauer
SequencerBlock sb1(30000, 50);
SequencerBlock sb2(30000, 50);
// Button States
struct ButtonState {
@@ -45,10 +38,9 @@ ButtonState btn_sb2_play;
const unsigned long DEBOUNCE_DELAY = 50;
// Hilfsfunktion zum Lesen eines Buttons mit Debouncing
bool readButton(byte pin, ButtonState &state)
{
bool reading = digitalRead(pin) == LOW; // LOW = gedrückt (mit Pull-Up)
bool reading = digitalRead(pin) == HIGH;
bool buttonPressed = false;
if(reading != state.last)
@@ -61,7 +53,7 @@ bool readButton(byte pin, ButtonState &state)
if(reading != state.current)
{
state.current = reading;
if(state.current == true) // Button wurde gerade gedrückt
if(state.current == true)
{
buttonPressed = true;
}
@@ -74,10 +66,10 @@ bool readButton(byte pin, ButtonState &state)
void initButtons()
{
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);
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);
btn_sb1_rec.current = false;
btn_sb1_rec.last = false;
@@ -111,23 +103,54 @@ void handleSequencerButtons()
{
if(sb1.isPlaying()) sb1.stopPlay();
sb1.startRecord();
Serial.printf("\n\r[SEQ1] Recording started...");
Serial.printf("\n\r[SEQ1] Recording started (2 channels)...");
}
}
// Sequencer 1 Play Button
// Sequencer 1 Play Button - 3 Modi: Play / Loop / Stop
if(readButton(PIN_SB_1_PLAY, btn_sb1_play))
{
if(sb1.isPlaying())
if(!sb1.isPlaying())
{
sb1.stopPlay();
Serial.printf("\n\r[SEQ1] Playback stopped");
// Nicht am Spielen -> Starte Playback (ohne Loop)
if(sb1.isRecording()) sb1.stopRecord();
sb1.setLoop(false);
sb1.startPlay();
Serial.printf("\n\r[SEQ1] Playback started (single)\n\r\tSteps: %i, Duartion: %ims", sb1.getStepCount(), sb1.getTotalDuration());
}
else
{
if(sb1.isRecording()) sb1.stopRecord();
sb1.startPlay();
Serial.printf("\n\r[SEQ1] Playback started");
// 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");
}
}
}
}
@@ -144,23 +167,40 @@ void handleSequencerButtons()
{
if(sb2.isPlaying()) sb2.stopPlay();
sb2.startRecord();
Serial.printf("\n\r[SEQ2] Recording started...");
Serial.printf("\n\r[SEQ2] Recording started (2 channels)...");
}
}
// Sequencer 2 Play Button
// Sequencer 2 Play Button - 3 Modi: Play / Loop / Stop
if(readButton(PIN_SB_2_PLAY, btn_sb2_play))
{
if(sb2.isPlaying())
static bool seq2_loop_active = false;
if(!sb2.isPlaying())
{
sb2.stopPlay();
Serial.printf("\n\r[SEQ2] Playback stopped");
// 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)");
}
else
{
if(sb2.isRecording()) sb2.stopRecord();
sb2.startPlay();
Serial.printf("\n\r[SEQ2] Playback started");
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");
}
}
}
}
@@ -175,13 +215,16 @@ void setup()
sb1.setLoop(false);
sb2.setLoop(false);
Serial.printf("\n\r=== Sequencer System Started ===");
Serial.printf("\n\r=== Dual-Channel Sequencer System Started ===");
Serial.printf("\n\rControls:");
Serial.printf("\n\r PIN_SB_1_REC: SEQ1 Record Start/Stop");
Serial.printf("\n\r PIN_SB_1_PLAY: SEQ1 Play/Stop");
Serial.printf("\n\r PIN_SB_2_REC: SEQ2 Record Start/Stop");
Serial.printf("\n\r PIN_SB_2_PLAY: SEQ2 Play/Stop");
Serial.printf("\n\r================================\n\r");
Serial.printf("\n\r PIN_SB_1_REC: SEQ1 Record Start/Stop (CH1+CH2)");
Serial.printf("\n\r PIN_SB_1_PLAY: SEQ1 Play Mode Toggle:");
Serial.printf("\n\r 1st click: Play once");
Serial.printf("\n\r 2nd click: Loop mode");
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==============================================\n\r");
}
void loop()
@@ -195,71 +238,54 @@ void loop()
int n = keyboard.getQueueLength();
// Keyboard-Tasten verarbeiten
// Aktuelle Spannungen für beide Kanäle ermitteln
uint16_t voltage_ch1 = 0;
uint16_t voltage_ch2 = 0;
if(n > 0)
{
// Alle Keyboard-Tasten für CV-Ausgabe verwenden
int cvIndex = 0;
for(int i = 0; i < n && cvIndex < N_CV_GATES; i++)
Key k1 = keyboard.getQueue(0);
if(!isNotKey(k1))
{
Key k = keyboard.getQueue(i);
if(!isNotKey(k))
{
uint16_t voltage = keyToVoltage[k.row * N_KEYBOARD_COL + k.col];
// Bei Recording: Spannung aufnehmen
if(sb1.isRecording())
{
sb1.addStep(voltage);
}
if(sb2.isRecording())
{
sb2.addStep(voltage);
}
// Live-Ausgabe nur wenn nicht gerade wiedergegeben wird
if(!sb1.isPlaying() && !sb2.isPlaying())
{
cv.setVoltage(cvIndex++, k);
}
}
}
// Restliche CV-Ausgänge auf 0 setzen wenn live gespielt wird
if(!sb1.isPlaying() && !sb2.isPlaying())
{
for(int i = cvIndex; i < N_CV_GATES; i++)
{
cv.setVoltage(i, 0);
}
}
}
else
{
// Keine Tasten gedrückt
if(sb1.isRecording())
{
sb1.addStep(0);
}
if(sb2.isRecording())
{
sb2.addStep(0);
}
if(!sb1.isPlaying() && !sb2.isPlaying())
{
cv.clearAll();
voltage_ch1 = keyToVoltage[k1.row * N_KEYBOARD_COL + k1.col];
}
}
// Sequencer-Wiedergabe auf CV-Ausgänge
if(n > 1)
{
Key k2 = keyboard.getQueue(1);
if(!isNotKey(k2))
{
voltage_ch2 = keyToVoltage[k2.row * N_KEYBOARD_COL + k2.col];
}
}
// Bei Recording: Beide Kanäle aufnehmen
if(sb1.isRecording())
{
sb1.addStep(voltage_ch1, voltage_ch2);
}
if(sb2.isRecording())
{
sb2.addStep(voltage_ch1, voltage_ch2);
}
// CV-Ausgabe: Priorität hat Sequencer-Wiedergabe
if(sb1.isPlaying())
{
cv.setVoltage(0, sb1.getCurrentVoltage());
cv.setVoltage(0, sb1.getCurrentVoltageCh1());
cv.setVoltage(1, sb1.getCurrentVoltageCh2());
}
if(sb2.isPlaying())
else if(sb2.isPlaying())
{
cv.setVoltage(1, sb2.getCurrentVoltage());
cv.setVoltage(0, sb2.getCurrentVoltageCh1());
cv.setVoltage(1, sb2.getCurrentVoltageCh2());
}
else
{
// Live-Ausgabe wenn kein Sequencer spielt
cv.setVoltage(0, voltage_ch1);
cv.setVoltage(1, voltage_ch2);
}
// Time-Limit Warnung
@@ -267,12 +293,16 @@ void loop()
{
sb1.stopRecord();
Serial.printf("\n\r[SEQ1] Time limit reached! Recording stopped.");
Serial.printf("\n\r[SEQ1] Final: Steps: %i, Duration: %ims",
sb1.getStepCount(), sb1.getTotalDuration());
}
if(sb2.isRecording() && sb2.timeLimitReached())
{
sb2.stopRecord();
Serial.printf("\n\r[SEQ2] Time limit reached! Recording stopped.");
Serial.printf("\n\r[SEQ2] Final: Steps: %i, Duration: %ims",
sb2.getStepCount(), sb2.getTotalDuration());
}
delay(10); // Kürzeres Delay für bessere Sequencer-Auflösung
delay(10);
}