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 0000000..adf473d
Binary files /dev/null and b/config/wavetables/sigmoid.wt differ
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());