integrate audio config into engine and synth
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -29,7 +29,9 @@ AudioEngine = (
|
||||
BufferSize = 512;
|
||||
PitchStandard = 440.0;
|
||||
MidiHome = 69;
|
||||
NotesPerOctave = 12;
|
||||
NotesPerOctave = 12.0;
|
||||
OctaveConstant = 2.0;
|
||||
MaxVoices = 32;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,13 +28,22 @@ 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;
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_) {
|
||||
|
||||
@@ -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
|
||||
|
||||
filter_.setSampleRate(44100.0f);
|
||||
voices_.assign(engineConfiguration_.maxVoices, Voice(config_, logger_, static_cast<float>(engineConfiguration_.sampleRate), &engineConfiguration_));
|
||||
|
||||
filter_.setSampleRate(engineConfiguration_.sampleRate);
|
||||
filter_.setParams(Filter::Type::BiquadLowpass, 4000.0f, 0.707f);
|
||||
|
||||
}
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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_);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user