configurable wavetable files
This commit is contained in:
@@ -83,10 +83,10 @@ Use the install_dependencies script to manually install dependencies.
|
|||||||
Build troubleshooting:
|
Build troubleshooting:
|
||||||
On windows, `bcdedit /set IncreaseUserVa 3072` solved cc1plus.exe: out of memory errors while building qt for me
|
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. \
|
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. \
|
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
|
||||||
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 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.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -1,5 +1,38 @@
|
|||||||
|
|
||||||
import math
|
import math
|
||||||
|
|
||||||
def process(phase):
|
WAVETABLE_FILE_NAME = "triangle"
|
||||||
|
|
||||||
|
def sine(phase):
|
||||||
return math.sin(phase)
|
return math.sin(phase)
|
||||||
|
|
||||||
|
def square(phase):
|
||||||
|
sample = 1.0
|
||||||
|
if(phase <= math.pi):
|
||||||
|
sample = -1
|
||||||
|
return sample
|
||||||
|
|
||||||
|
def saw(phase):
|
||||||
|
return (phase / math.pi) - 1.0
|
||||||
|
|
||||||
|
def triangle(phase):
|
||||||
|
sample = 0.0
|
||||||
|
if(phase <= math.pi/2.0):
|
||||||
|
sample = phase * 2.0/math.pi
|
||||||
|
elif(phase <= 3.0*math.pi/2.0):
|
||||||
|
sample = phase * (-2.0/math.pi) + 2.0
|
||||||
|
else:
|
||||||
|
sample = phase * 2.0/math.pi - 4.0
|
||||||
|
return sample
|
||||||
|
|
||||||
|
def sharkFin(phase):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def sphere(phase):
|
||||||
|
return 1
|
||||||
|
|
||||||
|
# process get called by generate_wavetable.py
|
||||||
|
# it calculates a single sample at a specified phase
|
||||||
|
# normalization is handled by generate_wavetable.py
|
||||||
|
def process(phase):
|
||||||
|
return triangle(phase)
|
||||||
|
|||||||
@@ -21,8 +21,9 @@ import example_wavetable
|
|||||||
wavetableLength = 2048
|
wavetableLength = 2048
|
||||||
|
|
||||||
def createFile():
|
def createFile():
|
||||||
print("creating file")
|
filename = example_wavetable.WAVETABLE_FILE_NAME + ".wt"
|
||||||
file = open("sine.wt", "wb")
|
print("creating file " + filename)
|
||||||
|
file = open(filename, "wb")
|
||||||
return file
|
return file
|
||||||
|
|
||||||
def writeMetadata(file):
|
def writeMetadata(file):
|
||||||
|
|||||||
@@ -16,46 +16,26 @@ WavetableController::WavetableController() {
|
|||||||
|
|
||||||
void WavetableController::init() {
|
void WavetableController::init() {
|
||||||
|
|
||||||
// find number of wavetable files
|
// find wavetable files
|
||||||
std::vector<std::filesystem::path> wavetableFiles;
|
std::vector<std::filesystem::path> wavetableFiles;
|
||||||
for(std::filesystem::directory_entry entry : std::filesystem::directory_iterator(wavetablesRoot_)) {
|
for(std::filesystem::directory_entry entry : std::filesystem::directory_iterator(wavetablesRoot_)) {
|
||||||
if(std::filesystem::is_regular_file(entry.status())) {
|
if(std::filesystem::is_regular_file(entry.status())) {
|
||||||
wavetableFiles.push_back(entry.path());
|
wavetableFiles.push_back(entry.path());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
uint32_t wavetableCount = wavetableFiles.size();
|
||||||
|
wavetables_.resize(wavetableCount);
|
||||||
|
|
||||||
wavetables_.resize(4); // resize for however many files we find
|
// load the wavetable files
|
||||||
|
for(int i = 0; i < wavetableCount; i++) {
|
||||||
// wavetable file structure is best explained in scripts/generate_wavetable.py
|
std::cout << "loading wavetable file [" << i << "]: " << wavetableFiles[i] << std::endl;
|
||||||
|
std::ifstream inputFile(wavetableFiles[i], std::ios::in | std::ios::binary);
|
||||||
// read the wavetable file
|
if(!inputFile) std::cout << "error opening file" << std::endl;
|
||||||
std::ifstream inputFile("config/wavetables/sine.wt", std::ios::in | std::ios::binary);
|
inputFile.read(reinterpret_cast<char*>(wavetables_[i].data()), SYNTH_WAVETABLE_SIZE * sizeof(float));
|
||||||
if(!inputFile) std::cout << "error opening file" << std::endl;
|
|
||||||
inputFile.read(reinterpret_cast<char*>(wavetables_[0].data()), SYNTH_WAVETABLE_SIZE * sizeof(float));
|
|
||||||
|
|
||||||
float phase = 0.0f;
|
|
||||||
float phaseInc = 2.0f * M_PI / static_cast<float>(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wavetable data structure is best explained in scripts/generate_wavetable.py
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float WavetableController::sample(uint8_t wavetableIndex, float phase) {
|
float WavetableController::sample(uint8_t wavetableIndex, float phase) {
|
||||||
|
|||||||
@@ -48,6 +48,17 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
this, [this](int index) {
|
this, [this](int index) {
|
||||||
audio_->parameters()->set(ParamId::Osc1WaveSelector2, 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
|
// rogue sliders, TODO: clean these up in a package
|
||||||
connect(ui_->sliderMasterOctave, &SmartSlider::valueChanged,
|
connect(ui_->sliderMasterOctave, &SmartSlider::valueChanged,
|
||||||
|
|||||||
Reference in New Issue
Block a user