muzgen/lib/AudioEngine.cpp

112 lines
2.5 KiB
C++
Raw Permalink Normal View History

#include "AudioEngine.hpp"
#include "Signal.hpp"
namespace muz
{
/*explicit*/ AudioEngine::AudioEngine(AudioConf const& conf)
: m_conf { conf }
, m_frame { std::vector<float>(m_conf.channels(), 0.0f) }
{
check_error(Pa_Initialize());
}
/*virtual*/ AudioEngine::~AudioEngine()
{
check_error(Pa_Terminate());
}
void AudioEngine::init()
{
PaError err = Pa_OpenDefaultStream(&m_stream,
0 /*input*/,
m_conf.channels() /*output*/,
paFloat32,
m_conf.samplerate(),
m_conf.frames_per_buffer() ,
&AudioEngine::callback,
this);
check_error(err);
}
void AudioEngine::run()
{
if (!m_stream)
{
throw audio_error {"audio engine not initialized"};
}
Pa_StartStream(m_stream);
while (!m_sig_queue.empty())
{
std::cin.get();
pop_signal();
}
Pa_StopStream(m_stream);
}
void AudioEngine::push_signal(std::unique_ptr<Signal> signal)
{
std::lock_guard<std::mutex> mtx(m_sig_mtx);
m_sig_queue.push_back(std::move(signal));
}
void AudioEngine::pop_signal()
{
std::lock_guard<std::mutex> mtx(m_sig_mtx);
if (!m_sig_queue.empty())
{
m_sig_queue.pop_back();
}
}
void AudioEngine::check_error(PaError err)
{
if (err != paNoError)
{
std::string msg = Pa_GetErrorText(err);
throw audio_error {"cannot initalize portaudio: " + msg};
}
}
std::vector<float> AudioEngine::next()
{
std::lock_guard<std::mutex> mtx(m_sig_mtx);
if (m_sig_queue.empty())
{
return {0.0f, 0.0f};
}
m_sig_queue.back()->next(m_frame);
return m_frame;
}
/*static*/ int AudioEngine::callback(void const*, void* output,
unsigned long frames_per_buffer,
PaStreamCallbackTimeInfo const*,
PaStreamCallbackFlags,
void* data)
{
AudioEngine* engine = static_cast<AudioEngine*>(data);
float* out = static_cast<float*>(output);
size_t k = 0;
for (size_t i=0; i<frames_per_buffer; i++)
{
auto const& frame = engine->next();
for (float val: frame)
{
out[k++] = val;
}
}
return paContinue;
}
}