From 21bf285aff6479d67cfb3ce0ebf7d659cb7b98d8 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sun, 25 Jan 2026 14:11:03 -0600 Subject: [PATCH] basic profile loading --- src/ConfigInterface.cpp | 75 ++++++++++++++++++- src/ConfigInterface.h | 15 ++++ src/ParameterStore.cpp | 42 +---------- src/ParameterStore.h | 10 +-- src/synth/AudioEngine.cpp | 3 +- src/synth/AudioEngine.h | 10 ++- src/synth/Synth.cpp | 4 +- src/synth/Synth.h | 5 +- src/ui/MainWindow.cpp | 14 ++-- src/ui/MainWindow.h | 4 +- .../EnvelopeGenerator/EnvelopeGenerator.cpp | 18 +++++ .../EnvelopeGenerator/EnvelopeGenerator.h | 1 + 12 files changed, 134 insertions(+), 67 deletions(-) diff --git a/src/ConfigInterface.cpp b/src/ConfigInterface.cpp index 733bdd6..058ae0b 100644 --- a/src/ConfigInterface.cpp +++ b/src/ConfigInterface.cpp @@ -1,8 +1,6 @@ #include "ConfigInterface.h" -#include "yaml-cpp/yaml.h" - #include #include @@ -14,6 +12,11 @@ ConfigInterface::ConfigInterface() { } +ConfigInterface::ConfigInterface(ParameterStore* params): params_(params) { + +} + +// lots of checking in this to make this safe int ConfigInterface::getValue(ConfigFile file, std::string key, int defaultVal) { // assemble filepath @@ -23,7 +26,7 @@ int ConfigInterface::getValue(ConfigFile file, std::string key, int defaultVal) // attempt to open file YAML::Node config; try { - + YAML::Node config = YAML::LoadFile(filepath); // read key if it exists @@ -42,3 +45,69 @@ int ConfigInterface::getValue(ConfigFile file, std::string key, int defaultVal) return -1; } + +// ugly but if it works it works +void ConfigInterface::loadProfile(std::string filename) { + + // load file + std::string filepath = "config/profiles/" + filename + ".yaml"; + filepath = std::filesystem::absolute(filepath).string(); + YAML::Node config; + try { + config = YAML::LoadFile(filepath); + } catch(const std::exception& e) { + std::cerr << e.what() << std::endl; + return; + } + + // check version + int version = config["version"].as(); // yaml-cpp parses unquoted hex as integers + if(version < CONFIG_VERSION) { + std::cout << "Parameter profile version " << version << "is outdated below the compatible version " << CONFIG_VERSION << std::endl; + return; + } else { + std::cout << version << std::endl; + } + + // extract values from the config file + std::array osc1VolumeProfile = loadEnvProfile(&config, "Osc1Volume"); + std::array fCutoffProfile = loadEnvProfile(&config, "FilterCutoff"); + std::array fResonanceProfile = loadEnvProfile(&config, "FilterResonance"); + + // TODO: remove this once all the parameters are set properly + params_->resetToDefaults(); + + // set the values in the paramstore + params_->set(EnvelopeId::Osc1Volume, osc1VolumeProfile[0].def, osc1VolumeProfile[1].def, osc1VolumeProfile[2].def, osc1VolumeProfile[3].def, osc1VolumeProfile[4].def); + params_->set(EnvelopeId::FilterCutoff, fCutoffProfile[0].def, fCutoffProfile[1].def, fCutoffProfile[2].def, fCutoffProfile[3].def, fCutoffProfile[4].def); + params_->set(EnvelopeId::FilterResonance, fResonanceProfile[0].def, fResonanceProfile[1].def, fResonanceProfile[2].def, fResonanceProfile[3].def, fResonanceProfile[4].def); + // TODO: why do I bother passing in 5 values independently when I can just do an array ? + // VVV look down there its so easy + + // TODO: + // load wavetable settings + // load oscillator pitch settings + +} + +std::array ConfigInterface::loadEnvProfile(YAML::Node* node, std::string profile) { + + YAML::Node envelopeNode = (*node)[profile]; + + std::array paramProfile; + + for(int i = 0; i < paramProfile.size(); i++) { + paramProfile[i] = { envelopeNode[i][0].as(), envelopeNode[i][1].as(), envelopeNode[i][2].as() }; + } + + return paramProfile; +} + +std::array ConfigInterface::loadEnvProfile(std::string filename, std::string profile) { + + std::string filepath = "config/profiles/" + filename + ".yaml"; + filepath = std::filesystem::absolute(filepath).string(); + YAML::Node config = YAML::LoadFile(filepath); + + return loadEnvProfile(&config, profile); +} diff --git a/src/ConfigInterface.h b/src/ConfigInterface.h index faea952..084c7ad 100644 --- a/src/ConfigInterface.h +++ b/src/ConfigInterface.h @@ -4,6 +4,11 @@ #include #include #include +#include "yaml-cpp/yaml.h" + +#include "ParameterStore.h" + +#define CONFIG_VERSION 0x0002 enum class ConfigFile { Audio = 0 @@ -15,17 +20,27 @@ const std::vector filePaths = { "audio.yaml" }; +// Reads from yaml config files +// Handles things like profile loading class ConfigInterface { public: ConfigInterface(); + ConfigInterface(ParameterStore* params); ~ConfigInterface() = default; int getValue(ConfigFile file, std::string key, int defaultVal); + void loadProfile(std::string filename); + std::array loadEnvProfile(YAML::Node* node, std::string profile); + std::array loadEnvProfile(std::string filename, std::string profile); + private: const std::string configRoot = "config"; + // loading parameters + ParameterStore* params_; + }; diff --git a/src/ParameterStore.cpp b/src/ParameterStore.cpp index a58a14e..3e7d895 100644 --- a/src/ParameterStore.cpp +++ b/src/ParameterStore.cpp @@ -5,8 +5,8 @@ #include "yaml-cpp/yaml.h" // TODO: using yaml.h outside of ConfigInterface feels spaghetti to me #include -ParameterStore::ParameterStore(ConfigInterface* config) : config_(config) { - resetToDefaults(); +ParameterStore::ParameterStore() { + //resetToDefaults(); } // set parameter value @@ -31,45 +31,9 @@ float ParameterStore::get(ParamId id) const { } void ParameterStore::resetToDefaults() { + for(size_t i = 0; i < PARAM_COUNT; i++) { values_[i].store(PARAM_DEFS[i].def, std::memory_order_relaxed); } - loadParameterProfile("config/profiles/default.yaml"); - -} - -void ParameterStore::loadParameterProfile(std::string filepath) { - - // TODO: abstract the actual yaml interfacing to the ConfigInterface instead of here - // TODO: update ui based on changes that happen not in the ui - // it will require some architecture rework :( - - // load file - filepath = std::filesystem::absolute(filepath).string(); - YAML::Node config; - try { - config = YAML::LoadFile(filepath); - } catch(const std::exception& e) { - std::cerr << e.what() << std::endl; - return; - } - - // check version - int version = config["version"].as(); // yaml-cpp parses unquoted hex as integers - if(version < CONFIG_VERSION) { - std::cout << "Parameter profile version " << version << "is outdated below the compatible version " << CONFIG_VERSION << std::endl; - return; - } else { - //std::cout << version << std::endl; - } - - // start setting some values - YAML::Node osc1Volume = config["Osc1Volume"]; - YAML::Node fCutoff = config["FilterCutoff"]; - YAML::Node fResonance = config["FilterResonance"]; - set(EnvelopeId::Osc1Volume, osc1Volume[0][0].as(), osc1Volume[1][0].as(), osc1Volume[2][0].as(), osc1Volume[3][0].as(), osc1Volume[4][0].as()); - set(EnvelopeId::FilterCutoff, fCutoff[0][0].as(), fCutoff[1][0].as(), fCutoff[2][0].as(), fCutoff[3][0].as(), fCutoff[4][0].as()); - set(EnvelopeId::FilterResonance, fResonance[0][0].as(), fResonance[1][0].as(), fResonance[2][0].as(), fResonance[3][0].as(), fResonance[4][0].as()); - } diff --git a/src/ParameterStore.h b/src/ParameterStore.h index cf3b696..70fdef1 100644 --- a/src/ParameterStore.h +++ b/src/ParameterStore.h @@ -1,14 +1,10 @@ #pragma once -#include "ConfigInterface.h" - #include #include #include -#define CONFIG_VERSION 0x0001 - enum class ParamId : uint16_t { Osc1Frequency, Osc1WaveSelector1, @@ -111,7 +107,7 @@ class ParameterStore { public: - ParameterStore(ConfigInterface* config); + ParameterStore(); ~ParameterStore() = default; void set(ParamId id, float value); @@ -123,10 +119,6 @@ public: private: - void loadParameterProfile(std::string filepath); - std::array, PARAM_COUNT> values_; - ConfigInterface* config_; - }; diff --git a/src/synth/AudioEngine.cpp b/src/synth/AudioEngine.cpp index 56772a9..6c80fdd 100644 --- a/src/synth/AudioEngine.cpp +++ b/src/synth/AudioEngine.cpp @@ -3,7 +3,8 @@ #include -AudioEngine::AudioEngine(ConfigInterface* config) : params_(ParameterStore(config)), synth_(params_), config_(config) { +AudioEngine::AudioEngine(ConfigInterface* config, ParameterStore* params) : params_(params), synth_(params), config_(config) { + if(audio_.getDeviceCount() < 1) { throw std::runtime_error("No audio devices found"); } diff --git a/src/synth/AudioEngine.h b/src/synth/AudioEngine.h index 9c1f128..7d00764 100644 --- a/src/synth/AudioEngine.h +++ b/src/synth/AudioEngine.h @@ -5,9 +5,9 @@ #include #include +#include "../ConfigInterface.h" #include "Synth.h" #include "../KeyboardController.h" -#include "../ConfigInterface.h" #if defined(_WIN32) #define AUDIO_API RtAudio::WINDOWS_WASAPI @@ -18,7 +18,9 @@ class AudioEngine { public: - AudioEngine(ConfigInterface* config); + + AudioEngine() = default; + AudioEngine(ConfigInterface* config, ParameterStore* params); ~AudioEngine(); // starts the audio stream. returns true on success and false on failure @@ -28,7 +30,7 @@ public: void stop(); // getters - ParameterStore* parameters() { return ¶ms_; } + ParameterStore* parameters() { return params_; } NoteQueue& noteQueue() { return noteQueue_; } ScopeBuffer& scopeBuffer() { return scope_; } @@ -41,7 +43,7 @@ private: int32_t process(float* out, uint32_t nFrames); ConfigInterface* config_; // access to config files - ParameterStore params_; // stores the control parameters + ParameterStore* params_; // stores the control parameters NoteQueue noteQueue_; // stores note events for passing between threads Synth synth_; // generates audio ScopeBuffer scope_ { 1024 }; // stores audio samples for visualization diff --git a/src/synth/Synth.cpp b/src/synth/Synth.cpp index b9b5929..f21c659 100644 --- a/src/synth/Synth.cpp +++ b/src/synth/Synth.cpp @@ -8,13 +8,13 @@ #define M_PI 3.14159265358979323846 #endif -Synth::Synth(const ParameterStore& params) : paramStore_(params) { +Synth::Synth(ParameterStore* params) : paramStore_(params) { voices_.fill(Voice(params_.data(), &wavetable_)); } void Synth::updateParams() { for(size_t i = 0; i < PARAM_COUNT; i++) { - params_[i].target = paramStore_.get(static_cast(i)); + params_[i].target = paramStore_->get(static_cast(i)); } } diff --git a/src/synth/Synth.h b/src/synth/Synth.h index b57f513..cdb4259 100644 --- a/src/synth/Synth.h +++ b/src/synth/Synth.h @@ -15,7 +15,8 @@ class Synth { public: - Synth(const ParameterStore& params); + Synth() = default; + Synth(ParameterStore* params); ~Synth() = default; // generates a buffer of audio samples nFrames long @@ -39,7 +40,7 @@ private: Voice* findFreeVoice(); Voice* findVoiceByNote(uint8_t note); - const ParameterStore& paramStore_; + ParameterStore* paramStore_; // smoothed params creates a buffer in case the thread controlling paramStore gets blocked std::array params_; diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 4a942eb..c51bdd9 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -8,7 +8,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui_(new Ui::MainWindow), - audio_(new AudioEngine(&config_)), + config_(ConfigInterface(¶ms_)), + audio_(new AudioEngine(&config_, ¶ms_)), keyboard_(audio_->noteQueue()), midi_(audio_->noteQueue()) { @@ -74,12 +75,13 @@ void MainWindow::onResetClicked() { // initialize to defaults - // envelopeGenerators - ui_->envelopeOsc1Volume->init(EnvelopeId::Osc1Volume); - ui_->envelopeFilterCutoff->init(EnvelopeId::FilterCutoff); - ui_->envelopeFilterResonance->init(EnvelopeId::FilterResonance); + config_.loadProfile("default"); + + // update ui from the paramstore + ui_->envelopeOsc1Volume->init(EnvelopeId::Osc1Volume, config_.loadEnvProfile("default", "Osc1Volume")); + ui_->envelopeFilterCutoff->init(EnvelopeId::FilterCutoff, config_.loadEnvProfile("default", "FilterCutoff")); + ui_->envelopeFilterResonance->init(EnvelopeId::FilterResonance, config_.loadEnvProfile("default", "FilterResonance")); - // comboBoxes ui_->comboOsc1WaveSelector1->setCurrentIndex(static_cast(PARAM_DEFS[static_cast(ParamId::Osc1WaveSelector1)].def)); ui_->comboOsc1WaveSelector2->setCurrentIndex(static_cast(PARAM_DEFS[static_cast(ParamId::Osc1WaveSelector2)].def)); diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 73b00ad..77d2ac1 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -4,9 +4,9 @@ #include #include +#include "../ConfigInterface.h" #include "../synth/AudioEngine.h" #include "../MidiController.h" -#include "../ConfigInterface.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -28,8 +28,10 @@ private slots: void onResetClicked(); private: + Ui::MainWindow *ui_; + ParameterStore params_; ConfigInterface config_; AudioEngine* audio_ = nullptr; KeyboardController keyboard_; diff --git a/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.cpp b/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.cpp index 5b961c5..09e0f2d 100644 --- a/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.cpp +++ b/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.cpp @@ -92,3 +92,21 @@ void EnvelopeGenerator::init(EnvelopeId id) { setRelease(PARAM_DEFS[static_cast(params.r)].def); } + +void EnvelopeGenerator::init(EnvelopeId id, std::array profile) { + + EnvelopeParam params = ENV_PARAMS[static_cast(id)]; + + ui_->sliderDepth->setRange(profile[0].min, profile[0].max); + ui_->sliderAttack->setRange(profile[1].min, profile[1].max); + ui_->sliderDecay->setRange(profile[2].min, profile[2].max); + ui_->sliderSustain->setRange(profile[3].min, profile[3].max); + ui_->sliderRelease->setRange(profile[4].min, profile[4].max); + + setDepth(profile[0].def); + setAttack(profile[1].def); + setDecay(profile[2].def); + setSustain(profile[3].def); + setRelease(profile[4].def); + +} diff --git a/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.h b/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.h index a412c62..935a76c 100644 --- a/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.h +++ b/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.h @@ -18,6 +18,7 @@ public: // connects signals, sets parameters to the defaults defined in paramStore void init(EnvelopeId id); + void init(EnvelopeId id, std::array profile); // setters void setDepth(float v);