organize voice profile + tweaks

This commit is contained in:
2026-02-08 16:08:29 -06:00
parent 0f17ab09c4
commit 3a07cb6319
8 changed files with 120 additions and 91 deletions

View File

@@ -107,5 +107,14 @@ target_link_libraries(metabolus
RtMidi::rtmidi RtMidi::rtmidi
yaml-cpp yaml-cpp
Qt6::Widgets Qt6::Widgets
)
# needed some different calls here
if (WIN32)
else()
target_link_libraries(metabolus
PRIVATE
atomic atomic
) )
endif()

View File

@@ -14,3 +14,15 @@ stereoMode: 2
# number of samples per audio buffer # number of samples per audio buffer
bufferSize: 512 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

View File

@@ -4,51 +4,46 @@
# sequences in the form [x, x, x] denote [setValue, sliderMinimum, sliderMaximum] # sequences in the form [x, x, x] denote [setValue, sliderMinimum, sliderMaximum]
version: 0x0003 version: 0x04
# deprecated, useless
Osc1Freq: [100, 20, 600]
# wavetable selections # wavetable selections
OscWaveSelector1: 2 OscWaveSelector1: 2
OscWaveSelector2: 3 OscWaveSelector2: 3
# Frequency parameters # Frequency parameters
MasterOctaveOffset: [0, -5, 5] MasterPitchOffset:
MasterSemitoneOffset: [0, -12, 12] Octave: [0, -5, 5]
MasterPitchOffset: [0, -100, 100] Semitone: [0, -12, 12]
Osc1OctaveOffset: [0, -5, 5] Pitch: [0, -100, 100]
Osc1SemitoneOffset: [0, -12, 12] Osc1PitchOffset:
Osc1PitchOffset: [1.34, -100, 100] Octave: [0, -5, 5]
Osc2OctaveOffset: [1, -5, 5] Semitone: [0, -12, 12]
Osc2SemitoneOffset: [0, -12, 12] Pitch: [1.34, -100, 100]
Osc2PitchOffset: [12.86, -100, 100] Osc2PitchOffset:
Osc3OctaveOffset: [1, -5, 5] Octave: [1, -5, 5]
Osc3SemitoneOffset: [7, -12, 12] Semitone: [0, -12, 12]
Osc3PitchOffset: [-8.79, -100, 100] Pitch: [12.86, -100, 100]
Osc3PitchOffset:
# gonna have something like this: Octave: [1, -5, 5]
#MasterPitchOffset: Semitone: [7, -12, 12]
# - [0, -5, 5] # Octave Pitch: [-8.79, -100, 100]
# - [0, -12, -12] # Semitone
# - [0, -100, 100] # Pitch
# Envelope generator parameters # Envelope generator parameters
Osc1Volume: Osc1Volume:
- [1, 0, 10] # Depth Depth: [1, 0, 10]
- [0.05, 0, 2] # Attack Attack: [0.05, 0, 2]
- [0.2, 0, 2] # Decay Decay: [0.2, 0, 2]
- [0.7, 0, 1] # Sustain Sustain: [0.7, 0, 1]
- [0.2, 0, 2] # Release Release: [0.2, 0, 2]
FilterCutoff: FilterCutoff:
- [4, 0, 8] # Depth Depth: [4, 0, 8]
- [0.05, 0, 2] # Attack Attack: [0.05, 0, 2]
- [0.2, 0, 2] # Decay Decay: [0.2, 0, 2]
- [0.2, 0, 1] # Sustain Sustain: [0.2, 0, 1]
- [0.25, 0, 2] # Release Release: [0.25, 0, 2]
FilterResonance: FilterResonance:
- [3, 0, 8] # Depth Depth: [3, 0, 8]
- [0.05, 0, 2] # Attack Attack: [0.05, 0, 2]
- [0.2, 0, 2] # Decay Decay: [0.2, 0, 2]
- [0.5, 0, 1] # Sustain Sustain: [0.5, 0, 1]
- [0.3, 0, 2] # Release Release: [0.3, 0, 2]

Binary file not shown.

View File

@@ -3,7 +3,7 @@ import math
from generate_wavetable import generateWavetable 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 # process expects a waveform from x=[0, 2pi) centered around f(x)=0
# normalization is handled by the wavetableGenerator # normalization is handled by the wavetableGenerator
@@ -38,12 +38,15 @@ def sharkFin(phase):
sample = -2 * (phase/math.pi - 1.0) ** k + 1.0 sample = -2 * (phase/math.pi - 1.0) ** k + 1.0
return sample return sample
def sphere(phase): def sigmoid(phase):
return 1 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 # pass in the name of your wavtable file and the process function
def main(): def main():
generateWavetable(WAVETABLE_FILE_NAME, process) generateWavetable(WAVETABLE_FILE_NAME, sigmoid)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -74,25 +74,29 @@ YAML::Node ConfigInterface::loadProfile(std::string filename) {
std::array<Param, 5> fCutoffProfile = loadEnvProfile(&config, "FilterCutoff"); std::array<Param, 5> fCutoffProfile = loadEnvProfile(&config, "FilterCutoff");
std::array<Param, 5> fResonanceProfile = loadEnvProfile(&config, "FilterResonance"); std::array<Param, 5> 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<Param, 3> masterPitchOffsets = {{ std::array<Param, 3> masterPitchOffsets = {{
{ config["MasterOctaveOffset"][0].as<float>(), config["MasterOctaveOffset"][1].as<float>(), config["MasterOctaveOffset"][2].as<float>() }, { masterNode["Octave"][0].as<float>(), masterNode["Octave"][1].as<float>(), masterNode["Octave"][2].as<float>() },
{ config["MasterSemitoneOffset"][0].as<float>(), config["MasterSemitoneOffset"][1].as<float>(), config["MasterSemitoneOffset"][2].as<float>() }, { masterNode["Semitone"][0].as<float>(), masterNode["Semitone"][1].as<float>(), masterNode["Semitone"][2].as<float>() },
{ config["MasterPitchOffset"][0].as<float>(), config["MasterPitchOffset"][1].as<float>(), config["MasterPitchOffset"][2].as<float>() }, { masterNode["Pitch"][0].as<float>(), masterNode["Pitch"][1].as<float>(), masterNode["Pitch"][2].as<float>() },
}}; }};
std::array<Param, 3> osc1PitchOffsets = {{ std::array<Param, 3> osc1PitchOffsets = {{
{ config["Osc1OctaveOffset"][0].as<float>(), config["Osc1OctaveOffset"][1].as<float>(), config["Osc1OctaveOffset"][2].as<float>() }, { osc1Node["Octave"][0].as<float>(), osc1Node["Octave"][1].as<float>(), osc1Node["Octave"][2].as<float>() },
{ config["Osc1SemitoneOffset"][0].as<float>(), config["Osc1SemitoneOffset"][1].as<float>(), config["Osc1SemitoneOffset"][2].as<float>() }, { osc1Node["Semitone"][0].as<float>(), osc1Node["Semitone"][1].as<float>(), osc1Node["Semitone"][2].as<float>() },
{ config["Osc1PitchOffset"][0].as<float>(), config["Osc1PitchOffset"][1].as<float>(), config["Osc1PitchOffset"][2].as<float>() }, { osc1Node["Pitch"][0].as<float>(),osc1Node["Pitch"][1].as<float>(), osc1Node["Pitch"][2].as<float>() },
}}; }};
std::array<Param, 3> osc2PitchOffsets = {{ std::array<Param, 3> osc2PitchOffsets = {{
{ config["Osc2OctaveOffset"][0].as<float>(), config["Osc2OctaveOffset"][1].as<float>(), config["Osc2OctaveOffset"][2].as<float>() }, { osc2Node["Octave"][0].as<float>(), osc2Node["Octave"][1].as<float>(), osc2Node["Octave"][2].as<float>() },
{ config["Osc2SemitoneOffset"][0].as<float>(), config["Osc2SemitoneOffset"][1].as<float>(), config["Osc2SemitoneOffset"][2].as<float>() }, { osc2Node["Semitone"][0].as<float>(), osc2Node["Semitone"][1].as<float>(), osc2Node["Semitone"][2].as<float>() },
{ config["Osc2PitchOffset"][0].as<float>(), config["Osc2PitchOffset"][1].as<float>(), config["Osc2PitchOffset"][2].as<float>() }, { osc2Node["Pitch"][0].as<float>(), osc2Node["Pitch"][1].as<float>(), osc2Node["Pitch"][2].as<float>() },
}}; }};
std::array<Param, 3> osc3PitchOffsets = {{ std::array<Param, 3> osc3PitchOffsets = {{
{ config["Osc3OctaveOffset"][0].as<float>(), config["Osc3OctaveOffset"][1].as<float>(), config["Osc3OctaveOffset"][2].as<float>() }, { osc3Node["Octave"][0].as<float>(), osc3Node["Octave"][1].as<float>(), osc3Node["Octave"][2].as<float>() },
{ config["Osc3SemitoneOffset"][0].as<float>(), config["Osc3SemitoneOffset"][1].as<float>(), config["Osc3SemitoneOffset"][2].as<float>() }, { osc3Node["Semitone"][0].as<float>(), osc3Node["Semitone"][1].as<float>(), osc3Node["Semitone"][2].as<float>() },
{ config["Osc3PitchOffset"][0].as<float>(), config["Osc3PitchOffset"][1].as<float>(), config["Osc3PitchOffset"][2].as<float>() }, { osc3Node["Pitch"][0].as<float>(), osc3Node["Pitch"][1].as<float>(), osc3Node["Pitch"][2].as<float>() },
}}; }};
// set the values in the paramstore // set the values in the paramstore
@@ -126,9 +130,11 @@ std::array<Param, 5> ConfigInterface::loadEnvProfile(YAML::Node* node, std::stri
std::array<Param, 5> paramProfile; std::array<Param, 5> paramProfile;
for(int i = 0; i < paramProfile.size(); i++) { paramProfile[0] = { envelopeNode["Depth"][0].as<float>(), envelopeNode["Depth"][1].as<float>(), envelopeNode["Depth"][2].as<float>() };
paramProfile[i] = { envelopeNode[i][0].as<float>(), envelopeNode[i][1].as<float>(), envelopeNode[i][2].as<float>() }; paramProfile[1] = { envelopeNode["Attack"][0].as<float>(), envelopeNode["Attack"][1].as<float>(), envelopeNode["Attack"][2].as<float>() };
} paramProfile[2] = { envelopeNode["Decay"][0].as<float>(), envelopeNode["Decay"][1].as<float>(), envelopeNode["Decay"][2].as<float>() };
paramProfile[3] = { envelopeNode["Sustain"][0].as<float>(), envelopeNode["Sustain"][1].as<float>(), envelopeNode["Sustain"][2].as<float>() };
paramProfile[4] = { envelopeNode["Release"][0].as<float>(), envelopeNode["Release"][1].as<float>(), envelopeNode["Release"][2].as<float>() };
return paramProfile; return paramProfile;
} }

View File

@@ -8,7 +8,7 @@
#include "ParameterStore.h" #include "ParameterStore.h"
#define CONFIG_VERSION 0x0002 #define CONFIG_VERSION 0x04
enum class ConfigFile { enum class ConfigFile {
Audio = 0 Audio = 0

View File

@@ -157,49 +157,53 @@ 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) // 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 // what I might do is make a variable-length slider-package object
ui_->sliderMasterOctave->setResolution(configRoot["MasterOctaveOffset"][2].as<int>() - configRoot["MasterOctaveOffset"][1].as<int>()); YAML::Node masterNode = configRoot["MasterPitchOffset"];
ui_->sliderMasterOctave->setRange(configRoot["MasterOctaveOffset"][1].as<int>(), configRoot["MasterOctaveOffset"][2].as<int>()); YAML::Node osc1Node = configRoot["Osc1PitchOffset"];
ui_->sliderMasterOctave->setValue(configRoot["MasterOctaveOffset"][0].as<int>()); YAML::Node osc2Node = configRoot["Osc2PitchOffset"];
YAML::Node osc3Node = configRoot["Osc3PitchOffset"];
ui_->sliderMasterOctave->setResolution(masterNode["Octave"][2].as<int>() - masterNode["Octave"][1].as<int>());
ui_->sliderMasterOctave->setRange(masterNode["Octave"][1].as<int>(), masterNode["Octave"][2].as<int>());
ui_->sliderMasterOctave->setValue(masterNode["Octave"][0].as<int>());
ui_->sliderMasterSemitone->setResolution(configRoot["MasterSemitoneOffset"][2].as<int>() - configRoot["MasterSemitoneOffset"][1].as<int>()); ui_->sliderMasterSemitone->setResolution(masterNode["Semitone"][2].as<int>() - masterNode["Semitone"][1].as<int>());
ui_->sliderMasterSemitone->setRange(configRoot["MasterSemitoneOffset"][1].as<int>(), configRoot["MasterSemitoneOffset"][2].as<int>()); ui_->sliderMasterSemitone->setRange(masterNode["Semitone"][1].as<int>(), masterNode["Semitone"][2].as<int>());
ui_->sliderMasterSemitone->setValue(configRoot["MasterSemitoneOffset"][0].as<int>()); ui_->sliderMasterSemitone->setValue(masterNode["Semitone"][0].as<int>());
ui_->sliderMasterPitch->setRange(configRoot["MasterPitchOffset"][1].as<float>(), configRoot["MasterPitchOffset"][2].as<float>()); ui_->sliderMasterPitch->setRange(masterNode["Pitch"][1].as<float>(), masterNode["Pitch"][2].as<float>());
ui_->sliderMasterPitch->setValue(configRoot["MasterPitchOffset"][0].as<float>()); ui_->sliderMasterPitch->setValue(masterNode["Pitch"][0].as<float>());
ui_->sliderOsc1Octave->setResolution(configRoot["Osc1OctaveOffset"][2].as<int>() - configRoot["Osc1OctaveOffset"][1].as<int>()); ui_->sliderOsc1Octave->setResolution(osc1Node["Octave"][2].as<int>() - osc1Node["Octave"][1].as<int>());
ui_->sliderOsc1Octave->setRange(configRoot["Osc1OctaveOffset"][1].as<int>(), configRoot["Osc1OctaveOffset"][2].as<int>()); ui_->sliderOsc1Octave->setRange(osc1Node["Octave"][1].as<int>(), osc1Node["Octave"][2].as<int>());
ui_->sliderOsc1Octave->setValue(configRoot["Osc1OctaveOffset"][0].as<int>()); ui_->sliderOsc1Octave->setValue(osc1Node["Octave"][0].as<int>());
ui_->sliderOsc1Semitone->setResolution(configRoot["Osc1SemitoneOffset"][2].as<int>() - configRoot["Osc1SemitoneOffset"][1].as<int>()); ui_->sliderOsc1Semitone->setResolution(osc1Node["Semitone"][2].as<int>() - osc1Node["Semitone"][1].as<int>());
ui_->sliderOsc1Semitone->setRange(configRoot["Osc1SemitoneOffset"][1].as<int>(), configRoot["Osc1SemitoneOffset"][2].as<int>()); ui_->sliderOsc1Semitone->setRange(osc1Node["Semitone"][1].as<int>(), osc1Node["Semitone"][2].as<int>());
ui_->sliderOsc1Semitone->setValue(configRoot["Osc1SemitoneOffset"][0].as<int>()); ui_->sliderOsc1Semitone->setValue(osc1Node["Semitone"][0].as<int>());
ui_->sliderOsc1Pitch->setRange(configRoot["Osc1PitchOffset"][1].as<float>(), configRoot["Osc1PitchOffset"][2].as<float>()); ui_->sliderOsc1Pitch->setRange(osc1Node["Pitch"][1].as<float>(), osc1Node["Pitch"][2].as<float>());
ui_->sliderOsc1Pitch->setValue(configRoot["Osc1PitchOffset"][0].as<float>()); ui_->sliderOsc1Pitch->setValue(osc1Node["Pitch"][0].as<float>());
ui_->sliderOsc2Octave->setResolution(configRoot["Osc2OctaveOffset"][2].as<int>() - configRoot["Osc2OctaveOffset"][1].as<int>()); ui_->sliderOsc2Octave->setResolution(osc2Node["Octave"][2].as<int>() - osc2Node["Octave"][1].as<int>());
ui_->sliderOsc2Octave->setRange(configRoot["Osc2OctaveOffset"][1].as<int>(), configRoot["Osc2OctaveOffset"][2].as<int>()); ui_->sliderOsc2Octave->setRange(osc2Node["Octave"][1].as<int>(), osc2Node["Octave"][2].as<int>());
ui_->sliderOsc2Octave->setValue(configRoot["Osc2OctaveOffset"][0].as<int>()); ui_->sliderOsc2Octave->setValue(osc2Node["Octave"][0].as<int>());
ui_->sliderOsc2Semitone->setResolution(configRoot["Osc2SemitoneOffset"][2].as<int>() - configRoot["Osc2SemitoneOffset"][1].as<int>()); ui_->sliderOsc2Semitone->setResolution(osc2Node["Semitone"][2].as<int>() - osc2Node["Semitone"][1].as<int>());
ui_->sliderOsc2Semitone->setRange(configRoot["Osc2SemitoneOffset"][1].as<int>(), configRoot["Osc2SemitoneOffset"][2].as<int>()); ui_->sliderOsc2Semitone->setRange(osc2Node["Semitone"][1].as<int>(), osc2Node["Semitone"][2].as<int>());
ui_->sliderOsc2Semitone->setValue(configRoot["Osc2SemitoneOffset"][0].as<int>()); ui_->sliderOsc2Semitone->setValue(osc2Node["Semitone"][0].as<int>());
ui_->sliderOsc2Pitch->setRange(configRoot["Osc2PitchOffset"][1].as<float>(), configRoot["Osc2PitchOffset"][2].as<float>()); ui_->sliderOsc2Pitch->setRange(osc2Node["Pitch"][1].as<float>(), osc2Node["Pitch"][2].as<float>());
ui_->sliderOsc2Pitch->setValue(configRoot["Osc2PitchOffset"][0].as<float>()); ui_->sliderOsc2Pitch->setValue(osc2Node["Pitch"][0].as<float>());
ui_->sliderOsc3Octave->setResolution(configRoot["Osc3OctaveOffset"][2].as<int>() - configRoot["Osc3OctaveOffset"][1].as<int>()); ui_->sliderOsc3Octave->setResolution(osc3Node["Octave"][2].as<int>() - osc3Node["Octave"][1].as<int>());
ui_->sliderOsc3Octave->setRange(configRoot["Osc3OctaveOffset"][1].as<int>(), configRoot["Osc3OctaveOffset"][2].as<int>()); ui_->sliderOsc3Octave->setRange(osc3Node["Octave"][1].as<int>(), osc3Node["Octave"][2].as<int>());
ui_->sliderOsc3Octave->setValue(configRoot["Osc3OctaveOffset"][0].as<int>()); ui_->sliderOsc3Octave->setValue(osc3Node["Octave"][0].as<int>());
ui_->sliderOsc3Semitone->setResolution(configRoot["Osc3SemitoneOffset"][2].as<int>() - configRoot["Osc3SemitoneOffset"][1].as<int>()); ui_->sliderOsc3Semitone->setResolution(osc3Node["Semitone"][2].as<int>() - osc3Node["Semitone"][1].as<int>());
ui_->sliderOsc3Semitone->setRange(configRoot["Osc3SemitoneOffset"][1].as<int>(), configRoot["Osc3SemitoneOffset"][2].as<int>()); ui_->sliderOsc3Semitone->setRange(osc3Node["Semitone"][1].as<int>(), osc3Node["Semitone"][2].as<int>());
ui_->sliderOsc3Semitone->setValue(configRoot["Osc3SemitoneOffset"][0].as<int>()); ui_->sliderOsc3Semitone->setValue(osc3Node["Semitone"][0].as<int>());
ui_->sliderOsc3Pitch->setRange(configRoot["Osc3PitchOffset"][1].as<float>(), configRoot["Osc3PitchOffset"][2].as<float>()); ui_->sliderOsc3Pitch->setRange(osc3Node["Pitch"][1].as<float>(), osc3Node["Pitch"][2].as<float>());
ui_->sliderOsc3Pitch->setValue(configRoot["Osc3PitchOffset"][0].as<float>()); ui_->sliderOsc3Pitch->setValue(osc3Node["Pitch"][0].as<float>());
ui_->comboOsc1WaveSelector1->setCurrentIndex(configRoot["OscWaveSelector1"].as<int>()); ui_->comboOsc1WaveSelector1->setCurrentIndex(configRoot["OscWaveSelector1"].as<int>());
ui_->comboOsc1WaveSelector2->setCurrentIndex(configRoot["OscWaveSelector2"].as<int>()); ui_->comboOsc1WaveSelector2->setCurrentIndex(configRoot["OscWaveSelector2"].as<int>());