diff --git a/src/contrib/directsoundout/DirectSoundOut.cpp b/src/contrib/directsoundout/DirectSoundOut.cpp index edc7cffaf..19d0e3a1d 100644 --- a/src/contrib/directsoundout/DirectSoundOut.cpp +++ b/src/contrib/directsoundout/DirectSoundOut.cpp @@ -187,6 +187,8 @@ void DirectSoundOut::Stop() { } bool DirectSoundOut::Play(IBuffer *buffer, IBufferProvider *provider) { + IDirectSoundBuffer8 *outputBuffer = nullptr; + { Lock lock(this->stateMutex); @@ -199,64 +201,70 @@ bool DirectSoundOut::Play(IBuffer *buffer, IBufferProvider *provider) { return false; } - unsigned char *dst1 = nullptr, *dst2 = nullptr; - DWORD size1 = 0, size2 = 0; - DWORD availableBytes = 0; - DWORD bufferBytes = buffer->Bytes(); - - do { - availableBytes = getAvailableBytes( - this->secondaryBuffer, - this->writeOffset, - this->bufferSize); - - if (bufferBytes > availableBytes) { - int samples = (bufferBytes - availableBytes) / sizeof(float) / channels; - int sleepMs = ((long long)(samples * 1000) / rate) + 1; - Sleep(sleepMs); - } - } while (this->state == StatePlaying && availableBytes < bufferBytes); - - if (this->state != StatePlaying) { - return false; - } - - HRESULT result = - this->secondaryBuffer->Lock( - writeOffset, - bufferBytes, - (void **) &dst1, &size1, - (void **) &dst2, &size2, - 0); - - if (result == DSERR_BUFFERLOST) { - this->secondaryBuffer->Restore(); - - result = this->secondaryBuffer->Lock( - writeOffset, - bufferBytes, - (void **) &dst1, &size1, - (void **) &dst2, &size2, - 0); - } - - if (result != DS_OK) { - return false; - } - - char* bufferPointer = (char *) buffer->BufferPointer(); - - memcpy(dst1, bufferPointer, size1); - if (size2 > 0) { - memcpy(dst2, bufferPointer + size1, size2); - } - - writeOffset += bufferBytes; - writeOffset %= this->bufferSize; - - this->secondaryBuffer->Unlock((void *)dst1, size1, (void *)dst2, size2); + /* reduce lock contention: cache a reference to the buffer here, + just in case someone comes along and release it */ + outputBuffer = this->secondaryBuffer; + outputBuffer->AddRef(); } + unsigned char *dst1 = nullptr, *dst2 = nullptr; + DWORD size1 = 0, size2 = 0; + DWORD availableBytes = 0; + DWORD bufferBytes = buffer->Bytes(); + + do { + availableBytes = getAvailableBytes( + outputBuffer, + this->writeOffset, + this->bufferSize); + + if (bufferBytes > availableBytes) { + int samples = (bufferBytes - availableBytes) / sizeof(float) / channels; + int sleepMs = ((long long)(samples * 1000) / rate) + 1; + Sleep(sleepMs); + } + } while (this->state == StatePlaying && availableBytes < bufferBytes); + + if (this->state != StatePlaying) { + return false; + } + + HRESULT result = + outputBuffer->Lock( + writeOffset, + bufferBytes, + (void **) &dst1, &size1, + (void **) &dst2, &size2, + 0); + + if (result == DSERR_BUFFERLOST) { + outputBuffer->Restore(); + + result = this->secondaryBuffer->Lock( + writeOffset, + bufferBytes, + (void **) &dst1, &size1, + (void **) &dst2, &size2, + 0); + } + + if (result != DS_OK) { + return false; + } + + char* bufferPointer = (char *) buffer->BufferPointer(); + + memcpy(dst1, bufferPointer, size1); + if (size2 > 0) { + memcpy(dst2, bufferPointer + size1, size2); + } + + writeOffset += bufferBytes; + writeOffset %= this->bufferSize; + + outputBuffer->Unlock((void *)dst1, size1, (void *)dst2, size2); + outputBuffer->Release(); + provider->OnBufferProcessed(buffer); return true; diff --git a/src/contrib/wasapiout/WasapiOut.cpp b/src/contrib/wasapiout/WasapiOut.cpp index 12b99ac0b..8691df466 100644 --- a/src/contrib/wasapiout/WasapiOut.cpp +++ b/src/contrib/wasapiout/WasapiOut.cpp @@ -149,6 +149,12 @@ void WasapiOut::Drain() { } bool WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) { + IAudioRenderClient *renderClient = nullptr; + IAudioClient *audioClient = nullptr; + + /* reduce lock contention by snagging the references to the + COM interfaces we care about and calling AddRef(), then operating + on the local copies. */ { Lock lock(this->stateMutex); @@ -161,27 +167,31 @@ bool WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) { return false; } - UINT32 availableFrames = 0; - UINT32 frameOffset = 0; - UINT32 samples = (UINT32) buffer->Samples(); - UINT32 framesToWrite = samples / (UINT32) buffer->Channels(); - int channels = buffer->Channels(); + renderClient = this->renderClient; + renderClient->AddRef(); - do { - this->audioClient->GetCurrentPadding(&frameOffset); - availableFrames = (this->outputBufferFrames - frameOffset); + audioClient = this->audioClient; + audioClient->AddRef(); + } - if (availableFrames < framesToWrite) { - UINT32 delta = framesToWrite - availableFrames; - REFERENCE_TIME sleepTime = (delta * 1000 * 1000 * 10) / buffer->SampleRate(); - std::this_thread::sleep_for(std::chrono::microseconds(sleepTime)); - } - } while (this->state == StatePlaying && availableFrames < framesToWrite); + UINT32 availableFrames = 0; + UINT32 frameOffset = 0; + UINT32 samples = (UINT32) buffer->Samples(); + UINT32 framesToWrite = samples / (UINT32) buffer->Channels(); + int channels = buffer->Channels(); - if (state != StatePlaying) { - return false; + do { + audioClient->GetCurrentPadding(&frameOffset); + availableFrames = (this->outputBufferFrames - frameOffset); + + if (availableFrames < framesToWrite) { + UINT32 delta = framesToWrite - availableFrames; + REFERENCE_TIME sleepTime = (delta * 1000 * 1000 * 10) / buffer->SampleRate(); + std::this_thread::sleep_for(std::chrono::microseconds(sleepTime)); } + } while (this->state == StatePlaying && availableFrames < framesToWrite); + if (state == StatePlaying) { if (availableFrames >= framesToWrite) { BYTE *data = 0; HRESULT result = this->renderClient->GetBuffer(framesToWrite, &data); @@ -191,10 +201,13 @@ bool WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) { } memcpy(data, buffer->BufferPointer(), sizeof(float) * samples); - this->renderClient->ReleaseBuffer(framesToWrite, 0); + renderClient->ReleaseBuffer(framesToWrite, 0); } } + renderClient->Release(); + audioClient->Release(); + provider->OnBufferProcessed(buffer); return true; diff --git a/src/core/audio/Crossfader.cpp b/src/core/audio/Crossfader.cpp index b517d9c33..b1ef09d29 100644 --- a/src/core/audio/Crossfader.cpp +++ b/src/core/audio/Crossfader.cpp @@ -38,10 +38,12 @@ #include #include +#include using namespace musik::core::audio; using namespace musik::core::sdk; using namespace musik::core::runtime; +using namespace std::chrono; #define TICKS_PER_SECOND 30 #define TICK_TIME_MILLIS (1000 / TICKS_PER_SECOND) @@ -51,6 +53,10 @@ using namespace musik::core::runtime; this->messageQueue.Post(Message::Create( \ this, MESSAGE_TICK, 0, 0), TICK_TIME_MILLIS) +#define ENQUEUE_ADJUSTED_TICK(delay) \ + this->messageQueue.Post(Message::Create( \ + this, MESSAGE_TICK, 0, 0), delay > 0 ? delay : 0) + #define LOCK(x) \ std::unique_lock lock(x); @@ -214,6 +220,8 @@ void Crossfader::ProcessMessage(IMessage &message) { case MESSAGE_TICK: { bool emptied = false; + auto start = system_clock::now().time_since_epoch(); + { LOCK(this->contextListLock); @@ -278,12 +286,7 @@ void Crossfader::ProcessMessage(IMessage &message) { } } - if (this->contextList.size()) { - ENQUEUE_TICK(); - } - else { - emptied = true; - } + emptied = (this->contextList.size() == 0); } /* end critical section */ /* notify outside of the critical section! */ @@ -291,6 +294,11 @@ void Crossfader::ProcessMessage(IMessage &message) { this->Emptied(); this->drainCondition.notify_all(); } + else { + auto end = system_clock::now().time_since_epoch(); + int64 duration = duration_cast(end - start).count(); + ENQUEUE_ADJUSTED_TICK(TICK_TIME_MILLIS - duration); + } } break; }