diff --git a/CMakeLists.txt b/CMakeLists.txt index ff93914..d35e02a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/config/sonobulus.cfg b/config/sonobulus.cfg index 1fe0fdf..ea523a9 100644 --- a/config/sonobulus.cfg +++ b/config/sonobulus.cfg @@ -29,7 +29,9 @@ AudioEngine = ( BufferSize = 512; PitchStandard = 440.0; MidiHome = 69; - NotesPerOctave = 12; + NotesPerOctave = 12.0; + OctaveConstant = 2.0; + MaxVoices = 32; } ); diff --git a/src/LoggerService.cpp b/src/LoggerService.cpp index b8e140b..1106824 100644 --- a/src/LoggerService.cpp +++ b/src/LoggerService.cpp @@ -14,7 +14,7 @@ namespace fs = std::filesystem; LoggerService::LoggerService(ConfigService* config, const std::string& loggerId) { if(!(config->getConfig("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; } diff --git a/src/config/AudioConfig.hpp b/src/config/AudioConfig.hpp index 13d59e5..7d4627f 100644 --- a/src/config/AudioConfig.hpp +++ b/src/config/AudioConfig.hpp @@ -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; } diff --git a/src/synth/AudioEngine.cpp b/src/synth/AudioEngine.cpp index 512ff85..01a2423 100644 --- a/src/synth/AudioEngine.cpp +++ b/src/synth/AudioEngine.cpp @@ -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("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(¶ms, nullptr, RTAUDIO_FLOAT32, sampleRate_, &bufferFrames_, &AudioEngine::audioCallback, this, &options); + RtAudioErrorType status = audioDevice_.openStream( + ¶ms, + 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; diff --git a/src/synth/AudioEngine.hpp b/src/synth/AudioEngine.hpp index dcb7b50..167c637 100644 --- a/src/synth/AudioEngine.hpp +++ b/src/synth/AudioEngine.hpp @@ -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_; }; diff --git a/src/synth/Instruments/Instrument.cpp b/src/synth/Instruments/Instrument.cpp index d8b7cb4..c5462f4 100644 --- a/src/synth/Instruments/Instrument.cpp +++ b/src/synth/Instruments/Instrument.cpp @@ -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) { } diff --git a/src/synth/Instruments/Instrument.hpp b/src/synth/Instruments/Instrument.hpp index dc8b523..5b509cb 100644 --- a/src/synth/Instruments/Instrument.hpp +++ b/src/synth/Instruments/Instrument.hpp @@ -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_; diff --git a/src/synth/Instruments/PianoString.cpp b/src/synth/Instruments/PianoString.cpp index e192eef..b5fd7b5 100644 --- a/src/synth/Instruments/PianoString.cpp +++ b/src/synth/Instruments/PianoString.cpp @@ -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); diff --git a/src/synth/Instruments/PianoString.hpp b/src/synth/Instruments/PianoString.hpp index d2d1fc6..2ea3ecc 100644 --- a/src/synth/Instruments/PianoString.hpp +++ b/src/synth/Instruments/PianoString.hpp @@ -3,14 +3,14 @@ #include -#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; diff --git a/src/synth/KeyboardController.cpp b/src/synth/KeyboardController.cpp index b4d20fe..41c7361 100644 --- a/src/synth/KeyboardController.cpp +++ b/src/synth/KeyboardController.cpp @@ -13,7 +13,7 @@ KeyboardController::KeyboardController(ConfigService* config, LoggerService* log // load keymap from config service if(!(config->getConfig("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; } diff --git a/src/synth/Scope.cpp b/src/synth/Scope.cpp index 28b2d05..c645dbc 100644 --- a/src/synth/Scope.cpp +++ b/src/synth/Scope.cpp @@ -4,7 +4,7 @@ #include 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_) { diff --git a/src/synth/Synth.cpp b/src/synth/Synth.cpp index 268289c..7a50325 100644 --- a/src/synth/Synth.cpp +++ b/src/synth/Synth.cpp @@ -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("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(engineConfiguration_.sampleRate), &engineConfiguration_)); - filter_.setSampleRate(44100.0f); + filter_.setSampleRate(engineConfiguration_.sampleRate); filter_.setParams(Filter::Type::BiquadLowpass, 4000.0f, 0.707f); } diff --git a/src/synth/Synth.hpp b/src/synth/Synth.hpp index 9f0858d..02b0493 100644 --- a/src/synth/Synth.hpp +++ b/src/synth/Synth.hpp @@ -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 voices_; + size_t maxVoices; + std::vector voices_; ConfigService* config_; + AudioParams engineConfiguration_; LoggerService* logger_; ScopeBuffer* scope_;; NoteQueue* noteQueue_; diff --git a/src/synth/Voice.cpp b/src/synth/Voice.cpp index 6b87ead..b81e78a 100644 --- a/src/synth/Voice.cpp +++ b/src/synth/Voice.cpp @@ -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_); } diff --git a/src/synth/Voice.hpp b/src/synth/Voice.hpp index 1c2e839..7e8d476 100644 --- a/src/synth/Voice.hpp +++ b/src/synth/Voice.hpp @@ -3,8 +3,9 @@ #include -#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(note - 69) / static_cast(12)); + return params_->pitchStandard * pow(params_->octaveConstant, static_cast(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_;