// // Created by WolverinDEV on 13/02/2020. // #include #include #include "./PortAudio.h" #include "../../logger.h" using namespace tc::audio::pa; PortAudioPlayback::PortAudioPlayback(PaDeviceIndex index, const PaDeviceInfo *info) : index{index}, info{info} { } PortAudioPlayback::~PortAudioPlayback() { this->stop(); } bool PortAudioPlayback::impl_start(std::string &error) { //TODO: Detect correct sample rate { auto device_info = Pa_GetDeviceInfo(this->index); if(this->info != device_info) { error = "invalid info pointer"; return false; } } const auto proxied_write_callback = []( const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { assert(output); auto player = reinterpret_cast(userData); assert(player); player->write_callback(output, frameCount, timeInfo, statusFlags); return 0; }; PaStreamParameters parameters{}; memset(¶meters, 0, sizeof(parameters)); parameters.channelCount = (int) kChannelCount; parameters.device = this->index; parameters.sampleFormat = paFloat32; parameters.suggestedLatency = this->info->defaultLowInputLatency; auto err = Pa_OpenStream( &this->stream, nullptr, ¶meters, (double) kSampleRate, kSampleRate * kTimeSpan, paClipOff, proxied_write_callback, this); if(err != paNoError) { this->stream = nullptr; error = std::string{Pa_GetErrorText(err)} + " (open stream: " + std::to_string(err) + ")"; return false; } err = Pa_StartStream(this->stream); if(err != paNoError) { error = std::string{Pa_GetErrorText(err)} + "(start stream: " + std::to_string(err) + ")"; err = Pa_CloseStream(this->stream); if(err != paNoError) log_critical(category::audio, tr("Failed to close opened pa stream. This will cause memory leaks. Error: {}/{}"), err, Pa_GetErrorText(err)); return false; } return true; } void PortAudioPlayback::impl_stop() { if(Pa_IsStreamActive(this->stream)) Pa_AbortStream(this->stream); auto error = Pa_CloseStream(this->stream); if(error != paNoError) log_error(category::audio, tr("Failed to close PA stream: {}"), error); this->stream = nullptr; } size_t PortAudioPlayback::sample_rate() const { return this->info->defaultSampleRate; } void PortAudioPlayback::write_callback(void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) { this->fill_buffer(output, frameCount, kChannelCount); }