From b3fe941464844f8c7f8ff461561b3e0d70752e81 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sun, 18 Jan 2026 21:06:13 -0600 Subject: [PATCH 01/13] comments --- CMakeLists.txt | 8 ++--- scripts/build.bat | 29 ++++++------------- src/MidiController.cpp | 6 ++-- src/NoteQueue.cpp | 4 +-- src/NoteQueue.h | 5 ++-- src/synth/ScopeBuffer.cpp | 1 + src/synth/WavetableController.cpp | 2 +- .../EnvelopeGenerator/EnvelopeGenerator.cpp | 2 -- src/ui/widgets/Scope/Scope.cpp | 1 + 9 files changed, 22 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 50d0f52..de6e8cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,28 +9,23 @@ find_package(Qt6 REQUIRED COMPONENTS Widgets) if (WIN32) # windows 11 x86_64 - # Header-only target (real target, no ::) + # dont judge me i had a lot of issues with this add_library(rtaudio_headers INTERFACE) target_include_directories(rtaudio_headers INTERFACE "C:/rtaudio/include" "C:/rtaudio/include/rtAudio" ) - - # Imported binary (real target, no ::) add_library(rtaudio_binary SHARED IMPORTED) set_target_properties(rtaudio_binary PROPERTIES IMPORTED_LOCATION "C:/rtaudio/bin/rtaudio.dll" IMPORTED_IMPLIB "C:/rtaudio/lib/rtaudio.lib" ) - - # Unified interface target add_library(rtaudio INTERFACE) target_link_libraries(rtaudio INTERFACE rtaudio_headers rtaudio_binary ) - # Public alias (this is where :: belongs) add_library(RtAudio::RtAudio ALIAS rtaudio) add_library(rtmidi_headers INTERFACE) @@ -59,6 +54,7 @@ endif() qt_standard_project_setup() +# TODO: prob fix this to make it less ugly qt_add_executable(metabolus src/main.cpp src/ui/MainWindow.cpp diff --git a/scripts/build.bat b/scripts/build.bat index 15999a5..8ee5bbb 100644 --- a/scripts/build.bat +++ b/scripts/build.bat @@ -1,9 +1,7 @@ @echo off setlocal -REM ================================ -REM Configuration -REM ================================ +REM config set BUILD_DIR=build set CONFIG=Release @@ -12,22 +10,18 @@ set QT_ROOT=C:\Qt\6.10.1\msvc2022_64 set RTAUDIO_ROOT=C:\rtaudio set RTMIDI_ROOT=C:\rtmidi -REM ================================ -REM Environment setup -REM ================================ +REM setup call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" set PATH=%QT_ROOT%\bin;%PATH% -REM ================================ -REM Configure -REM ================================ - if not exist %BUILD_DIR% ( mkdir %BUILD_DIR% ) +REM configure + cmake -S . -B %BUILD_DIR% ^ -G Ninja ^ -DCMAKE_BUILD_TYPE=%CONFIG% ^ @@ -36,17 +30,12 @@ cmake -S . -B %BUILD_DIR% ^ if errorlevel 1 goto error -REM ================================ -REM Build -REM ================================ - +REM build cmake --build %BUILD_DIR% if errorlevel 1 goto error -REM ================================ -REM Deploy Qt + RtAudio -REM ================================ +REM link dlls cd %BUILD_DIR% @@ -56,14 +45,14 @@ copy "%RTAUDIO_ROOT%\bin\rtaudio.dll" . copy "%RTMIDI_ROOT%\bin\rtmidi.dll" . echo. -echo Build successful. +echo Build successful goto end :error echo. -echo Build FAILED. +echo Build failed exit /b 1 :end endlocal -pause \ No newline at end of file +pause diff --git a/src/MidiController.cpp b/src/MidiController.cpp index f1422e8..e2aee4e 100644 --- a/src/MidiController.cpp +++ b/src/MidiController.cpp @@ -9,7 +9,7 @@ MidiController::MidiController(NoteQueue& queue) : noteQueue_(queue) { midiIn_ = std::make_unique(); midiIn_->ignoreTypes(false, false, false); } catch (RtMidiError& e) { - std::cerr << "RtMidi init failed: " << e.getMessage() << std::endl; + std::cout << "RtMidi init failed: " << e.getMessage() << std::endl; } // TODO: this still doesnt work on windows } @@ -22,7 +22,7 @@ MidiController::~MidiController() { bool MidiController::openDefaultPort() { if (!midiIn_) return false; if (midiIn_->getPortCount() == 0) { - std::cerr << "No MIDI input ports available\n"; + std::cout << "No MIDI input ports available" << std::endl; return false; } return openPort(0); @@ -34,7 +34,7 @@ bool MidiController::openPort(unsigned int index) { try { midiIn_->openPort(index); midiIn_->setCallback(&MidiController::midiCallback, this); - std::cout << "Opened MIDI port: " << midiIn_->getPortName(index) << "\n"; + std::cout << "Opened MIDI port: " << midiIn_->getPortName(index) << std::endl; return true; } catch (RtMidiError& e) { std::cerr << e.getMessage() << std::endl; diff --git a/src/NoteQueue.cpp b/src/NoteQueue.cpp index 85df585..1306af7 100644 --- a/src/NoteQueue.cpp +++ b/src/NoteQueue.cpp @@ -5,7 +5,7 @@ // add event to noteQueue, called by MidiController or keyboardController bool NoteQueue::push(const NoteEvent& event) { size_t head = head_.load(std::memory_order_relaxed); - size_t next = (head + 1) % SIZE; + size_t next = (head + 1) % SYNTH_NOTE_QUEUE_SIZE; if(next == tail_.load(std::memory_order_relaxed)) return false; // full @@ -22,7 +22,7 @@ bool NoteQueue::pop(NoteEvent& event) { if(tail == head_.load(std::memory_order_acquire)) return false; // empty event = buffer_[tail]; - tail_.store((tail + 1) % SIZE, std::memory_order_release); + tail_.store((tail + 1) % SYNTH_NOTE_QUEUE_SIZE, std::memory_order_release); return true; } diff --git a/src/NoteQueue.h b/src/NoteQueue.h index 8d87e6f..811dcd8 100644 --- a/src/NoteQueue.h +++ b/src/NoteQueue.h @@ -6,6 +6,8 @@ #include #include +#define SYNTH_NOTE_QUEUE_SIZE 128 + enum class NoteEventType { NoteOn, NoteOff @@ -29,9 +31,8 @@ public: bool pop(NoteEvent& event); private: - static constexpr size_t SIZE = 128; - std::array buffer_; + std::array buffer_; std::atomic head_{ 0 }; std::atomic tail_{ 0 }; diff --git a/src/synth/ScopeBuffer.cpp b/src/synth/ScopeBuffer.cpp index e418cdb..8e9263f 100644 --- a/src/synth/ScopeBuffer.cpp +++ b/src/synth/ScopeBuffer.cpp @@ -11,6 +11,7 @@ void ScopeBuffer::push(float sample) { buffer_[w % buffer_.size()] = sample; } +// TODO: needs a mutex to prevent flickering // outputs value from the scope buffer, called by the scope widget void ScopeBuffer::read(std::vector& out) const { size_t w = writeIndex_.load(std::memory_order_relaxed); diff --git a/src/synth/WavetableController.cpp b/src/synth/WavetableController.cpp index 791d81d..4fa6c7b 100644 --- a/src/synth/WavetableController.cpp +++ b/src/synth/WavetableController.cpp @@ -9,7 +9,7 @@ WavetableController::WavetableController() { init(); - std::cout << "wavetable init" << std::endl; + //std::cout << "wavetable init" << std::endl; } diff --git a/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.cpp b/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.cpp index 21ec020..5b961c5 100644 --- a/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.cpp +++ b/src/ui/widgets/EnvelopeGenerator/EnvelopeGenerator.cpp @@ -4,8 +4,6 @@ #include -// TODO: package the rogue sliders into the envelopeGenerators with a "base" column (its what the "peak" slider in the esp synth was supposed to be) - EnvelopeGenerator::EnvelopeGenerator(QWidget* parent) : QWidget(parent), ui_(new Ui::EnvelopeGenerator) { ui_->setupUi(this); diff --git a/src/ui/widgets/Scope/Scope.cpp b/src/ui/widgets/Scope/Scope.cpp index 1b7e3f7..44b7765 100644 --- a/src/ui/widgets/Scope/Scope.cpp +++ b/src/ui/widgets/Scope/Scope.cpp @@ -2,6 +2,7 @@ #include "Scope.h" #include "ui_Scope.h" +// TODO: fix include directories because what is this #include "../../../synth/ScopeBuffer.h" #include From 6b794578ce62802729f5917fd94f991918c2cff8 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sun, 18 Jan 2026 22:07:19 -0600 Subject: [PATCH 02/13] add submodules --- .gitmodules | 9 +++++++++ README.md | 2 ++ lib/qtbase | 1 + lib/rtaudio | 1 + lib/rtmidi | 1 + 5 files changed, 14 insertions(+) create mode 100644 .gitmodules create mode 160000 lib/qtbase create mode 160000 lib/rtaudio create mode 160000 lib/rtmidi diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c180e6a --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "rtmidi"] + path = lib/rtmidi + url = https://github.com/thestk/rtmidi.git +[submodule "rtaudio"] + path = lib/rtaudio + url = https://github.com/thestk/rtaudio.git +[submodule "qtbase"] + path = lib/qtbase + url = https://github.com/qt/qtbase.git diff --git a/README.md b/README.md index dc858fe..5afeade 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,8 @@ This synthesizer isn't very good, but it's neat :3 ## setup TODO: instructions on build setup +TODO: add dependencies into lib +TODO: autobuild libs in build scripts Package Dependencies: Qt 6, RtAudio, RtMidi $ ./scripts/build.sh diff --git a/lib/qtbase b/lib/qtbase new file mode 160000 index 0000000..dcad7c6 --- /dev/null +++ b/lib/qtbase @@ -0,0 +1 @@ +Subproject commit dcad7c6d6f86bbc70ecda0aa4b450e658aa9f37a diff --git a/lib/rtaudio b/lib/rtaudio new file mode 160000 index 0000000..409636b --- /dev/null +++ b/lib/rtaudio @@ -0,0 +1 @@ +Subproject commit 409636b5dcad3054ae5a9e85014bba3861b8edab diff --git a/lib/rtmidi b/lib/rtmidi new file mode 160000 index 0000000..a3233c2 --- /dev/null +++ b/lib/rtmidi @@ -0,0 +1 @@ +Subproject commit a3233c22949342f6697681e2cf2403e27fcf0c9e From fac95b1baedaec9f316e2f0f0195308eefc6f4f5 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Mon, 19 Jan 2026 18:29:41 -0600 Subject: [PATCH 03/13] submodule checkpoint --- .gitmodules | 15 ++++--- lib/qt5 | 1 + lib/qtbase | 1 - scripts/build.bat | 58 ------------------------ scripts/build.ps1 | 77 ++++++++++++++++++++++++++++++++ scripts/install_dependencies.ps1 | 34 ++++++++++++++ scripts/install_dependencies.sh | 0 src/synth/AudioEngine.cpp | 4 +- 8 files changed, 123 insertions(+), 67 deletions(-) create mode 160000 lib/qt5 delete mode 160000 lib/qtbase delete mode 100644 scripts/build.bat create mode 100644 scripts/build.ps1 create mode 100644 scripts/install_dependencies.ps1 create mode 100644 scripts/install_dependencies.sh diff --git a/.gitmodules b/.gitmodules index c180e6a..24da897 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,10 @@ -[submodule "rtmidi"] - path = lib/rtmidi - url = https://github.com/thestk/rtmidi.git -[submodule "rtaudio"] +[submodule "lib/rtaudio"] path = lib/rtaudio url = https://github.com/thestk/rtaudio.git -[submodule "qtbase"] - path = lib/qtbase - url = https://github.com/qt/qtbase.git +[submodule "lib/rtmidi"] + path = lib/rtmidi + url = https://github.com/thestk/rtmidi.git +[submodule "lib/qt5"] + path = lib/qt5 + url = https://github.com/qt/qt5.git + branch = 6.11 diff --git a/lib/qt5 b/lib/qt5 new file mode 160000 index 0000000..f5f14e7 --- /dev/null +++ b/lib/qt5 @@ -0,0 +1 @@ +Subproject commit f5f14e7b5885256a8901dda5e95b5424774e29e7 diff --git a/lib/qtbase b/lib/qtbase deleted file mode 160000 index dcad7c6..0000000 --- a/lib/qtbase +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dcad7c6d6f86bbc70ecda0aa4b450e658aa9f37a diff --git a/scripts/build.bat b/scripts/build.bat deleted file mode 100644 index 8ee5bbb..0000000 --- a/scripts/build.bat +++ /dev/null @@ -1,58 +0,0 @@ -@echo off -setlocal - -REM config - -set BUILD_DIR=build -set CONFIG=Release - -set QT_ROOT=C:\Qt\6.10.1\msvc2022_64 -set RTAUDIO_ROOT=C:\rtaudio -set RTMIDI_ROOT=C:\rtmidi - -REM setup - -call "%ProgramFiles%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" - -set PATH=%QT_ROOT%\bin;%PATH% - -if not exist %BUILD_DIR% ( - mkdir %BUILD_DIR% -) - -REM configure - -cmake -S . -B %BUILD_DIR% ^ - -G Ninja ^ - -DCMAKE_BUILD_TYPE=%CONFIG% ^ - -DRTAUDIO_ROOT=%RTAUDIO_ROOT% ^ - -DRTMIDI_ROOT=%RTMIDI_ROOT% - -if errorlevel 1 goto error - -REM build -cmake --build %BUILD_DIR% - -if errorlevel 1 goto error - -REM link dlls - -cd %BUILD_DIR% - -windeployqt metabolus.exe - -copy "%RTAUDIO_ROOT%\bin\rtaudio.dll" . -copy "%RTMIDI_ROOT%\bin\rtmidi.dll" . - -echo. -echo Build successful -goto end - -:error -echo. -echo Build failed -exit /b 1 - -:end -endlocal -pause diff --git a/scripts/build.ps1 b/scripts/build.ps1 new file mode 100644 index 0000000..a5ef320 --- /dev/null +++ b/scripts/build.ps1 @@ -0,0 +1,77 @@ + +# config + +$BUILD_DIR = "build" +$CONFIG = "Release" + +# change these to the build libs +$QT_ROOT = "C:\Qt\6.10.1\msvc2022_64" +$RTAUDIO_ROOT = "C:\rtaudio" +$RTMIDI_ROOT = "C:\rtmidi" + +# setup + +& "$Env:Programfiles\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" + +$PATH="$QT_ROOT\bin;$PATH" + +if (-not (Test-Path -Path $BUILD_DIR)) { + mkdir $BUILD_DIR +} + +# detect dependencies + +$libraries = @("qt", "rtaudio", "rtmidi", "yaml-cpp") +$dependencies_found = 0 +foreach ($lib in $libraries) { + if (Test-Path -Path ".\build\lib\$lib") { + Write-Host "found $lib" + $dependencies_found++ + } else { + Write-Host "did not find $lib" + } +} + +if (-not ($dependencies_found -eq $libraries.Count)) { + & "scripts\install_dependencies.ps1" +} + +# configure + +<# +cmake -S . -B $BUILD_DIR ^ + -G Ninja ^ + -DCMAKE_BUILD_TYPE=$CONFIG ^ + -DRTAUDIO_ROOT=$RTAUDIO_ROOT ^ + -DRTMIDI_ROOT=$RTMIDI_ROOT + + +if errorlevel 1 goto error + +# build +cmake --build $BUILD_DIR + +if errorlevel 1 goto error + +# link dlls + +cd %BUILD_DIR% + +windeployqt metabolus.exe + +copy "%RTAUDIO_ROOT%\bin\rtaudio.dll" . +copy "%RTMIDI_ROOT%\bin\rtmidi.dll" . + +echo. +echo Build successful +goto end + +:error +echo. +echo Build failed +exit /b 1 + +:end +endlocal +pause +#> \ No newline at end of file diff --git a/scripts/install_dependencies.ps1 b/scripts/install_dependencies.ps1 new file mode 100644 index 0000000..776edd7 --- /dev/null +++ b/scripts/install_dependencies.ps1 @@ -0,0 +1,34 @@ + +echo "Installing dependencies ... " + +$project_root = $PWD +$build_lib_dir = "$PWD\build\lib" + + +# qt +mkdir "$build_lib_dir\qt" +cd $project_root\lib\qtbase + + +# rtaudio +mkdir "$build_lib_dir\rtaudio" -Force +cd $project_root\lib\rtaudio +cmake -S . -B build -DRTDUIO_API_WASAPI=ON -DRTAUDIO_API_DS=OFF -DRT_AUDIO_API_ASIO=OFF -DRTAUDIO_BUILD_SHARED_LIBS=ON +cmake --build build --config Release +cmake --install build --prefix "$build_lib_dir\rtaudio" + + +# rtmidi +mkdir "$build_lib_dir\rtmidi" -Force +cd $project_root\lib\rtmidi +cmake -S . -B build -DRT_MIDI_API_WINMM=ON -DRTMIDI_BUILD_SHARED_LIBS=ON +cmake --build build --config Release +cmake --install build --prefix "$build_lib_dir\rtmidi" + +# yaml-cpp +<# +cd $project_root\lib\qtbase + +#> + +cd $project_root diff --git a/scripts/install_dependencies.sh b/scripts/install_dependencies.sh new file mode 100644 index 0000000..e69de29 diff --git a/src/synth/AudioEngine.cpp b/src/synth/AudioEngine.cpp index fa1673b..44cab86 100644 --- a/src/synth/AudioEngine.cpp +++ b/src/synth/AudioEngine.cpp @@ -46,11 +46,13 @@ void AudioEngine::stop() { if(audio_.isStreamOpen()) audio_.closeStream(); } -int32_t AudioEngine::audioCallback( void* outputBuffer, void*, uint32_t nFrames, double, RtAudioStreamStatus status, void* userData) { +// called by RtAudio continuously, sends outputBuffer to audio drivers +int32_t AudioEngine::audioCallback(void* outputBuffer, void*, uint32_t nFrames, double, RtAudioStreamStatus status, void* userData) { // error if process is too slow for the callback. If this is consistent, then need to optimize synth.process() or whatever cascades from it if (status) std::cerr << "Stream underflow" << std::endl; + // populate audio buffer return static_cast(userData)->process(static_cast(outputBuffer), nFrames); } From 1acbbce033ef7c8ea813e44e9ddec2d8aa631ac5 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Wed, 21 Jan 2026 22:31:25 -0600 Subject: [PATCH 04/13] build scripts checkpoint --- CMakeLists.txt | 12 -------- README.md | 48 ++++++++++++++++++++++++----- scripts/build.ps1 | 53 ++++++++++++-------------------- scripts/install_dependencies.ps1 | 14 +++++++-- 4 files changed, 71 insertions(+), 56 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de6e8cc..b80acd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,18 +111,6 @@ if (WIN32) RtMidi::RtMidi ) - add_custom_command(TARGET metabolus POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "C:/rtaudio/bin/rtaudio.dll" - $ - ) - - add_custom_command(TARGET metabolus POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different - "C:/rtmidi/bin/rtmidi.dll" - $ - ) - else() target_include_directories(metabolus PRIVATE ${RTAUDIO_INCLUDE_DIRS} ${RTMIDI_INCLUDE_DIRS}) target_link_libraries(metabolus PRIVATE Qt6::Widgets ${RTAUDIO_LIBRARIES} ${RTMIDI_LIBRARIES}) diff --git a/README.md b/README.md index 5afeade..31b3bf7 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,45 @@ This synthesizer isn't very good, but it's neat :3 - [ ] Noise - [ ] LFO modulation -## setup -TODO: instructions on build setup -TODO: add dependencies into lib -TODO: autobuild libs in build scripts -Package Dependencies: Qt 6, RtAudio, RtMidi +## Build Instructions -$ ./scripts/build.sh -PS ./scripts/build.bat +Prerequisites: +CMake: https://cmake.org/download/ +Ninja: https://github.com/ninja-build/ninja/releases + +Windows: MSVC +Linux: GCC + +Clone repository +```PowerShell +git clone https://github.com/Blitblank/metabalus.git --recursive +``` +or if you forgot to --recursive: +```PowerShell +git clone https://github.com/Blitblank/metabalus.git +git submodule update --init --recursive +``` +Build. The script will build and install dependencies automatically + +On Windows (MSVC): +```PowerShell +.\scripts\build.ps1 +``` + +On Linux (GCC): +```Bash +./scripts/build.sh +``` + +Configure the CMake/build script if you have issues + +To clean: +``` +.\scripts\clean.ps1 +./scripts/clean.sh +``` +Note: dependencies are built into build/lib, so don't delete that unless you want to rebuild qt which takes forever :) +Use the install_dependencies script to manually install dependencies. + +Build troubleshooting: +On windows, `bcdedit /set IncreaseUserVa 3072` solved cc1plus.exe: out of memory errors while building qt for me diff --git a/scripts/build.ps1 b/scripts/build.ps1 index a5ef320..a424e49 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -1,13 +1,15 @@ +$PROJECT_ROOT = $PWD + # config -$BUILD_DIR = "build" +$BUILD_DIR = "$PWD/build" $CONFIG = "Release" # change these to the build libs -$QT_ROOT = "C:\Qt\6.10.1\msvc2022_64" -$RTAUDIO_ROOT = "C:\rtaudio" -$RTMIDI_ROOT = "C:\rtmidi" +$QT_ROOT = "$BUILD_DIR\lib\qt\install" +$RTAUDIO_ROOT = "$BUILD_DIR\lib\rtaudio" +$RTMIDI_ROOT = "$BUILD_DIR\lib\rtmidi" # setup @@ -21,7 +23,7 @@ if (-not (Test-Path -Path $BUILD_DIR)) { # detect dependencies -$libraries = @("qt", "rtaudio", "rtmidi", "yaml-cpp") +$libraries = @("qt", "rtaudio", "rtmidi") $dependencies_found = 0 foreach ($lib in $libraries) { if (Test-Path -Path ".\build\lib\$lib") { @@ -34,44 +36,27 @@ foreach ($lib in $libraries) { if (-not ($dependencies_found -eq $libraries.Count)) { & "scripts\install_dependencies.ps1" +} else { + Write-Host "All dependencies detected, skipping depency install step..." } # configure - -<# -cmake -S . -B $BUILD_DIR ^ - -G Ninja ^ - -DCMAKE_BUILD_TYPE=$CONFIG ^ - -DRTAUDIO_ROOT=$RTAUDIO_ROOT ^ - -DRTMIDI_ROOT=$RTMIDI_ROOT - - -if errorlevel 1 goto error +Write-Host "Configuring metabolus..." +cmake -S . -B $BUILD_DIR -G Ninja -DCMAKE_BUILD_TYPE=$CONFIG -DQt6_ROOT="$QT_ROOT\lib\cmake\Qt6" -DRTAUDIO_ROOT=$RTAUDIO_ROOT -DRTMIDI_ROOT=$RTMIDI_ROOT # build +Write-Host "Building metabolus..." cmake --build $BUILD_DIR -if errorlevel 1 goto error - # link dlls +Write-Host "Deploying metabolus..." +cd $BUILD_DIR -cd %BUILD_DIR% +& "$QT_ROOT\bin\windeployqt.exe" metabolus.exe -windeployqt metabolus.exe +Copy-Item -Path "$RTAUDIO_ROOT\bin\rtaudio.dll" -Destination . +Copy-Item -Path "$RTMIDI_ROOT\bin\rtmidi.dll" -Destination . -copy "%RTAUDIO_ROOT%\bin\rtaudio.dll" . -copy "%RTMIDI_ROOT%\bin\rtmidi.dll" . -echo. -echo Build successful -goto end - -:error -echo. -echo Build failed -exit /b 1 - -:end -endlocal -pause -#> \ No newline at end of file +# TODO: allow input of an external qt install because this one is huge +# TODO: remove unnecessary qt modules bc why is this install like 80 gb diff --git a/scripts/install_dependencies.ps1 b/scripts/install_dependencies.ps1 index 776edd7..d502a52 100644 --- a/scripts/install_dependencies.ps1 +++ b/scripts/install_dependencies.ps1 @@ -2,12 +2,20 @@ echo "Installing dependencies ... " $project_root = $PWD + +if (-not (Test-Path -Path "$PWD\build\lib")) { + mkdir "$PWD\build\lib" +} + $build_lib_dir = "$PWD\build\lib" - # qt -mkdir "$build_lib_dir\qt" -cd $project_root\lib\qtbase +mkdir "$build_lib_dir\qt" -Force +#cd $project_root\lib\qt5 +cd $build_lib_dir\qt +& "$project_root\lib\qt5\configure.bat" -prefix .\install -submodules qttools,qtbase -widgets +cmake --build . --parallel +cmake --install . # rtaudio From 6bdf9c0e1f0569a2eab3dd628fad3d68a1ac8e59 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Thu, 22 Jan 2026 22:59:28 -0600 Subject: [PATCH 05/13] fixed windows build platform --- .gitmodules | 4 --- CMakeLists.txt | 49 +++++++++----------------------- README.md | 7 +++-- lib/qt5 | 1 - scripts/build.ps1 | 18 ++++++------ scripts/install_dependencies.ps1 | 13 ++------- src/synth/Voice.cpp | 8 +++--- src/synth/WavetableController.h | 1 + src/ui/MainWindow.cpp | 3 ++ 9 files changed, 38 insertions(+), 66 deletions(-) delete mode 160000 lib/qt5 diff --git a/.gitmodules b/.gitmodules index 24da897..2708e9d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,7 +4,3 @@ [submodule "lib/rtmidi"] path = lib/rtmidi url = https://github.com/thestk/rtmidi.git -[submodule "lib/qt5"] - path = lib/qt5 - url = https://github.com/qt/qt5.git - branch = 6.11 diff --git a/CMakeLists.txt b/CMakeLists.txt index b80acd5..cb85d16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,41 +9,19 @@ find_package(Qt6 REQUIRED COMPONENTS Widgets) if (WIN32) # windows 11 x86_64 - # dont judge me i had a lot of issues with this - add_library(rtaudio_headers INTERFACE) - target_include_directories(rtaudio_headers INTERFACE - "C:/rtaudio/include" - "C:/rtaudio/include/rtAudio" + add_library(RtAudio::rtaudio SHARED IMPORTED) + set_target_properties(RtAudio::rtaudio PROPERTIES + IMPORTED_LOCATION "${RtAudio_ROOT}/bin/rtaudio.dll" + IMPORTED_IMPLIB "${RtAudio_ROOT}/lib/rtaudio.lib" + INTERFACE_INCLUDE_DIRECTORIES "${RtAudio_ROOT}/include/rtaudio" ) - add_library(rtaudio_binary SHARED IMPORTED) - set_target_properties(rtaudio_binary PROPERTIES - IMPORTED_LOCATION "C:/rtaudio/bin/rtaudio.dll" - IMPORTED_IMPLIB "C:/rtaudio/lib/rtaudio.lib" + + add_library(RtMidi::rtmidi SHARED IMPORTED) + set_target_properties(RtMidi::rtmidi PROPERTIES + IMPORTED_LOCATION "${RtMidi_ROOT}/bin/rtmidi.dll" + IMPORTED_IMPLIB "${RtMidi_ROOT}/lib/rtmidi.lib" + INTERFACE_INCLUDE_DIRECTORIES "${RtMidi_ROOT}/include/rtmidi" ) - add_library(rtaudio INTERFACE) - target_link_libraries(rtaudio INTERFACE - rtaudio_headers - rtaudio_binary - ) - - add_library(RtAudio::RtAudio ALIAS rtaudio) - - add_library(rtmidi_headers INTERFACE) - target_include_directories(rtmidi_headers INTERFACE - "C:/rtmidi/include" - "C:/rtmidi/include/rtMidi" - ) - add_library(rtmidi_binary SHARED IMPORTED) - set_target_properties(rtmidi_binary PROPERTIES - IMPORTED_LOCATION "C:/rtmidi/bin/rtmidi.dll" - IMPORTED_IMPLIB "C:/rtmidi/lib/rtmidi.lib" - ) - add_library(rtmidi INTERFACE) - target_link_libraries(rtmidi INTERFACE - rtmidi_headers - rtmidi_binary - ) - add_library(RtMidi::RtMidi ALIAS rtmidi) else() # debian 12 x86_64 @@ -97,6 +75,7 @@ qt_add_executable(metabolus ) set_target_properties(metabolus PROPERTIES AUTOUIC ON) +target_compile_options(metabolus PUBLIC "/Zc:__cplusplus") target_include_directories(metabolus PRIVATE ${CMAKE_SOURCE_DIR}/src/ui @@ -106,9 +85,9 @@ target_include_directories(metabolus PRIVATE if (WIN32) target_link_libraries(metabolus PRIVATE + RtAudio::rtaudio + RtMidi::rtmidi Qt6::Widgets - RtAudio::RtAudio - RtMidi::RtMidi ) else() diff --git a/README.md b/README.md index 31b3bf7..f9d6cc5 100644 --- a/README.md +++ b/README.md @@ -40,10 +40,10 @@ This synthesizer isn't very good, but it's neat :3 ## Build Instructions Prerequisites: -CMake: https://cmake.org/download/ -Ninja: https://github.com/ninja-build/ninja/releases +CMake: https://cmake.org/download/ \ +QtWidgets: https://www.qt.io/development/download-qt-installer-oss -Windows: MSVC +Windows: MSVC (The build scripts use Visual Studio 17 2022) Linux: GCC Clone repository @@ -55,6 +55,7 @@ or if you forgot to --recursive: git clone https://github.com/Blitblank/metabalus.git git submodule update --init --recursive ``` +\ Build. The script will build and install dependencies automatically On Windows (MSVC): diff --git a/lib/qt5 b/lib/qt5 deleted file mode 160000 index f5f14e7..0000000 --- a/lib/qt5 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f5f14e7b5885256a8901dda5e95b5424774e29e7 diff --git a/scripts/build.ps1 b/scripts/build.ps1 index a424e49..c219c3f 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -6,8 +6,8 @@ $PROJECT_ROOT = $PWD $BUILD_DIR = "$PWD/build" $CONFIG = "Release" -# change these to the build libs -$QT_ROOT = "$BUILD_DIR\lib\qt\install" +# change these to your need +$QT_ROOT = "C:\Qt\6.10.1\msvc2022_64" $RTAUDIO_ROOT = "$BUILD_DIR\lib\rtaudio" $RTMIDI_ROOT = "$BUILD_DIR\lib\rtmidi" @@ -23,7 +23,7 @@ if (-not (Test-Path -Path $BUILD_DIR)) { # detect dependencies -$libraries = @("qt", "rtaudio", "rtmidi") +$libraries = @("rtaudio", "rtmidi") $dependencies_found = 0 foreach ($lib in $libraries) { if (Test-Path -Path ".\build\lib\$lib") { @@ -37,12 +37,12 @@ foreach ($lib in $libraries) { if (-not ($dependencies_found -eq $libraries.Count)) { & "scripts\install_dependencies.ps1" } else { - Write-Host "All dependencies detected, skipping depency install step..." + Write-Host "All dependencies detected, skipping dependency install step..." } # configure Write-Host "Configuring metabolus..." -cmake -S . -B $BUILD_DIR -G Ninja -DCMAKE_BUILD_TYPE=$CONFIG -DQt6_ROOT="$QT_ROOT\lib\cmake\Qt6" -DRTAUDIO_ROOT=$RTAUDIO_ROOT -DRTMIDI_ROOT=$RTMIDI_ROOT +cmake -S . -B $BUILD_DIR -G "Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DQt6_ROOT="$QT_ROOT\lib\cmake\Qt6" -DRtAudio_ROOT="$RTAUDIO_ROOT" -DRtMidi_ROOT="$RTMIDI_ROOT" # build Write-Host "Building metabolus..." @@ -52,11 +52,13 @@ cmake --build $BUILD_DIR Write-Host "Deploying metabolus..." cd $BUILD_DIR -& "$QT_ROOT\bin\windeployqt.exe" metabolus.exe +& "$QT_ROOT\bin\windeployqt6.exe" .\Debug\metabolus.exe -Copy-Item -Path "$RTAUDIO_ROOT\bin\rtaudio.dll" -Destination . -Copy-Item -Path "$RTMIDI_ROOT\bin\rtmidi.dll" -Destination . +Copy-Item -Path "$RTAUDIO_ROOT\bin\rtaudio.dll" -Destination .\Debug +Copy-Item -Path "$RTMIDI_ROOT\bin\rtmidi.dll" -Destination .\Debug # TODO: allow input of an external qt install because this one is huge # TODO: remove unnecessary qt modules bc why is this install like 80 gb + +cd $PROJECT_ROOT diff --git a/scripts/install_dependencies.ps1 b/scripts/install_dependencies.ps1 index d502a52..1220906 100644 --- a/scripts/install_dependencies.ps1 +++ b/scripts/install_dependencies.ps1 @@ -9,19 +9,10 @@ if (-not (Test-Path -Path "$PWD\build\lib")) { $build_lib_dir = "$PWD\build\lib" -# qt -mkdir "$build_lib_dir\qt" -Force -#cd $project_root\lib\qt5 -cd $build_lib_dir\qt -& "$project_root\lib\qt5\configure.bat" -prefix .\install -submodules qttools,qtbase -widgets -cmake --build . --parallel -cmake --install . - - # rtaudio mkdir "$build_lib_dir\rtaudio" -Force cd $project_root\lib\rtaudio -cmake -S . -B build -DRTDUIO_API_WASAPI=ON -DRTAUDIO_API_DS=OFF -DRT_AUDIO_API_ASIO=OFF -DRTAUDIO_BUILD_SHARED_LIBS=ON +cmake -S . -B build -G "Visual Studio 17 2022" -DRTDUIO_API_WASAPI=ON -DRTAUDIO_API_DS=OFF -DRT_AUDIO_API_ASIO=OFF -DRTAUDIO_BUILD_SHARED_LIBS=ON cmake --build build --config Release cmake --install build --prefix "$build_lib_dir\rtaudio" @@ -29,7 +20,7 @@ cmake --install build --prefix "$build_lib_dir\rtaudio" # rtmidi mkdir "$build_lib_dir\rtmidi" -Force cd $project_root\lib\rtmidi -cmake -S . -B build -DRT_MIDI_API_WINMM=ON -DRTMIDI_BUILD_SHARED_LIBS=ON +cmake -S . -B build -G "Visual Studio 17 2022" -DRT_MIDI_API_WINMM=ON -DRTMIDI_BUILD_SHARED_LIBS=ON cmake --build build --config Release cmake --install build --prefix "$build_lib_dir\rtmidi" diff --git a/src/synth/Voice.cpp b/src/synth/Voice.cpp index 11b9ba2..c7a03a1 100644 --- a/src/synth/Voice.cpp +++ b/src/synth/Voice.cpp @@ -98,7 +98,7 @@ float Voice::process(float* params, bool& scopeTrigger) { float osc3 = oscillators_[2].process(osc3NoteOffset + note_, getParam(ParamId::Osc3PitchOffset)/100.0f, temp); // mix oscillators - float sampleOut = (osc1 + osc2*0.5f + osc3*0.25f) * gain; + float sampleOut = (osc1 + osc2*0.25f + osc3*0.125f) * gain; // filter sample float baseFreq = oscillators_[0].frequency(); @@ -106,8 +106,8 @@ float Voice::process(float* params, bool& scopeTrigger) { float resonance = resonanceEnv * getParam(ParamId::FilterResonanceDepth) * velocityGain; filter1_.setParams(Filter::Type::BiquadLowpass, cutoffFreq, resonance); filter2_.setParams(Filter::Type::BiquadLowpass, cutoffFreq, resonance); - sampleOut = filter1_.biquadProcess(sampleOut); - sampleOut = filter2_.biquadProcess(sampleOut); + float filteredSample = filter1_.biquadProcess(sampleOut); + // sampleOut = filter2_.biquadProcess(sampleOut); // TODO: for some reason second filter is unstable only on windows 🤷 - return sampleOut; + return filteredSample; } \ No newline at end of file diff --git a/src/synth/WavetableController.h b/src/synth/WavetableController.h index f03d835..83c7a1c 100644 --- a/src/synth/WavetableController.h +++ b/src/synth/WavetableController.h @@ -3,6 +3,7 @@ #include #include +#include #define SYNTH_WAVETABLE_SIZE 2048 #ifndef M_PI // I hate my stupid chungus life diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 185d7c4..36d8f41 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -52,7 +52,10 @@ MainWindow::MainWindow(QWidget *parent) : audio_->start(); // midi +#ifndef _WIN32 midi_.openPort(1); // TODO: error check +#endif + } MainWindow::~MainWindow() { From 2a1ea708a1e281e5b11cddf892feb2adc4d63b3c Mon Sep 17 00:00:00 2001 From: Blitblank Date: Thu, 22 Jan 2026 23:02:52 -0600 Subject: [PATCH 06/13] linux build fixes --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb85d16..6c4c32a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,6 @@ qt_add_executable(metabolus ) set_target_properties(metabolus PROPERTIES AUTOUIC ON) -target_compile_options(metabolus PUBLIC "/Zc:__cplusplus") target_include_directories(metabolus PRIVATE ${CMAKE_SOURCE_DIR}/src/ui @@ -83,6 +82,9 @@ target_include_directories(metabolus PRIVATE ) if (WIN32) + + target_compile_options(metabolus PUBLIC "/Zc:__cplusplus") + target_link_libraries(metabolus PRIVATE RtAudio::rtaudio From 9ca60ced76295a17428432f41f3684e554272d50 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Fri, 23 Jan 2026 21:53:03 -0600 Subject: [PATCH 07/13] added a spinlock on the scope --- README.md | 4 ++-- src/synth/ScopeBuffer.cpp | 3 +++ src/synth/ScopeBuffer.h | 3 +++ src/synth/Synth.cpp | 6 ++++++ src/synth/Voice.h | 1 + src/ui/widgets/Scope/Scope.cpp | 2 +- src/ui/widgets/Scope/Scope.h | 1 + 7 files changed, 17 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f9d6cc5..c136bdb 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ This synthesizer isn't very good, but it's neat :3 responds to note-on/note-off events - [x] Make midi/keyboard control cross-platform. Use case will mostly be Midi -> linux and Keyboard -> windows though -- [ ] Create oscillator class where the actual tone generation occurs. Multiple +- [x] Create oscillator class where the actual tone generation occurs. Multiple oscillators increase the sound complexity considerably - [x] Create a UI scope to visualize the synthesized composite waveform - [ ] Create wavetables for more complex tone generation. Needs to be selectable from ui @@ -60,7 +60,7 @@ Build. The script will build and install dependencies automatically On Windows (MSVC): ```PowerShell -.\scripts\build.ps1 +.\scripts\build.ps1 # builds in build/Debug/ ``` On Linux (GCC): diff --git a/src/synth/ScopeBuffer.cpp b/src/synth/ScopeBuffer.cpp index 8e9263f..d94d452 100644 --- a/src/synth/ScopeBuffer.cpp +++ b/src/synth/ScopeBuffer.cpp @@ -14,6 +14,9 @@ void ScopeBuffer::push(float sample) { // TODO: needs a mutex to prevent flickering // outputs value from the scope buffer, called by the scope widget void ScopeBuffer::read(std::vector& out) const { + + while(!spinLock_) { int x = 1 + 1; } + size_t w = writeIndex_.load(std::memory_order_relaxed); for (size_t i = 0; i < out.size(); i++) { size_t idx = (w + trigger_ + i * wavelength_ / out.size()) % buffer_.size(); diff --git a/src/synth/ScopeBuffer.h b/src/synth/ScopeBuffer.h index 571b9f5..9ddae93 100644 --- a/src/synth/ScopeBuffer.h +++ b/src/synth/ScopeBuffer.h @@ -21,6 +21,7 @@ public: void setWavelength(int32_t wavelength) { wavelength_ = wavelength; } int32_t trigger() { return trigger_; } int32_t wavelength() { return wavelength_; } + void spinlock(bool lock) { spinLock_ = lock; }; // NOTE: there are limits to the wavelengths that the scope can show cleanly due to the size of the audio buffer // at a buffer size of 256 at 44100hz the min visible steady frequency is ~172hz @@ -33,4 +34,6 @@ private: int32_t trigger_ = 0; // units in array indices int32_t wavelength_ = 400; + bool spinLock_ = false; + }; diff --git a/src/synth/Synth.cpp b/src/synth/Synth.cpp index 0e10cb6..b9b5929 100644 --- a/src/synth/Synth.cpp +++ b/src/synth/Synth.cpp @@ -92,6 +92,9 @@ void Synth::process(float* out, uint32_t nFrames, uint32_t sampleRate) { } } + // lock the scope from the buffer + scope_->spinlock(true); + for (uint32_t i = 0; i < nFrames; i++) { // updates internal buffered parameters for smoothing @@ -133,4 +136,7 @@ void Synth::process(float* out, uint32_t nFrames, uint32_t sampleRate) { } } + // unlock the scope from the buffer + scope_->spinlock(false); + } \ No newline at end of file diff --git a/src/synth/Voice.h b/src/synth/Voice.h index fa65795..79f12e5 100644 --- a/src/synth/Voice.h +++ b/src/synth/Voice.h @@ -63,6 +63,7 @@ private: // filters Filter filter1_; Filter filter2_; + // TODO: I think the filter's state being uninitialized is what's causing popping when a voice starts for the first time // paramstore pointer SmoothedParam* params_; diff --git a/src/ui/widgets/Scope/Scope.cpp b/src/ui/widgets/Scope/Scope.cpp index 44b7765..1769903 100644 --- a/src/ui/widgets/Scope/Scope.cpp +++ b/src/ui/widgets/Scope/Scope.cpp @@ -24,7 +24,7 @@ void Scope::setScopeBuffer(ScopeBuffer* buffer) { } void Scope::paintEvent(QPaintEvent*) { - if (!buffer_) return; + if(!buffer_) return; int32_t wavelength = buffer_->wavelength(); int32_t trigger = buffer_->trigger(); diff --git a/src/ui/widgets/Scope/Scope.h b/src/ui/widgets/Scope/Scope.h index 0689df9..0b6ad7e 100644 --- a/src/ui/widgets/Scope/Scope.h +++ b/src/ui/widgets/Scope/Scope.h @@ -32,4 +32,5 @@ private: ScopeBuffer* buffer_ = nullptr; std::vector samples_; QTimer timer_; + }; From 60ba371a0545f5af8151811e59f3f3ea553b1098 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Fri, 23 Jan 2026 22:18:47 -0600 Subject: [PATCH 08/13] add yaml-cpp --- .gitmodules | 3 +++ CMakeLists.txt | 15 +++++++++++++-- lib/yaml-cpp | 1 + scripts/build.ps1 | 13 +++++++++++-- scripts/install_dependencies.ps1 | 10 +++++----- src/ConfigInterface.cpp | 8 ++++++++ src/ConfigInterface.h | 13 +++++++++++++ src/main.cpp | 5 ++++- 8 files changed, 58 insertions(+), 10 deletions(-) create mode 160000 lib/yaml-cpp create mode 100644 src/ConfigInterface.cpp create mode 100644 src/ConfigInterface.h diff --git a/.gitmodules b/.gitmodules index 2708e9d..f070881 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/rtmidi"] path = lib/rtmidi url = https://github.com/thestk/rtmidi.git +[submodule "lib/yaml-cpp"] + path = lib/yaml-cpp + url = https://github.com/jbeder/yaml-cpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c4c32a..a4ed177 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,16 +23,24 @@ if (WIN32) # windows 11 x86_64 INTERFACE_INCLUDE_DIRECTORIES "${RtMidi_ROOT}/include/rtmidi" ) + add_library(yaml-cpp SHARED IMPORTED) + set_target_properties(yaml-cpp PROPERTIES + IMPORTED_LOCATION "${yaml-cpp_ROOT}/bin/yaml-cpp.dll" + IMPORTED_IMPLIB "${yaml-cpp_ROOT}/lib/yaml-cpp.lib" + INTERFACE_INCLUDE_DIRECTORIES "${yaml-cpp_ROOT}/include" + ) else() # debian 12 x86_64 find_package(PkgConfig REQUIRED) pkg_check_modules(RTAUDIO REQUIRED rtaudio) pkg_check_modules(RTMIDI REQUIRED rtmidi) + pkg_check_modules(YAMLCPP REQUIRED yaml-cpp) endif() qt_standard_project_setup() # TODO: prob fix this to make it less ugly +# might nest CMakeList.txt files once I clean up the directory structure qt_add_executable(metabolus src/main.cpp src/ui/MainWindow.cpp @@ -46,6 +54,8 @@ qt_add_executable(metabolus src/MidiController.h src/NoteQueue.cpp src/NoteQueue.h + src/ConfigInterface.cpp + src/ConfigInterface.h src/synth/AudioEngine.cpp src/synth/AudioEngine.h src/synth/Envelope.cpp @@ -89,11 +99,12 @@ if (WIN32) PRIVATE RtAudio::rtaudio RtMidi::rtmidi + yaml-cpp Qt6::Widgets ) else() - target_include_directories(metabolus PRIVATE ${RTAUDIO_INCLUDE_DIRS} ${RTMIDI_INCLUDE_DIRS}) - target_link_libraries(metabolus PRIVATE Qt6::Widgets ${RTAUDIO_LIBRARIES} ${RTMIDI_LIBRARIES}) + target_include_directories(metabolus PRIVATE ${RTAUDIO_INCLUDE_DIRS} ${RTMIDI_INCLUDE_DIRS} ${YAMLCPP_INCLUDE_DIRS}) + target_link_libraries(metabolus PRIVATE Qt6::Widgets ${RTAUDIO_LIBRARIES} ${RTMIDI_LIBRARIES} ${YAMLCPP_LIBARIES}) target_compile_options(metabolus PRIVATE ${RTAUDIO_CFLAGS_OTHER}) endif() diff --git a/lib/yaml-cpp b/lib/yaml-cpp new file mode 160000 index 0000000..89ff142 --- /dev/null +++ b/lib/yaml-cpp @@ -0,0 +1 @@ +Subproject commit 89ff142b991af432b5d7a7cee55282f082a7e629 diff --git a/scripts/build.ps1 b/scripts/build.ps1 index c219c3f..6122cef 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -7,9 +7,11 @@ $BUILD_DIR = "$PWD/build" $CONFIG = "Release" # change these to your need +# or TODO: make qt_root configurable $QT_ROOT = "C:\Qt\6.10.1\msvc2022_64" $RTAUDIO_ROOT = "$BUILD_DIR\lib\rtaudio" $RTMIDI_ROOT = "$BUILD_DIR\lib\rtmidi" +$YAMLCPP_ROOT = "$BUILD_DIR\lib\yaml-cpp" # setup @@ -23,7 +25,7 @@ if (-not (Test-Path -Path $BUILD_DIR)) { # detect dependencies -$libraries = @("rtaudio", "rtmidi") +$libraries = @("rtaudio", "rtmidi", "yaml-cpp") $dependencies_found = 0 foreach ($lib in $libraries) { if (Test-Path -Path ".\build\lib\$lib") { @@ -42,7 +44,13 @@ if (-not ($dependencies_found -eq $libraries.Count)) { # configure Write-Host "Configuring metabolus..." -cmake -S . -B $BUILD_DIR -G "Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=Release -DQt6_ROOT="$QT_ROOT\lib\cmake\Qt6" -DRtAudio_ROOT="$RTAUDIO_ROOT" -DRtMidi_ROOT="$RTMIDI_ROOT" +cmake -S . -B $BUILD_DIR -G "Visual Studio 17 2022" ` + -DCMAKE_BUILD_TYPE=Release ` + -DQt6_ROOT="$QT_ROOT\lib\cmake\Qt6" ` + -DRtAudio_ROOT="$RTAUDIO_ROOT" ` + -DRtMidi_ROOT="$RTMIDI_ROOT" ` + -Dyaml-cpp_ROOT="$YAMLCPP_ROOT" ` + # build Write-Host "Building metabolus..." @@ -56,6 +64,7 @@ cd $BUILD_DIR Copy-Item -Path "$RTAUDIO_ROOT\bin\rtaudio.dll" -Destination .\Debug Copy-Item -Path "$RTMIDI_ROOT\bin\rtmidi.dll" -Destination .\Debug +Copy-Item -Path "$YAMLCPP_ROOT\bin\yaml-cpp.dll" -Destination .\Debug # TODO: allow input of an external qt install because this one is huge diff --git a/scripts/install_dependencies.ps1 b/scripts/install_dependencies.ps1 index 1220906..c885611 100644 --- a/scripts/install_dependencies.ps1 +++ b/scripts/install_dependencies.ps1 @@ -16,7 +16,6 @@ cmake -S . -B build -G "Visual Studio 17 2022" -DRTDUIO_API_WASAPI=ON -DRTAUDIO_ cmake --build build --config Release cmake --install build --prefix "$build_lib_dir\rtaudio" - # rtmidi mkdir "$build_lib_dir\rtmidi" -Force cd $project_root\lib\rtmidi @@ -25,9 +24,10 @@ cmake --build build --config Release cmake --install build --prefix "$build_lib_dir\rtmidi" # yaml-cpp -<# -cd $project_root\lib\qtbase - -#> +mkdir "$build_lib_dir\yaml-cpp" -Force +cd $project_root\lib\yaml-cpp +cmake -S . -B build -G "Visual Studio 17 2022" -DYAML_BUILD_SHARED_LIBS=ON +cmake --build build --config Release +cmake --install build --prefix "$build_lib_dir\yaml-cpp" cd $project_root diff --git a/src/ConfigInterface.cpp b/src/ConfigInterface.cpp new file mode 100644 index 0000000..c253237 --- /dev/null +++ b/src/ConfigInterface.cpp @@ -0,0 +1,8 @@ + +#include "ConfigInterface.h" + +#include + +ConfigInterface::ConfigInterface() { + std::cout << "Config constructor" << std::endl; +} diff --git a/src/ConfigInterface.h b/src/ConfigInterface.h new file mode 100644 index 0000000..162f82f --- /dev/null +++ b/src/ConfigInterface.h @@ -0,0 +1,13 @@ + +#pragma once + +class ConfigInterface { + +public: + + ConfigInterface(); + ~ConfigInterface() = default; + +private: + +}; diff --git a/src/main.cpp b/src/main.cpp index 92ca2a1..d12d7fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,7 @@ #include #include "ui/MainWindow.h" +#include "ConfigInterface.h" #include @@ -9,12 +10,14 @@ int main(int argc, char *argv[]) { // std::cout << "Main()" << std::endl; + ConfigInterface config = ConfigInterface(); + QApplication app(argc, argv); MainWindow window; // entry point goes to MainWindow::MainWindow() window.show(); - int status = app.exec(); // assembles ui + int status = app.exec(); // app execution; blocks until window close return status; } From a4ea6490a4a164153f2566f716bcf4ecda297bbc Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sat, 24 Jan 2026 02:23:42 -0600 Subject: [PATCH 09/13] yaml testing (not good) :( --- README.md | 12 +++++++++- config/audio.yaml | 16 ++++++++++++++ config/profiles/default.yaml | 0 scripts/build.ps1 | 10 ++++++--- src/ConfigInterface.cpp | 43 +++++++++++++++++++++++++++++++++++- src/ConfigInterface.h | 5 +++++ 6 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 config/audio.yaml create mode 100644 config/profiles/default.yaml diff --git a/README.md b/README.md index c136bdb..2c9e266 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ On Linux (GCC): ./scripts/build.sh ``` +TODO: right now you need to run the executable from the executable's directory because of CWD paths and such. Needs to be fixed because annoying + Configure the CMake/build script if you have issues To clean: @@ -75,8 +77,16 @@ To clean: .\scripts\clean.ps1 ./scripts/clean.sh ``` -Note: dependencies are built into build/lib, so don't delete that unless you want to rebuild qt which takes forever :) +Note: dependencies are built into build/lib, so don't delete unless you need to rebuild the libraries Use the install_dependencies script to manually install dependencies. Build troubleshooting: On windows, `bcdedit /set IncreaseUserVa 3072` solved cc1plus.exe: out of memory errors while building qt for me + +## Configurations (NOT YET IMPLEMENTED) +Default config files are located in the config/ directory, and they are replicated into build/config/ if they dont already exist there. To edit the configurations, edit the config files in the build directory, not the defaults. Most config files are loaded/parsed at startup (TODO: investigate some reloading functions), so the program must be restarted, although not recompiled, for new configs to take effect. \ +Voice profiles are saved into config files into a human-readable format (YAML) and can be edited manually or by saving within the app. \ + +## Wavetables (NOT YET IMPLEMENTED) +Wavetables are this synthesizer's starting point for audio synthesis. A wavetable (as defined for this synthesizer, not elsewhere) contains a single period of a particular wave-shape with a discrete number of samples. Wavetables are loaded at runtime and sampled by oscillator objects to define and mix different wave shapes. Further specifications, as well as instructions for generating your own wavetable (including an example python script << TODO), are located within config/wavetables/README.md + diff --git a/config/audio.yaml b/config/audio.yaml new file mode 100644 index 0000000..3e356bd --- /dev/null +++ b/config/audio.yaml @@ -0,0 +1,16 @@ + +# audio.yaml +# Configures properties for the RtAudio engine + +# Number of samples per second +sampleRate: 44100 +# unconfigurable: sampleFormat; [-1, 1] float + +# number of audio channels +channels: 2 + +# 0 = mono, 1 = stereo, 2 = pseudo-stereo +stereoMode: 2 + +# number of samples per audio buffer +bufferSize: 512 diff --git a/config/profiles/default.yaml b/config/profiles/default.yaml new file mode 100644 index 0000000..e69de29 diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 6122cef..74408b3 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -13,6 +13,8 @@ $RTAUDIO_ROOT = "$BUILD_DIR\lib\rtaudio" $RTMIDI_ROOT = "$BUILD_DIR\lib\rtmidi" $YAMLCPP_ROOT = "$BUILD_DIR\lib\yaml-cpp" +$CONFIG_ROOT = "$PROJECT_ROOT\config" + # setup & "$Env:Programfiles\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvars64.bat" @@ -56,18 +58,20 @@ cmake -S . -B $BUILD_DIR -G "Visual Studio 17 2022" ` Write-Host "Building metabolus..." cmake --build $BUILD_DIR +# TODO: install + # link dlls Write-Host "Deploying metabolus..." cd $BUILD_DIR & "$QT_ROOT\bin\windeployqt6.exe" .\Debug\metabolus.exe +# copy dlls Copy-Item -Path "$RTAUDIO_ROOT\bin\rtaudio.dll" -Destination .\Debug Copy-Item -Path "$RTMIDI_ROOT\bin\rtmidi.dll" -Destination .\Debug Copy-Item -Path "$YAMLCPP_ROOT\bin\yaml-cpp.dll" -Destination .\Debug - -# TODO: allow input of an external qt install because this one is huge -# TODO: remove unnecessary qt modules bc why is this install like 80 gb +# copy configs, but don't overwrite +Copy-Item -Path "$CONFIG_ROOT" -Destination ".\Debug\" -Recurse -ErrorAction SilentlyContinue cd $PROJECT_ROOT diff --git a/src/ConfigInterface.cpp b/src/ConfigInterface.cpp index c253237..22e6451 100644 --- a/src/ConfigInterface.cpp +++ b/src/ConfigInterface.cpp @@ -1,8 +1,49 @@ #include "ConfigInterface.h" +#include "yaml-cpp/yaml.h" + #include +#include +#include + +namespace fs = std::filesystem; ConfigInterface::ConfigInterface() { - std::cout << "Config constructor" << std::endl; + + std::string audioConfig = configRoot + "/" + filename; + + std::cout << "CWD: " << fs::current_path() << std::endl; + std::cout << "Audio config: " << fs::absolute(audioConfig).string() << std::endl; + + // opening the file myself because YAML::LoadFile didn't seem to work on windows + std::ifstream file(fs::absolute(audioConfig).string()); + if(file.good()) { + std::cout << "File exists" << std::endl; + } else { + std::cout << "File does not exist" << std::endl; + } + + //YAML::Node config; + /* + try { + YAML::Node config = YAML::Load("{sampleRate: 44100}"); + std::cout << "Loaded config file" << std::endl; + + std::cout << "Type enum: " << static_cast(config.Type()) << std::endl; + + if(config["sampleRate"]) { + //int sampleRate = config["sampleRate"].as(); + //std::cout << sampleRate << std::endl; + } else { + std::cout << "Key does not exist" << std::endl; + } + } catch(const std::exception& e) { + std::cerr << e.what() << '\n'; + } + */ + + YAML::Node n = YAML::Load("{sampleRate: 44100}"); + std::cout << n["sampleRate"].as() << "\n"; + } diff --git a/src/ConfigInterface.h b/src/ConfigInterface.h index 162f82f..82da440 100644 --- a/src/ConfigInterface.h +++ b/src/ConfigInterface.h @@ -1,6 +1,8 @@ #pragma once +#include + class ConfigInterface { public: @@ -10,4 +12,7 @@ public: private: + const std::string configRoot = "config"; + const std::string filename = "audio.yaml"; + }; From 69a507d57b64eed6eed9a3e00c7a272939de8a6a Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sat, 24 Jan 2026 14:07:38 -0600 Subject: [PATCH 10/13] make basic audio engine parameters configurable --- scripts/build.ps1 | 13 +++---- scripts/install_dependencies.ps1 | 15 ++++++-- src/ConfigInterface.cpp | 63 +++++++++++++++----------------- src/ConfigInterface.h | 14 ++++++- src/main.cpp | 4 +- src/synth/AudioEngine.cpp | 12 ++++-- src/synth/AudioEngine.h | 4 +- src/synth/ScopeBuffer.cpp | 5 ++- src/ui/MainWindow.cpp | 2 +- src/ui/MainWindow.h | 2 + 10 files changed, 78 insertions(+), 56 deletions(-) diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 74408b3..aa98e78 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -47,7 +47,6 @@ if (-not ($dependencies_found -eq $libraries.Count)) { # configure Write-Host "Configuring metabolus..." cmake -S . -B $BUILD_DIR -G "Visual Studio 17 2022" ` - -DCMAKE_BUILD_TYPE=Release ` -DQt6_ROOT="$QT_ROOT\lib\cmake\Qt6" ` -DRtAudio_ROOT="$RTAUDIO_ROOT" ` -DRtMidi_ROOT="$RTMIDI_ROOT" ` @@ -56,7 +55,7 @@ cmake -S . -B $BUILD_DIR -G "Visual Studio 17 2022" ` # build Write-Host "Building metabolus..." -cmake --build $BUILD_DIR +cmake --build $BUILD_DIR --config $CONFIG # TODO: install @@ -64,14 +63,14 @@ cmake --build $BUILD_DIR Write-Host "Deploying metabolus..." cd $BUILD_DIR -& "$QT_ROOT\bin\windeployqt6.exe" .\Debug\metabolus.exe +& "$QT_ROOT\bin\windeployqt6.exe" .\$CONFIG\metabolus.exe # copy dlls -Copy-Item -Path "$RTAUDIO_ROOT\bin\rtaudio.dll" -Destination .\Debug -Copy-Item -Path "$RTMIDI_ROOT\bin\rtmidi.dll" -Destination .\Debug -Copy-Item -Path "$YAMLCPP_ROOT\bin\yaml-cpp.dll" -Destination .\Debug +Copy-Item -Path "$RTAUDIO_ROOT\bin\rtaudio.dll" -Destination .\$CONFIG +Copy-Item -Path "$RTMIDI_ROOT\bin\rtmidi.dll" -Destination .\$CONFIG +Copy-Item -Path "$YAMLCPP_ROOT\bin\yaml-cpp.dll" -Destination .\$CONFIG # copy configs, but don't overwrite -Copy-Item -Path "$CONFIG_ROOT" -Destination ".\Debug\" -Recurse -ErrorAction SilentlyContinue +Copy-Item -Path "$CONFIG_ROOT" -Destination ".\$CONFIG\" -Recurse -ErrorAction SilentlyContinue cd $PROJECT_ROOT diff --git a/scripts/install_dependencies.ps1 b/scripts/install_dependencies.ps1 index c885611..1e6478b 100644 --- a/scripts/install_dependencies.ps1 +++ b/scripts/install_dependencies.ps1 @@ -1,6 +1,8 @@ echo "Installing dependencies ... " +# TODO: add a clean (like delete build dirs) script + $project_root = $PWD if (-not (Test-Path -Path "$PWD\build\lib")) { @@ -12,21 +14,28 @@ $build_lib_dir = "$PWD\build\lib" # rtaudio mkdir "$build_lib_dir\rtaudio" -Force cd $project_root\lib\rtaudio -cmake -S . -B build -G "Visual Studio 17 2022" -DRTDUIO_API_WASAPI=ON -DRTAUDIO_API_DS=OFF -DRT_AUDIO_API_ASIO=OFF -DRTAUDIO_BUILD_SHARED_LIBS=ON +cmake -S . -B build -G "Visual Studio 17 2022" ` + -DRTDUIO_API_WASAPI=ON ` + -DRTAUDIO_API_DS=OFF ` + -DRT_AUDIO_API_ASIO=OFF ` + -DRTAUDIO_BUILD_SHARED_LIBS=ON cmake --build build --config Release cmake --install build --prefix "$build_lib_dir\rtaudio" # rtmidi mkdir "$build_lib_dir\rtmidi" -Force cd $project_root\lib\rtmidi -cmake -S . -B build -G "Visual Studio 17 2022" -DRT_MIDI_API_WINMM=ON -DRTMIDI_BUILD_SHARED_LIBS=ON +cmake -S . -B build -G "Visual Studio 17 2022" ` + -DRT_MIDI_API_WINMM=ON ` + -DRTMIDI_BUILD_SHARED_LIBS=ON cmake --build build --config Release cmake --install build --prefix "$build_lib_dir\rtmidi" # yaml-cpp mkdir "$build_lib_dir\yaml-cpp" -Force cd $project_root\lib\yaml-cpp -cmake -S . -B build -G "Visual Studio 17 2022" -DYAML_BUILD_SHARED_LIBS=ON +cmake -S . -B build -G "Visual Studio 17 2022" ` + -DYAML_BUILD_SHARED_LIBS=ON cmake --build build --config Release cmake --install build --prefix "$build_lib_dir\yaml-cpp" diff --git a/src/ConfigInterface.cpp b/src/ConfigInterface.cpp index 22e6451..4c209ef 100644 --- a/src/ConfigInterface.cpp +++ b/src/ConfigInterface.cpp @@ -11,39 +11,34 @@ namespace fs = std::filesystem; ConfigInterface::ConfigInterface() { - std::string audioConfig = configRoot + "/" + filename; - - std::cout << "CWD: " << fs::current_path() << std::endl; - std::cout << "Audio config: " << fs::absolute(audioConfig).string() << std::endl; - - // opening the file myself because YAML::LoadFile didn't seem to work on windows - std::ifstream file(fs::absolute(audioConfig).string()); - if(file.good()) { - std::cout << "File exists" << std::endl; - } else { - std::cout << "File does not exist" << std::endl; - } - - //YAML::Node config; - /* - try { - YAML::Node config = YAML::Load("{sampleRate: 44100}"); - std::cout << "Loaded config file" << std::endl; - - std::cout << "Type enum: " << static_cast(config.Type()) << std::endl; - - if(config["sampleRate"]) { - //int sampleRate = config["sampleRate"].as(); - //std::cout << sampleRate << std::endl; - } else { - std::cout << "Key does not exist" << std::endl; - } - } catch(const std::exception& e) { - std::cerr << e.what() << '\n'; - } - */ - - YAML::Node n = YAML::Load("{sampleRate: 44100}"); - std::cout << n["sampleRate"].as() << "\n"; + //std::cout << "Config constructor" << std::endl; + +} + +int ConfigInterface::getValue(ConfigFile file, std::string key, int defaultVal) { + + // assemble filepath + std::string filepath = configRoot + "/" + filePaths[static_cast(file)]; + filepath = fs::absolute(filepath).string(); + + // attempt to open file + YAML::Node config; + try { + YAML::Node config = YAML::LoadFile(filepath); + + // read key if it exists + if(config[key]) { + return config[key].as(defaultVal); + } else { + return -1; // key does not exist + } + + } catch(const std::exception& e) { + std::cerr << e.what() << std::endl; + return -1; + } + + // unreachable + return -1; } diff --git a/src/ConfigInterface.h b/src/ConfigInterface.h index 82da440..6bd15bd 100644 --- a/src/ConfigInterface.h +++ b/src/ConfigInterface.h @@ -2,6 +2,17 @@ #pragma once #include +#include + +enum class ConfigFile { + Audio = 0 + // other files here +}; + +// might have a config file for specifying paths to other config files instead of this +const std::vector filePaths = { + "audio.yaml" +}; class ConfigInterface { @@ -10,9 +21,10 @@ public: ConfigInterface(); ~ConfigInterface() = default; + int getValue(ConfigFile file, std::string key, int defaultVal); + private: const std::string configRoot = "config"; - const std::string filename = "audio.yaml"; }; diff --git a/src/main.cpp b/src/main.cpp index d12d7fb..7d68bee 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -9,9 +9,7 @@ int main(int argc, char *argv[]) { // std::cout << "Main()" << std::endl; - - ConfigInterface config = ConfigInterface(); - + QApplication app(argc, argv); MainWindow window; // entry point goes to MainWindow::MainWindow() diff --git a/src/synth/AudioEngine.cpp b/src/synth/AudioEngine.cpp index 44cab86..4b29f7d 100644 --- a/src/synth/AudioEngine.cpp +++ b/src/synth/AudioEngine.cpp @@ -3,14 +3,11 @@ #include -AudioEngine::AudioEngine() : synth_(params_) { +AudioEngine::AudioEngine(ConfigInterface* config) : synth_(params_), config_(config) { if(audio_.getDeviceCount() < 1) { throw std::runtime_error("No audio devices found"); } - // TODO: get audio configurations - synth_.setSampleRate(sampleRate_); - synth_.setScopeBuffer(&scope_); } @@ -21,6 +18,13 @@ AudioEngine::~AudioEngine() { bool AudioEngine::start() { + // get config values + sampleRate_ = config_->getValue(ConfigFile::Audio, "sampleRate", sampleRate_); + bufferFrames_ = config_->getValue(ConfigFile::Audio, "bufferSize", bufferFrames_); + channels_ = config_->getValue(ConfigFile::Audio, "channels", channels_); + + synth_.setSampleRate(sampleRate_); + // initialize the audio engine RtAudio::StreamParameters params; params.deviceId = audio_.getDefaultOutputDevice(); diff --git a/src/synth/AudioEngine.h b/src/synth/AudioEngine.h index b1c6b7d..ae673fc 100644 --- a/src/synth/AudioEngine.h +++ b/src/synth/AudioEngine.h @@ -7,6 +7,7 @@ #include "Synth.h" #include "../KeyboardController.h" +#include "../ConfigInterface.h" #if defined(_WIN32) #define AUDIO_API RtAudio::WINDOWS_WASAPI @@ -17,7 +18,7 @@ class AudioEngine { public: - AudioEngine(); + AudioEngine(ConfigInterface* config); ~AudioEngine(); // starts the audio stream. returns true on success and false on failure @@ -43,6 +44,7 @@ private: NoteQueue noteQueue_; // stores note events for passing between threads Synth synth_; // generates audio ScopeBuffer scope_ { 1024 }; // stores audio samples for visualization + ConfigInterface* config_; // access to config files RtAudio audio_{AUDIO_API}; // audio device // TODO: id like a yml config file or something for these diff --git a/src/synth/ScopeBuffer.cpp b/src/synth/ScopeBuffer.cpp index d94d452..4c6682e 100644 --- a/src/synth/ScopeBuffer.cpp +++ b/src/synth/ScopeBuffer.cpp @@ -11,11 +11,12 @@ void ScopeBuffer::push(float sample) { buffer_[w % buffer_.size()] = sample; } -// TODO: needs a mutex to prevent flickering +// TODO: needs a mutex/spinlock to prevent flickering // outputs value from the scope buffer, called by the scope widget void ScopeBuffer::read(std::vector& out) const { - while(!spinLock_) { int x = 1 + 1; } + // yeah this didn't work, maybe it needs to be atomic or something + //while(!spinLock_) { int x = 1 + 1; } size_t w = writeIndex_.load(std::memory_order_relaxed); for (size_t i = 0; i < out.size(); i++) { diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 36d8f41..4a942eb 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -8,7 +8,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui_(new Ui::MainWindow), - audio_(new AudioEngine()), + audio_(new AudioEngine(&config_)), keyboard_(audio_->noteQueue()), midi_(audio_->noteQueue()) { diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index fe17f42..ff71068 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -6,6 +6,7 @@ #include "../synth/AudioEngine.h" #include "../MidiController.h" +#include "../ConfigInterface.h" QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } @@ -32,5 +33,6 @@ private: AudioEngine* audio_ = nullptr; KeyboardController keyboard_; MidiController midi_; + ConfigInterface config_; }; From 5bc3a5d7ff7de04cf1d97bd8fa8df02b2b8a1d42 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sat, 24 Jan 2026 18:53:48 -0600 Subject: [PATCH 11/13] profile loading checkpoint --- config/profiles/default.yaml | 45 ++++++++++++++++++++++++++++++++++++ src/ConfigInterface.cpp | 2 +- src/ConfigInterface.h | 1 + src/ParameterStore.cpp | 13 ++++++++++- src/ParameterStore.h | 6 ++++- src/synth/AudioEngine.cpp | 2 +- src/synth/AudioEngine.h | 2 +- src/ui/MainWindow.h | 2 +- 8 files changed, 67 insertions(+), 6 deletions(-) diff --git a/config/profiles/default.yaml b/config/profiles/default.yaml index e69de29..19fe404 100644 --- a/config/profiles/default.yaml +++ b/config/profiles/default.yaml @@ -0,0 +1,45 @@ + +# default.yaml +# Default voice profile + +# sequences in the form [x, x, x] denote [setValue, sliderMinimum, sliderMaximum] + +version: 00001 + +# deprecated, useless +Osc1Freq: [100, 20, 600] + +# wavetable selections +OscWaveSelector1: 2 +OscWaveSelector2: 1 + +# Oscillator frequency parameters +Osc1OctaveOffset: [0, -5, 5] +Osc1SemitoneOffset: [0, -12, 12] +Osc1PitchOffset: [0, -100, 100] +Osc2OctaveOffset: [1, -5, 5] +Osc2SemitoneOffset: [0, -12, 12] +Osc2PitchOffset: [0, -100, 100] +Osc3OctaveOffset: [1, -5, 5] +Osc3SemitoneOffset: [7, -12, 12] +Osc3PitchOffset: [1.96, -100, 100] + +# Envelope generator parameters +Osc1Volume: + - [1, 0, 2] # Depth + - [0.05, 0, 2] # Attack + - [0.2, 0, 2] # Decay + - [0.7, 0, 1] # Sustain + - [0.2, 0, 2] # Release +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 +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 diff --git a/src/ConfigInterface.cpp b/src/ConfigInterface.cpp index 4c209ef..733bdd6 100644 --- a/src/ConfigInterface.cpp +++ b/src/ConfigInterface.cpp @@ -3,7 +3,6 @@ #include "yaml-cpp/yaml.h" -#include #include #include @@ -24,6 +23,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 diff --git a/src/ConfigInterface.h b/src/ConfigInterface.h index 6bd15bd..faea952 100644 --- a/src/ConfigInterface.h +++ b/src/ConfigInterface.h @@ -3,6 +3,7 @@ #include #include +#include enum class ConfigFile { Audio = 0 diff --git a/src/ParameterStore.cpp b/src/ParameterStore.cpp index 49f1d94..0262b20 100644 --- a/src/ParameterStore.cpp +++ b/src/ParameterStore.cpp @@ -1,7 +1,9 @@ #include "ParameterStore.h" -ParameterStore::ParameterStore() { +#include + +ParameterStore::ParameterStore(ConfigInterface* config) : config_(config) { resetToDefaults(); } @@ -30,6 +32,15 @@ void ParameterStore::resetToDefaults() { for(size_t i = 0; i < PARAM_COUNT; i++) { values_[i].store(PARAM_DEFS[i].def, std::memory_order_relaxed); } + + if(config_) { + int x = config_->getValue(ConfigFile::Audio, "sampleRate", 0); + std::cout << "test: " << x << std::endl; + } else { + std::cout << "pointer null" << std::endl; + } + + } // TODO: applying parameter profiles will work similarly to above function diff --git a/src/ParameterStore.h b/src/ParameterStore.h index 70fdef1..9b2493a 100644 --- a/src/ParameterStore.h +++ b/src/ParameterStore.h @@ -1,6 +1,8 @@ #pragma once +#include "ConfigInterface.h" + #include #include #include @@ -107,7 +109,7 @@ class ParameterStore { public: - ParameterStore(); + ParameterStore(ConfigInterface* config); ~ParameterStore() = default; void set(ParamId id, float value); @@ -121,4 +123,6 @@ private: std::array, PARAM_COUNT> values_; + ConfigInterface* config_; + }; diff --git a/src/synth/AudioEngine.cpp b/src/synth/AudioEngine.cpp index 4b29f7d..56772a9 100644 --- a/src/synth/AudioEngine.cpp +++ b/src/synth/AudioEngine.cpp @@ -3,7 +3,7 @@ #include -AudioEngine::AudioEngine(ConfigInterface* config) : synth_(params_), config_(config) { +AudioEngine::AudioEngine(ConfigInterface* config) : params_(ParameterStore(config)), 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 ae673fc..9c1f128 100644 --- a/src/synth/AudioEngine.h +++ b/src/synth/AudioEngine.h @@ -40,11 +40,11 @@ private: // calls the synth.process to generate a buffer of audio samples int32_t process(float* out, uint32_t nFrames); + ConfigInterface* config_; // access to config files 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 - ConfigInterface* config_; // access to config files RtAudio audio_{AUDIO_API}; // audio device // TODO: id like a yml config file or something for these diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index ff71068..73b00ad 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -30,9 +30,9 @@ private slots: private: Ui::MainWindow *ui_; + ConfigInterface config_; AudioEngine* audio_ = nullptr; KeyboardController keyboard_; MidiController midi_; - ConfigInterface config_; }; From a6ef39bb11d0ec1571cef09febe9f1293105e801 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sun, 25 Jan 2026 00:47:45 -0600 Subject: [PATCH 12/13] loading default profile from yaml --- config/profiles/default.yaml | 2 +- src/ParameterStore.cpp | 45 +++++++++++++++++++++++++++++------- src/ParameterStore.h | 4 ++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/config/profiles/default.yaml b/config/profiles/default.yaml index 19fe404..7fa4d50 100644 --- a/config/profiles/default.yaml +++ b/config/profiles/default.yaml @@ -4,7 +4,7 @@ # sequences in the form [x, x, x] denote [setValue, sliderMinimum, sliderMaximum] -version: 00001 +version: 0x0002 # deprecated, useless Osc1Freq: [100, 20, 600] diff --git a/src/ParameterStore.cpp b/src/ParameterStore.cpp index 0262b20..a58a14e 100644 --- a/src/ParameterStore.cpp +++ b/src/ParameterStore.cpp @@ -2,6 +2,8 @@ #include "ParameterStore.h" #include +#include "yaml-cpp/yaml.h" // TODO: using yaml.h outside of ConfigInterface feels spaghetti to me +#include ParameterStore::ParameterStore(ConfigInterface* config) : config_(config) { resetToDefaults(); @@ -33,14 +35,41 @@ void ParameterStore::resetToDefaults() { values_[i].store(PARAM_DEFS[i].def, std::memory_order_relaxed); } - if(config_) { - int x = config_->getValue(ConfigFile::Audio, "sampleRate", 0); - std::cout << "test: " << x << std::endl; - } else { - std::cout << "pointer null" << std::endl; - } - + loadParameterProfile("config/profiles/default.yaml"); } -// TODO: applying parameter profiles will work similarly to above function +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 9b2493a..cf3b696 100644 --- a/src/ParameterStore.h +++ b/src/ParameterStore.h @@ -7,6 +7,8 @@ #include #include +#define CONFIG_VERSION 0x0001 + enum class ParamId : uint16_t { Osc1Frequency, Osc1WaveSelector1, @@ -121,6 +123,8 @@ public: private: + void loadParameterProfile(std::string filepath); + std::array, PARAM_COUNT> values_; ConfigInterface* config_; From 21bf285aff6479d67cfb3ce0ebf7d659cb7b98d8 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sun, 25 Jan 2026 14:11:03 -0600 Subject: [PATCH 13/13] 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);