Skip to content

Commit

Permalink
Add workaround for audio bug on Windows when output device has more t…
Browse files Browse the repository at this point in the history
…han 2 channels

Co-authored-by: Casper Jeukendrup <[email protected]>
  • Loading branch information
mathesoncalum and cbjeukendrup committed Sep 25, 2024
1 parent 967f2e1 commit b6933ff
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 6 deletions.
4 changes: 2 additions & 2 deletions src/framework/audio/audiomodule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ void AudioModule::setupAudioDriver(const IApplication::RunMode& mode)

if (m_configuration->shouldMeasureInputLag()) {
requiredSpec.callback = [this](void* /*userdata*/, uint8_t* stream, int byteCount) {
auto samplesPerChannel = byteCount / (2 * sizeof(float));
auto samplesPerChannel = byteCount / (2 * sizeof(float)); // 2 == m_configuration->audioChannelsCount()
float* dest = reinterpret_cast<float*>(stream);
m_audioBuffer->pop(dest, samplesPerChannel);
measureInputLag(dest, samplesPerChannel * m_audioBuffer->audioChannelCount());
Expand Down Expand Up @@ -298,7 +298,7 @@ void AudioModule::setupAudioWorker(const IAudioDriver::Spec& activeSpec)

// Setup audio engine
m_audioEngine->init(m_audioBuffer, consts);
m_audioEngine->setAudioChannelsCount(activeSpec.channels);
m_audioEngine->setAudioChannelsCount(m_configuration->audioChannelsCount());
m_audioEngine->setSampleRate(activeSpec.sampleRate);
m_audioEngine->setReadBufferSize(activeSpec.samples);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ void AudioOutputDeviceController::onOutputDeviceChanged()
IAudioDriver::Spec activeSpec = audioDriver()->activeSpec();

async::Async::call(this, [this, activeSpec]() {
audioEngine()->setAudioChannelsCount(activeSpec.channels);
// TODO: audioEngine()->setAudioChannelsCount(activeSpec.channels);
audioEngine()->setSampleRate(activeSpec.sampleRate);
audioEngine()->setReadBufferSize(activeSpec.samples);
}, AudioThread::ID);
Expand Down
28 changes: 25 additions & 3 deletions src/framework/audio/internal/platform/win/wasapiaudioclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -560,11 +560,33 @@ void WasapiAudioClient::getSamples(uint32_t framesAvailable)
uint8_t* data;

uint32_t actualFramesToRead = framesAvailable;
uint32_t actualBytesToRead = actualFramesToRead * m_mixFormat->nBlockAlign;

// WASAPI: "nBlockAlign must be equal to the product of nChannels and wBitsPerSample divided by 8 (bits per byte)"
const uint32_t clientFrameSize = m_mixFormat->nBlockAlign;

// MuseScore assumes only 2 audio channels (same calculation as above to determine frame size)
const uint32_t muFrameSize = 2 * m_mixFormat->wBitsPerSample / 8;

check_hresult(m_audioRenderClient->GetBuffer(actualFramesToRead, &data));
if (actualBytesToRead > 0) {
m_sampleRequestCallback(nullptr, data, actualBytesToRead);
if (actualFramesToRead > 0) {
// Based on the previous calculations, the only way that clientFrameSize will be larger than muFrameSize is
// if the client specifies more than 2 channels. MuseScore doesn't support this (yet), so we use a workaround
// where the missing channels are padded with zeroes...
if (clientFrameSize > muFrameSize) {
const size_t tempBufferDesiredSize = actualFramesToRead * muFrameSize;
if (m_tempBuffer.size() < tempBufferDesiredSize) {
m_tempBuffer.resize(tempBufferDesiredSize);
}

m_sampleRequestCallback(nullptr, m_tempBuffer.data(), (int)tempBufferDesiredSize);

for (uint32_t i = 0; i < actualFramesToRead; ++i) {
std::memcpy(data + i * clientFrameSize, m_tempBuffer.data() + i * muFrameSize, muFrameSize);
std::memset(data + i * clientFrameSize + muFrameSize, 0, clientFrameSize - muFrameSize);
}
} else {
m_sampleRequestCallback(nullptr, data, actualFramesToRead * clientFrameSize);
}
}
check_hresult(m_audioRenderClient->ReleaseBuffer(actualFramesToRead, 0));
}
Expand Down
2 changes: 2 additions & 0 deletions src/framework/audio/internal/platform/win/wasapiaudioclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ struct WasapiAudioClient : implements<WasapiAudioClient, IActivateAudioInterface
void setState(const DeviceState newState);
void setStateAndNotify(const DeviceState newState, hresult resultCode);

std::vector<uint8_t> m_tempBuffer; //! NOTE: Used for surround workaround - see #17648

hstring m_deviceIdString;
hstring m_fallbackDeviceIdString;
uint32_t m_bufferFrames = 0;
Expand Down

0 comments on commit b6933ff

Please sign in to comment.