Software Update 4: Sequencer Block

Sequencer Klasse eingebaut
  Überprüfung noch ausständig
This commit is contained in:
Erik Tóth
2025-11-13 17:03:04 +01:00
parent 3ad41e6ba5
commit b3d66fdfd8
12 changed files with 641 additions and 234 deletions

View File

@@ -8,6 +8,8 @@
#include "FIRMWARE.h"
// ==================== Helper-Functions ====================
bool isNotKey(Key k)
{
if((k.row == NOT_A_KEY.row) && (k.col == NOT_A_KEY.col)) return true;
@@ -20,6 +22,8 @@ bool isEqualKey(Key k1, Key k2)
else return false;
}
// ==================== Keyboard ====================
Keyboard::Keyboard(uint8_t nRows, uint8_t nCols, uint8_t *pinsRow, uint8_t *pinsCol)
{
_nRows = nRows;
@@ -171,6 +175,8 @@ void Keyboard::_removeActiveKey(uint8_t row, uint8_t col)
}
}
// ==================== CV ====================
/*!
* @param dac Adafruit_MCP4728 object
* @param wire TwoWire object
@@ -232,4 +238,264 @@ uint8_t CV::_getKeyToVoltageIndex(uint8_t row, uint8_t col)
uint8_t CV::_getKeyToVoltageIndex(Key k)
{
return (k.row*_col + k.col);
}
// ==================== SequencerBlock ====================
/*!
* @param maxDurationMS maximum loop duration of recording in milliseconds
* @param timeoutMS stops recording after timeout in milliseconds
* @brief TODO
*/
SequencerBlock::SequencerBlock(uint16_t maxDurationMS, uint16_t timeoutMS)
{
_maxDurationMS = maxDurationMS;
_timeoutMS = timeoutMS;
_stepCount = 0;
_currentStep = 0;
_isRecording = false;
_isPlaying = false;
_loop = false;
_lastVoltage = 0;
_recordStartTime = 0;
_lastStepTime = 0;
_playStartTime = 0;
_stepStartTime = 0;
}
/*!
* @brief starts sequence block recording
*/
void SequencerBlock::startRecord()
{
if(_isPlaying) stopPlay();
clear();
_isRecording = true;
_recordStartTime = millis();
_lastStepTime = _recordStartTime;
_lastVoltage = 0;
}
/*!
* @brief stops sequence block recording and saves it
*/
void SequencerBlock::stopRecord()
{
if(!_isRecording) return;
_finishCurrentStep();
_isRecording = false;
}
/*!
* @brief adds step to sequencer block
* @param voltage voltage step for CV-Gate in millivolts
*/
void SequencerBlock::addStep(uint16_t voltage)
{
if(!_isRecording) return;
if(!_canAddStep()) return;
unsigned long now = millis();
// Wenn sich die Spannung geändert hat, vorherigen Schritt abschließen
if(voltage != _lastVoltage && _stepCount > 0)
{
_finishCurrentStep();
}
// Neuen Schritt beginnen oder vorhandenen aktualisieren
if(voltage != _lastVoltage || _stepCount == 0)
{
if(_canAddStep())
{
_sequence[_stepCount].voltage = voltage;
_sequence[_stepCount].duration = 0;
_lastStepTime = now;
_lastVoltage = voltage;
}
}
else
{
if(_stepCount > 0)
{
_sequence[_stepCount - 1].duration = now - _lastStepTime;
}
}
}
/*!
* @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;
if(_isRecording) stopRecord();
_isPlaying = true;
_currentStep = 0;
_playStartTime = millis();
_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;
unsigned long now = millis();
unsigned long elapsed = now - _stepStartTime;
// Prüfen ob aktueller Schritt abgelaufen ist
if(elapsed >= _sequence[_currentStep].duration)
{
_currentStep++;
// Sequenz-Ende erreicht?
if(_currentStep >= _stepCount)
{
if(_loop)
{
_currentStep = 0;
_stepStartTime = now;
}
else
{
stopPlay();
return;
}
}
else
{
_stepStartTime = now;
}
}
}
/*!
* @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;
for(uint8_t i = 0; i < N_MAX_SEQUENCE_STEPS; i++)
{
_sequence[i].voltage = 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;
unsigned long now = millis();
unsigned long elapsed = now - _recordStartTime;
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()
{
if(!_isPlaying || _stepCount == 0) return 0;
if(_currentStep >= _stepCount) return 0;
return _sequence[_currentStep].voltage;
}
/*!
* @brief gets the length of the recording in the block
* @return uint16_t time in milliseconds
*/
uint16_t SequencerBlock::getTotalDuration()
{
uint16_t total = 0;
for(uint8_t i = 0; i < _stepCount; i++)
{
total += _sequence[i].duration;
}
return total;
}
void SequencerBlock::_finishCurrentStep()
{
if(_stepCount == 0) return;
unsigned long now = millis();
_sequence[_stepCount - 1].duration = now - _lastStepTime;
// Timeout prüfen - wenn zu lange keine Änderung, Schritt nicht hinzufügen
if(_sequence[_stepCount - 1].duration < _timeoutMS)
{
_stepCount++;
}
}
bool SequencerBlock::_canAddStep()
{
if(_stepCount >= N_MAX_SEQUENCE_STEPS) return false;
if(timeLimitReached()) return false;
return true;
}