mirror of
https://github.com/erik-toth/audio-synth.git
synced 2026-03-12 09:07:41 +00:00
Compare commits
7 Commits
9e4ef1c330
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| c953f17090 | |||
| 04884bc243 | |||
|
|
d9964ced3e | ||
| 2108552078 | |||
| a7e466932b | |||
|
|
1216ea923c | ||
|
|
f2becb78b7 |
Binary file not shown.
@@ -3,7 +3,7 @@
|
|||||||
@author: Erik Tóth
|
@author: Erik Tóth
|
||||||
@contact: etoth@tsn.at
|
@contact: etoth@tsn.at
|
||||||
@date: 2025-10-26
|
@date: 2025-10-26
|
||||||
@updated: 2025-12-06
|
@updated: 2026-03-08
|
||||||
@brief: Header for constant definitions
|
@brief: Header for constant definitions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -12,38 +12,38 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
// CONSTANTS DEFINITONS
|
// CONSTANTS DEFINITONS
|
||||||
#define N_KEYBOARD_ROW 5 // for PROD. change to 5
|
#define N_KEYBOARD_ROW 5
|
||||||
#define N_KEYBOARD_COL 5 // for PROD. change to 5
|
#define N_KEYBOARD_COL 5
|
||||||
#define N_CV_GATES 2 // PROD. OK
|
#define N_CV_GATES 2
|
||||||
#define N_SB 2 // PROD. OK
|
#define N_SB 2
|
||||||
#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 // PROD. pin OK
|
#define PIN_SDA 15
|
||||||
#define PIN_SCL 16 // PROD. pin OK
|
#define PIN_SCL 16
|
||||||
// KEYBOARD PINS
|
// KEYBOARD PINS
|
||||||
#define PIN_K_R0 7 // PROD. pin OK
|
#define PIN_K_R0 7
|
||||||
#define PIN_K_R1 8 // PROD. pin OK
|
#define PIN_K_R1 8
|
||||||
#define PIN_K_R2 9 // PROD. pin OK
|
#define PIN_K_R2 9
|
||||||
#define PIN_K_R3 10 // PROD. pin OK
|
#define PIN_K_R3 10
|
||||||
#define PIN_K_R4 11 // DEV. not in use - PROD. pin OK
|
#define PIN_K_R4 11
|
||||||
#define PIN_K_C0 1 // PROD. pin OK
|
#define PIN_K_C0 1
|
||||||
#define PIN_K_C1 2 // PROD. pin OK
|
#define PIN_K_C1 2
|
||||||
#define PIN_K_C2 4 // PROD. pin OK
|
#define PIN_K_C2 4
|
||||||
#define PIN_K_C3 5 // DEV. not in use - PROD. pin OK
|
#define PIN_K_C3 5
|
||||||
#define PIN_K_C4 6 // DEV. not in use - PROD. pin OK
|
#define PIN_K_C4 6
|
||||||
// SEQUENCER BUTTON PINS
|
// SEQUENCER BUTTON PINS
|
||||||
#define PIN_SB_1_REC 33 // for PROD. change to 33 / not available on dev board
|
#define PIN_SB_1_REC 33
|
||||||
#define PIN_SB_1_PLAY 34 // for PROD. change to 34 / not available on dev board
|
#define PIN_SB_1_PLAY 34
|
||||||
#define PIN_SB_2_REC 35 // 35
|
#define PIN_SB_2_REC 35
|
||||||
#define PIN_SB_2_PLAY 36 // 36
|
#define PIN_SB_2_PLAY 36
|
||||||
// MISC/INFO PINS
|
// MISC/INFO PINS
|
||||||
#define PIN_VCO1_EN 38 // PROD. pin 38 TODO: if there is an active key mapped to CV-Gate 1 --> HIGH
|
#define PIN_VCO1_EN 38
|
||||||
#define PIN_VCO2_EN 39 // PROD. pin 39 TODO: if there is an active key mapped to CV-Gate 2 --> HIGH
|
#define PIN_VCO2_EN 39
|
||||||
#define PIN_REC 37 // PROD. pin 37 TODO: if any sb is recording LED on (active-low)
|
#define PIN_REC 37
|
||||||
#define PIN_BPM 12 // PROD. pin 12 TODO: get bpm through potentiometer analog value -> ADC-Pin
|
#define PIN_BPM 12
|
||||||
#define PIN_B_METRONOME 14 // PROD. pin 13 TODO: button activates/deactivates bpm led output (pull-up)
|
#define PIN_B_METRONOME 14
|
||||||
#define PIN_L_METRONOME 13 // PROD. pin 14 TODO: led blinks according to bpm value (active-low)
|
#define PIN_L_METRONOME 13
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -3,13 +3,13 @@
|
|||||||
@author: Erik Tóth
|
@author: Erik Tóth
|
||||||
@contact: etoth@tsn.at
|
@contact: etoth@tsn.at
|
||||||
@date: 2025-10-26
|
@date: 2025-10-26
|
||||||
@updated: 2025-12-06
|
@updated: 2026-03-08
|
||||||
@brief: Firmware für MCU - FIXED VERSION mit Bounds Checks
|
@brief: Firmware für MCU
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "FIRMWARE.h"
|
#include "FIRMWARE.h"
|
||||||
|
|
||||||
// ==================== Helper-Functions ====================
|
// Helper-Functions
|
||||||
|
|
||||||
bool isNotKey(Key k)
|
bool isNotKey(Key k)
|
||||||
{
|
{
|
||||||
@@ -23,7 +23,7 @@ bool isEqualKey(Key k1, Key k2)
|
|||||||
else return false;
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== Keyboard ====================
|
// Keyboard
|
||||||
|
|
||||||
Keyboard::Keyboard(uint8_t nRows, uint8_t nCols, uint8_t *pinsRow, uint8_t *pinsCol)
|
Keyboard::Keyboard(uint8_t nRows, uint8_t nCols, uint8_t *pinsRow, uint8_t *pinsCol)
|
||||||
{
|
{
|
||||||
@@ -178,7 +178,7 @@ void Keyboard::_removeActiveKey(uint8_t row, uint8_t col)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== CV ====================
|
// CV
|
||||||
|
|
||||||
CV::CV(Adafruit_MCP4728 *dac, TwoWire *wire, uint8_t nCV, MCP4728_channel_t *cvChannelMap, uint16_t *keyToVoltage, uint8_t row, uint8_t col)
|
CV::CV(Adafruit_MCP4728 *dac, TwoWire *wire, uint8_t nCV, MCP4728_channel_t *cvChannelMap, uint16_t *keyToVoltage, uint8_t row, uint8_t col)
|
||||||
{
|
{
|
||||||
@@ -209,7 +209,7 @@ void CV::setVoltage(uint8_t cvIndex, uint16_t mV)
|
|||||||
{
|
{
|
||||||
if(cvIndex >= _nCV) return;
|
if(cvIndex >= _nCV) return;
|
||||||
MCP4728_channel_t ch = _cvChannelMap[cvIndex];
|
MCP4728_channel_t ch = _cvChannelMap[cvIndex];
|
||||||
_dac->setChannelValue(ch, map(mV, 0, 1992, 0, 2048), MCP4728_VREF_INTERNAL);
|
_dac->setChannelValue(ch, map(mV, 0, 2048, 0, 4095), MCP4728_VREF_INTERNAL, MCP4728_GAIN_1X);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CV::setVoltage(uint8_t cvIndex, Key k)
|
void CV::setVoltage(uint8_t cvIndex, Key k)
|
||||||
@@ -234,7 +234,7 @@ uint8_t CV::_getKeyToVoltageIndex(Key k)
|
|||||||
return (k.row*_col + k.col);
|
return (k.row*_col + k.col);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== SequencerBlock (FIXED) ====================
|
// SequencerBlock
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* @param maxDurationMS maximum loop duration of recording in milliseconds
|
* @param maxDurationMS maximum loop duration of recording in milliseconds
|
||||||
@@ -255,7 +255,7 @@ SequencerBlock::SequencerBlock(uint16_t maxDurationMS, uint16_t maxStepCount)
|
|||||||
_lastStepTime = 0;
|
_lastStepTime = 0;
|
||||||
_playStartTime = 0;
|
_playStartTime = 0;
|
||||||
_stepStartTime = 0;
|
_stepStartTime = 0;
|
||||||
_lastAddStepTime = 0; // NEU: Rate-Limiting
|
_lastAddStepTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SequencerBlock::startRecord()
|
void SequencerBlock::startRecord()
|
||||||
@@ -266,7 +266,7 @@ void SequencerBlock::startRecord()
|
|||||||
_isRecording = true;
|
_isRecording = true;
|
||||||
_recordStartTime = millis();
|
_recordStartTime = millis();
|
||||||
_lastStepTime = _recordStartTime;
|
_lastStepTime = _recordStartTime;
|
||||||
_lastAddStepTime = _recordStartTime; // NEU
|
_lastAddStepTime = _recordStartTime;
|
||||||
_lastVoltageCh1 = 0xFFFF;
|
_lastVoltageCh1 = 0xFFFF;
|
||||||
_lastVoltageCh2 = 0xFFFF;
|
_lastVoltageCh2 = 0xFFFF;
|
||||||
}
|
}
|
||||||
@@ -281,10 +281,8 @@ void SequencerBlock::stopRecord()
|
|||||||
|
|
||||||
void SequencerBlock::addStep(uint16_t voltage_ch1, uint16_t voltage_ch2)
|
void SequencerBlock::addStep(uint16_t voltage_ch1, uint16_t voltage_ch2)
|
||||||
{
|
{
|
||||||
// KRITISCHE SICHERHEITSPRÜFUNGEN ZUERST
|
|
||||||
if(!_isRecording) return;
|
if(!_isRecording) return;
|
||||||
|
|
||||||
// Prüfe ob wir überhaupt noch Platz haben (mit Sicherheitsabstand!)
|
|
||||||
if(_stepCount >= _MAX_SEQUENCE_STEPS - 1)
|
if(_stepCount >= _MAX_SEQUENCE_STEPS - 1)
|
||||||
{
|
{
|
||||||
Serial.println("\n\r[ERROR] Step limit reached! Stopping recording.");
|
Serial.println("\n\r[ERROR] Step limit reached! Stopping recording.");
|
||||||
@@ -301,19 +299,16 @@ 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
|
|
||||||
if((unsigned long)(now - _lastAddStepTime) < 5)
|
if((unsigned long)(now - _lastAddStepTime) < 5)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_lastAddStepTime = now;
|
_lastAddStepTime = now;
|
||||||
|
|
||||||
// Hat sich die Spannung geändert?
|
|
||||||
bool voltageChanged = (voltage_ch1 != _lastVoltageCh1) || (voltage_ch2 != _lastVoltageCh2);
|
bool voltageChanged = (voltage_ch1 != _lastVoltageCh1) || (voltage_ch2 != _lastVoltageCh2);
|
||||||
|
|
||||||
if(voltageChanged)
|
if(voltageChanged)
|
||||||
{
|
{
|
||||||
// WICHTIG: Prüfe nochmal ob wir Platz haben BEVOR wir schreiben!
|
|
||||||
if(_stepCount >= _MAX_SEQUENCE_STEPS - 1)
|
if(_stepCount >= _MAX_SEQUENCE_STEPS - 1)
|
||||||
{
|
{
|
||||||
Serial.println("\n\r[ERROR] Array full! Stopping recording.");
|
Serial.println("\n\r[ERROR] Array full! Stopping recording.");
|
||||||
@@ -321,19 +316,17 @@ void SequencerBlock::addStep(uint16_t voltage_ch1, uint16_t voltage_ch2)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vorherigen Step abschließen (wenn vorhanden)
|
|
||||||
if(_stepCount > 0 && _stepCount <= _MAX_SEQUENCE_STEPS)
|
if(_stepCount > 0 && _stepCount <= _MAX_SEQUENCE_STEPS)
|
||||||
{
|
{
|
||||||
_finishCurrentStep();
|
_finishCurrentStep();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Neuen Step beginnen - mit Bounds Check!
|
|
||||||
if(_stepCount < _MAX_SEQUENCE_STEPS)
|
if(_stepCount < _MAX_SEQUENCE_STEPS)
|
||||||
{
|
{
|
||||||
_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
|
_sequence[_stepCount].active = (voltage_ch1 > 0 || voltage_ch2 > 0);
|
||||||
_stepCount++;
|
_stepCount++;
|
||||||
|
|
||||||
_lastStepTime = now;
|
_lastStepTime = now;
|
||||||
@@ -343,8 +336,6 @@ void SequencerBlock::addStep(uint16_t voltage_ch1, uint16_t voltage_ch2)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Gleiche Spannung - Duration des aktuellen Steps aktualisieren
|
|
||||||
// WICHTIG: Bounds Check!
|
|
||||||
if(_stepCount > 0 && _stepCount <= _MAX_SEQUENCE_STEPS)
|
if(_stepCount > 0 && _stepCount <= _MAX_SEQUENCE_STEPS)
|
||||||
{
|
{
|
||||||
_sequence[_stepCount - 1].duration = now - _lastStepTime;
|
_sequence[_stepCount - 1].duration = now - _lastStepTime;
|
||||||
@@ -378,7 +369,6 @@ void SequencerBlock::update()
|
|||||||
{
|
{
|
||||||
if(!_isPlaying || _stepCount == 0) return;
|
if(!_isPlaying || _stepCount == 0) return;
|
||||||
|
|
||||||
// WICHTIG: Bounds Check BEVOR wir auf Array zugreifen!
|
|
||||||
if(_currentStep >= _stepCount || _currentStep >= _MAX_SEQUENCE_STEPS)
|
if(_currentStep >= _stepCount || _currentStep >= _MAX_SEQUENCE_STEPS)
|
||||||
{
|
{
|
||||||
Serial.println("\n\r[ERROR] Invalid step index in update()!");
|
Serial.println("\n\r[ERROR] Invalid step index in update()!");
|
||||||
@@ -389,7 +379,6 @@ void SequencerBlock::update()
|
|||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
unsigned long elapsed = now - _stepStartTime;
|
unsigned long elapsed = now - _stepStartTime;
|
||||||
|
|
||||||
// Sicherung gegen Division durch Null / Endlosschleife
|
|
||||||
if(_sequence[_currentStep].duration == 0)
|
if(_sequence[_currentStep].duration == 0)
|
||||||
{
|
{
|
||||||
_currentStep++;
|
_currentStep++;
|
||||||
@@ -409,12 +398,10 @@ void SequencerBlock::update()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prüfen ob aktueller Schritt abgelaufen ist
|
|
||||||
if(elapsed >= _sequence[_currentStep].duration)
|
if(elapsed >= _sequence[_currentStep].duration)
|
||||||
{
|
{
|
||||||
_currentStep++;
|
_currentStep++;
|
||||||
|
|
||||||
// Sequenz-Ende erreicht?
|
|
||||||
if(_currentStep >= _stepCount)
|
if(_currentStep >= _stepCount)
|
||||||
{
|
{
|
||||||
if(_loop)
|
if(_loop)
|
||||||
@@ -447,7 +434,6 @@ void SequencerBlock::clear()
|
|||||||
_lastVoltageCh1 = 0;
|
_lastVoltageCh1 = 0;
|
||||||
_lastVoltageCh2 = 0;
|
_lastVoltageCh2 = 0;
|
||||||
|
|
||||||
// Optional: Array löschen (kann je nach Use-Case weggelassen werden)
|
|
||||||
for(uint16_t i = 0; i < _MAX_SEQUENCE_STEPS; i++)
|
for(uint16_t i = 0; i < _MAX_SEQUENCE_STEPS; i++)
|
||||||
{
|
{
|
||||||
_sequence[i].voltage_ch1 = 0;
|
_sequence[i].voltage_ch1 = 0;
|
||||||
@@ -500,7 +486,7 @@ uint16_t SequencerBlock::getCurrentVoltageCh2()
|
|||||||
|
|
||||||
uint16_t SequencerBlock::getTotalDuration()
|
uint16_t SequencerBlock::getTotalDuration()
|
||||||
{
|
{
|
||||||
uint32_t total = 0; // uint32 um Overflow zu vermeiden
|
uint32_t total = 0;
|
||||||
for(uint16_t i = 0; i < _stepCount && i < _MAX_SEQUENCE_STEPS; i++)
|
for(uint16_t i = 0; i < _stepCount && i < _MAX_SEQUENCE_STEPS; i++)
|
||||||
{
|
{
|
||||||
total += _sequence[i].duration;
|
total += _sequence[i].duration;
|
||||||
@@ -519,7 +505,7 @@ bool SequencerBlock::isCurrentStepActive()
|
|||||||
void SequencerBlock::_finishCurrentStep()
|
void SequencerBlock::_finishCurrentStep()
|
||||||
{
|
{
|
||||||
if(_stepCount == 0) return;
|
if(_stepCount == 0) return;
|
||||||
if(_stepCount > _MAX_SEQUENCE_STEPS) return; // Sicherheitsprüfung
|
if(_stepCount > _MAX_SEQUENCE_STEPS) return;
|
||||||
|
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
uint16_t duration = now - _lastStepTime;
|
uint16_t duration = now - _lastStepTime;
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* Example Code Three - Dual Channel Sequencer (COMPLETE)
|
* Analoger Audiosynthesizer mit digitaler Steuereinheit
|
||||||
* - Alle TODOs implementiert
|
* Firmware-Code für die digitale Einheit
|
||||||
* - VCO Gates, Recording LED, Metronome
|
* Autor: Erik Tóth
|
||||||
*/
|
*/
|
||||||
#include "FIRMWARE_DEF.h"
|
#include "FIRMWARE_DEF.h"
|
||||||
#include "FIRMWARE.h"
|
#include "FIRMWARE.h"
|
||||||
|
|
||||||
|
// Calibration table for optimal note accurarcy
|
||||||
|
const uint16_t NOTE_MV[25] = {
|
||||||
|
64, 140, 216, 293, 369,
|
||||||
|
445, 521, 597, 673, 750,
|
||||||
|
826, 902, 978, 1054, 1131,
|
||||||
|
1207, 1283, 1359, 1435, 1511,
|
||||||
|
1588, 1664, 1740, 1816, 1892,
|
||||||
|
};
|
||||||
|
#define HLFSTEP(n) NOTE_MV[n]
|
||||||
|
|
||||||
byte pins_keyboard_row[N_KEYBOARD_ROW] = {PIN_K_R0, PIN_K_R1, PIN_K_R2, PIN_K_R3, PIN_K_R4};
|
byte pins_keyboard_row[N_KEYBOARD_ROW] = {PIN_K_R0, PIN_K_R1, PIN_K_R2, PIN_K_R3, PIN_K_R4};
|
||||||
byte pins_keyboard_col[N_KEYBOARD_COL] = {PIN_K_C0, PIN_K_C1, PIN_K_C2, PIN_K_C3, PIN_K_C4};
|
byte pins_keyboard_col[N_KEYBOARD_COL] = {PIN_K_C0, PIN_K_C1, PIN_K_C2, PIN_K_C3, PIN_K_C4};
|
||||||
|
|
||||||
@@ -14,15 +24,16 @@ 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] = {
|
uint16_t keyToVoltage[N_KEYBOARD_ROW*N_KEYBOARD_COL] = {
|
||||||
0*83, 1*83, 2*83, 3*83, 4*83,
|
HLFSTEP(0), HLFSTEP(1), HLFSTEP(2), HLFSTEP(3), HLFSTEP(4),
|
||||||
5*83, 6*83, 7*83, 8*83, 9*83,
|
HLFSTEP(5), HLFSTEP(6), HLFSTEP(7), HLFSTEP(8), HLFSTEP(9),
|
||||||
10*83, 11*83, 12*83, 13*83, 14*83,
|
HLFSTEP(10), HLFSTEP(11), HLFSTEP(12), HLFSTEP(13), HLFSTEP(14),
|
||||||
15*83, 16*83, 17*83, 18*83, 19*83,
|
HLFSTEP(15), HLFSTEP(16), HLFSTEP(17), HLFSTEP(18), HLFSTEP(19),
|
||||||
20*83, 21*83, 22*83, 23*83, 24*83
|
HLFSTEP(20), HLFSTEP(21), HLFSTEP(22), HLFSTEP(23), HLFSTEP(24)
|
||||||
};
|
};
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
// SB1 -> VCO1 (CV-Channel 0), SB2 -> VCO2 (CV-Channel 1)
|
||||||
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);
|
||||||
|
|
||||||
@@ -43,8 +54,12 @@ const unsigned long DEBOUNCE_DELAY = 50;
|
|||||||
|
|
||||||
static bool seq1_loop_active = false;
|
static bool seq1_loop_active = false;
|
||||||
static bool seq2_loop_active = false;
|
static bool seq2_loop_active = false;
|
||||||
static uint16_t last_voltage_ch1 = 0xFFFF;
|
|
||||||
static uint16_t last_voltage_ch2 = 0xFFFF;
|
// Separate last-voltage tracking per sequencer
|
||||||
|
static uint16_t sb1_last_voltage_ch1 = 0xFFFF;
|
||||||
|
static uint16_t sb1_last_voltage_ch2 = 0xFFFF;
|
||||||
|
static uint16_t sb2_last_voltage_ch1 = 0xFFFF;
|
||||||
|
static uint16_t sb2_last_voltage_ch2 = 0xFFFF;
|
||||||
|
|
||||||
bool readButton(byte pin, ButtonState &state)
|
bool readButton(byte pin, ButtonState &state)
|
||||||
{
|
{
|
||||||
@@ -123,26 +138,24 @@ void initOutputs()
|
|||||||
|
|
||||||
void handleSequencerButtons()
|
void handleSequencerButtons()
|
||||||
{
|
{
|
||||||
// ===== Sequencer 1 Record Button =====
|
|
||||||
if(readButton(PIN_SB_1_REC, btn_sb1_rec))
|
if(readButton(PIN_SB_1_REC, btn_sb1_rec))
|
||||||
{
|
{
|
||||||
if(sb1.isRecording())
|
if(sb1.isRecording())
|
||||||
{
|
{
|
||||||
sb1.stopRecord();
|
sb1.stopRecord();
|
||||||
Serial.printf("\n\r[SEQ1] Recording stopped. Steps: %i, Duration: %ims",
|
Serial.printf("\n\r[SEQ1->VCO1] Recording stopped. Steps: %i, Duration: %ims",
|
||||||
sb1.getStepCount(), sb1.getTotalDuration());
|
sb1.getStepCount(), sb1.getTotalDuration());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(sb1.isPlaying()) sb1.stopPlay();
|
if(sb1.isPlaying()) sb1.stopPlay();
|
||||||
sb1.startRecord();
|
sb1.startRecord();
|
||||||
last_voltage_ch1 = 0xFFFF;
|
sb1_last_voltage_ch1 = 0xFFFF;
|
||||||
last_voltage_ch2 = 0xFFFF;
|
sb1_last_voltage_ch2 = 0xFFFF;
|
||||||
Serial.printf("\n\r[SEQ1] Recording started (2 channels)...");
|
Serial.printf("\n\r[SEQ1->VCO1] Recording started...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 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())
|
||||||
@@ -151,43 +164,41 @@ void handleSequencerButtons()
|
|||||||
sb1.setLoop(false);
|
sb1.setLoop(false);
|
||||||
seq1_loop_active = false;
|
seq1_loop_active = false;
|
||||||
sb1.startPlay();
|
sb1.startPlay();
|
||||||
Serial.printf("\n\r[SEQ1] Playback started (single)\n\r\tSteps: %i, Duration: %ims",
|
Serial.printf("\n\r[SEQ1->VCO1] Playback started (single)\n\r\tSteps: %i, Duration: %ims",
|
||||||
sb1.getStepCount(), sb1.getTotalDuration());
|
sb1.getStepCount(), sb1.getTotalDuration());
|
||||||
}
|
}
|
||||||
else if(!seq1_loop_active)
|
else if(!seq1_loop_active)
|
||||||
{
|
{
|
||||||
sb1.setLoop(true);
|
sb1.setLoop(true);
|
||||||
seq1_loop_active = true;
|
seq1_loop_active = true;
|
||||||
Serial.printf("\n\r[SEQ1] Loop activated");
|
Serial.printf("\n\r[SEQ1->VCO1] Loop activated");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sb1.stopPlay();
|
sb1.stopPlay();
|
||||||
seq1_loop_active = false;
|
seq1_loop_active = false;
|
||||||
Serial.printf("\n\r[SEQ1] Playback stopped");
|
Serial.printf("\n\r[SEQ1->VCO1] Playback stopped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Sequencer 2 Record Button =====
|
|
||||||
if(readButton(PIN_SB_2_REC, btn_sb2_rec))
|
if(readButton(PIN_SB_2_REC, btn_sb2_rec))
|
||||||
{
|
{
|
||||||
if(sb2.isRecording())
|
if(sb2.isRecording())
|
||||||
{
|
{
|
||||||
sb2.stopRecord();
|
sb2.stopRecord();
|
||||||
Serial.printf("\n\r[SEQ2] Recording stopped. Steps: %i, Duration: %ims",
|
Serial.printf("\n\r[SEQ2->VCO2] Recording stopped. Steps: %i, Duration: %ims",
|
||||||
sb2.getStepCount(), sb2.getTotalDuration());
|
sb2.getStepCount(), sb2.getTotalDuration());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(sb2.isPlaying()) sb2.stopPlay();
|
if(sb2.isPlaying()) sb2.stopPlay();
|
||||||
sb2.startRecord();
|
sb2.startRecord();
|
||||||
last_voltage_ch1 = 0xFFFF;
|
sb2_last_voltage_ch1 = 0xFFFF;
|
||||||
last_voltage_ch2 = 0xFFFF;
|
sb2_last_voltage_ch2 = 0xFFFF;
|
||||||
Serial.printf("\n\r[SEQ2] Recording started (2 channels)...");
|
Serial.printf("\n\r[SEQ2->VCO2] Recording started...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 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())
|
||||||
@@ -196,20 +207,20 @@ void handleSequencerButtons()
|
|||||||
sb2.setLoop(false);
|
sb2.setLoop(false);
|
||||||
seq2_loop_active = false;
|
seq2_loop_active = false;
|
||||||
sb2.startPlay();
|
sb2.startPlay();
|
||||||
Serial.printf("\n\r[SEQ2] Playback started (single)\n\r\tSteps: %i, Duration: %ims",
|
Serial.printf("\n\r[SEQ2->VCO2] Playback started (single)\n\r\tSteps: %i, Duration: %ims",
|
||||||
sb2.getStepCount(), sb2.getTotalDuration());
|
sb2.getStepCount(), sb2.getTotalDuration());
|
||||||
}
|
}
|
||||||
else if(!seq2_loop_active)
|
else if(!seq2_loop_active)
|
||||||
{
|
{
|
||||||
sb2.setLoop(true);
|
sb2.setLoop(true);
|
||||||
seq2_loop_active = true;
|
seq2_loop_active = true;
|
||||||
Serial.printf("\n\r[SEQ2] Loop activated");
|
Serial.printf("\n\r[SEQ2->VCO2] Loop activated");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sb2.stopPlay();
|
sb2.stopPlay();
|
||||||
seq2_loop_active = false;
|
seq2_loop_active = false;
|
||||||
Serial.printf("\n\r[SEQ2] Playback stopped");
|
Serial.printf("\n\r[SEQ2->VCO2] Playback stopped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,17 +235,14 @@ void updateMetronome()
|
|||||||
{
|
{
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
|
|
||||||
// BPM von Potentiometer lesen (alle 100ms)
|
|
||||||
static unsigned long last_bpm_read = 0;
|
static unsigned long last_bpm_read = 0;
|
||||||
if((now - last_bpm_read) > 100)
|
if((now - last_bpm_read) > 100)
|
||||||
{
|
{
|
||||||
int adc_value = analogRead(PIN_BPM);
|
int adc_value = analogRead(PIN_BPM);
|
||||||
// Map ADC (0-4095) zu BPM (40-240)
|
|
||||||
current_bpm = map(adc_value, 0, 4095, 40, 240);
|
current_bpm = map(adc_value, 0, 4095, 40, 240);
|
||||||
last_bpm_read = now;
|
last_bpm_read = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metronome Button (Toggle)
|
|
||||||
if(readButton(PIN_B_METRONOME, btn_metronome))
|
if(readButton(PIN_B_METRONOME, btn_metronome))
|
||||||
{
|
{
|
||||||
metronome_enabled = !metronome_enabled;
|
metronome_enabled = !metronome_enabled;
|
||||||
@@ -243,55 +251,41 @@ void updateMetronome()
|
|||||||
|
|
||||||
if(!metronome_enabled)
|
if(!metronome_enabled)
|
||||||
{
|
{
|
||||||
digitalWrite(PIN_L_METRONOME, HIGH); // Active-low: HIGH = OFF
|
digitalWrite(PIN_L_METRONOME, HIGH);
|
||||||
metronome_led_on = false;
|
metronome_led_on = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!metronome_enabled) return;
|
if(!metronome_enabled) return;
|
||||||
|
|
||||||
// Berechne Beat-Intervall in ms
|
|
||||||
unsigned long beat_interval = 60000UL / current_bpm;
|
unsigned long beat_interval = 60000UL / current_bpm;
|
||||||
|
|
||||||
// Neue Beat?
|
|
||||||
if((now - last_beat_time) >= beat_interval)
|
if((now - last_beat_time) >= beat_interval)
|
||||||
{
|
{
|
||||||
digitalWrite(PIN_L_METRONOME, LOW); // Active-low: LOW = ON
|
digitalWrite(PIN_L_METRONOME, LOW);
|
||||||
metronome_led_on = true;
|
metronome_led_on = true;
|
||||||
last_beat_time = now;
|
last_beat_time = now;
|
||||||
last_pulse_end_time = now + 50; // 50ms Pulse
|
last_pulse_end_time = now + 50;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pulse beenden?
|
|
||||||
if(metronome_led_on && (now >= last_pulse_end_time))
|
if(metronome_led_on && (now >= last_pulse_end_time))
|
||||||
{
|
{
|
||||||
digitalWrite(PIN_L_METRONOME, HIGH); // Active-low: HIGH = OFF
|
digitalWrite(PIN_L_METRONOME, HIGH);
|
||||||
metronome_led_on = false;
|
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()
|
void updateRecordingLED()
|
||||||
{
|
{
|
||||||
// PIN_REC: Active-low (LOW = LED ON)
|
|
||||||
bool any_recording = sb1.isRecording() || sb2.isRecording();
|
bool any_recording = sb1.isRecording() || sb2.isRecording();
|
||||||
digitalWrite(PIN_REC, any_recording ? LOW : HIGH);
|
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=== COMPLETE VERSION with TODOs ===");
|
Serial.printf("\n\r=== DUAL SEQUENCER: SB1->VCO1 | SB2->VCO2 ===");
|
||||||
Serial.printf("\n\rSerial OK!");
|
Serial.printf("\n\rSerial OK!");
|
||||||
|
|
||||||
keyboard.begin();
|
keyboard.begin();
|
||||||
@@ -317,45 +311,43 @@ void setup()
|
|||||||
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=== System Started ===");
|
||||||
Serial.printf("\n\rFeatures:");
|
Serial.printf("\n\rMapping:");
|
||||||
Serial.printf("\n\r - VCO1/VCO2 Gate Outputs");
|
Serial.printf("\n\r SB1 -> VCO1 (CV-Ch 0) | SB2 -> VCO2 (CV-Ch 1)");
|
||||||
Serial.printf("\n\r - Recording LED Indicator");
|
Serial.printf("\n\rManual fallback:");
|
||||||
Serial.printf("\n\r - BPM Metronome (40-240 BPM)");
|
Serial.printf("\n\r SB1 playing, SB2 idle -> VCO2 manual (Queue[0])");
|
||||||
Serial.printf("\n\r==============================================\n\r");
|
Serial.printf("\n\r SB2 playing, SB1 idle -> VCO1 manual (Queue[0])");
|
||||||
|
Serial.printf("\n\r Both idle -> VCO1=Queue[0], VCO2=Queue[1]");
|
||||||
|
Serial.printf("\n\r=====================================\n\r");
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
// ===== DEBUG HEARTBEAT =====
|
// DEBUG HEARTBEAT
|
||||||
static unsigned long lastDebugPrint = 0;
|
static unsigned long lastDebugPrint = 0;
|
||||||
static unsigned long loopCounter = 0;
|
static unsigned long loopCounter = 0;
|
||||||
|
|
||||||
loopCounter++;
|
loopCounter++;
|
||||||
|
|
||||||
if(millis() - lastDebugPrint > 5000)
|
if(millis() - lastDebugPrint > 5000)
|
||||||
{
|
{
|
||||||
Serial.printf("\n\r[HEARTBEAT] Loop: %lu | BPM: %d | Metro: %s",
|
Serial.printf("\n\r[HEARTBEAT] Loop: %lu | BPM: %d | Metro: %s",
|
||||||
loopCounter, current_bpm, metronome_enabled ? "ON" : "OFF");
|
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->VCO1: 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->VCO2: Rec=%d, Play=%d, Steps=%d",
|
||||||
sb2.isRecording(), sb2.isPlaying(), sb2.getStepCount());
|
sb2.isRecording(), sb2.isPlaying(), sb2.getStepCount());
|
||||||
lastDebugPrint = millis();
|
lastDebugPrint = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== NON-BLOCKING TIMING =====
|
// 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;
|
const unsigned long LOOP_INTERVAL = 10;
|
||||||
|
|
||||||
if((now - lastLoopTime) < LOOP_INTERVAL)
|
if((now - lastLoopTime) < LOOP_INTERVAL) return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
lastLoopTime = now;
|
lastLoopTime = now;
|
||||||
|
|
||||||
// ===== UPDATE FUNCTIONS =====
|
// UPDATE
|
||||||
keyboard.update();
|
keyboard.update();
|
||||||
handleSequencerButtons();
|
handleSequencerButtons();
|
||||||
updateMetronome();
|
updateMetronome();
|
||||||
@@ -364,97 +356,150 @@ void loop()
|
|||||||
sb1.update();
|
sb1.update();
|
||||||
sb2.update();
|
sb2.update();
|
||||||
|
|
||||||
|
// KEYBOARD INPUT
|
||||||
int n = keyboard.getQueueLength();
|
int n = keyboard.getQueueLength();
|
||||||
|
|
||||||
// Aktuelle Spannungen ermitteln
|
// Key 0 -> wird als manueller Eingang für den jeweils freien VCO genutzt
|
||||||
uint16_t voltage_ch1 = 0;
|
uint16_t manual_voltage_0 = 0;
|
||||||
uint16_t voltage_ch2 = 0;
|
uint16_t manual_voltage_1 = 0;
|
||||||
bool cv1_active = false;
|
bool manual_active_0 = false;
|
||||||
bool cv2_active = false;
|
bool manual_active_1 = false;
|
||||||
|
|
||||||
if(n > 0)
|
if(n > 0)
|
||||||
{
|
{
|
||||||
Key k1 = keyboard.getQueue(0);
|
Key k = keyboard.getQueue(0);
|
||||||
if(!isNotKey(k1))
|
if(!isNotKey(k))
|
||||||
{
|
{
|
||||||
Serial.printf("\n\r[DEBUG] K1: R%iC%i", k1.row, k1.col);
|
manual_voltage_0 = keyToVoltage[k.row * N_KEYBOARD_COL + k.col];
|
||||||
voltage_ch1 = keyToVoltage[k1.row * N_KEYBOARD_COL + k1.col];
|
manual_active_0 = true;
|
||||||
cv1_active = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(n > 1)
|
if(n > 1)
|
||||||
{
|
{
|
||||||
Key k2 = keyboard.getQueue(1);
|
Key k = keyboard.getQueue(1);
|
||||||
if(!isNotKey(k2))
|
if(!isNotKey(k))
|
||||||
{
|
{
|
||||||
Serial.printf("\n\r[DEBUG] K2: R%iC%i", k2.row, k2.col);
|
manual_voltage_1 = keyToVoltage[k.row * N_KEYBOARD_COL + k.col];
|
||||||
voltage_ch2 = keyToVoltage[k2.row * N_KEYBOARD_COL + k2.col];
|
manual_active_1 = true;
|
||||||
cv2_active = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recording
|
// ===== RECORDING =====
|
||||||
bool voltageChanged = (voltage_ch1 != last_voltage_ch1) || (voltage_ch2 != last_voltage_ch2);
|
// SB1 nimmt immer ch1=manual_voltage_0 / ch2=manual_voltage_1 auf
|
||||||
|
// (SB1 ist für VCO1 zuständig, nutzt den vollen Keyboard-Input)
|
||||||
if(sb1.isRecording() && voltageChanged)
|
if(sb1.isRecording())
|
||||||
{
|
{
|
||||||
sb1.addStep(voltage_ch1, voltage_ch2);
|
bool changed = (manual_voltage_0 != sb1_last_voltage_ch1) ||
|
||||||
last_voltage_ch1 = voltage_ch1;
|
(manual_voltage_1 != sb1_last_voltage_ch2);
|
||||||
last_voltage_ch2 = voltage_ch2;
|
if(changed)
|
||||||
|
{
|
||||||
|
sb1.addStep(manual_voltage_0, manual_voltage_1);
|
||||||
|
sb1_last_voltage_ch1 = manual_voltage_0;
|
||||||
|
sb1_last_voltage_ch2 = manual_voltage_1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sb2.isRecording() && voltageChanged)
|
// SB2 nimmt ebenfalls den vollen Keyboard-Input auf
|
||||||
|
if(sb2.isRecording())
|
||||||
{
|
{
|
||||||
sb2.addStep(voltage_ch1, voltage_ch2);
|
bool changed = (manual_voltage_0 != sb2_last_voltage_ch1) ||
|
||||||
last_voltage_ch1 = voltage_ch1;
|
(manual_voltage_1 != sb2_last_voltage_ch2);
|
||||||
last_voltage_ch2 = voltage_ch2;
|
if(changed)
|
||||||
|
{
|
||||||
|
sb2.addStep(manual_voltage_0, manual_voltage_1);
|
||||||
|
sb2_last_voltage_ch1 = manual_voltage_0;
|
||||||
|
sb2_last_voltage_ch2 = manual_voltage_1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CV-Ausgabe & VCO Gates
|
// ===== CV OUTPUT & VCO GATES =====
|
||||||
if(sb1.isPlaying())
|
//
|
||||||
{
|
// SB1 state | SB2 state | VCO1 (ch 0) | VCO2 (ch 1)
|
||||||
uint16_t seq_v1 = sb1.getCurrentVoltageCh1();
|
// ------------|-------------|---------------------|----------------------
|
||||||
uint16_t seq_v2 = sb1.getCurrentVoltageCh2();
|
// playing | playing | SB1 seq voltage | SB2 seq voltage
|
||||||
cv.setVoltage(0, seq_v1);
|
// playing | recording | SB1 seq voltage | live manual Queue[0]
|
||||||
cv.setVoltage(1, seq_v2);
|
// playing | idle | SB1 seq voltage | live manual Queue[0]
|
||||||
|
// idle | playing | live manual Queue[0]| SB2 seq voltage
|
||||||
|
// idle | recording | live manual Queue[0]| live manual Queue[0]
|
||||||
|
// idle | idle | live manual Queue[0]| live manual Queue[1]
|
||||||
|
|
||||||
// KORREKT: Nutze isCurrentStepActive() statt Spannung > 0
|
bool sb1_playing = sb1.isPlaying();
|
||||||
// Da 0V eine gültige Note sein kann!
|
bool sb1_recording = sb1.isRecording();
|
||||||
bool gate_active = sb1.isCurrentStepActive();
|
bool sb2_playing = sb2.isPlaying();
|
||||||
updateVCOGates(gate_active, gate_active);
|
bool sb2_recording = sb2.isRecording();
|
||||||
|
|
||||||
|
uint16_t out_vco1 = 0;
|
||||||
|
uint16_t out_vco2 = 0;
|
||||||
|
bool gate_vco1 = false;
|
||||||
|
bool gate_vco2 = false;
|
||||||
|
|
||||||
|
// VCO1
|
||||||
|
if(sb1_playing)
|
||||||
|
{
|
||||||
|
// SB1 Sequenz läuft -> Sequenz-Ausgabe
|
||||||
|
out_vco1 = sb1.getCurrentVoltageCh1();
|
||||||
|
gate_vco1 = sb1.isCurrentStepActive();
|
||||||
}
|
}
|
||||||
else if(sb2.isPlaying())
|
else if(sb1_recording)
|
||||||
{
|
{
|
||||||
uint16_t seq_v1 = sb2.getCurrentVoltageCh1();
|
// SB1 nimmt auf -> Live-Ausgabe damit man hört was man spielt
|
||||||
uint16_t seq_v2 = sb2.getCurrentVoltageCh2();
|
out_vco1 = manual_voltage_0;
|
||||||
cv.setVoltage(0, seq_v1);
|
gate_vco1 = manual_active_0;
|
||||||
cv.setVoltage(1, seq_v2);
|
|
||||||
|
|
||||||
bool gate_active = sb2.isCurrentStepActive();
|
|
||||||
updateVCOGates(gate_active, gate_active);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Live-Modus: cv1_active/cv2_active basieren auf tatsächlich gedrückten Tasten
|
// SB1 idle -> manuell
|
||||||
cv.setVoltage(0, voltage_ch1);
|
out_vco1 = manual_voltage_0;
|
||||||
cv.setVoltage(1, voltage_ch2);
|
gate_vco1 = manual_active_0;
|
||||||
updateVCOGates(cv1_active, cv2_active);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time-Limit Check
|
// VCO2
|
||||||
|
if(sb2_playing)
|
||||||
|
{
|
||||||
|
// SB2 Sequenz läuft -> Sequenz-Ausgabe
|
||||||
|
out_vco2 = sb2.getCurrentVoltageCh1();
|
||||||
|
gate_vco2 = sb2.isCurrentStepActive();
|
||||||
|
}
|
||||||
|
else if(sb2_recording)
|
||||||
|
{
|
||||||
|
// SB2 nimmt auf -> Live-Ausgabe damit man hört was man spielt
|
||||||
|
out_vco2 = manual_voltage_0;
|
||||||
|
gate_vco2 = manual_active_0;
|
||||||
|
gate_vco1 = false;
|
||||||
|
}
|
||||||
|
else if(sb1_playing)
|
||||||
|
{
|
||||||
|
// SB1 läuft, SB2 idle -> VCO2 manuell mit Queue[0]
|
||||||
|
out_vco2 = manual_voltage_0;
|
||||||
|
gate_vco2 = manual_active_0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Beide idle -> VCO2 bekommt Queue[1]
|
||||||
|
out_vco2 = manual_voltage_1;
|
||||||
|
gate_vco2 = manual_active_1;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv.setVoltage(0, out_vco1); // CH_A -> VCO1
|
||||||
|
cv.setVoltage(1, out_vco2); // CH_B -> VCO2
|
||||||
|
|
||||||
|
digitalWrite(PIN_VCO1_EN, gate_vco1 ? HIGH : LOW);
|
||||||
|
digitalWrite(PIN_VCO2_EN, gate_vco2 ? HIGH : LOW);
|
||||||
|
|
||||||
|
// TIME-LIMIT CHECK
|
||||||
if(sb1.isRecording() && sb1.timeLimitReached())
|
if(sb1.isRecording() && sb1.timeLimitReached())
|
||||||
{
|
{
|
||||||
sb1.stopRecord();
|
sb1.stopRecord();
|
||||||
Serial.printf("\n\r[SEQ1] Time limit reached! Recording stopped.");
|
Serial.printf("\n\r[SEQ1->VCO1] Time limit reached! Recording stopped.");
|
||||||
Serial.printf("\n\r[SEQ1] Final: Steps: %i, Duration: %ims",
|
Serial.printf("\n\r[SEQ1->VCO1] Final: Steps: %i, Duration: %ims",
|
||||||
sb1.getStepCount(), sb1.getTotalDuration());
|
sb1.getStepCount(), sb1.getTotalDuration());
|
||||||
}
|
}
|
||||||
if(sb2.isRecording() && sb2.timeLimitReached())
|
if(sb2.isRecording() && sb2.timeLimitReached())
|
||||||
{
|
{
|
||||||
sb2.stopRecord();
|
sb2.stopRecord();
|
||||||
Serial.printf("\n\r[SEQ2] Time limit reached! Recording stopped.");
|
Serial.printf("\n\r[SEQ2->VCO2] Time limit reached! Recording stopped.");
|
||||||
Serial.printf("\n\r[SEQ2] Final: Steps: %i, Duration: %ims",
|
Serial.printf("\n\r[SEQ2->VCO2] Final: Steps: %i, Duration: %ims",
|
||||||
sb2.getStepCount(), sb2.getTotalDuration());
|
sb2.getStepCount(), sb2.getTotalDuration());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
dev/print/1_tasten/OldVersions/cut_left.0002.ipt
Normal file
BIN
dev/print/1_tasten/OldVersions/cut_left.0002.ipt
Normal file
Binary file not shown.
BIN
dev/print/1_tasten/OldVersions/cut_right.0004.ipt
Normal file
BIN
dev/print/1_tasten/OldVersions/cut_right.0004.ipt
Normal file
Binary file not shown.
BIN
dev/print/1_tasten/OldVersions/half_new.0003.ipt
Normal file
BIN
dev/print/1_tasten/OldVersions/half_new.0003.ipt
Normal file
Binary file not shown.
BIN
dev/print/1_tasten/OldVersions/mid_new.0002.ipt
Normal file
BIN
dev/print/1_tasten/OldVersions/mid_new.0002.ipt
Normal file
Binary file not shown.
BIN
dev/print/1_tasten/OldVersions/note.0006.ipt
Normal file
BIN
dev/print/1_tasten/OldVersions/note.0006.ipt
Normal file
Binary file not shown.
BIN
dev/print/1_tasten/OldVersions/notes.0008.iam
Normal file
BIN
dev/print/1_tasten/OldVersions/notes.0008.iam
Normal file
Binary file not shown.
BIN
dev/print/1_tasten/cut_left.ipt
Normal file
BIN
dev/print/1_tasten/cut_left.ipt
Normal file
Binary file not shown.
BIN
dev/print/1_tasten/cut_right.ipt
Normal file
BIN
dev/print/1_tasten/cut_right.ipt
Normal file
Binary file not shown.
BIN
dev/print/1_tasten/half_new.ipt
Normal file
BIN
dev/print/1_tasten/half_new.ipt
Normal file
Binary file not shown.
BIN
dev/print/1_tasten/mid_new.ipt
Normal file
BIN
dev/print/1_tasten/mid_new.ipt
Normal file
Binary file not shown.
Binary file not shown.
BIN
dev/print/1_tasten/notes.iam
Normal file
BIN
dev/print/1_tasten/notes.iam
Normal file
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/connection.0004.iam
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/connection.0004.iam
Normal file
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/connection.0007.ipt
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/connection.0007.ipt
Normal file
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/connection_text.0002.ipt
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/connection_text.0002.ipt
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_left.0015.iam
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_left.0015.iam
Normal file
Binary file not shown.
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_left_base.0029.ipt
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_left_base.0029.ipt
Normal file
Binary file not shown.
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_left_text.0010.ipt
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_left_text.0010.ipt
Normal file
Binary file not shown.
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_right_base.0029.ipt
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_right_base.0029.ipt
Normal file
Binary file not shown.
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_right_text.0006.ipt
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/frontplate_right_text.0006.ipt
Normal file
Binary file not shown.
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/housing.0039.iam
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/housing.0039.iam
Normal file
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/housing.0052.iam
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/housing.0052.iam
Normal file
Binary file not shown.
Binary file not shown.
BIN
dev/print/2_gehaeuse/OldVersions/housing_base.0030.ipt
Normal file
BIN
dev/print/2_gehaeuse/OldVersions/housing_base.0030.ipt
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
dev/print/2_gehaeuse/connection.iam
Normal file
BIN
dev/print/2_gehaeuse/connection.iam
Normal file
Binary file not shown.
BIN
dev/print/2_gehaeuse/connection.ipt
Normal file
BIN
dev/print/2_gehaeuse/connection.ipt
Normal file
Binary file not shown.
BIN
dev/print/2_gehaeuse/connection_text.ipt
Normal file
BIN
dev/print/2_gehaeuse/connection_text.ipt
Normal file
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
dev/print/2_gehaeuse/rod.ipt
Normal file
BIN
dev/print/2_gehaeuse/rod.ipt
Normal file
Binary file not shown.
BIN
dev/print/4_caps/OldVersions/caps.0004.iam
Normal file
BIN
dev/print/4_caps/OldVersions/caps.0004.iam
Normal file
Binary file not shown.
BIN
dev/print/4_caps/OldVersions/poti_cap.0003.ipt
Normal file
BIN
dev/print/4_caps/OldVersions/poti_cap.0003.ipt
Normal file
Binary file not shown.
BIN
dev/print/4_caps/OldVersions/rot_switch_cap.0001.ipt
Normal file
BIN
dev/print/4_caps/OldVersions/rot_switch_cap.0001.ipt
Normal file
Binary file not shown.
BIN
dev/print/4_caps/caps.iam
Normal file
BIN
dev/print/4_caps/caps.iam
Normal file
Binary file not shown.
BIN
dev/print/4_caps/poti_cap.ipt
Normal file
BIN
dev/print/4_caps/poti_cap.ipt
Normal file
Binary file not shown.
BIN
dev/print/4_caps/rot_switch_cap.ipt
Normal file
BIN
dev/print/4_caps/rot_switch_cap.ipt
Normal file
Binary file not shown.
61185
dev/print/test/connection.stp
Normal file
61185
dev/print/test/connection.stp
Normal file
File diff suppressed because it is too large
Load Diff
7793
dev/print/test/housing_base.step
Normal file
7793
dev/print/test/housing_base.step
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
BIN
lit/USB-PD-ELKO.pdf
Normal file
BIN
lit/USB-PD-ELKO.pdf
Normal file
Binary file not shown.
BIN
lit/ds1117.pdf
Normal file
BIN
lit/ds1117.pdf
Normal file
Binary file not shown.
BIN
lit/lm2940c.pdf
Normal file
BIN
lit/lm2940c.pdf
Normal file
Binary file not shown.
Reference in New Issue
Block a user