integrate audio config into engine and synth

This commit is contained in:
2026-07-05 00:02:53 -05:00
parent 9365e12bb4
commit 295a869839
16 changed files with 71 additions and 41 deletions

View File

@@ -57,8 +57,8 @@ add_library(sonobulus_core STATIC
src/synth/Synth.cpp
src/synth/Voice.cpp
src/synth/Filter.cpp
src/synth/Instruments/Instrument.cpp
src/synth/Instruments/PianoString.cpp
src/synth/instruments/Instrument.cpp
src/synth/instruments/PianoString.cpp
)
if (WIN32)

View File

@@ -29,7 +29,9 @@ AudioEngine = (
BufferSize = 512;
PitchStandard = 440.0;
MidiHome = 69;
NotesPerOctave = 12;
NotesPerOctave = 12.0;
OctaveConstant = 2.0;
MaxVoices = 32;
}
);

View File

@@ -14,7 +14,7 @@ namespace fs = std::filesystem;
LoggerService::LoggerService(ConfigService* config, const std::string& loggerId) {
if(!(config->getConfig<LoggerParams, LoggerConfig>("Logger", loggerId, &configuration_))) {
std::cout << "Failed to get logger configuration fom config service" << std::endl;
std::cout << "Failed to get logger configuration from config service" << std::endl;
return;
}

View File

@@ -1,18 +1,19 @@
#pragma once
#include "IConfig.hpp"
struct AudioParams : IParams {
std::string id;
uint32_t sampleRate;
uint32_t channels;
uint32_t stereoMode;
uint32_t bufferSize;
float pitchStandard;
int32_t midiHome;
int32_t notesPerOctave;
float notesPerOctave;
float octaveConstant;
size_t maxVoices;
};
@@ -31,9 +32,12 @@ public:
setting.lookupValue("SampleRate", params_->sampleRate);
setting.lookupValue("StereoMode", params_->stereoMode);
setting.lookupValue("BufferSize", params_->bufferSize);
setting.lookupValue("Channels", params_->channels);
setting.lookupValue("PitchStandard", params_->pitchStandard);
setting.lookupValue("MidiHome", params_->midiHome);
setting.lookupValue("NotesPerOctave", params_->notesPerOctave);
setting.lookupValue("OctaveConstant", params_->octaveConstant);
setting.lookupValue("MaxVoices", params_->maxVoices);
return true;
}

View File

@@ -8,6 +8,12 @@
AudioEngine::AudioEngine(ConfigService* config, LoggerService* logger, Synth* synth) : config_(config), logger_(logger), synth_(synth) {
// load audio settings from config service
if(!(config->getConfig<AudioParams, AudioConfig>("AudioEngine", "Main", &configuration_))) {
logger_->log("Audio", LogFlag::Error, "Failed to get logger configuration from config service");
return;
}
if(audioDevice_.getDeviceCount() < 1) {
logger_->log("Audio", LogFlag::Error, "No audio devices found.");
}
@@ -22,17 +28,26 @@ bool AudioEngine::start() {
// initialize the audio engine
RtAudio::StreamParameters params;
params.deviceId = audioDevice_.getDefaultOutputDevice();
params.nChannels = channels_; // we're doing two duplicate channels for pseudo-mono
params.nChannels = configuration_.channels; // we're doing two duplicate channels for pseudo-mono
params.firstChannel = 0;
RtAudio::StreamOptions options;
options.flags = RTAUDIO_MINIMIZE_LATENCY;
RtAudioErrorType status = audioDevice_.openStream(&params, nullptr, RTAUDIO_FLOAT32, sampleRate_, &bufferFrames_, &AudioEngine::audioCallback, this, &options);
RtAudioErrorType status = audioDevice_.openStream(
&params,
nullptr,
RTAUDIO_FLOAT32,
configuration_.sampleRate,
&configuration_.bufferSize,
&AudioEngine::audioCallback,
this,
&options
);
if(status != RTAUDIO_NO_ERROR) {
logger_->log("Audio", LogFlag::Error, "Error opening RtAudio stream.");
return false;
}
}
status = audioDevice_.startStream();
if(status != RTAUDIO_NO_ERROR) {
@@ -41,7 +56,7 @@ bool AudioEngine::start() {
}
// sanity check
std::string msg = "sample rate: " + std::to_string(sampleRate_) + ", buffer frames: " + std::to_string(bufferFrames_);
std::string msg = "sample rate: " + std::to_string(configuration_.sampleRate) + ", buffer frames: " + std::to_string(configuration_.bufferSize);
logger_->log("AudioEngine", LogFlag::Info, msg);
return true;

View File

@@ -7,6 +7,7 @@
#include "LoggerService.hpp"
#include "config/ConfigService.hpp"
#include "config/AudioConfig.hpp"
#include "Synth.hpp"
#include "NoteQueue.hpp"
@@ -35,13 +36,10 @@ private:
RtAudio audioDevice_ { AUDIO_API };
// TODO: make these configurable
uint32_t sampleRate_ = 44100;
uint32_t bufferFrames_ = 512;
uint32_t channels_ = 2;
LoggerService* logger_;
ConfigService* config_;
AudioParams configuration_;
Synth* synth_;
};

View File

@@ -3,8 +3,8 @@
#include "string"
Instrument::Instrument(ConfigService* config, LoggerService* logger) :
config_(config), logger_(logger) {
Instrument::Instrument(ConfigService* config, LoggerService* logger, float sampleRate) :
config_(config), logger_(logger), sampleRate_(sampleRate) {
}

View File

@@ -5,13 +5,13 @@
#include "LoggerService.hpp"
// an instrument is our state space model. it calculates discrete samples as a function of time, the current states, and the inputs
// an instrumeent is owned by a voice
// an instrument is owned by a voice
class Instrument {
public:
Instrument() = default;
Instrument(ConfigService* config, LoggerService* logger);
Instrument(ConfigService* config, LoggerService* logger, float sampleRate);
~Instrument() = default;
virtual void noteOn(float frequency, float velocity);
@@ -23,7 +23,7 @@ public:
protected:
float sampleRate_ = 44100.0f;
float sampleRate_ = 1.0f;
bool active_ = false;
ConfigService* config_;

View File

@@ -1,7 +1,7 @@
#include "PianoString.hpp"
PianoString::PianoString(ConfigService* config, LoggerService* logger) : Instrument(config, logger) {
PianoString::PianoString(ConfigService* config, LoggerService* logger, float sampleRate) : Instrument(config, logger, sampleRate) {
stringY_current_.resize(segmentCount_ + 1);
stringY_previous_.resize(segmentCount_ + 1);

View File

@@ -3,14 +3,14 @@
#include <cmath>
#include "synth/Instruments/Instrument.hpp"
#include "synth/instruments/Instrument.hpp"
class PianoString : public Instrument {
public:
PianoString() = default;
PianoString(ConfigService* config, LoggerService* logger);
PianoString(ConfigService* config, LoggerService* logger, float sampleRate);
~PianoString() = default;
void noteOn(float frequency, float velocity) override;

View File

@@ -13,7 +13,7 @@ KeyboardController::KeyboardController(ConfigService* config, LoggerService* log
// load keymap from config service
if(!(config->getConfig<KeymapParams, KeymapConfig>("KeyboardController", "Main", &configuration_))) {
logger_->log("Keyboard", LogFlag::Error, "Failed to get logger configuration fom config service");
logger_->log("Keyboard", LogFlag::Error, "Failed to get logger configuration from config service");
return;
}

View File

@@ -4,7 +4,7 @@
#include <iostream>
ScopeBuffer::ScopeBuffer(QObject *parent) : QObject(parent) {
// timer is set in qml
}
ScopeBuffer::ScopeBuffer(ConfigService* config, LoggerService* logger, size_t size) :
@@ -45,7 +45,7 @@ void Scope::paint(QPainter *painter) {
if(scopeBuffer_ != nullptr) scopeBuffer_->read(buffer_);
// TODO: scale to max amplitude ?
// TODO: scale to max amplitude ? probably make it configurable if you want to make scope options
float maxAmp = 1.0f;
/*
for(float s : buffer_) {

View File

@@ -4,9 +4,17 @@
Synth::Synth(ConfigService* config, LoggerService* logger, ScopeBuffer* scope, NoteQueue* queue) :
config_(config), logger_(logger), scope_(scope), noteQueue_(queue) {
voices_.fill(Voice(config_, logger_));
// load audio settings from config service
if(!(config->getConfig<AudioParams, AudioConfig>("AudioEngine", "Main", &engineConfiguration_))) {
logger_->log("Audio", LogFlag::Error, "Failed to get logger configuration from config service");
return;
}
// note: this is the audio engine's configuration so we can grab sample rate
// it includes some synth params as well, so when synth gets its own config node they all need to be moved
voices_.assign(engineConfiguration_.maxVoices, Voice(config_, logger_, static_cast<float>(engineConfiguration_.sampleRate), &engineConfiguration_));
filter_.setSampleRate(44100.0f);
filter_.setSampleRate(engineConfiguration_.sampleRate);
filter_.setParams(Filter::Type::BiquadLowpass, 4000.0f, 0.707f);
}

View File

@@ -2,6 +2,7 @@
#pragma once
#include "config/ConfigService.hpp"
#include "config/AudioConfig.hpp"
#include "LoggerService.hpp"
#include "NoteQueue.hpp"
#include "Voice.hpp"
@@ -29,10 +30,11 @@ private:
Filter filter_;
// voices
static constexpr size_t MAX_VOICES = 32;
std::array<Voice, MAX_VOICES> voices_;
size_t maxVoices;
std::vector<Voice> voices_;
ConfigService* config_;
AudioParams engineConfiguration_;
LoggerService* logger_;
ScopeBuffer* scope_;;
NoteQueue* noteQueue_;

View File

@@ -1,11 +1,11 @@
#include "Voice.hpp"
Voice::Voice(ConfigService* config, LoggerService* logger) :
config_(config), logger_(logger) {
Voice::Voice(ConfigService* config, LoggerService* logger, float sampleRate, AudioParams* params) :
config_(config), logger_(logger), sampleRate_(sampleRate), params_(params) {
// TODO: instrument factory
instrument_ = PianoString(config_, logger_);
// TODO: instrument factory << WHAT DO YOU MEAN JUST TODO A WHOLE FACTORY
instrument_ = PianoString(config_, logger_, sampleRate_);
}

View File

@@ -3,8 +3,9 @@
#include <stdint.h>
#include "Instruments/Instrument.hpp"
#include "Instruments/PianoString.hpp"
#include "instruments/Instrument.hpp"
#include "instruments/PianoString.hpp"
#include "config/AudioConfig.hpp"
// a voice is a tone generator that the synth uses for polyphony
// the synth mixes multiple voices together into a polyphonic audio. calculations for samples are handled in the instrument
@@ -13,7 +14,7 @@ class Voice {
public:
Voice() = default;
Voice(ConfigService* config, LoggerService* logger);
Voice(ConfigService* config, LoggerService* logger, float sampleRate, AudioParams* params);
~Voice() = default;
void noteOn(uint8_t midiNote, float velocity);
@@ -26,17 +27,17 @@ public:
private:
float sampleRate_ = 44100.0f;
inline float noteToFrequency(uint8_t note) {
return 440.0f * pow(2.0f, static_cast<float>(note - 69) / static_cast<float>(12));
return params_->pitchStandard * pow(params_->octaveConstant, static_cast<float>(note - params_->midiHome) / params_->notesPerOctave);
}
float sampleRate_ = 1.0f;
uint8_t note_ = 0;
float velocity_ = 1.0f;
bool active_ = false;
ConfigService* config_;
AudioParams* params_;
LoggerService* logger_;
PianoString instrument_;