cleanup envelopeGenerator initialization

This commit is contained in:
2025-12-26 15:23:18 -06:00
parent c417801547
commit cbd60e2701
7 changed files with 79 additions and 52 deletions

View File

@@ -45,6 +45,7 @@ qt_add_executable(metabolus
src/ui/MainWindow.cpp src/ui/MainWindow.cpp
src/ui/MainWindow.h src/ui/MainWindow.h
src/ui/MainWindow.ui src/ui/MainWindow.ui
src/ParameterStore.cpp
src/ParameterStore.h src/ParameterStore.h
src/KeyboardController.cpp src/KeyboardController.cpp
src/KeyboardController.h src/KeyboardController.h

20
src/ParameterStore.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include "ParameterStore.h"
ParameterStore::ParameterStore() {
resetToDefaults();
}
void ParameterStore::set(ParamId id, float value) {
values_[static_cast<size_t>(id)].store(value, std::memory_order_relaxed);
}
float ParameterStore::get(ParamId id) const {
return values_[static_cast<size_t>(id)].load(std::memory_order_relaxed);
}
void ParameterStore::resetToDefaults() {
for(size_t i = 0; i < PARAM_COUNT; i++) {
values_[i].store(PARAM_DEFS[i].def, std::memory_order_relaxed);
}
}

View File

@@ -25,7 +25,17 @@ enum class ParamId : uint16_t {
Count // for sizing Count // for sizing
}; };
struct ParamDef { // TODO: might make a map of EnvelopeIds to a struct of 4 ParamIds
enum class EnvelopeId : uint16_t {
Osc1Volume,
Osc2Volume,
Osc3Volume,
FilterCutoff,
FilterResonance,
Count
};
struct ParamDefault {
float def; float def;
float min; float min;
float max; float max;
@@ -33,7 +43,7 @@ struct ParamDef {
// TODO: make these configurable via yml file too // TODO: make these configurable via yml file too
// later TODO: and then when I have full on profile saving there will be a default profile to load from // later TODO: and then when I have full on profile saving there will be a default profile to load from
constexpr std::array<ParamDef, static_cast<size_t>(ParamId::Count)> PARAM_DEFS {{ constexpr std::array<ParamDefault, static_cast<size_t>(ParamId::Count)> PARAM_DEFS {{
{ 100.0f, 20.0f, 600.0f}, // Osc1Freq { 100.0f, 20.0f, 600.0f}, // Osc1Freq
{ 0.8f, 0.0f, 1.0f}, // Osc1Gain { 0.8f, 0.0f, 1.0f}, // Osc1Gain
{ 0.05f, 0.0f, 2.0f}, // Osc1VolumeEnvA, { 0.05f, 0.0f, 2.0f}, // Osc1VolumeEnvA,
@@ -56,22 +66,12 @@ class ParameterStore {
public: public:
ParameterStore() { resetToDefaults(); } ParameterStore();
~ParameterStore() = default; ~ParameterStore() = default;
// TODO: move into implementation file ? idk void set(ParamId id, float value);
void set(ParamId id, float value) { float get(ParamId id) const;
values_[static_cast<size_t>(id)].store(value, std::memory_order_relaxed); void resetToDefaults();
}
float get(ParamId id) const {
return values_[static_cast<size_t>(id)].load(std::memory_order_relaxed);
}
void resetToDefaults() {
for(size_t i = 0; i < PARAM_COUNT; i++) {
values_[i].store(PARAM_DEFS[i].def, std::memory_order_relaxed);
}
}
private: private:

View File

@@ -26,7 +26,7 @@ float Envelope::process() {
state_ = State::Decay; state_ = State::Decay;
} }
break; break;
case State::Decay: case State::Decay: // TODO: if noteOff occurs during decay, release doesn't trigger until decay finishes
value_ -= (1.0f - sustain_) / (decay_ * sampleRate_); value_ -= (1.0f - sustain_) / (decay_ * sampleRate_);
if(value_ <= sustain_) { if(value_ <= sustain_) {
value_ = sustain_; value_ = sustain_;

View File

@@ -21,28 +21,16 @@ MainWindow::MainWindow(QWidget *parent) :
// synth business // synth business
audio_->start(); audio_->start();
// connect envelopeGenerator
ui_->envelopeOsc1Volume->init(EnvelopeId::Osc1Volume);
connect(ui_->envelopeOsc1Volume, &EnvelopeGenerator::envelopeChanged, connect(ui_->envelopeOsc1Volume, &EnvelopeGenerator::envelopeChanged,
this, [this](float a, float d, float s, float r) { this, [this](float a, float d, float s, float r) {
audio_->parameters()->set(ParamId::Osc1VolumeEnvA, a); audio_->parameters()->set(ParamId::Osc1VolumeEnvA, a);
audio_->parameters()->set(ParamId::Osc1VolumeEnvD, d); audio_->parameters()->set(ParamId::Osc1VolumeEnvD, d);
audio_->parameters()->set(ParamId::Osc1VolumeEnvS, s); audio_->parameters()->set(ParamId::Osc1VolumeEnvS, s);
audio_->parameters()->set(ParamId::Osc1VolumeEnvR, r); audio_->parameters()->set(ParamId::Osc1VolumeEnvR, r);
// TODO: parameters()->setEnv(ENV_ID, a, d, s ,r)
}); });
// this should be easy enough to put into a for each envelopeGenerator loop
// initialize to defaults
ui_->envelopeOsc1Volume->setRanges(
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].max
);
ui_->envelopeOsc1Volume->setAttack(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].def);
ui_->envelopeOsc1Volume->setDecay(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].def);
ui_->envelopeOsc1Volume->setSustain(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].def);
ui_->envelopeOsc1Volume->setRelease(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].def);
// TODO: assign envelope widget a ParamID so that intialization and parameter bindings can be done from the envelope controller
} }
MainWindow::~MainWindow() { MainWindow::~MainWindow() {
@@ -60,14 +48,5 @@ void MainWindow::keyReleaseEvent(QKeyEvent* event) {
void MainWindow::onResetClicked() { void MainWindow::onResetClicked() {
// initialize to defaults // initialize to defaults
ui_->envelopeOsc1Volume->setRanges( ui_->envelopeOsc1Volume->init(EnvelopeId::Osc1Volume);
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].max,
PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].min, PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].max
);
ui_->envelopeOsc1Volume->setAttack(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvA)].def);
ui_->envelopeOsc1Volume->setDecay(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvD)].def);
ui_->envelopeOsc1Volume->setSustain(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvS)].def);
ui_->envelopeOsc1Volume->setRelease(PARAM_DEFS[static_cast<size_t>(ParamId::Osc1VolumeEnvR)].def);
} }

View File

@@ -56,13 +56,6 @@ void EnvelopeGenerator::setRelease(float v) {
ui_->sliderRelease->setValue(v); ui_->sliderRelease->setValue(v);
} }
void EnvelopeGenerator::setRanges(float minA, float maxA, float minD, float maxD, float minS, float maxS, float minR, float maxR) {
ui_->sliderAttack->setRange(minA, maxA);
ui_->sliderDecay->setRange(minD, maxD);
ui_->sliderSustain->setRange(minS, maxS);
ui_->sliderRelease->setRange(minR, maxR);
}
void EnvelopeGenerator::emitEnvelope() { void EnvelopeGenerator::emitEnvelope() {
emit envelopeChanged( emit envelopeChanged(
attack(), attack(),
@@ -70,4 +63,34 @@ void EnvelopeGenerator::emitEnvelope() {
sustain(), sustain(),
release() release()
); );
} }
void EnvelopeGenerator::init(EnvelopeId id) {
// could probably make this simpler with a map
ParamId aId, dId, sId, rId;
switch (id) {
case EnvelopeId::Osc1Volume:
aId = ParamId::Osc1VolumeEnvA; dId = ParamId::Osc1VolumeEnvD; sId = ParamId::Osc1VolumeEnvS; rId = ParamId::Osc1VolumeEnvR;
break;
case EnvelopeId::FilterCutoff:
aId = ParamId::FilterCutoffEnvA; dId = ParamId::FilterCutoffEnvD; sId = ParamId::FilterCutoffEnvS; rId = ParamId::FilterCutoffEnvR;
break;
case EnvelopeId::FilterResonance:
aId = ParamId::FilterResonanceEnvA; dId = ParamId::FilterResonanceEnvD; sId = ParamId::FilterResonanceEnvS; rId = ParamId::FilterResonanceEnvR;
break;
default: // not found
break;
}
ui_->sliderAttack->setRange(PARAM_DEFS[static_cast<size_t>(aId)].min, PARAM_DEFS[static_cast<size_t>(aId)].max);
ui_->sliderDecay->setRange(PARAM_DEFS[static_cast<size_t>(dId)].min, PARAM_DEFS[static_cast<size_t>(dId)].max);
ui_->sliderSustain->setRange(PARAM_DEFS[static_cast<size_t>(sId)].min, PARAM_DEFS[static_cast<size_t>(sId)].max);
ui_->sliderRelease->setRange(PARAM_DEFS[static_cast<size_t>(rId)].min, PARAM_DEFS[static_cast<size_t>(rId)].max);
setAttack(PARAM_DEFS[static_cast<size_t>(aId)].def);
setDecay(PARAM_DEFS[static_cast<size_t>(sId)].def);
setSustain(PARAM_DEFS[static_cast<size_t>(sId)].def);
setRelease(PARAM_DEFS[static_cast<size_t>(rId)].def);
}

View File

@@ -3,6 +3,8 @@
#include <QWidget> #include <QWidget>
#include "../../ParameterStore.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace Ui { class EnvelopeGenerator; } namespace Ui { class EnvelopeGenerator; }
QT_END_NAMESPACE QT_END_NAMESPACE
@@ -14,13 +16,15 @@ public:
explicit EnvelopeGenerator(QWidget* parent = nullptr); explicit EnvelopeGenerator(QWidget* parent = nullptr);
~EnvelopeGenerator(); ~EnvelopeGenerator();
void init(EnvelopeId id);
// setters
void setAttack(float v); void setAttack(float v);
void setDecay(float v); void setDecay(float v);
void setSustain(float v); void setSustain(float v);
void setRelease(float v); void setRelease(float v);
void setRanges(float minA, float maxA, float minD, float maxD, float minS, float maxS, float minR, float maxR); // getters
float attack() const; float attack() const;
float decay() const; float decay() const;
float sustain() const; float sustain() const;