diff --git a/examples/Demo/JuceDemo.jucer b/examples/Demo/JuceDemo.jucer
index 4864348849..e1cd233563 100644
--- a/examples/Demo/JuceDemo.jucer
+++ b/examples/Demo/JuceDemo.jucer
@@ -290,7 +290,7 @@
-
+
diff --git a/examples/Demo/JuceLibraryCode/AppConfig.h b/examples/Demo/JuceLibraryCode/AppConfig.h
index af5e76555e..11fa1455f1 100644
--- a/examples/Demo/JuceLibraryCode/AppConfig.h
+++ b/examples/Demo/JuceLibraryCode/AppConfig.h
@@ -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
diff --git a/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
index 2eac2fb3cc..bb334babad 100644
--- a/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
+++ b/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp
@@ -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);