1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Fixes for WASAPI exclusive mode.

This commit is contained in:
jules 2014-12-31 12:25:20 +00:00
parent 5c802e340c
commit 79f59afb32
3 changed files with 111 additions and 49 deletions

View file

@ -290,7 +290,7 @@
</GROUP>
</GROUP>
</MAINGROUP>
<JUCEOPTIONS JUCE_ENABLE_LIVE_CONSTANT_EDITOR="enabled"/>
<JUCEOPTIONS JUCE_ENABLE_LIVE_CONSTANT_EDITOR="enabled" JUCE_WASAPI_EXCLUSIVE="enabled"/>
<MODULES>
<MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="0"/>
<MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="0"/>

View file

@ -54,7 +54,7 @@ extern bool juceDemoRepaintDebuggingActive;
#endif
#ifndef JUCE_WASAPI_EXCLUSIVE
//#define JUCE_WASAPI_EXCLUSIVE
#define JUCE_WASAPI_EXCLUSIVE 1
#endif
#ifndef JUCE_DIRECTSOUND

View file

@ -376,16 +376,39 @@ public:
rates.addUsingDefaultSort (defaultSampleRate);
static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000 };
while (useExclusiveMode)
{
bool isOK = false;
for (int bitsToTest = format.Format.wBitsPerSample; bitsToTest >= 16; bitsToTest = ((bitsToTest - 1) & ~7))
{
format.Samples.wValidBitsPerSample = (bitsToTest + 7) & ~7;
if (SUCCEEDED (tempClient->IsFormatSupported (AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*) &format, 0)))
{
isOK = true;
break;
}
}
if (isOK || format.SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
break;
format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; // Try PCM as a fallback from float..
}
static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
for (int i = 0; i < numElementsInArray (ratesToTest); ++i)
{
if (ratesToTest[i] == defaultSampleRate)
if (rates.contains (ratesToTest[i]))
continue;
format.Format.nSamplesPerSec = (DWORD) ratesToTest[i];
format.Format.nSamplesPerSec = (DWORD) ratesToTest[i];
format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8);
if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE
: AUDCLNT_SHAREMODE_SHARED,
(WAVEFORMATEX*) &format, 0)))
if (! rates.contains (ratesToTest[i]))
rates.addUsingDefaultSort (ratesToTest[i]);
@ -400,7 +423,7 @@ public:
bool isOk() const noexcept { return defaultBufferSize > 0 && defaultSampleRate > 0; }
bool openClient (const double newSampleRate, const BigInteger& newChannels)
bool openClient (const double newSampleRate, const BigInteger& newChannels, const int bufferSizeSamples)
{
sampleRate = newSampleRate;
channels = newChannels;
@ -413,8 +436,11 @@ public:
client = createClient();
if (client != nullptr
&& (tryInitialisingWithFormat (true, 4) || tryInitialisingWithFormat (false, 4)
|| tryInitialisingWithFormat (false, 3) || tryInitialisingWithFormat (false, 2)))
&& (tryInitialisingWithFormat (true, 4, 4, bufferSizeSamples)
|| tryInitialisingWithFormat (false, 4, 4, bufferSizeSamples)
|| tryInitialisingWithFormat (false, 3, 4, bufferSizeSamples)
|| tryInitialisingWithFormat (false, 3, 3, bufferSizeSamples)
|| tryInitialisingWithFormat (false, 2, 2, bufferSizeSamples)))
{
sampleRateHasChanged = false;
@ -536,7 +562,7 @@ private:
return client;
}
bool tryInitialisingWithFormat (const bool useFloat, const int bytesPerSampleToTry)
bool tryInitialisingWithFormat (const bool useFloat, const int bytesPerSampleToTry, const int bytesPerSampleContainer, const int bufferSizeSamples)
{
WAVEFORMATEXTENSIBLE format;
zerostruct (format);
@ -553,11 +579,11 @@ private:
format.Format.nSamplesPerSec = (DWORD) sampleRate;
format.Format.nChannels = (WORD) numChannels;
format.Format.wBitsPerSample = (WORD) (8 * bytesPerSampleToTry);
format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * numChannels * bytesPerSampleToTry);
format.Format.nBlockAlign = (WORD) (numChannels * bytesPerSampleToTry);
format.Format.wBitsPerSample = (WORD) (8 * bytesPerSampleContainer);
format.Samples.wValidBitsPerSample = (WORD) (8 * bytesPerSampleToTry);
format.Format.nBlockAlign = (WORD) (format.Format.nChannels * format.Format.wBitsPerSample / 8);
format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nBlockAlign);
format.SubFormat = useFloat ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM;
format.Samples.wValidBitsPerSample = format.Format.wBitsPerSample;
format.dwChannelMask = mixFormatChannelMask;
WAVEFORMATEXTENSIBLE* nearestFormat = nullptr;
@ -576,22 +602,49 @@ private:
CoTaskMemFree (nearestFormat);
REFERENCE_TIME defaultPeriod = 0, minPeriod = 0;
if (useExclusiveMode)
check (client->GetDevicePeriod (&defaultPeriod, &minPeriod));
GUID session;
if (hr == S_OK
&& check (client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
defaultPeriod, defaultPeriod, (WAVEFORMATEX*) &format, &session)))
if (hr == S_OK)
{
actualNumChannels = format.Format.nChannels;
const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
bytesPerSample = format.Format.wBitsPerSample / 8;
REFERENCE_TIME defaultPeriod = 0, minPeriod = 0;
updateFormat (isFloat);
return true;
if (useExclusiveMode)
{
check (client->GetDevicePeriod (&defaultPeriod, &minPeriod));
if (bufferSizeSamples > 0)
defaultPeriod = jmax (minPeriod, (REFERENCE_TIME) ((10000.0 * 1000.0 / format.Format.nSamplesPerSec * bufferSizeSamples) + 0.5));
}
for (;;)
{
GUID session;
hr = client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED,
0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/,
defaultPeriod, defaultPeriod, (WAVEFORMATEX*) &format, &session);
if (check (hr))
{
actualNumChannels = format.Format.nChannels;
const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
bytesPerSample = format.Format.wBitsPerSample / 8;
updateFormat (isFloat);
return true;
}
// Handle the "alignment dance" : http://msdn.microsoft.com/en-us/library/windows/desktop/dd370875(v=vs.85).aspx (see Remarks)
if (hr != MAKE_HRESULT (1, 0x889, 0x19)) // AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
break;
UINT32 numFrames = 0;
if (! check (client->GetBufferSize (&numFrames)))
break;
// Recreate client
client = nullptr;
client = createClient();
defaultPeriod = (REFERENCE_TIME) ((10000.0 * 1000.0 / format.Format.nSamplesPerSec * numFrames) + 0.5);
}
}
return false;
@ -615,12 +668,13 @@ public:
close();
}
bool open (const double newSampleRate, const BigInteger& newChannels)
bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
{
reservoirSize = 0;
reservoirCapacity = 16384;
reservoir.setSize (actualNumChannels * reservoirCapacity * sizeof (float));
return openClient (newSampleRate, newChannels)
return openClient (newSampleRate, newChannels, bufferSizeSamples)
&& (numChannels == 0 || check (client->GetService (__uuidof (IAudioCaptureClient),
(void**) captureClient.resetAndGetPointerAddress())));
}
@ -670,7 +724,7 @@ public:
else
{
UINT32 packetLength = 0;
if (! check (captureClient->GetNextPacketSize (&packetLength)))
if (! (useExclusiveMode || check (captureClient->GetNextPacketSize (&packetLength))))
break;
if (packetLength == 0)
@ -679,7 +733,8 @@ public:
|| WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
break;
continue;
if (! useExclusiveMode)
continue;
}
uint8* inputData;
@ -732,10 +787,11 @@ public:
close();
}
bool open (const double newSampleRate, const BigInteger& newChannels)
bool open (const double newSampleRate, const BigInteger& newChannels, int bufferSizeSamples)
{
return openClient (newSampleRate, newChannels)
&& (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient), (void**) renderClient.resetAndGetPointerAddress())));
return openClient (newSampleRate, newChannels, bufferSizeSamples)
&& (numChannels == 0 || check (client->GetService (__uuidof (IAudioRenderClient),
(void**) renderClient.resetAndGetPointerAddress())));
}
void close()
@ -753,10 +809,10 @@ public:
void updateFormat (bool isFloat)
{
if (isFloat) updateFormatWithType ((AudioData::Float32*) 0);
else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) 0);
else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) 0);
else updateFormatWithType ((AudioData::Int16*) 0);
if (isFloat) updateFormatWithType ((AudioData::Float32*) nullptr);
else if (bytesPerSample == 4) updateFormatWithType ((AudioData::Int32*) nullptr);
else if (bytesPerSample == 3) updateFormatWithType ((AudioData::Int24*) nullptr);
else updateFormatWithType ((AudioData::Int16*) nullptr);
}
void copyBuffers (const float** const srcBuffers, const int numSrcBuffers, int bufferSize, Thread& thread)
@ -768,17 +824,21 @@ public:
while (bufferSize > 0)
{
// In exclusive mode, GetCurrentPadding ALWAYS returns the buffer size, so we need
// to wait for the event before getting the buffer.
if (useExclusiveMode && (thread.threadShouldExit() || WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT))
break;
UINT32 padding = 0;
if (! check (client->GetCurrentPadding (&padding)))
return;
int samplesToDo = useExclusiveMode ? bufferSize
int samplesToDo = useExclusiveMode ? actualBufferSize
: jmin ((int) (actualBufferSize - padding), bufferSize);
if (samplesToDo <= 0)
{
if (thread.threadShouldExit()
|| WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
if (thread.threadShouldExit() || WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT)
break;
continue;
@ -812,13 +872,14 @@ class WASAPIAudioIODevice : public AudioIODevice,
{
public:
WASAPIAudioIODevice (const String& deviceName,
const String& outputDeviceId_,
const String& inputDeviceId_,
const String& typeName,
const String& outputDeviceID,
const String& inputDeviceID,
const bool exclusiveMode)
: AudioIODevice (deviceName, "Windows Audio"),
: AudioIODevice (deviceName, typeName),
Thread ("Juce WASAPI"),
outputDeviceId (outputDeviceId_),
inputDeviceId (inputDeviceId_),
outputDeviceId (outputDeviceID),
inputDeviceId (inputDeviceID),
useExclusiveMode (exclusiveMode),
isOpen_ (false),
isStarted (false),
@ -932,13 +993,13 @@ public:
lastKnownInputChannels = inputChannels;
lastKnownOutputChannels = outputChannels;
if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels))
if (inputDevice != nullptr && ! inputDevice->open (currentSampleRate, inputChannels, bufferSizeSamples))
{
lastError = TRANS("Couldn't open the input device!");
return lastError;
}
if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels))
if (outputDevice != nullptr && ! outputDevice->open (currentSampleRate, outputChannels, bufferSizeSamples))
{
close();
lastError = TRANS("Couldn't open the output device!");
@ -1277,6 +1338,7 @@ public:
{
device = new WASAPIAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName
: inputDeviceName,
getTypeName(),
outputDeviceIds [outputIndex],
inputDeviceIds [inputIndex],
exclusiveMode);