mirror of
https://github.com/clangen/musikcube.git
synced 2025-02-22 15:40:02 +00:00
- 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:
parent
6bffe8a54d
commit
4fd664b08a
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -38,10 +38,12 @@
|
||||
#include <core/runtime/Message.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
|
||||
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<std::recursive_mutex> 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<milliseconds>(end - start).count();
|
||||
ENQUEUE_ADJUSTED_TICK(TICK_TIME_MILLIS - duration);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user