diff --git a/src/plugins/portaudioout/PortAudioOut.cpp b/src/plugins/portaudioout/PortAudioOut.cpp index 501ecf4e1..b09a43fad 100644 --- a/src/plugins/portaudioout/PortAudioOut.cpp +++ b/src/plugins/portaudioout/PortAudioOut.cpp @@ -204,67 +204,81 @@ IDevice* PortAudioOut::GetDefaultDevice() { } OutputState PortAudioOut::Play(IBuffer *buffer, IBufferProvider *provider) { - std::unique_lockmutex)> lock(this->mutex); + bool bufferWritten = false; - if (this->state == StatePaused) { - return OutputState::InvalidState; - } + { + std::unique_lockmutex)> lock(this->mutex); - if (!this->paStream) { - auto device = static_cast(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, - ¶ms, - 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(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, + ¶ms, + 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(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() {