Skip to content

Commit

Permalink
Avoid calling GetCurrentPadding on exclusive mode streams
Browse files Browse the repository at this point in the history
  • Loading branch information
kcat committed Dec 27, 2024
1 parent 42476b6 commit b22c3ac
Showing 1 changed file with 72 additions and 60 deletions.
132 changes: 72 additions & 60 deletions alc/backends/wasapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,7 @@ struct WasapiPlayback final : public BackendBase {
uint mBufferFilled{0};
SampleConverterPtr mResampler;
bool mMonoUpsample{false};
bool mExclusiveMode{false};

WAVEFORMATEXTENSIBLE mFormat{};
std::atomic<UINT32> mPadding{0u};
Expand Down Expand Up @@ -1213,6 +1214,8 @@ FORCE_ALIGN void WasapiPlayback::mixerProc(PlainDevice &audio)
const UINT32 buffer_len{mOutBufferSize};
const void *resbufferptr{};

assert(buffer_len > 0);

#ifdef AVRTAPI
/* TODO: "Audio" or "Pro Audio"? The suggestion is to use "Pro Audio" for
* device periods less than 10ms, and "Audio" for greater than or equal to
Expand All @@ -1226,85 +1229,92 @@ FORCE_ALIGN void WasapiPlayback::mixerProc(PlainDevice &audio)
mBufferFilled = 0;
while(!mKillNow.load(std::memory_order_relaxed))
{
UINT32 written{};
HRESULT hr{audio.mClient->GetCurrentPadding(&written)};
if(FAILED(hr))
{
ERR("Failed to get padding: {:#x}", as_unsigned(hr));
mDevice->handleDisconnect("Failed to retrieve buffer padding: {:#x}",
as_unsigned(hr));
break;
}
mPadding.store(written, std::memory_order_relaxed);

const auto len = uint{buffer_len - written};
if(len == 0)
/* For exclusive mode, assume buffer_len sample frames are writable.
* The first pass will be a prefill of the buffer, while subsequent
* passes will only occur after notify events.
* IAudioClient::GetCurrentPadding shouldn't be used with exclusive
* streams that use event notifications, according to the docs, we
* should just assume a buffer length is writable after notification.
*/
auto written = UINT32{};
if(!mExclusiveMode)
{
if(prefilling)
if(auto hr = audio.mClient->GetCurrentPadding(&written); FAILED(hr))
{
prefilling = false;
ResetEvent(mNotifyEvent);
hr = audio.mClient->Start();
if(FAILED(hr))
{
ERR("Failed to start audio client: {:#x}", as_unsigned(hr));
mDevice->handleDisconnect("Failed to start audio client: {:#x}",
as_unsigned(hr));
break;
}
ERR("Failed to get padding: {:#x}", as_unsigned(hr));
mDevice->handleDisconnect("Failed to retrieve buffer padding: {:#x}",
as_unsigned(hr));
break;
}

if(DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)}; res != WAIT_OBJECT_0)
ERR("WaitForSingleObjectEx error: {:#x}", res);
continue;
mPadding.store(written, std::memory_order_relaxed);
}

BYTE *buffer{};
hr = audio.mRender->GetBuffer(len, &buffer);
if(SUCCEEDED(hr))
if(const auto len = uint{buffer_len - written})
{
if(mResampler)
auto buffer = LPBYTE{};
auto hr = audio.mRender->GetBuffer(len, &buffer);
if(SUCCEEDED(hr))
{
std::lock_guard<std::mutex> dlock{mMutex};
auto dst = al::span{buffer, size_t{len}*frame_size};
for(UINT32 done{0};done < len;)
if(mResampler)
{
if(mBufferFilled == 0)
auto dlock = std::lock_guard{mMutex};
auto dst = al::span{buffer, size_t{len}*frame_size};
for(UINT32 done{0};done < len;)
{
mDevice->renderSamples(mResampleBuffer.data(), mDevice->mUpdateSize,
mFormat.Format.nChannels);
resbufferptr = mResampleBuffer.data();
mBufferFilled = mDevice->mUpdateSize;
if(mBufferFilled == 0)
{
mDevice->renderSamples(mResampleBuffer.data(), mDevice->mUpdateSize,
mFormat.Format.nChannels);
resbufferptr = mResampleBuffer.data();
mBufferFilled = mDevice->mUpdateSize;
}

const auto got = mResampler->convert(&resbufferptr, &mBufferFilled,
dst.data(), len-done);
dst = dst.subspan(size_t{got}*frame_size);
done += got;
}
mPadding.store(written + len, std::memory_order_relaxed);
}
else
{
auto dlock = std::lock_guard{mMutex};
mDevice->renderSamples(buffer, len, mFormat.Format.nChannels);
mPadding.store(written + len, std::memory_order_relaxed);
}

uint got{mResampler->convert(&resbufferptr, &mBufferFilled, dst.data(),
len-done)};
dst = dst.subspan(size_t{got}*frame_size);
done += got;
if(mMonoUpsample)
{
DuplicateSamples(al::span{buffer, size_t{len}*frame_size}, mDevice->FmtType,
mFormat.Format.nChannels);
}
mPadding.store(written + len, std::memory_order_relaxed);

hr = audio.mRender->ReleaseBuffer(len, 0);
}
else
if(FAILED(hr))
{
std::lock_guard<std::mutex> dlock{mMutex};
mDevice->renderSamples(buffer, len, mFormat.Format.nChannels);
mPadding.store(written + len, std::memory_order_relaxed);
ERR("Failed to buffer data: {:#x}", as_unsigned(hr));
mDevice->handleDisconnect("Failed to send playback samples: {:#x}",
as_unsigned(hr));
break;
}
}

if(mMonoUpsample)
if(prefilling)
{
prefilling = false;
ResetEvent(mNotifyEvent);
if(auto hr = audio.mClient->Start(); FAILED(hr))
{
DuplicateSamples(al::span{buffer, size_t{len}*frame_size}, mDevice->FmtType,
mFormat.Format.nChannels);
ERR("Failed to start audio client: {:#x}", as_unsigned(hr));
mDevice->handleDisconnect("Failed to start audio client: {:#x}",
as_unsigned(hr));
break;
}

hr = audio.mRender->ReleaseBuffer(len, 0);
}
if(FAILED(hr))
{
ERR("Failed to buffer data: {:#x}", as_unsigned(hr));
mDevice->handleDisconnect("Failed to send playback samples: {:#x}", as_unsigned(hr));
break;
}

if(DWORD res{WaitForSingleObjectEx(mNotifyEvent, 2000, FALSE)}; res != WAIT_OBJECT_0)
ERR("WaitForSingleObjectEx error: {:#x}", res);
}
mPadding.store(0u, std::memory_order_release);
audio.mClient->Stop();
Expand Down Expand Up @@ -1945,6 +1955,7 @@ auto WasapiPlayback::resetProxy(DeviceHelper &helper, DeviceHandle &mmdev,

mDevice->Flags.reset(Virtualization);
mMonoUpsample = false;
mExclusiveMode = false;

auto &audio = audiodev.emplace<PlainDevice>();
auto hr = helper.activateAudioClient(mmdev, __uuidof(IAudioClient),
Expand Down Expand Up @@ -2095,6 +2106,7 @@ auto WasapiPlayback::resetProxy(DeviceHelper &helper, DeviceHandle &mmdev,
const auto sharemode =
GetConfigValueBool(mDevice->mDeviceName, "wasapi", "exclusive-mode", false)
? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED;
mExclusiveMode = (sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE);

TraceFormat("Requesting playback format", &OutputType.Format);
hr = audio.mClient->IsFormatSupported(sharemode, &OutputType.Format, al::out_ptr(wfx));
Expand Down

0 comments on commit b22c3ac

Please sign in to comment.