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/Synth.cpp
|
||||||
src/synth/Voice.cpp
|
src/synth/Voice.cpp
|
||||||
src/synth/Filter.cpp
|
src/synth/Filter.cpp
|
||||||
src/synth/Instruments/Instrument.cpp
|
src/synth/instruments/Instrument.cpp
|
||||||
src/synth/Instruments/PianoString.cpp
|
src/synth/instruments/PianoString.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ AudioEngine = (
|
|||||||
BufferSize = 512;
|
BufferSize = 512;
|
||||||
PitchStandard = 440.0;
|
PitchStandard = 440.0;
|
||||||
MidiHome = 69;
|
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) {
|
LoggerService::LoggerService(ConfigService* config, const std::string& loggerId) {
|
||||||
|
|
||||||
if(!(config->getConfig<LoggerParams, LoggerConfig>("Logger", loggerId, &configuration_))) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "IConfig.hpp"
|
#include "IConfig.hpp"
|
||||||
|
|
||||||
struct AudioParams : IParams {
|
struct AudioParams : IParams {
|
||||||
|
std::string id;
|
||||||
uint32_t sampleRate;
|
uint32_t sampleRate;
|
||||||
uint32_t channels;
|
uint32_t channels;
|
||||||
uint32_t stereoMode;
|
uint32_t stereoMode;
|
||||||
uint32_t bufferSize;
|
uint32_t bufferSize;
|
||||||
float pitchStandard;
|
float pitchStandard;
|
||||||
int32_t midiHome;
|
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("SampleRate", params_->sampleRate);
|
||||||
setting.lookupValue("StereoMode", params_->stereoMode);
|
setting.lookupValue("StereoMode", params_->stereoMode);
|
||||||
setting.lookupValue("BufferSize", params_->bufferSize);
|
setting.lookupValue("BufferSize", params_->bufferSize);
|
||||||
|
setting.lookupValue("Channels", params_->channels);
|
||||||
setting.lookupValue("PitchStandard", params_->pitchStandard);
|
setting.lookupValue("PitchStandard", params_->pitchStandard);
|
||||||
setting.lookupValue("MidiHome", params_->midiHome);
|
setting.lookupValue("MidiHome", params_->midiHome);
|
||||||
setting.lookupValue("NotesPerOctave", params_->notesPerOctave);
|
setting.lookupValue("NotesPerOctave", params_->notesPerOctave);
|
||||||
|
setting.lookupValue("OctaveConstant", params_->octaveConstant);
|
||||||
|
setting.lookupValue("MaxVoices", params_->maxVoices);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,12 @@
|
|||||||
|
|
||||||
AudioEngine::AudioEngine(ConfigService* config, LoggerService* logger, Synth* synth) : config_(config), logger_(logger), synth_(synth) {
|
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) {
|
if(audioDevice_.getDeviceCount() < 1) {
|
||||||
logger_->log("Audio", LogFlag::Error, "No audio devices found.");
|
logger_->log("Audio", LogFlag::Error, "No audio devices found.");
|
||||||
}
|
}
|
||||||
@@ -22,17 +28,26 @@ bool AudioEngine::start() {
|
|||||||
// initialize the audio engine
|
// initialize the audio engine
|
||||||
RtAudio::StreamParameters params;
|
RtAudio::StreamParameters params;
|
||||||
params.deviceId = audioDevice_.getDefaultOutputDevice();
|
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;
|
params.firstChannel = 0;
|
||||||
|
|
||||||
RtAudio::StreamOptions options;
|
RtAudio::StreamOptions options;
|
||||||
options.flags = RTAUDIO_MINIMIZE_LATENCY;
|
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) {
|
if(status != RTAUDIO_NO_ERROR) {
|
||||||
logger_->log("Audio", LogFlag::Error, "Error opening RtAudio stream.");
|
logger_->log("Audio", LogFlag::Error, "Error opening RtAudio stream.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = audioDevice_.startStream();
|
status = audioDevice_.startStream();
|
||||||
if(status != RTAUDIO_NO_ERROR) {
|
if(status != RTAUDIO_NO_ERROR) {
|
||||||
@@ -41,7 +56,7 @@ bool AudioEngine::start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sanity check
|
// 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);
|
logger_->log("AudioEngine", LogFlag::Info, msg);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "LoggerService.hpp"
|
#include "LoggerService.hpp"
|
||||||
#include "config/ConfigService.hpp"
|
#include "config/ConfigService.hpp"
|
||||||
|
#include "config/AudioConfig.hpp"
|
||||||
#include "Synth.hpp"
|
#include "Synth.hpp"
|
||||||
#include "NoteQueue.hpp"
|
#include "NoteQueue.hpp"
|
||||||
|
|
||||||
@@ -35,13 +36,10 @@ private:
|
|||||||
|
|
||||||
RtAudio audioDevice_ { AUDIO_API };
|
RtAudio audioDevice_ { AUDIO_API };
|
||||||
|
|
||||||
// TODO: make these configurable
|
|
||||||
uint32_t sampleRate_ = 44100;
|
|
||||||
uint32_t bufferFrames_ = 512;
|
|
||||||
uint32_t channels_ = 2;
|
|
||||||
|
|
||||||
LoggerService* logger_;
|
LoggerService* logger_;
|
||||||
ConfigService* config_;
|
ConfigService* config_;
|
||||||
|
AudioParams configuration_;
|
||||||
|
|
||||||
Synth* synth_;
|
Synth* synth_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
#include "string"
|
#include "string"
|
||||||
|
|
||||||
Instrument::Instrument(ConfigService* config, LoggerService* logger) :
|
Instrument::Instrument(ConfigService* config, LoggerService* logger, float sampleRate) :
|
||||||
config_(config), logger_(logger) {
|
config_(config), logger_(logger), sampleRate_(sampleRate) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,13 @@
|
|||||||
#include "LoggerService.hpp"
|
#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 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 {
|
class Instrument {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Instrument() = default;
|
Instrument() = default;
|
||||||
Instrument(ConfigService* config, LoggerService* logger);
|
Instrument(ConfigService* config, LoggerService* logger, float sampleRate);
|
||||||
~Instrument() = default;
|
~Instrument() = default;
|
||||||
|
|
||||||
virtual void noteOn(float frequency, float velocity);
|
virtual void noteOn(float frequency, float velocity);
|
||||||
@@ -23,7 +23,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
float sampleRate_ = 44100.0f;
|
float sampleRate_ = 1.0f;
|
||||||
bool active_ = false;
|
bool active_ = false;
|
||||||
|
|
||||||
ConfigService* config_;
|
ConfigService* config_;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
#include "PianoString.hpp"
|
#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_current_.resize(segmentCount_ + 1);
|
||||||
stringY_previous_.resize(segmentCount_ + 1);
|
stringY_previous_.resize(segmentCount_ + 1);
|
||||||
|
|||||||
@@ -3,14 +3,14 @@
|
|||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "synth/Instruments/Instrument.hpp"
|
#include "synth/instruments/Instrument.hpp"
|
||||||
|
|
||||||
class PianoString : public Instrument {
|
class PianoString : public Instrument {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
PianoString() = default;
|
PianoString() = default;
|
||||||
PianoString(ConfigService* config, LoggerService* logger);
|
PianoString(ConfigService* config, LoggerService* logger, float sampleRate);
|
||||||
~PianoString() = default;
|
~PianoString() = default;
|
||||||
|
|
||||||
void noteOn(float frequency, float velocity) override;
|
void noteOn(float frequency, float velocity) override;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ KeyboardController::KeyboardController(ConfigService* config, LoggerService* log
|
|||||||
|
|
||||||
// load keymap from config service
|
// load keymap from config service
|
||||||
if(!(config->getConfig<KeymapParams, KeymapConfig>("KeyboardController", "Main", &configuration_))) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
ScopeBuffer::ScopeBuffer(QObject *parent) : QObject(parent) {
|
ScopeBuffer::ScopeBuffer(QObject *parent) : QObject(parent) {
|
||||||
|
// timer is set in qml
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopeBuffer::ScopeBuffer(ConfigService* config, LoggerService* logger, size_t size) :
|
ScopeBuffer::ScopeBuffer(ConfigService* config, LoggerService* logger, size_t size) :
|
||||||
@@ -45,7 +45,7 @@ void Scope::paint(QPainter *painter) {
|
|||||||
|
|
||||||
if(scopeBuffer_ != nullptr) scopeBuffer_->read(buffer_);
|
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;
|
float maxAmp = 1.0f;
|
||||||
/*
|
/*
|
||||||
for(float s : buffer_) {
|
for(float s : buffer_) {
|
||||||
|
|||||||
@@ -4,9 +4,17 @@
|
|||||||
Synth::Synth(ConfigService* config, LoggerService* logger, ScopeBuffer* scope, NoteQueue* queue) :
|
Synth::Synth(ConfigService* config, LoggerService* logger, ScopeBuffer* scope, NoteQueue* queue) :
|
||||||
config_(config), logger_(logger), scope_(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);
|
filter_.setParams(Filter::Type::BiquadLowpass, 4000.0f, 0.707f);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "config/ConfigService.hpp"
|
#include "config/ConfigService.hpp"
|
||||||
|
#include "config/AudioConfig.hpp"
|
||||||
#include "LoggerService.hpp"
|
#include "LoggerService.hpp"
|
||||||
#include "NoteQueue.hpp"
|
#include "NoteQueue.hpp"
|
||||||
#include "Voice.hpp"
|
#include "Voice.hpp"
|
||||||
@@ -29,10 +30,11 @@ private:
|
|||||||
Filter filter_;
|
Filter filter_;
|
||||||
|
|
||||||
// voices
|
// voices
|
||||||
static constexpr size_t MAX_VOICES = 32;
|
size_t maxVoices;
|
||||||
std::array<Voice, MAX_VOICES> voices_;
|
std::vector<Voice> voices_;
|
||||||
|
|
||||||
ConfigService* config_;
|
ConfigService* config_;
|
||||||
|
AudioParams engineConfiguration_;
|
||||||
LoggerService* logger_;
|
LoggerService* logger_;
|
||||||
ScopeBuffer* scope_;;
|
ScopeBuffer* scope_;;
|
||||||
NoteQueue* noteQueue_;
|
NoteQueue* noteQueue_;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
|
|
||||||
#include "Voice.hpp"
|
#include "Voice.hpp"
|
||||||
|
|
||||||
Voice::Voice(ConfigService* config, LoggerService* logger) :
|
Voice::Voice(ConfigService* config, LoggerService* logger, float sampleRate, AudioParams* params) :
|
||||||
config_(config), logger_(logger) {
|
config_(config), logger_(logger), sampleRate_(sampleRate), params_(params) {
|
||||||
|
|
||||||
// TODO: instrument factory
|
// TODO: instrument factory << WHAT DO YOU MEAN JUST TODO A WHOLE FACTORY
|
||||||
instrument_ = PianoString(config_, logger_);
|
instrument_ = PianoString(config_, logger_, sampleRate_);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "Instruments/Instrument.hpp"
|
#include "instruments/Instrument.hpp"
|
||||||
#include "Instruments/PianoString.hpp"
|
#include "instruments/PianoString.hpp"
|
||||||
|
#include "config/AudioConfig.hpp"
|
||||||
|
|
||||||
// a voice is a tone generator that the synth uses for polyphony
|
// 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
|
// 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:
|
public:
|
||||||
|
|
||||||
Voice() = default;
|
Voice() = default;
|
||||||
Voice(ConfigService* config, LoggerService* logger);
|
Voice(ConfigService* config, LoggerService* logger, float sampleRate, AudioParams* params);
|
||||||
~Voice() = default;
|
~Voice() = default;
|
||||||
|
|
||||||
void noteOn(uint8_t midiNote, float velocity);
|
void noteOn(uint8_t midiNote, float velocity);
|
||||||
@@ -26,17 +27,17 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
float sampleRate_ = 44100.0f;
|
|
||||||
|
|
||||||
inline float noteToFrequency(uint8_t note) {
|
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;
|
uint8_t note_ = 0;
|
||||||
float velocity_ = 1.0f;
|
float velocity_ = 1.0f;
|
||||||
bool active_ = false;
|
bool active_ = false;
|
||||||
|
|
||||||
ConfigService* config_;
|
ConfigService* config_;
|
||||||
|
AudioParams* params_;
|
||||||
LoggerService* logger_;
|
LoggerService* logger_;
|
||||||
PianoString instrument_;
|
PianoString instrument_;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user