TeaSpeak-Client/native/serverconnection/src/audio/driver/PortAudioPlayback.cpp

95 lines
2.9 KiB
C++

//
// Created by WolverinDEV on 13/02/2020.
//
#include <algorithm>
#include <cmath>
#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<PortAudioPlayback*>(userData);
assert(player);
player->write_callback(output, frameCount, timeInfo, statusFlags);
return 0;
};
PaStreamParameters parameters{};
memset(&parameters, 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,
&parameters,
(double) kSampleRate,
(unsigned long) (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 (size_t) this->info->defaultSampleRate;
}
void PortAudioPlayback::write_callback(void *output, unsigned long frameCount,
const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags) {
this->fill_buffer(output, frameCount, kChannelCount);
}