From 3a07cb63195cf2aaac9b1c418d04f593fd6fa1ef Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sun, 8 Feb 2026 16:08:29 -0600 Subject: [PATCH] organize voice profile + tweaks --- CMakeLists.txt | 11 +++++- config/audio.yaml | 12 ++++++ config/profiles/default.yaml | 69 ++++++++++++++++------------------ config/wavetables/sigmoid.wt | Bin 0 -> 8192 bytes scripts/example_wavetable.py | 11 ++++-- src/ConfigInterface.cpp | 36 ++++++++++-------- src/ConfigInterface.h | 2 +- src/ui/MainWindow.cpp | 70 ++++++++++++++++++----------------- 8 files changed, 120 insertions(+), 91 deletions(-) create mode 100644 config/wavetables/sigmoid.wt diff --git a/CMakeLists.txt b/CMakeLists.txt index 08d9ace..f3c4578 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,5 +107,14 @@ target_link_libraries(metabolus RtMidi::rtmidi yaml-cpp Qt6::Widgets - atomic ) + +# needed some different calls here +if (WIN32) + +else() + target_link_libraries(metabolus + PRIVATE + atomic + ) +endif() diff --git a/config/audio.yaml b/config/audio.yaml index ed94762..ccd6fea 100644 --- a/config/audio.yaml +++ b/config/audio.yaml @@ -14,3 +14,15 @@ stereoMode: 2 # number of samples per audio buffer bufferSize: 512 + + +# synth settings + +#define SYNTH_PITCH_STANDARD 440.0f // frequency of home pitch +#define SYNTH_MIDI_HOME 69 // midi note index of home pitch +#define SYNTH_NOTES_PER_OCTAVE 12 + +# midi home note (usually A4=69) = pitchStandard hz (usually 440 or 432 hz) +pitchStandard: 440.0 +midi_home: 69 +notesPerOctave: 12 # changes to this give some interesting non-western harmonics diff --git a/config/profiles/default.yaml b/config/profiles/default.yaml index 79e3489..7cef78a 100644 --- a/config/profiles/default.yaml +++ b/config/profiles/default.yaml @@ -4,51 +4,46 @@ # sequences in the form [x, x, x] denote [setValue, sliderMinimum, sliderMaximum] -version: 0x0003 - -# deprecated, useless -Osc1Freq: [100, 20, 600] +version: 0x04 # wavetable selections OscWaveSelector1: 2 OscWaveSelector2: 3 # Frequency parameters -MasterOctaveOffset: [0, -5, 5] -MasterSemitoneOffset: [0, -12, 12] -MasterPitchOffset: [0, -100, 100] -Osc1OctaveOffset: [0, -5, 5] -Osc1SemitoneOffset: [0, -12, 12] -Osc1PitchOffset: [1.34, -100, 100] -Osc2OctaveOffset: [1, -5, 5] -Osc2SemitoneOffset: [0, -12, 12] -Osc2PitchOffset: [12.86, -100, 100] -Osc3OctaveOffset: [1, -5, 5] -Osc3SemitoneOffset: [7, -12, 12] -Osc3PitchOffset: [-8.79, -100, 100] - -# gonna have something like this: -#MasterPitchOffset: -# - [0, -5, 5] # Octave -# - [0, -12, -12] # Semitone -# - [0, -100, 100] # Pitch +MasterPitchOffset: + Octave: [0, -5, 5] + Semitone: [0, -12, 12] + Pitch: [0, -100, 100] +Osc1PitchOffset: + Octave: [0, -5, 5] + Semitone: [0, -12, 12] + Pitch: [1.34, -100, 100] +Osc2PitchOffset: + Octave: [1, -5, 5] + Semitone: [0, -12, 12] + Pitch: [12.86, -100, 100] +Osc3PitchOffset: + Octave: [1, -5, 5] + Semitone: [7, -12, 12] + Pitch: [-8.79, -100, 100] # Envelope generator parameters Osc1Volume: - - [1, 0, 10] # Depth - - [0.05, 0, 2] # Attack - - [0.2, 0, 2] # Decay - - [0.7, 0, 1] # Sustain - - [0.2, 0, 2] # Release + Depth: [1, 0, 10] + Attack: [0.05, 0, 2] + Decay: [0.2, 0, 2] + Sustain: [0.7, 0, 1] + Release: [0.2, 0, 2] FilterCutoff: - - [4, 0, 8] # Depth - - [0.05, 0, 2] # Attack - - [0.2, 0, 2] # Decay - - [0.2, 0, 1] # Sustain - - [0.25, 0, 2] # Release + Depth: [4, 0, 8] + Attack: [0.05, 0, 2] + Decay: [0.2, 0, 2] + Sustain: [0.2, 0, 1] + Release: [0.25, 0, 2] FilterResonance: - - [3, 0, 8] # Depth - - [0.05, 0, 2] # Attack - - [0.2, 0, 2] # Decay - - [0.5, 0, 1] # Sustain - - [0.3, 0, 2] # Release + Depth: [3, 0, 8] + Attack: [0.05, 0, 2] + Decay: [0.2, 0, 2] + Sustain: [0.5, 0, 1] + Release: [0.3, 0, 2] diff --git a/config/wavetables/sigmoid.wt b/config/wavetables/sigmoid.wt new file mode 100644 index 0000000000000000000000000000000000000000..adf473d9cc399cef57f72574d46fe71316fdda89 GIT binary patch literal 8192 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 Wa6gdWYcVYOho$^n2IT!(4*w62hW9uC literal 0 HcmV?d00001 diff --git a/scripts/example_wavetable.py b/scripts/example_wavetable.py index c5bad20..7be1dc3 100644 --- a/scripts/example_wavetable.py +++ b/scripts/example_wavetable.py @@ -3,7 +3,7 @@ import math from generate_wavetable import generateWavetable -WAVETABLE_FILE_NAME = "sharkFin" +WAVETABLE_FILE_NAME = "sigmoid" # process expects a waveform from x=[0, 2pi) centered around f(x)=0 # normalization is handled by the wavetableGenerator @@ -38,12 +38,15 @@ def sharkFin(phase): sample = -2 * (phase/math.pi - 1.0) ** k + 1.0 return sample -def sphere(phase): - return 1 +def sigmoid(phase): + k = 4 + a = -k * (phase - math.pi) + sample = 2 / (1 + math.exp(a)) - 1 + return sample # pass in the name of your wavtable file and the process function def main(): - generateWavetable(WAVETABLE_FILE_NAME, process) + generateWavetable(WAVETABLE_FILE_NAME, sigmoid) if __name__ == "__main__": main() diff --git a/src/ConfigInterface.cpp b/src/ConfigInterface.cpp index 84e39e4..f215851 100644 --- a/src/ConfigInterface.cpp +++ b/src/ConfigInterface.cpp @@ -74,25 +74,29 @@ YAML::Node ConfigInterface::loadProfile(std::string filename) { std::array fCutoffProfile = loadEnvProfile(&config, "FilterCutoff"); std::array fResonanceProfile = loadEnvProfile(&config, "FilterResonance"); + YAML::Node masterNode = config["MasterPitchOffset"]; + YAML::Node osc1Node = config["Osc1PitchOffset"]; + YAML::Node osc2Node = config["Osc2PitchOffset"]; + YAML::Node osc3Node = config["Osc3PitchOffset"]; std::array masterPitchOffsets = {{ - { config["MasterOctaveOffset"][0].as(), config["MasterOctaveOffset"][1].as(), config["MasterOctaveOffset"][2].as() }, - { config["MasterSemitoneOffset"][0].as(), config["MasterSemitoneOffset"][1].as(), config["MasterSemitoneOffset"][2].as() }, - { config["MasterPitchOffset"][0].as(), config["MasterPitchOffset"][1].as(), config["MasterPitchOffset"][2].as() }, + { masterNode["Octave"][0].as(), masterNode["Octave"][1].as(), masterNode["Octave"][2].as() }, + { masterNode["Semitone"][0].as(), masterNode["Semitone"][1].as(), masterNode["Semitone"][2].as() }, + { masterNode["Pitch"][0].as(), masterNode["Pitch"][1].as(), masterNode["Pitch"][2].as() }, }}; std::array osc1PitchOffsets = {{ - { config["Osc1OctaveOffset"][0].as(), config["Osc1OctaveOffset"][1].as(), config["Osc1OctaveOffset"][2].as() }, - { config["Osc1SemitoneOffset"][0].as(), config["Osc1SemitoneOffset"][1].as(), config["Osc1SemitoneOffset"][2].as() }, - { config["Osc1PitchOffset"][0].as(), config["Osc1PitchOffset"][1].as(), config["Osc1PitchOffset"][2].as() }, + { osc1Node["Octave"][0].as(), osc1Node["Octave"][1].as(), osc1Node["Octave"][2].as() }, + { osc1Node["Semitone"][0].as(), osc1Node["Semitone"][1].as(), osc1Node["Semitone"][2].as() }, + { osc1Node["Pitch"][0].as(),osc1Node["Pitch"][1].as(), osc1Node["Pitch"][2].as() }, }}; std::array osc2PitchOffsets = {{ - { config["Osc2OctaveOffset"][0].as(), config["Osc2OctaveOffset"][1].as(), config["Osc2OctaveOffset"][2].as() }, - { config["Osc2SemitoneOffset"][0].as(), config["Osc2SemitoneOffset"][1].as(), config["Osc2SemitoneOffset"][2].as() }, - { config["Osc2PitchOffset"][0].as(), config["Osc2PitchOffset"][1].as(), config["Osc2PitchOffset"][2].as() }, + { osc2Node["Octave"][0].as(), osc2Node["Octave"][1].as(), osc2Node["Octave"][2].as() }, + { osc2Node["Semitone"][0].as(), osc2Node["Semitone"][1].as(), osc2Node["Semitone"][2].as() }, + { osc2Node["Pitch"][0].as(), osc2Node["Pitch"][1].as(), osc2Node["Pitch"][2].as() }, }}; std::array osc3PitchOffsets = {{ - { config["Osc3OctaveOffset"][0].as(), config["Osc3OctaveOffset"][1].as(), config["Osc3OctaveOffset"][2].as() }, - { config["Osc3SemitoneOffset"][0].as(), config["Osc3SemitoneOffset"][1].as(), config["Osc3SemitoneOffset"][2].as() }, - { config["Osc3PitchOffset"][0].as(), config["Osc3PitchOffset"][1].as(), config["Osc3PitchOffset"][2].as() }, + { osc3Node["Octave"][0].as(), osc3Node["Octave"][1].as(), osc3Node["Octave"][2].as() }, + { osc3Node["Semitone"][0].as(), osc3Node["Semitone"][1].as(), osc3Node["Semitone"][2].as() }, + { osc3Node["Pitch"][0].as(), osc3Node["Pitch"][1].as(), osc3Node["Pitch"][2].as() }, }}; // set the values in the paramstore @@ -126,9 +130,11 @@ std::array ConfigInterface::loadEnvProfile(YAML::Node* node, std::stri 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() }; - } + paramProfile[0] = { envelopeNode["Depth"][0].as(), envelopeNode["Depth"][1].as(), envelopeNode["Depth"][2].as() }; + paramProfile[1] = { envelopeNode["Attack"][0].as(), envelopeNode["Attack"][1].as(), envelopeNode["Attack"][2].as() }; + paramProfile[2] = { envelopeNode["Decay"][0].as(), envelopeNode["Decay"][1].as(), envelopeNode["Decay"][2].as() }; + paramProfile[3] = { envelopeNode["Sustain"][0].as(), envelopeNode["Sustain"][1].as(), envelopeNode["Sustain"][2].as() }; + paramProfile[4] = { envelopeNode["Release"][0].as(), envelopeNode["Release"][1].as(), envelopeNode["Release"][2].as() }; return paramProfile; } diff --git a/src/ConfigInterface.h b/src/ConfigInterface.h index 2a9f91c..4bfa96d 100644 --- a/src/ConfigInterface.h +++ b/src/ConfigInterface.h @@ -8,7 +8,7 @@ #include "ParameterStore.h" -#define CONFIG_VERSION 0x0002 +#define CONFIG_VERSION 0x04 enum class ConfigFile { Audio = 0 diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 377fcae..531f4bf 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -157,50 +157,54 @@ void MainWindow::onResetClicked() { // TODO: clean these up, maybe put them in a package like the envelope generators (it'll help encapsulate the int-snapping business) // what I might do is make a variable-length slider-package object - ui_->sliderMasterOctave->setResolution(configRoot["MasterOctaveOffset"][2].as() - configRoot["MasterOctaveOffset"][1].as()); - ui_->sliderMasterOctave->setRange(configRoot["MasterOctaveOffset"][1].as(), configRoot["MasterOctaveOffset"][2].as()); - ui_->sliderMasterOctave->setValue(configRoot["MasterOctaveOffset"][0].as()); + YAML::Node masterNode = configRoot["MasterPitchOffset"]; + YAML::Node osc1Node = configRoot["Osc1PitchOffset"]; + YAML::Node osc2Node = configRoot["Osc2PitchOffset"]; + YAML::Node osc3Node = configRoot["Osc3PitchOffset"]; + ui_->sliderMasterOctave->setResolution(masterNode["Octave"][2].as() - masterNode["Octave"][1].as()); + ui_->sliderMasterOctave->setRange(masterNode["Octave"][1].as(), masterNode["Octave"][2].as()); + ui_->sliderMasterOctave->setValue(masterNode["Octave"][0].as()); - ui_->sliderMasterSemitone->setResolution(configRoot["MasterSemitoneOffset"][2].as() - configRoot["MasterSemitoneOffset"][1].as()); - ui_->sliderMasterSemitone->setRange(configRoot["MasterSemitoneOffset"][1].as(), configRoot["MasterSemitoneOffset"][2].as()); - ui_->sliderMasterSemitone->setValue(configRoot["MasterSemitoneOffset"][0].as()); + ui_->sliderMasterSemitone->setResolution(masterNode["Semitone"][2].as() - masterNode["Semitone"][1].as()); + ui_->sliderMasterSemitone->setRange(masterNode["Semitone"][1].as(), masterNode["Semitone"][2].as()); + ui_->sliderMasterSemitone->setValue(masterNode["Semitone"][0].as()); - ui_->sliderMasterPitch->setRange(configRoot["MasterPitchOffset"][1].as(), configRoot["MasterPitchOffset"][2].as()); - ui_->sliderMasterPitch->setValue(configRoot["MasterPitchOffset"][0].as()); + ui_->sliderMasterPitch->setRange(masterNode["Pitch"][1].as(), masterNode["Pitch"][2].as()); + ui_->sliderMasterPitch->setValue(masterNode["Pitch"][0].as()); - ui_->sliderOsc1Octave->setResolution(configRoot["Osc1OctaveOffset"][2].as() - configRoot["Osc1OctaveOffset"][1].as()); - ui_->sliderOsc1Octave->setRange(configRoot["Osc1OctaveOffset"][1].as(), configRoot["Osc1OctaveOffset"][2].as()); - ui_->sliderOsc1Octave->setValue(configRoot["Osc1OctaveOffset"][0].as()); + ui_->sliderOsc1Octave->setResolution(osc1Node["Octave"][2].as() - osc1Node["Octave"][1].as()); + ui_->sliderOsc1Octave->setRange(osc1Node["Octave"][1].as(), osc1Node["Octave"][2].as()); + ui_->sliderOsc1Octave->setValue(osc1Node["Octave"][0].as()); - ui_->sliderOsc1Semitone->setResolution(configRoot["Osc1SemitoneOffset"][2].as() - configRoot["Osc1SemitoneOffset"][1].as()); - ui_->sliderOsc1Semitone->setRange(configRoot["Osc1SemitoneOffset"][1].as(), configRoot["Osc1SemitoneOffset"][2].as()); - ui_->sliderOsc1Semitone->setValue(configRoot["Osc1SemitoneOffset"][0].as()); + ui_->sliderOsc1Semitone->setResolution(osc1Node["Semitone"][2].as() - osc1Node["Semitone"][1].as()); + ui_->sliderOsc1Semitone->setRange(osc1Node["Semitone"][1].as(), osc1Node["Semitone"][2].as()); + ui_->sliderOsc1Semitone->setValue(osc1Node["Semitone"][0].as()); - ui_->sliderOsc1Pitch->setRange(configRoot["Osc1PitchOffset"][1].as(), configRoot["Osc1PitchOffset"][2].as()); - ui_->sliderOsc1Pitch->setValue(configRoot["Osc1PitchOffset"][0].as()); + ui_->sliderOsc1Pitch->setRange(osc1Node["Pitch"][1].as(), osc1Node["Pitch"][2].as()); + ui_->sliderOsc1Pitch->setValue(osc1Node["Pitch"][0].as()); - ui_->sliderOsc2Octave->setResolution(configRoot["Osc2OctaveOffset"][2].as() - configRoot["Osc2OctaveOffset"][1].as()); - ui_->sliderOsc2Octave->setRange(configRoot["Osc2OctaveOffset"][1].as(), configRoot["Osc2OctaveOffset"][2].as()); - ui_->sliderOsc2Octave->setValue(configRoot["Osc2OctaveOffset"][0].as()); + ui_->sliderOsc2Octave->setResolution(osc2Node["Octave"][2].as() - osc2Node["Octave"][1].as()); + ui_->sliderOsc2Octave->setRange(osc2Node["Octave"][1].as(), osc2Node["Octave"][2].as()); + ui_->sliderOsc2Octave->setValue(osc2Node["Octave"][0].as()); - ui_->sliderOsc2Semitone->setResolution(configRoot["Osc2SemitoneOffset"][2].as() - configRoot["Osc2SemitoneOffset"][1].as()); - ui_->sliderOsc2Semitone->setRange(configRoot["Osc2SemitoneOffset"][1].as(), configRoot["Osc2SemitoneOffset"][2].as()); - ui_->sliderOsc2Semitone->setValue(configRoot["Osc2SemitoneOffset"][0].as()); + ui_->sliderOsc2Semitone->setResolution(osc2Node["Semitone"][2].as() - osc2Node["Semitone"][1].as()); + ui_->sliderOsc2Semitone->setRange(osc2Node["Semitone"][1].as(), osc2Node["Semitone"][2].as()); + ui_->sliderOsc2Semitone->setValue(osc2Node["Semitone"][0].as()); - ui_->sliderOsc2Pitch->setRange(configRoot["Osc2PitchOffset"][1].as(), configRoot["Osc2PitchOffset"][2].as()); - ui_->sliderOsc2Pitch->setValue(configRoot["Osc2PitchOffset"][0].as()); + ui_->sliderOsc2Pitch->setRange(osc2Node["Pitch"][1].as(), osc2Node["Pitch"][2].as()); + ui_->sliderOsc2Pitch->setValue(osc2Node["Pitch"][0].as()); - ui_->sliderOsc3Octave->setResolution(configRoot["Osc3OctaveOffset"][2].as() - configRoot["Osc3OctaveOffset"][1].as()); - ui_->sliderOsc3Octave->setRange(configRoot["Osc3OctaveOffset"][1].as(), configRoot["Osc3OctaveOffset"][2].as()); - ui_->sliderOsc3Octave->setValue(configRoot["Osc3OctaveOffset"][0].as()); + ui_->sliderOsc3Octave->setResolution(osc3Node["Octave"][2].as() - osc3Node["Octave"][1].as()); + ui_->sliderOsc3Octave->setRange(osc3Node["Octave"][1].as(), osc3Node["Octave"][2].as()); + ui_->sliderOsc3Octave->setValue(osc3Node["Octave"][0].as()); - ui_->sliderOsc3Semitone->setResolution(configRoot["Osc3SemitoneOffset"][2].as() - configRoot["Osc3SemitoneOffset"][1].as()); - ui_->sliderOsc3Semitone->setRange(configRoot["Osc3SemitoneOffset"][1].as(), configRoot["Osc3SemitoneOffset"][2].as()); - ui_->sliderOsc3Semitone->setValue(configRoot["Osc3SemitoneOffset"][0].as()); + ui_->sliderOsc3Semitone->setResolution(osc3Node["Semitone"][2].as() - osc3Node["Semitone"][1].as()); + ui_->sliderOsc3Semitone->setRange(osc3Node["Semitone"][1].as(), osc3Node["Semitone"][2].as()); + ui_->sliderOsc3Semitone->setValue(osc3Node["Semitone"][0].as()); + + ui_->sliderOsc3Pitch->setRange(osc3Node["Pitch"][1].as(), osc3Node["Pitch"][2].as()); + ui_->sliderOsc3Pitch->setValue(osc3Node["Pitch"][0].as()); - ui_->sliderOsc3Pitch->setRange(configRoot["Osc3PitchOffset"][1].as(), configRoot["Osc3PitchOffset"][2].as()); - ui_->sliderOsc3Pitch->setValue(configRoot["Osc3PitchOffset"][0].as()); - ui_->comboOsc1WaveSelector1->setCurrentIndex(configRoot["OscWaveSelector1"].as()); ui_->comboOsc1WaveSelector2->setCurrentIndex(configRoot["OscWaveSelector2"].as());