100 lines
3.1 KiB
C++
100 lines
3.1 KiB
C++
|
|
#include "AudioEngine.hpp"
|
|
|
|
#include <iostream>
|
|
#include <cmath>
|
|
#include <numbers>
|
|
#include <string>
|
|
|
|
AudioEngine::AudioEngine(ConfigService* config, LoggerService* logger, NoteQueue* noteQueue) : config_(config), logger_(logger), noteQueue_(noteQueue) {
|
|
|
|
if(audioDevice_.getDeviceCount() < 1) {
|
|
std::cout << "No audio devices found" << std::endl;
|
|
}
|
|
|
|
if(logger_ == nullptr) std::cout << "err: logger nullptr" << std::endl;
|
|
|
|
}
|
|
|
|
AudioEngine::~AudioEngine() {
|
|
(void)stop();
|
|
}
|
|
|
|
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.firstChannel = 0;
|
|
|
|
RtAudio::StreamOptions options;
|
|
options.flags = RTAUDIO_MINIMIZE_LATENCY;
|
|
|
|
RtAudioErrorType status = audioDevice_.openStream(¶ms, nullptr, RTAUDIO_FLOAT32, sampleRate_, &bufferFrames_, &AudioEngine::audioCallback, this, &options);
|
|
if(status != RTAUDIO_NO_ERROR) {
|
|
std::cout << "Error opening RtAudio stream" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
status = audioDevice_.startStream();
|
|
if(status != RTAUDIO_NO_ERROR) {
|
|
std::cout << "Error starting RtAudio stream" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
// sanity check
|
|
std::string msg = "sample rate: " + std::to_string(sampleRate_) + ", buffer frames: " + std::to_string(bufferFrames_);
|
|
logger_->log("AudioEngine", LogFlag::Info, msg);
|
|
return true;
|
|
|
|
}
|
|
|
|
bool AudioEngine::stop() {
|
|
|
|
if(audioDevice_.isStreamRunning()) audioDevice_.stopStream();
|
|
if(audioDevice_.isStreamOpen()) audioDevice_.closeStream();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
int32_t AudioEngine::audioCallback(void* outputBuffer, void* inputBuffer, uint32_t nFrames, double, RtAudioStreamStatus status, void* userData) {
|
|
|
|
// error if the callback is late
|
|
if(status) static_cast<AudioEngine*>(userData)->logger_->log("AudioEngine", LogFlag::Warning, "Audio buffer underflow");
|
|
|
|
return static_cast<AudioEngine*>(userData)->process(static_cast<float*>(outputBuffer), static_cast<size_t>(nFrames));
|
|
|
|
}
|
|
|
|
int32_t AudioEngine::process(float* out, size_t nFrames) {
|
|
|
|
NoteEvent noteEvent;
|
|
while(noteQueue_->pop(noteEvent)) {
|
|
//std::string msg = "[NoteEvent] Type: " + std::to_string(noteEvent.type) +" Velocity: " + std::to_string(noteEvent.velocity) + " Note:" + std::to_string(noteEvent.note);
|
|
//logger_->log("Audio", LogFlag::Debug, msg);
|
|
noteOn_ = (noteEvent.type == NoteEventType::NoteOn) ? true : false;
|
|
freq_ = 440.0f * pow(2.0f, (static_cast<float>(noteEvent.note) - 69.0f) / 12.0f);
|
|
}
|
|
|
|
const float twoPi = 2.0f*std::numbers::pi_v<float>;
|
|
float phaseIncrement = twoPi * freq_ / sampleRate_;
|
|
|
|
for(size_t i = 0; i < nFrames; i++) {
|
|
|
|
// simulate a sine wave
|
|
phase_ += phaseIncrement;
|
|
if(phase_ > twoPi) {
|
|
phase_ -= twoPi;
|
|
}
|
|
float outSample = std::sin(phase_) / 4.0f * noteOn_;
|
|
|
|
out[2*i] = outSample;
|
|
out[2*i+1] = outSample;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|