From 18ff8dfc9f2d709230aae29c0a6ec32b49f41da9 Mon Sep 17 00:00:00 2001 From: Bliblank Date: Sat, 7 Feb 2026 18:08:38 -0600 Subject: [PATCH] configurable wavetable files --- README.md | 6 ++--- config/wavetables/sine.wt | Bin 8192 -> 8192 bytes scripts/example_wavetable.py | 35 ++++++++++++++++++++++++- scripts/generate_wavetable.py | 5 ++-- src/synth/WavetableController.cpp | 42 ++++++++---------------------- src/ui/MainWindow.cpp | 11 ++++++++ 6 files changed, 62 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index c502925..1eebec7 100644 --- a/README.md +++ b/README.md @@ -83,10 +83,10 @@ 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) +## Configurations 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 +## Wavetables +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), are located within the scripts directory. diff --git a/config/wavetables/sine.wt b/config/wavetables/sine.wt index 27b3b3c084e80d686e1a2ca757d5112bef0b765d..c37a42bfb9f10881bad9169936d268aa39cdcf1d 100644 GIT binary patch literal 8192 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 wavetableFiles; for(std::filesystem::directory_entry entry : std::filesystem::directory_iterator(wavetablesRoot_)) { if(std::filesystem::is_regular_file(entry.status())) { wavetableFiles.push_back(entry.path()); } } + uint32_t wavetableCount = wavetableFiles.size(); + wavetables_.resize(wavetableCount); - wavetables_.resize(4); // resize for however many files we find - - // wavetable file structure is best explained in scripts/generate_wavetable.py - - // read the wavetable file - std::ifstream inputFile("config/wavetables/sine.wt", std::ios::in | std::ios::binary); - if(!inputFile) std::cout << "error opening file" << std::endl; - inputFile.read(reinterpret_cast(wavetables_[0].data()), SYNTH_WAVETABLE_SIZE * sizeof(float)); - - float phase = 0.0f; - float phaseInc = 2.0f * M_PI / static_cast(SYNTH_WAVETABLE_SIZE); - - for(int i = 0; i < SYNTH_WAVETABLE_SIZE; i++) { - - //wavetables_[0][i] = std::sin(phase) / 0.707f; // sine - wavetables_[1][i] = (phase >= M_PI) ? 1.0f : -1.0f; // square - wavetables_[2][i] = ((phase / M_PI) - 1.0f) / 0.577f; // saw - - // triangle - float tri = 0.0f; - if(phase <= M_PI/2.0f) { - tri = phase * 2.0f/M_PI; - } else if(phase <= 3.0f*M_PI/2.0f) { - tri = phase * -2.0f/M_PI + 2.0f; - } else { - tri = phase * 2.0f/M_PI - 4.0f; - } - wavetables_[3][i] = tri / 0.577f; - - phase += phaseInc; + // load the wavetable files + for(int i = 0; i < wavetableCount; i++) { + std::cout << "loading wavetable file [" << i << "]: " << wavetableFiles[i] << std::endl; + std::ifstream inputFile(wavetableFiles[i], std::ios::in | std::ios::binary); + if(!inputFile) std::cout << "error opening file" << std::endl; + inputFile.read(reinterpret_cast(wavetables_[i].data()), SYNTH_WAVETABLE_SIZE * sizeof(float)); } + // wavetable data structure is best explained in scripts/generate_wavetable.py + } float WavetableController::sample(uint8_t wavetableIndex, float phase) { diff --git a/src/ui/MainWindow.cpp b/src/ui/MainWindow.cpp index 7fcb343..377fcae 100644 --- a/src/ui/MainWindow.cpp +++ b/src/ui/MainWindow.cpp @@ -48,6 +48,17 @@ MainWindow::MainWindow(QWidget *parent) : this, [this](int index) { audio_->parameters()->set(ParamId::Osc1WaveSelector2, index); }); + ui_->comboOsc1WaveSelector1->clear(); + ui_->comboOsc1WaveSelector2->clear(); + for(std::filesystem::directory_entry entry : std::filesystem::directory_iterator("config/wavetables")) { + if(std::filesystem::is_regular_file(entry.status())) { + std::string fileName = entry.path().string().substr(18); + fileName.erase(fileName.length() - 3); + ui_->comboOsc1WaveSelector1->addItem(QString::fromStdString(fileName)); + ui_->comboOsc1WaveSelector2->addItem(QString::fromStdString(fileName)); + } + } + // rogue sliders, TODO: clean these up in a package connect(ui_->sliderMasterOctave, &SmartSlider::valueChanged,