- Fixed Crossfader to adjust for time spent actually adjusting the

output's volume

- Reduced lock contention in WasapiOut and DirectSoundOut by caching
  AddRef()'d copies of important interfaces in the ::Play() methods.
This commit is contained in:
casey langen 2016-12-26 15:22:49 -08:00
parent 6bffe8a54d
commit 4fd664b08a
3 changed files with 108 additions and 79 deletions

View File

@ -187,6 +187,8 @@ void DirectSoundOut::Stop() {
} }
bool DirectSoundOut::Play(IBuffer *buffer, IBufferProvider *provider) { bool DirectSoundOut::Play(IBuffer *buffer, IBufferProvider *provider) {
IDirectSoundBuffer8 *outputBuffer = nullptr;
{ {
Lock lock(this->stateMutex); Lock lock(this->stateMutex);
@ -199,6 +201,12 @@ bool DirectSoundOut::Play(IBuffer *buffer, IBufferProvider *provider) {
return false; return false;
} }
/* 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; unsigned char *dst1 = nullptr, *dst2 = nullptr;
DWORD size1 = 0, size2 = 0; DWORD size1 = 0, size2 = 0;
DWORD availableBytes = 0; DWORD availableBytes = 0;
@ -206,7 +214,7 @@ bool DirectSoundOut::Play(IBuffer *buffer, IBufferProvider *provider) {
do { do {
availableBytes = getAvailableBytes( availableBytes = getAvailableBytes(
this->secondaryBuffer, outputBuffer,
this->writeOffset, this->writeOffset,
this->bufferSize); this->bufferSize);
@ -222,7 +230,7 @@ bool DirectSoundOut::Play(IBuffer *buffer, IBufferProvider *provider) {
} }
HRESULT result = HRESULT result =
this->secondaryBuffer->Lock( outputBuffer->Lock(
writeOffset, writeOffset,
bufferBytes, bufferBytes,
(void **) &dst1, &size1, (void **) &dst1, &size1,
@ -230,7 +238,7 @@ bool DirectSoundOut::Play(IBuffer *buffer, IBufferProvider *provider) {
0); 0);
if (result == DSERR_BUFFERLOST) { if (result == DSERR_BUFFERLOST) {
this->secondaryBuffer->Restore(); outputBuffer->Restore();
result = this->secondaryBuffer->Lock( result = this->secondaryBuffer->Lock(
writeOffset, writeOffset,
@ -254,8 +262,8 @@ bool DirectSoundOut::Play(IBuffer *buffer, IBufferProvider *provider) {
writeOffset += bufferBytes; writeOffset += bufferBytes;
writeOffset %= this->bufferSize; writeOffset %= this->bufferSize;
this->secondaryBuffer->Unlock((void *)dst1, size1, (void *)dst2, size2); outputBuffer->Unlock((void *)dst1, size1, (void *)dst2, size2);
} outputBuffer->Release();
provider->OnBufferProcessed(buffer); provider->OnBufferProcessed(buffer);

View File

@ -149,6 +149,12 @@ void WasapiOut::Drain() {
} }
bool WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) { 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); Lock lock(this->stateMutex);
@ -161,6 +167,13 @@ bool WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) {
return false; return false;
} }
renderClient = this->renderClient;
renderClient->AddRef();
audioClient = this->audioClient;
audioClient->AddRef();
}
UINT32 availableFrames = 0; UINT32 availableFrames = 0;
UINT32 frameOffset = 0; UINT32 frameOffset = 0;
UINT32 samples = (UINT32) buffer->Samples(); UINT32 samples = (UINT32) buffer->Samples();
@ -168,7 +181,7 @@ bool WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) {
int channels = buffer->Channels(); int channels = buffer->Channels();
do { do {
this->audioClient->GetCurrentPadding(&frameOffset); audioClient->GetCurrentPadding(&frameOffset);
availableFrames = (this->outputBufferFrames - frameOffset); availableFrames = (this->outputBufferFrames - frameOffset);
if (availableFrames < framesToWrite) { if (availableFrames < framesToWrite) {
@ -178,10 +191,7 @@ bool WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) {
} }
} while (this->state == StatePlaying && availableFrames < framesToWrite); } while (this->state == StatePlaying && availableFrames < framesToWrite);
if (state != StatePlaying) { if (state == StatePlaying) {
return false;
}
if (availableFrames >= framesToWrite) { if (availableFrames >= framesToWrite) {
BYTE *data = 0; BYTE *data = 0;
HRESULT result = this->renderClient->GetBuffer(framesToWrite, &data); 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); memcpy(data, buffer->BufferPointer(), sizeof(float) * samples);
this->renderClient->ReleaseBuffer(framesToWrite, 0); renderClient->ReleaseBuffer(framesToWrite, 0);
} }
} }
renderClient->Release();
audioClient->Release();
provider->OnBufferProcessed(buffer); provider->OnBufferProcessed(buffer);
return true; return true;

View File

@ -38,10 +38,12 @@
#include <core/runtime/Message.h> #include <core/runtime/Message.h>
#include <algorithm> #include <algorithm>
#include <chrono>
using namespace musik::core::audio; using namespace musik::core::audio;
using namespace musik::core::sdk; using namespace musik::core::sdk;
using namespace musik::core::runtime; using namespace musik::core::runtime;
using namespace std::chrono;
#define TICKS_PER_SECOND 30 #define TICKS_PER_SECOND 30
#define TICK_TIME_MILLIS (1000 / TICKS_PER_SECOND) #define TICK_TIME_MILLIS (1000 / TICKS_PER_SECOND)
@ -51,6 +53,10 @@ using namespace musik::core::runtime;
this->messageQueue.Post(Message::Create( \ this->messageQueue.Post(Message::Create( \
this, MESSAGE_TICK, 0, 0), TICK_TIME_MILLIS) 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) \ #define LOCK(x) \
std::unique_lock<std::recursive_mutex> lock(x); std::unique_lock<std::recursive_mutex> lock(x);
@ -214,6 +220,8 @@ void Crossfader::ProcessMessage(IMessage &message) {
case MESSAGE_TICK: { case MESSAGE_TICK: {
bool emptied = false; bool emptied = false;
auto start = system_clock::now().time_since_epoch();
{ {
LOCK(this->contextListLock); LOCK(this->contextListLock);
@ -278,12 +286,7 @@ void Crossfader::ProcessMessage(IMessage &message) {
} }
} }
if (this->contextList.size()) { emptied = (this->contextList.size() == 0);
ENQUEUE_TICK();
}
else {
emptied = true;
}
} /* end critical section */ } /* end critical section */
/* notify outside of the critical section! */ /* notify outside of the critical section! */
@ -291,6 +294,11 @@ void Crossfader::ProcessMessage(IMessage &message) {
this->Emptied(); this->Emptied();
this->drainCondition.notify_all(); this->drainCondition.notify_all();
} }
else {
auto end = system_clock::now().time_since_epoch();
int64 duration = duration_cast<milliseconds>(end - start).count();
ENQUEUE_ADJUSTED_TICK(TICK_TIME_MILLIS - duration);
}
} }
break; break;
} }