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

@@ -7,4 +7,4 @@ From : Project [TRI-SQR-VCO_OTA_SS.PrjPcb]
Files Generated : 1
Documents Printed : 0
Finished Output Generation At 21:27:47 On 23.10.2025
Finished Output Generation At 12:23:27 On 28.11.2025

View File

@@ -1,5 +1,5 @@
TRI-SQR-VCO_OTA_SS
*SPICE Netlist generated by Advanced Sim server on 18.11.2025 14:07:43
*SPICE Netlist generated by Advanced Sim server on 28.11.2025 12:29:53
.options MixedSimGenerated
*Schematic Netlist:
@@ -29,6 +29,8 @@ RR_A 0 U_SQR_OTA 3.63k
RR_CV NetR_CV_1 NetIC2_9 59.941k
RR_E NetC_an_2 NetR_E_2 10k
RR_lambda_T NetIC2_9 U_C 1.1k
RR_offset_1 NetR_CV_1 GND 10k
RR_offset_2 VAP NetR_CV_1 10k
RR_PWM_a GND NetIC3_6 15k
RR_PWM_b NetIC3_6 VAP 10k
RR_PWM_c U_PWM NetIC3_7 1k
@@ -55,15 +57,17 @@ VU_var NetR_CV_1 0 1
.PLOT TRAN {v(U_TRI)} =PLOT(2) =AXIS(1) =NAME(U_TRI) =UNITS(V)
.PLOT TRAN {v(U_SAW)} =PLOT(3) =AXIS(1) =NAME(U_SAW) =UNITS(V)
.PLOT TRAN {v(U_PWM)} =PLOT(4) =AXIS(1) =NAME(U_PWM) =UNITS(V)
.PLOT TRAN {i(U_mess)} =PLOT(5) =AXIS(1) =NAME(I_GND) =UNITS(A)
.PLOT TRAN {v(U_in)} =PLOT(2) =AXIS(1) =NAME(U_in) =UNITS(V)
.PLOT TRAN {v(U_C)} =PLOT(5) =AXIS(1) =NAME(U_C) =UNITS(V)
.OPTIONS ABSTOL=1e-10 RELTOL=1e-2 VNTOL=1e-4 METHOD=GEAR MAXORD=2
*Selected Circuit Analyses:
.TRAN 25u 20m 5m 25u UIC
.CONTROL
SWEEP R_offset_2 LIST 10k 20k 30k
.ENDC
*Global Parameters:
.PARAM POS=0
.PARAM POS={0}
*Models and Subcircuits:
* A dual opamp ngspice model

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
SB=SB1.R,SB1.P,SB2.R,SB2.P

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -26,9 +26,10 @@ struct Key
int col;
};
struct voltageDurationPair
struct DualVoltageDurationPair
{
uint16_t voltage;
uint16_t voltage_ch1;
uint16_t voltage_ch2;
uint16_t duration;
};
@@ -93,12 +94,12 @@ class CV
class SequencerBlock
{
public:
SequencerBlock(uint16_t maxDurationMS, uint16_t timeoutMS);
SequencerBlock(uint16_t maxDurationMS, uint16_t minStepDurationMS);
// Aufnahme-Funktionen
void startRecord();
void stopRecord();
void addStep(uint16_t voltage);
void addStep(uint16_t voltage_ch1, uint16_t voltage_ch2);
bool isRecording();
// Wiedergabe-Funktionen
@@ -114,18 +115,19 @@ class SequencerBlock
// Status-Abfragen
bool timeLimitReached();
uint8_t getStepCount();
uint16_t getCurrentVoltage();
uint16_t getCurrentVoltageCh1();
uint16_t getCurrentVoltageCh2();
uint16_t getTotalDuration();
private:
// Sequenz-Speicher
voltageDurationPair _sequence[N_MAX_SEQUENCE_STEPS];
DualVoltageDurationPair _sequence[N_MAX_SEQUENCE_STEPS];
uint8_t _stepCount;
uint8_t _currentStep;
// Zeitverwaltung
uint16_t _maxDurationMS;
uint16_t _timeoutMS;
uint16_t _minStepDurationMS;
unsigned long _recordStartTime;
unsigned long _lastStepTime;
unsigned long _playStartTime;
@@ -136,8 +138,9 @@ class SequencerBlock
bool _isPlaying;
bool _loop;
// Letzte aufgenommene Spannung
uint16_t _lastVoltage;
// Letzte aufgenommene Spannungen
uint16_t _lastVoltageCh1;
uint16_t _lastVoltageCh2;
// Hilfsfunktionen
void _finishCurrentStep();

View File

@@ -25,15 +25,15 @@
#define PIN_K_R1 8
#define PIN_K_R2 9
#define PIN_K_R3 10
#define PIN_K_R4 // NOT IN USE
#define PIN_K_R4 // 11 NOT IN USE
#define PIN_K_C0 1
#define PIN_K_C1 2
#define PIN_K_C2 4
#define PIN_K_C3 // NOT IN USE
#define PIN_K_C4 // NOT IN USE
#define PIN_K_C3 // 5 NOT IN USE
#define PIN_K_C4 // 6 NOT IN USE
// SEQUENCER BUTTON PINS
#define PIN_SB_1_REC 0
#define PIN_SB_1_PLAY 0
#define PIN_SB_2_REC 0
#define PIN_SB_2_PLAY 0
#define PIN_SB_1_REC 37 // 33 not available on dev board
#define PIN_SB_1_PLAY 38 // 34 not available on dev board
#define PIN_SB_2_REC 35
#define PIN_SB_2_PLAY 36
#endif

View File

@@ -177,15 +177,6 @@ void Keyboard::_removeActiveKey(uint8_t row, uint8_t col)
// ==================== CV ====================
/*!
* @param dac Adafruit_MCP4728 object
* @param wire TwoWire object
* @param nCV Number of CV-Gates
* @param cvChannelMap Maps CV-Gate-Index to a MCP4728 output-channel
* @param keyToVoltage One dimensional array of size row times col
* @param row Keyboard matrix rows
* @param col Keyboard matrix cols
*/
CV::CV(Adafruit_MCP4728 *dac, TwoWire *wire, uint8_t nCV, MCP4728_channel_t *cvChannelMap, uint16_t *keyToVoltage, uint8_t row, uint8_t col)
{
_dac = dac;
@@ -242,31 +233,27 @@ uint8_t CV::_getKeyToVoltageIndex(Key k)
// ==================== SequencerBlock ====================
/*!
* @param maxDurationMS maximum loop duration of recording in milliseconds
* @param timeoutMS stops recording after timeout in milliseconds
* @brief TODO
* @param minStepDurationMS minimum duration for a step to be recorded (prevents ultra-short steps)
*/
SequencerBlock::SequencerBlock(uint16_t maxDurationMS, uint16_t timeoutMS)
SequencerBlock::SequencerBlock(uint16_t maxDurationMS, uint16_t minStepDurationMS)
{
_maxDurationMS = maxDurationMS;
_timeoutMS = timeoutMS;
_minStepDurationMS = minStepDurationMS;
_stepCount = 0;
_currentStep = 0;
_isRecording = false;
_isPlaying = false;
_loop = false;
_lastVoltage = 0;
_lastVoltageCh1 = 0;
_lastVoltageCh2 = 0;
_recordStartTime = 0;
_lastStepTime = 0;
_playStartTime = 0;
_stepStartTime = 0;
}
/*!
* @brief starts sequence block recording
*/
void SequencerBlock::startRecord()
{
if(_isPlaying) stopPlay();
@@ -275,12 +262,10 @@ void SequencerBlock::startRecord()
_isRecording = true;
_recordStartTime = millis();
_lastStepTime = _recordStartTime;
_lastVoltage = 0;
_lastVoltageCh1 = 0xFFFF; // Ungültiger Wert zum Triggern des ersten Steps
_lastVoltageCh2 = 0xFFFF;
}
/*!
* @brief stops sequence block recording and saves it
*/
void SequencerBlock::stopRecord()
{
if(!_isRecording) return;
@@ -289,36 +274,44 @@ void SequencerBlock::stopRecord()
_isRecording = false;
}
/*!
* @brief adds step to sequencer block
* @param voltage voltage step for CV-Gate in millivolts
*/
void SequencerBlock::addStep(uint16_t voltage)
void SequencerBlock::addStep(uint16_t voltage_ch1, uint16_t voltage_ch2)
{
if(!_isRecording) return;
if(!_canAddStep()) return;
if(timeLimitReached())
{
stopRecord();
return;
}
unsigned long now = millis();
// Wenn sich die Spannung geändert hat, vorherigen Schritt abschließen
if(voltage != _lastVoltage && _stepCount > 0)
// Hat sich die Spannung geändert?
bool voltageChanged = (voltage_ch1 != _lastVoltageCh1) || (voltage_ch2 != _lastVoltageCh2);
if(voltageChanged)
{
// Vorherigen Step abschließen (wenn vorhanden)
if(_stepCount > 0)
{
_finishCurrentStep();
}
// Neuen Schritt beginnen oder vorhandenen aktualisieren
if(voltage != _lastVoltage || _stepCount == 0)
{
// Neuen Step beginnen
if(_canAddStep())
{
_sequence[_stepCount].voltage = voltage;
_sequence[_stepCount].voltage_ch1 = voltage_ch1;
_sequence[_stepCount].voltage_ch2 = voltage_ch2;
_sequence[_stepCount].duration = 0;
_stepCount++;
_lastStepTime = now;
_lastVoltage = voltage;
_lastVoltageCh1 = voltage_ch1;
_lastVoltageCh2 = voltage_ch2;
}
}
else
{
// Gleiche Spannung - Duration des aktuellen Steps aktualisieren
if(_stepCount > 0)
{
_sequence[_stepCount - 1].duration = now - _lastStepTime;
@@ -326,18 +319,11 @@ void SequencerBlock::addStep(uint16_t voltage)
}
}
/*!
* @brief checks if sequencer block is recording
* @return true or false
*/
bool SequencerBlock::isRecording()
{
return _isRecording;
}
/*!
* @brief starts playing sequencer block
*/
void SequencerBlock::startPlay()
{
if(_stepCount == 0) return;
@@ -349,19 +335,12 @@ void SequencerBlock::startPlay()
_stepStartTime = _playStartTime;
}
/*!
* @brief stops playing sequencer block
*/
void SequencerBlock::stopPlay()
{
_isPlaying = false;
_currentStep = 0;
}
/*!
* @brief updates sequencer block
* @attention Has to be called every cycle!
*/
void SequencerBlock::update()
{
if(!_isPlaying || _stepCount == 0) return;
@@ -395,44 +374,31 @@ void SequencerBlock::update()
}
}
/*!
* @brief checks if sequencer block is playing
* @return true or false
*/
bool SequencerBlock::isPlaying()
{
return _isPlaying;
}
/*!
* @brief clears recording of sequencer block
*/
void SequencerBlock::clear()
{
_stepCount = 0;
_currentStep = 0;
_lastVoltage = 0;
_lastVoltageCh1 = 0;
_lastVoltageCh2 = 0;
for(uint8_t i = 0; i < N_MAX_SEQUENCE_STEPS; i++)
{
_sequence[i].voltage = 0;
_sequence[i].voltage_ch1 = 0;
_sequence[i].voltage_ch2 = 0;
_sequence[i].duration = 0;
}
}
/*!
* @brief sets configuation for looping over the recording
* @param loop if set to true, saved recording gets played in a loop
*/
void SequencerBlock::setLoop(bool loop)
{
_loop = loop;
}
/*!
* @brief checks if the recording time limit has been reached
* @return true of false
*/
bool SequencerBlock::timeLimitReached()
{
if(!_isRecording) return false;
@@ -443,31 +409,27 @@ bool SequencerBlock::timeLimitReached()
return (elapsed >= _maxDurationMS);
}
/*!
* @brief returns the currently recoreded steps
* @return uint8_t between 0 and 128
*/
uint8_t SequencerBlock::getStepCount()
{
return _stepCount;
}
/*!
* @brief if sequencer is playing, returns the current voltage level
* @return uint16_t voltage range for CV
*/
uint16_t SequencerBlock::getCurrentVoltage()
uint16_t SequencerBlock::getCurrentVoltageCh1()
{
if(!_isPlaying || _stepCount == 0) return 0;
if(_currentStep >= _stepCount) return 0;
return _sequence[_currentStep].voltage;
return _sequence[_currentStep].voltage_ch1;
}
uint16_t SequencerBlock::getCurrentVoltageCh2()
{
if(!_isPlaying || _stepCount == 0) return 0;
if(_currentStep >= _stepCount) return 0;
return _sequence[_currentStep].voltage_ch2;
}
/*!
* @brief gets the length of the recording in the block
* @return uint16_t time in milliseconds
*/
uint16_t SequencerBlock::getTotalDuration()
{
uint16_t total = 0;
@@ -483,12 +445,17 @@ void SequencerBlock::_finishCurrentStep()
if(_stepCount == 0) return;
unsigned long now = millis();
_sequence[_stepCount - 1].duration = now - _lastStepTime;
uint16_t duration = now - _lastStepTime;
// Timeout prüfen - wenn zu lange keine Änderung, Schritt nicht hinzufügen
if(_sequence[_stepCount - 1].duration < _timeoutMS)
// Nur Steps mit ausreichender Dauer speichern
if(duration >= _minStepDurationMS)
{
_stepCount++;
_sequence[_stepCount - 1].duration = duration;
}
else
{
// Step war zu kurz, verwerfen
_stepCount--;
}
}

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();
// 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");
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];
voltage_ch1 = keyToVoltage[k1.row * N_KEYBOARD_COL + k1.col];
}
}
// Bei Recording: Spannung aufnehmen
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);
sb1.addStep(voltage_ch1, voltage_ch2);
}
if(sb2.isRecording())
{
sb2.addStep(voltage);
sb2.addStep(voltage_ch1, voltage_ch2);
}
// Live-Ausgabe nur wenn nicht gerade wiedergegeben wird
if(!sb1.isPlaying() && !sb2.isPlaying())
// CV-Ausgabe: Priorität hat Sequencer-Wiedergabe
if(sb1.isPlaying())
{
cv.setVoltage(cvIndex++, k);
cv.setVoltage(0, sb1.getCurrentVoltageCh1());
cv.setVoltage(1, sb1.getCurrentVoltageCh2());
}
}
}
// Restliche CV-Ausgänge auf 0 setzen wenn live gespielt wird
if(!sb1.isPlaying() && !sb2.isPlaying())
else if(sb2.isPlaying())
{
for(int i = cvIndex; i < N_CV_GATES; i++)
{
cv.setVoltage(i, 0);
}
}
cv.setVoltage(0, sb2.getCurrentVoltageCh1());
cv.setVoltage(1, sb2.getCurrentVoltageCh2());
}
else
{
// Keine Tasten gedrückt
if(sb1.isRecording())
{
sb1.addStep(0);
}
if(sb2.isRecording())
{
sb2.addStep(0);
}
if(!sb1.isPlaying() && !sb2.isPlaying())
{
cv.clearAll();
}
}
// Sequencer-Wiedergabe auf CV-Ausgänge
if(sb1.isPlaying())
{
cv.setVoltage(0, sb1.getCurrentVoltage());
}
if(sb2.isPlaying())
{
cv.setVoltage(1, sb2.getCurrentVoltage());
// 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);
}