Fix synchronization issues and ensure we don't call Pa_WriteStream() if

the buffer is full, otherwise it'll block.
This commit is contained in:
casey langen 2022-12-29 17:43:29 -08:00
parent 4afc8af219
commit 04fe549bf5

View File

@ -204,67 +204,81 @@ IDevice* PortAudioOut::GetDefaultDevice() {
}
OutputState PortAudioOut::Play(IBuffer *buffer, IBufferProvider *provider) {
std::unique_lock<decltype(this->mutex)> lock(this->mutex);
bool bufferWritten = false;
if (this->state == StatePaused) {
return OutputState::InvalidState;
}
{
std::unique_lock<decltype(this->mutex)> lock(this->mutex);
if (!this->paStream) {
auto device = static_cast<PortAudioDevice*>(this->GetDefaultDevice());
if (device) {
PaStreamParameters params = { 0 };
params.device = device->Index();
params.channelCount = buffer->Channels();
params.sampleFormat = paFloat32;
params.suggestedLatency = Pa_GetDeviceInfo(device->Index())->defaultHighOutputLatency;
params.hostApiSpecificStreamInfo = nullptr;
PaError result = Pa_OpenStream(
&this->paStream,
nullptr,
&params,
buffer->SampleRate(),
buffer->Samples() / buffer->Channels(),
0 /* stream flags */,
nullptr /* buffer callback; nullptr = blocking */,
nullptr /* callback context */);
logPaError("Pa_OpenStream", result);
if (result != paNoError) {
return OutputState::InvalidState;
}
result = Pa_StartStream(this->paStream);
logPaError("Pa_StartStream", result);
device->Release();
if (this->state == StatePaused) {
return OutputState::InvalidState;
}
}
if (this->paStream) {
auto audio = buffer->BufferPointer();
auto const samples = buffer->Samples();
auto const frameCount = samples / buffer->Channels();
if (volume != 1.0f) {
float gain = 0.0;
if (volume > 0) {
float dB = 20.0 * std::log(volume/1.0);
gain = std::pow(10.0, dB / 20.0);
}
for (size_t i = 0; i < samples; i++) {
(*audio) *= gain;
++audio;
if (!this->paStream) {
auto device = static_cast<PortAudioDevice*>(this->GetDefaultDevice());
if (device) {
PaStreamParameters params = { 0 };
params.device = device->Index();
params.channelCount = buffer->Channels();
params.sampleFormat = paFloat32;
params.suggestedLatency = Pa_GetDeviceInfo(device->Index())->defaultHighOutputLatency;
params.hostApiSpecificStreamInfo = nullptr;
PaError result = Pa_OpenStream(
&this->paStream,
nullptr,
&params,
buffer->SampleRate(),
buffer->Samples() / buffer->Channels(),
0 /* stream flags */,
nullptr /* buffer callback; nullptr = blocking */,
nullptr /* callback context */);
logPaError("Pa_OpenStream", result);
if (result != paNoError) {
return OutputState::InvalidState;
}
result = Pa_StartStream(this->paStream);
logPaError("Pa_StartStream", result);
device->Release();
}
}
PaError result = Pa_WriteStream(
this->paStream, buffer->BufferPointer(), frameCount);
if (this->paStream) {
auto audio = buffer->BufferPointer();
auto const samples = buffer->Samples();
auto const frameCount = samples / buffer->Channels();
auto const writeAvailable = Pa_GetStreamWriteAvailable(this->paStream);
if (result == paNoError) {
provider->OnBufferProcessed(buffer);
return OutputState::BufferWritten;
if (writeAvailable < frameCount) {
int retryMs = buffer->SampleRate() / samples;
return static_cast<OutputState>(retryMs);
}
if (volume != 1.0f) {
float gain = 0.0;
if (volume > 0) {
float dB = 20.0 * std::log(volume/1.0);
gain = std::pow(10.0, dB / 20.0);
}
for (size_t i = 0; i < samples; i++) {
(*audio) *= gain;
++audio;
}
}
PaError result = Pa_WriteStream(
this->paStream, buffer->BufferPointer(), frameCount);
if (result == paNoError) {
bufferWritten = true;
}
}
}
return OutputState::BufferFull;
if (bufferWritten) {
provider->OnBufferProcessed(buffer);
return OutputState::BufferWritten;
}
return OutputState::InvalidState;
}
double PortAudioOut::Latency() {