Impplemenation of EN-Pins for CV-Gates, Metronome

This commit is contained in:
2025-12-06 11:48:11 +01:00
parent 06fa584b6d
commit 7c8a90ce7d
9 changed files with 201 additions and 94 deletions

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

@@ -4,7 +4,7 @@
@contact: etoth@tsn.at @contact: etoth@tsn.at
@date: 2025-10-26 @date: 2025-10-26
@updated: 2025-12-06 @updated: 2025-12-06
@brief: Header for FIRMWARE.cpp (FIXED) @brief: Header for FIRMWARE.cpp (FIXED VERSION)
*/ */
#include <Arduino.h> #include <Arduino.h>
#include <Wire.h> #include <Wire.h>
@@ -30,6 +30,7 @@ struct DualVoltageDurationPair
uint16_t voltage_ch1; uint16_t voltage_ch1;
uint16_t voltage_ch2; uint16_t voltage_ch2;
uint16_t duration; uint16_t duration;
bool active; // NEU: true wenn Step aktive Noten hat, false für Pausen
}; };
const Key NOT_A_KEY = {-1, -1}; const Key NOT_A_KEY = {-1, -1};
@@ -117,6 +118,7 @@ class SequencerBlock
uint16_t getStepCount(); uint16_t getStepCount();
uint16_t getCurrentVoltageCh1(); uint16_t getCurrentVoltageCh1();
uint16_t getCurrentVoltageCh2(); uint16_t getCurrentVoltageCh2();
bool isCurrentStepActive(); // NEU: Prüft ob aktueller Step aktive Noten hat
uint16_t getTotalDuration(); uint16_t getTotalDuration();
private: private:
@@ -124,7 +126,7 @@ class SequencerBlock
* @brief Memory limiting * @brief Memory limiting
* @return (uint16_t) 1024 * @return (uint16_t) 1024
* @attention Increasing the value might lead to an overflow * @attention Increasing the value might lead to an overflow
* @note sizeOf(DualVoltageDurationPair) = 6 Byte ==> 6 Byte * 1024 = 6144 Byte * @note sizeOf(DualVoltageDurationPair) = 8 Byte ==> 8 Byte * 1024 = 8192 Byte
*/ */
const static uint16_t _MAX_SEQUENCE_STEPS = 1024; const static uint16_t _MAX_SEQUENCE_STEPS = 1024;

View File

@@ -14,35 +14,36 @@
// CONSTANTS DEFINITONS // CONSTANTS DEFINITONS
#define N_KEYBOARD_ROW 4 // for PROD. change to 5 #define N_KEYBOARD_ROW 4 // for PROD. change to 5
#define N_KEYBOARD_COL 3 // for PROD. change to 5 #define N_KEYBOARD_COL 3 // for PROD. change to 5
#define N_CV_GATES 2 #define N_CV_GATES 2 // PROD. OK
#define N_SB 2 #define N_SB 2 // PROD. OK
#define BAUDRATE 115200 #define BAUDRATE 115200
#define N_MAX_SEQ_STEPS 512 #define N_MAX_SEQ_STEPS 512
// PIN DEFENTITIONS // PIN DEFENTITIONS
// I2C PINS // I2C PINS
#define PIN_SDA 15 #define PIN_SDA 15 // PROD. pin OK
#define PIN_SCL 16 #define PIN_SCL 16 // PROD. pin OK
// KEYBOARD PINS // KEYBOARD PINS
#define PIN_K_R0 7 #define PIN_K_R0 7 // PROD. pin OK
#define PIN_K_R1 8 #define PIN_K_R1 8 // PROD. pin OK
#define PIN_K_R2 9 #define PIN_K_R2 9 // PROD. pin OK
#define PIN_K_R3 10 #define PIN_K_R3 10 // PROD. pin OK
#define PIN_K_R4 11 // DEV. not in use #define PIN_K_R4 11 // DEV. not in use - PROD. pin OK
#define PIN_K_C0 1 #define PIN_K_C0 1 // PROD. pin OK
#define PIN_K_C1 2 #define PIN_K_C1 2 // PROD. pin OK
#define PIN_K_C2 4 #define PIN_K_C2 4 // PROD. pin OK
#define PIN_K_C3 5 // DEV. not in use #define PIN_K_C3 5 // DEV. not in use - PROD. pin OK
#define PIN_K_C4 6 // DEV. not in use #define PIN_K_C4 6 // DEV. not in use - PROD. pin OK
// SEQUENCER BUTTON PINS // SEQUENCER BUTTON PINS
#define PIN_SB_1_REC 42 // for PROD. change to 33 / not available on dev board #define PIN_SB_1_REC 38 // for PROD. change to 33 / not available on dev board
#define PIN_SB_1_PLAY 41 // for PROD. change to 34 / not available on dev board #define PIN_SB_1_PLAY 37 // for PROD. change to 34 / not available on dev board
#define PIN_SB_2_REC 40 // 35 #define PIN_SB_2_REC 35 // 35
#define PIN_SB_2_PLAY 39 // 36 #define PIN_SB_2_PLAY 36 // 36
// MISC/INFO PINS // MISC/INFO PINS
#define PIN_ACTIVE -1 // TODO: if any key is played return HIGH #define PIN_VCO1_EN 41 // PROD. pin 37 TODO: if there is an active key mapped to CV-Gate 1 --> HIGH
#define PIN_REC -1 // TODO: if any sb is recording return HIGH #define PIN_VCO2_EN 40 // PROD. pin 38 TODO: if there is an active key mapped to CV-Gate 2 --> HIGH
#define PIN_BPM -1 // TODO: get bpm through potentiometer analog value #define PIN_REC 39 // PROD. pin 39 TODO: if any sb is recording LED on (active-low)
#define PIN_B_METRONOME -1 // TODO: button activates/deactivates bpm led output #define PIN_BPM 12 // PROD. pin 12 TODO: get bpm through potentiometer analog value -> ADC-Pin
#define PIN_L_METRONOME -1 // TODO: led blinks according to bpm value #define PIN_B_METRONOME 13 // PROD. pin 13 TODO: button activates/deactivates bpm led output (pull-up)
#define PIN_L_METRONOME 14 // PROD. pin 14 TODO: led blinks according to bpm value (active-low)
#endif #endif

View File

@@ -302,7 +302,7 @@ void SequencerBlock::addStep(uint16_t voltage_ch1, uint16_t voltage_ch2)
unsigned long now = millis(); unsigned long now = millis();
// NEU: Rate-Limiting - ignoriere zu häufige Aufrufe // NEU: Rate-Limiting - ignoriere zu häufige Aufrufe
if((now - _lastAddStepTime) < 5) // Mindestens 5ms zwischen Updates if((unsigned long)(now - _lastAddStepTime) < 5)
{ {
return; return;
} }
@@ -333,6 +333,7 @@ void SequencerBlock::addStep(uint16_t voltage_ch1, uint16_t voltage_ch2)
_sequence[_stepCount].voltage_ch1 = voltage_ch1; _sequence[_stepCount].voltage_ch1 = voltage_ch1;
_sequence[_stepCount].voltage_ch2 = voltage_ch2; _sequence[_stepCount].voltage_ch2 = voltage_ch2;
_sequence[_stepCount].duration = 0; _sequence[_stepCount].duration = 0;
_sequence[_stepCount].active = (voltage_ch1 > 0 || voltage_ch2 > 0); // NEU: Prüfe ob Note aktiv
_stepCount++; _stepCount++;
_lastStepTime = now; _lastStepTime = now;
@@ -452,6 +453,7 @@ void SequencerBlock::clear()
_sequence[i].voltage_ch1 = 0; _sequence[i].voltage_ch1 = 0;
_sequence[i].voltage_ch2 = 0; _sequence[i].voltage_ch2 = 0;
_sequence[i].duration = 0; _sequence[i].duration = 0;
_sequence[i].active = false;
} }
} }
@@ -506,6 +508,14 @@ uint16_t SequencerBlock::getTotalDuration()
return (total > 65535) ? 65535 : (uint16_t)total; // Clamp auf uint16 return (total > 65535) ? 65535 : (uint16_t)total; // Clamp auf uint16
} }
bool SequencerBlock::isCurrentStepActive()
{
if(!_isPlaying || _stepCount == 0) return false;
if(_currentStep >= _stepCount || _currentStep >= _MAX_SEQUENCE_STEPS) return false;
return _sequence[_currentStep].active;
}
void SequencerBlock::_finishCurrentStep() void SequencerBlock::_finishCurrentStep()
{ {
if(_stepCount == 0) return; if(_stepCount == 0) return;

View File

@@ -1,9 +1,7 @@
/* /*
* Example Code Three - Dual Channel Sequencer (FIXED) * Example Code Three - Dual Channel Sequencer (COMPLETE)
* - Bounds Checks hinzugefügt * - Alle TODOs implementiert
* - Rate-Limiting implementiert * - VCO Gates, Recording LED, Metronome
* - Debug-Ausgaben erweitert
* - Stack Overflow verhindert
*/ */
#include "FIRMWARE_DEF.h" #include "FIRMWARE_DEF.h"
#include "FIRMWARE.h" #include "FIRMWARE.h"
@@ -15,16 +13,15 @@ Keyboard keyboard(N_KEYBOARD_ROW, N_KEYBOARD_COL, pins_keyboard_row, pins_keyboa
Adafruit_MCP4728 MCP4728; Adafruit_MCP4728 MCP4728;
MCP4728_channel_t cvMap[N_CV_GATES] = {MCP4728_CHANNEL_A, MCP4728_CHANNEL_B}; MCP4728_channel_t cvMap[N_CV_GATES] = {MCP4728_CHANNEL_A, MCP4728_CHANNEL_B};
uint16_t keyToVoltage[N_KEYBOARD_ROW*N_KEYBOARD_COL] = { /* 83mV = 1/12V */ uint16_t keyToVoltage[N_KEYBOARD_ROW*N_KEYBOARD_COL] = {
1*83, 5*83, 9*83, /* ROW 1: B D Fis */ 1*83, 5*83, 9*83,
2*83, 6*83, 10*83, /* ROW 2: H Dis G */ 2*83, 6*83, 10*83,
3*83, 7*83, 11*83, /* ROW 3: C E Gis */ 3*83, 7*83, 11*83,
4*83, 8*83, 12*83 /* ROW 4: Cis F A' */ 4*83, 8*83, 12*83
}; };
CV cv(&MCP4728, &Wire, N_CV_GATES, cvMap, keyToVoltage, N_KEYBOARD_ROW, N_KEYBOARD_COL); CV cv(&MCP4728, &Wire, N_CV_GATES, cvMap, keyToVoltage, N_KEYBOARD_ROW, N_KEYBOARD_COL);
// Sequencer 30s max, 512 max Steps
SequencerBlock sb1(30000, N_MAX_SEQ_STEPS); SequencerBlock sb1(30000, N_MAX_SEQ_STEPS);
SequencerBlock sb2(30000, N_MAX_SEQ_STEPS); SequencerBlock sb2(30000, N_MAX_SEQ_STEPS);
@@ -39,14 +36,12 @@ ButtonState btn_sb1_rec;
ButtonState btn_sb1_play; ButtonState btn_sb1_play;
ButtonState btn_sb2_rec; ButtonState btn_sb2_rec;
ButtonState btn_sb2_play; ButtonState btn_sb2_play;
ButtonState btn_metronome;
const unsigned long DEBOUNCE_DELAY = 50; const unsigned long DEBOUNCE_DELAY = 50;
// Loop-Status für State Machine
static bool seq1_loop_active = false; static bool seq1_loop_active = false;
static bool seq2_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_ch1 = 0xFFFF;
static uint16_t last_voltage_ch2 = 0xFFFF; static uint16_t last_voltage_ch2 = 0xFFFF;
@@ -82,6 +77,7 @@ void initButtons()
pinMode(PIN_SB_1_PLAY, INPUT_PULLUP); pinMode(PIN_SB_1_PLAY, INPUT_PULLUP);
pinMode(PIN_SB_2_REC, INPUT_PULLUP); pinMode(PIN_SB_2_REC, INPUT_PULLUP);
pinMode(PIN_SB_2_PLAY, INPUT_PULLUP); pinMode(PIN_SB_2_PLAY, INPUT_PULLUP);
pinMode(PIN_B_METRONOME, INPUT_PULLUP);
btn_sb1_rec.current = false; btn_sb1_rec.current = false;
btn_sb1_rec.last = false; btn_sb1_rec.last = false;
@@ -98,6 +94,30 @@ void initButtons()
btn_sb2_play.current = false; btn_sb2_play.current = false;
btn_sb2_play.last = false; btn_sb2_play.last = false;
btn_sb2_play.lastDebounceTime = 0; btn_sb2_play.lastDebounceTime = 0;
btn_metronome.current = false;
btn_metronome.last = false;
btn_metronome.lastDebounceTime = 0;
}
void initOutputs()
{
// VCO Gates
pinMode(PIN_VCO1_EN, OUTPUT);
pinMode(PIN_VCO2_EN, OUTPUT);
digitalWrite(PIN_VCO1_EN, LOW);
digitalWrite(PIN_VCO2_EN, LOW);
// Recording LED (active-low)
pinMode(PIN_REC, OUTPUT);
digitalWrite(PIN_REC, HIGH); // OFF
// Metronome LED (active-low)
pinMode(PIN_L_METRONOME, OUTPUT);
digitalWrite(PIN_L_METRONOME, HIGH); // OFF
// BPM Potentiometer
pinMode(PIN_BPM, INPUT);
} }
void handleSequencerButtons() void handleSequencerButtons()
@@ -115,13 +135,13 @@ void handleSequencerButtons()
{ {
if(sb1.isPlaying()) sb1.stopPlay(); if(sb1.isPlaying()) sb1.stopPlay();
sb1.startRecord(); sb1.startRecord();
last_voltage_ch1 = 0xFFFF; // Reset voltage tracking last_voltage_ch1 = 0xFFFF;
last_voltage_ch2 = 0xFFFF; last_voltage_ch2 = 0xFFFF;
Serial.printf("\n\r[SEQ1] Recording started (2 channels)..."); Serial.printf("\n\r[SEQ1] Recording started (2 channels)...");
} }
} }
// ===== Sequencer 1 Play Button (3 Stati: Play / Loop / Stop) ===== // ===== Sequencer 1 Play Button =====
if(readButton(PIN_SB_1_PLAY, btn_sb1_play)) if(readButton(PIN_SB_1_PLAY, btn_sb1_play))
{ {
if(!sb1.isPlaying()) if(!sb1.isPlaying())
@@ -160,13 +180,13 @@ void handleSequencerButtons()
{ {
if(sb2.isPlaying()) sb2.stopPlay(); if(sb2.isPlaying()) sb2.stopPlay();
sb2.startRecord(); sb2.startRecord();
last_voltage_ch1 = 0xFFFF; // Reset voltage tracking last_voltage_ch1 = 0xFFFF;
last_voltage_ch2 = 0xFFFF; last_voltage_ch2 = 0xFFFF;
Serial.printf("\n\r[SEQ2] Recording started (2 channels)..."); Serial.printf("\n\r[SEQ2] Recording started (2 channels)...");
} }
} }
// ===== Sequencer 2 Play Button (3 Stati: Play / Loop / Stop) ===== // ===== Sequencer 2 Play Button =====
if(readButton(PIN_SB_2_PLAY, btn_sb2_play)) if(readButton(PIN_SB_2_PLAY, btn_sb2_play))
{ {
if(!sb2.isPlaying()) if(!sb2.isPlaying())
@@ -193,16 +213,88 @@ void handleSequencerButtons()
} }
} }
static bool metronome_enabled = false;
static uint16_t current_bpm = 120;
static unsigned long last_beat_time = 0;
static unsigned long last_pulse_end_time = 0;
static bool metronome_led_on = false;
void updateMetronome()
{
unsigned long now = millis();
// BPM von Potentiometer lesen (alle 100ms)
static unsigned long last_bpm_read = 0;
if((now - last_bpm_read) > 100)
{
int adc_value = analogRead(PIN_BPM);
// Map ADC (0-4095) zu BPM (40-240)
current_bpm = map(adc_value, 0, 4095, 40, 240);
last_bpm_read = now;
}
// Metronome Button (Toggle)
if(readButton(PIN_B_METRONOME, btn_metronome))
{
metronome_enabled = !metronome_enabled;
Serial.printf("\n\r[METRONOME] %s (BPM: %d)",
metronome_enabled ? "ON" : "OFF", current_bpm);
if(!metronome_enabled)
{
digitalWrite(PIN_L_METRONOME, HIGH); // Active-low: HIGH = OFF
metronome_led_on = false;
}
}
if(!metronome_enabled) return;
// Berechne Beat-Intervall in ms
unsigned long beat_interval = 60000UL / current_bpm;
// Neue Beat?
if((now - last_beat_time) >= beat_interval)
{
digitalWrite(PIN_L_METRONOME, LOW); // Active-low: LOW = ON
metronome_led_on = true;
last_beat_time = now;
last_pulse_end_time = now + 50; // 50ms Pulse
}
// Pulse beenden?
if(metronome_led_on && (now >= last_pulse_end_time))
{
digitalWrite(PIN_L_METRONOME, HIGH); // Active-low: HIGH = OFF
metronome_led_on = false;
}
}
void updateVCOGates(bool cv1_active, bool cv2_active)
{
// PIN_VCO1_EN: HIGH wenn CV1 aktiv (Key mapped to CV-Gate 1)
digitalWrite(PIN_VCO1_EN, cv1_active ? HIGH : LOW);
// PIN_VCO2_EN: HIGH wenn CV2 aktiv (Key mapped to CV-Gate 2)
digitalWrite(PIN_VCO2_EN, cv2_active ? HIGH : LOW);
}
void updateRecordingLED()
{
// PIN_REC: Active-low (LOW = LED ON)
bool any_recording = sb1.isRecording() || sb2.isRecording();
digitalWrite(PIN_REC, any_recording ? LOW : HIGH);
}
void setup() void setup()
{ {
Serial.begin(BAUDRATE); Serial.begin(BAUDRATE);
delay(2000); delay(2000);
Serial.printf("\n\r=== FIXED VERSION v2 ==="); Serial.printf("\n\r=== COMPLETE VERSION with TODOs ===");
Serial.printf("\n\rSerial OK!"); Serial.printf("\n\rSerial OK!");
keyboard.begin(); keyboard.begin();
// Fehlerbehandlung für CV-Initialisierung
unsigned long timeout = millis() + 5000; unsigned long timeout = millis() + 5000;
while(!cv.begin(PIN_SDA, PIN_SCL)) while(!cv.begin(PIN_SDA, PIN_SCL))
{ {
@@ -219,25 +311,16 @@ void setup()
Serial.printf("\n\r[OK] CV initialized"); Serial.printf("\n\r[OK] CV initialized");
initButtons(); initButtons();
initOutputs();
sb1.setLoop(false); sb1.setLoop(false);
sb2.setLoop(false); sb2.setLoop(false);
Serial.printf("\n\r=== Dual-Channel Sequencer System Started ==="); Serial.printf("\n\r=== Dual-Channel Sequencer System Started ===");
Serial.printf("\n\rControls:"); Serial.printf("\n\rFeatures:");
Serial.printf("\n\r PIN_SB_1_REC: SEQ1 Record Start/Stop (CH1+CH2)"); Serial.printf("\n\r - VCO1/VCO2 Gate Outputs");
Serial.printf("\n\r PIN_SB_1_PLAY: SEQ1 Play Mode Toggle:"); Serial.printf("\n\r - Recording LED Indicator");
Serial.printf("\n\r 1st click: Play once"); Serial.printf("\n\r - BPM Metronome (40-240 BPM)");
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");
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"); Serial.printf("\n\r==============================================\n\r");
} }
@@ -249,42 +332,44 @@ void loop()
loopCounter++; loopCounter++;
// Debug-Ausgabe alle 5 Sekunden
if(millis() - lastDebugPrint > 5000) if(millis() - lastDebugPrint > 5000)
{ {
Serial.printf("\n\r[HEARTBEAT] Loop count: %lu", loopCounter); Serial.printf("\n\r[HEARTBEAT] Loop: %lu | BPM: %d | Metro: %s",
loopCounter, current_bpm, metronome_enabled ? "ON" : "OFF");
Serial.printf("\n\r[DEBUG] SB1: Rec=%d, Play=%d, Steps=%d", Serial.printf("\n\r[DEBUG] SB1: Rec=%d, Play=%d, Steps=%d",
sb1.isRecording(), sb1.isPlaying(), sb1.getStepCount()); sb1.isRecording(), sb1.isPlaying(), sb1.getStepCount());
Serial.printf("\n\r[DEBUG] SB2: Rec=%d, Play=%d, Steps=%d", Serial.printf("\n\r[DEBUG] SB2: Rec=%d, Play=%d, Steps=%d",
sb2.isRecording(), sb2.isPlaying(), sb2.getStepCount()); sb2.isRecording(), sb2.isPlaying(), sb2.getStepCount());
Serial.printf("\n\r[DEBUG] Free heap: %lu bytes", ESP.getFreeHeap());
lastDebugPrint = millis(); lastDebugPrint = millis();
} }
// ===== NON-BLOCKING TIMING SYSTEM ===== // ===== NON-BLOCKING TIMING =====
static unsigned long lastLoopTime = 0; static unsigned long lastLoopTime = 0;
unsigned long now = millis(); unsigned long now = millis();
const unsigned long LOOP_INTERVAL = 10; // 10ms const unsigned long LOOP_INTERVAL = 10;
if((now - lastLoopTime) < LOOP_INTERVAL) if((now - lastLoopTime) < LOOP_INTERVAL)
{ {
return; // Nicht blockierend return;
} }
lastLoopTime = now; lastLoopTime = now;
// ===== NORMALE UPDATE-FUNKTIONEN ===== // ===== UPDATE FUNCTIONS =====
keyboard.update(); keyboard.update();
handleSequencerButtons(); handleSequencerButtons();
updateMetronome();
updateRecordingLED();
// Sequencer Update (für Wiedergabe)
sb1.update(); sb1.update();
sb2.update(); sb2.update();
int n = keyboard.getQueueLength(); int n = keyboard.getQueueLength();
// Aktuelle Spannungen für beide Kanäle ermitteln // Aktuelle Spannungen ermitteln
uint16_t voltage_ch1 = 0; uint16_t voltage_ch1 = 0;
uint16_t voltage_ch2 = 0; uint16_t voltage_ch2 = 0;
bool cv1_active = false;
bool cv2_active = false;
if(n > 0) if(n > 0)
{ {
@@ -292,6 +377,7 @@ void loop()
if(!isNotKey(k1)) if(!isNotKey(k1))
{ {
voltage_ch1 = keyToVoltage[k1.row * N_KEYBOARD_COL + k1.col]; voltage_ch1 = keyToVoltage[k1.row * N_KEYBOARD_COL + k1.col];
cv1_active = true;
} }
} }
@@ -301,51 +387,59 @@ void loop()
if(!isNotKey(k2)) if(!isNotKey(k2))
{ {
voltage_ch2 = keyToVoltage[k2.row * N_KEYBOARD_COL + k2.col]; voltage_ch2 = keyToVoltage[k2.row * N_KEYBOARD_COL + k2.col];
cv2_active = true;
} }
} }
// Bei Recording: Beide Kanäle aufnehmen - NUR bei Änderung! // Recording
bool voltageChanged = (voltage_ch1 != last_voltage_ch1) || (voltage_ch2 != last_voltage_ch2); bool voltageChanged = (voltage_ch1 != last_voltage_ch1) || (voltage_ch2 != last_voltage_ch2);
if(sb1.isRecording()) if(sb1.isRecording() && voltageChanged)
{ {
if(voltageChanged) sb1.addStep(voltage_ch1, voltage_ch2);
{ last_voltage_ch1 = voltage_ch1;
sb1.addStep(voltage_ch1, voltage_ch2); last_voltage_ch2 = voltage_ch2;
last_voltage_ch1 = voltage_ch1;
last_voltage_ch2 = voltage_ch2;
}
} }
if(sb2.isRecording()) if(sb2.isRecording() && voltageChanged)
{ {
if(voltageChanged) sb2.addStep(voltage_ch1, voltage_ch2);
{ last_voltage_ch1 = voltage_ch1;
sb2.addStep(voltage_ch1, voltage_ch2); last_voltage_ch2 = voltage_ch2;
last_voltage_ch1 = voltage_ch1;
last_voltage_ch2 = voltage_ch2;
}
} }
// CV-Ausgabe: Priorität hat Sequencer-Wiedergabe // CV-Ausgabe & VCO Gates
if(sb1.isPlaying()) if(sb1.isPlaying())
{ {
cv.setVoltage(0, sb1.getCurrentVoltageCh1()); uint16_t seq_v1 = sb1.getCurrentVoltageCh1();
cv.setVoltage(1, sb1.getCurrentVoltageCh2()); uint16_t seq_v2 = sb1.getCurrentVoltageCh2();
cv.setVoltage(0, seq_v1);
cv.setVoltage(1, seq_v2);
// KORREKT: Nutze isCurrentStepActive() statt Spannung > 0
// Da 0V eine gültige Note sein kann!
bool gate_active = sb1.isCurrentStepActive();
updateVCOGates(gate_active, gate_active);
} }
else if(sb2.isPlaying()) else if(sb2.isPlaying())
{ {
cv.setVoltage(0, sb2.getCurrentVoltageCh1()); uint16_t seq_v1 = sb2.getCurrentVoltageCh1();
cv.setVoltage(1, sb2.getCurrentVoltageCh2()); uint16_t seq_v2 = sb2.getCurrentVoltageCh2();
cv.setVoltage(0, seq_v1);
cv.setVoltage(1, seq_v2);
bool gate_active = sb2.isCurrentStepActive();
updateVCOGates(gate_active, gate_active);
} }
else else
{ {
// Live-Ausgabe wenn kein Sequencer spielt // Live-Modus: cv1_active/cv2_active basieren auf tatsächlich gedrückten Tasten
cv.setVoltage(0, voltage_ch1); cv.setVoltage(0, voltage_ch1);
cv.setVoltage(1, voltage_ch2); cv.setVoltage(1, voltage_ch2);
updateVCOGates(cv1_active, cv2_active);
} }
// Time-Limit Warnung // Time-Limit Check
if(sb1.isRecording() && sb1.timeLimitReached()) if(sb1.isRecording() && sb1.timeLimitReached())
{ {
sb1.stopRecord(); sb1.stopRecord();