diff --git a/BREAKING-CHANGES.txt b/BREAKING-CHANGES.txt index 80a5bc16e3..c3eee30864 100644 --- a/BREAKING-CHANGES.txt +++ b/BREAKING-CHANGES.txt @@ -4,6 +4,33 @@ JUCE breaking changes Develop ======= +Change +------ +The JUCE_WASAPI_EXCLUSIVE flag has been removed from juce_audio_devices and all +available WASAPI audio device modes (shared, shared low latency and exclusive) +are available by default when JUCE_WASAPI is enabled. The +AudioIODeviceType::createAudioIODeviceType_WASAPI() method which takes a single +boolean argument has also been deprecated in favour of a new method which takes +a WASAPIDeviceMode enum. + +Possible Issues +--------------- +Code that relied on the JUCE_WASAPI_EXCLUSIVE flag to disable WASAPI exclusive +mode will no longer work. + +Workaround +---------- +Override the AudioDeviceManager::createAudioDeviceTypes() method to omit the +WASAPI exclusive mode device if you do not want it to be available. + +Rationale +--------- +JUCE now supports shared low latency WASAPI audio devices via the AudioClient3 +interface and instead of adding an additional compile time config flag to +enable this functionality, which adds complexity to the build process when not +using the Projucer, JUCE makes all WASAPI device modes available by default. + + Change ------ The fields representing Mac OS X 10.4 to 10.6 inclusive have been removed from diff --git a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp index a0f789b029..bc47270775 100644 --- a/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp +++ b/modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp @@ -177,8 +177,9 @@ static void addIfNotNull (OwnedArray& list, AudioIODeviceType void AudioDeviceManager::createAudioDeviceTypes (OwnedArray& list) { - addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (false)); - addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (true)); + addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::shared)); + addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::exclusive)); + addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::sharedLowLatency)); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO()); addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio()); diff --git a/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp b/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp index 7850b092aa..f591f48127 100644 --- a/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp +++ b/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.cpp @@ -42,48 +42,105 @@ void AudioIODeviceType::callDeviceChangeListeners() } //============================================================================== -#if ! JUCE_MAC -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } +#if JUCE_MAC + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return new CoreAudioClasses::CoreAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } #endif -#if ! JUCE_IOS -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; } +#if JUCE_IOS + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return new iOSAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; } #endif -#if ! (JUCE_WINDOWS && JUCE_WASAPI) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; } +#if JUCE_WINDOWS && JUCE_WASAPI + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode) + { + auto windowsVersion = SystemStats::getOperatingSystemType(); + + if (windowsVersion < SystemStats::WinVista + || (WasapiClasses::isLowLatencyMode (deviceMode) && windowsVersion < SystemStats::Windows10)) + return nullptr; + + return new WasapiClasses::WASAPIAudioIODeviceType (deviceMode); + } + + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode) + { + return createAudioIODeviceType_WASAPI (exclusiveMode ? WASAPIDeviceMode::exclusive + : WASAPIDeviceMode::shared); + } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode) { return nullptr; } + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; } #endif -#if ! (JUCE_WINDOWS && JUCE_DIRECTSOUND) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; } +#if JUCE_WINDOWS && JUCE_DIRECTSOUND + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return new DSoundAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; } #endif -#if ! (JUCE_WINDOWS && JUCE_ASIO) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; } +#if JUCE_WINDOWS && JUCE_ASIO + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return new ASIOAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; } #endif -#if ! (JUCE_LINUX && JUCE_ALSA) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; } +#if JUCE_LINUX && JUCE_ALSA + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return createAudioIODeviceType_ALSA_PCMDevices(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; } #endif -#if ! (JUCE_LINUX && JUCE_JACK) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; } +#if JUCE_LINUX && JUCE_JACK + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return new JackAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; } #endif -#if ! (JUCE_LINUX && JUCE_BELA) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; } +#if JUCE_LINUX && JUCE_BELA + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return new BelaAudioIODeviceType(); } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; } #endif -#if ! JUCE_ANDROID -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; } +#if JUCE_ANDROID + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() + { + #if JUCE_USE_ANDROID_OBOE + if (isOboeAvailable()) + return nullptr; + #endif + + #if JUCE_USE_ANDROID_OPENSLES + if (isOpenSLAvailable()) + return nullptr; + #endif + + return new AndroidAudioIODeviceType(); + } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; } #endif -#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; } +#if JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() + { + return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr; + } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; } #endif -#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OBOE) -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; } +#if JUCE_ANDROID && JUCE_USE_ANDROID_OBOE + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() + { + return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr; + } +#else + AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; } #endif } // namespace juce diff --git a/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h b/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h index 686c906751..eca32bf9f2 100644 --- a/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h +++ b/modules/juce_audio_devices/audio_io/juce_AudioIODeviceType.h @@ -149,8 +149,8 @@ public: static AudioIODeviceType* createAudioIODeviceType_CoreAudio(); /** Creates an iOS device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_iOSAudio(); - /** Creates a WASAPI device type if it's available on this platform, or returns null. */ - static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode); + /** Creates a WASAPI device type in the specified mode if it's available on this platform, or returns null. */ + static AudioIODeviceType* createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode); /** Creates a DirectSound device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_DirectSound(); /** Creates an ASIO device type if it's available on this platform, or returns null. */ @@ -168,6 +168,9 @@ public: /** Creates a Bela device type if it's available on this platform, or returns null. */ static AudioIODeviceType* createAudioIODeviceType_Bela(); + /** This method has been deprecated. You should call the method which takes a WASAPIDeviceMode instead. */ + JUCE_DEPRECATED (static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode)); + protected: explicit AudioIODeviceType (const String& typeName); diff --git a/modules/juce_audio_devices/juce_audio_devices.cpp b/modules/juce_audio_devices/juce_audio_devices.cpp index cc530d1a53..007e5fd3f1 100644 --- a/modules/juce_audio_devices/juce_audio_devices.cpp +++ b/modules/juce_audio_devices/juce_audio_devices.cpp @@ -45,6 +45,8 @@ #include "juce_audio_devices.h" +#include "native/juce_MidiDataConcatenator.h" + //============================================================================== #if JUCE_MAC #define Point CarbonDummyPointName @@ -55,6 +57,9 @@ #undef Point #undef Component + #include "native/juce_mac_CoreAudio.cpp" + #include "native/juce_mac_CoreMidi.cpp" + #elif JUCE_IOS #import #import @@ -64,10 +69,18 @@ #import #endif + #include "native/juce_ios_Audio.cpp" + #include "native/juce_mac_CoreMidi.cpp" + //============================================================================== #elif JUCE_WINDOWS #if JUCE_WASAPI #include + #include "native/juce_win32_WASAPI.cpp" + #endif + + #if JUCE_DIRECTSOUND + #include "native/juce_win32_DirectSound.cpp" #endif #if JUCE_USE_WINRT_MIDI && JUCE_MSVC @@ -93,6 +106,8 @@ JUCE_END_IGNORE_WARNINGS_MSVC #endif + #include "native/juce_win32_Midi.cpp" + #if JUCE_ASIO /* This is very frustrating - we only need to use a handful of definitions from a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy @@ -114,6 +129,7 @@ needed - so to simplify things, you could just copy these into your JUCE directory). */ #include + #include "native/juce_win32_ASIO.cpp" #endif //============================================================================== @@ -128,6 +144,7 @@ just set the JUCE_ALSA flag to 0. */ #include + #include "native/juce_linux_ALSA.cpp" #endif #if JUCE_JACK @@ -140,6 +157,7 @@ JUCE with low latency audio support, just set the JUCE_JACK flag to 0. */ #include + #include "native/juce_linux_JackAudio.cpp" #endif #if JUCE_BELA @@ -149,89 +167,18 @@ */ #include #include + #include "native/juce_linux_Bela.cpp" #endif #undef SIZEOF -//============================================================================== -#elif JUCE_ANDROID - - #if JUCE_USE_ANDROID_OPENSLES - #include - #include - #include - #endif - - #if JUCE_USE_ANDROID_OBOE - #if JUCE_USE_ANDROID_OPENSLES - #error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES" - #endif - - JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter", - "-Wzero-as-null-pointer-constant", - "-Winconsistent-missing-destructor-override", - "-Wshadow-field-in-constructor", - "-Wshadow-field") - #include - JUCE_END_IGNORE_WARNINGS_GCC_LIKE - #endif - -#endif - -#include "audio_io/juce_AudioDeviceManager.cpp" -#include "audio_io/juce_AudioIODevice.cpp" -#include "audio_io/juce_AudioIODeviceType.cpp" -#include "midi_io/juce_MidiMessageCollector.cpp" -#include "midi_io/juce_MidiDevices.cpp" -#include "sources/juce_AudioSourcePlayer.cpp" -#include "sources/juce_AudioTransportSource.cpp" -#include "native/juce_MidiDataConcatenator.h" - -//============================================================================== -#if JUCE_MAC - #include "native/juce_mac_CoreAudio.cpp" - #include "native/juce_mac_CoreMidi.cpp" - -//============================================================================== -#elif JUCE_IOS - #include "native/juce_ios_Audio.cpp" - #include "native/juce_mac_CoreMidi.cpp" - -//============================================================================== -#elif JUCE_WINDOWS - - #if JUCE_WASAPI - #include "native/juce_win32_WASAPI.cpp" - #endif - - #if JUCE_DIRECTSOUND - #include "native/juce_win32_DirectSound.cpp" - #endif - - #include "native/juce_win32_Midi.cpp" - - #if JUCE_ASIO - #include "native/juce_win32_ASIO.cpp" - #endif - -//============================================================================== -#elif JUCE_LINUX - #if JUCE_ALSA - #include "native/juce_linux_ALSA.cpp" - #endif - - #if JUCE_JACK - #include "native/juce_linux_JackAudio.cpp" - #endif - - #if JUCE_BELA - #include "native/juce_linux_Bela.cpp" - #else + #if ! JUCE_BELA #include "native/juce_linux_Midi.cpp" #endif //============================================================================== #elif JUCE_ANDROID + #include "native/juce_android_Audio.cpp" #include "native/juce_android_Midi.cpp" @@ -239,10 +186,25 @@ #include "native/juce_android_HighPerformanceAudioHelpers.h" #if JUCE_USE_ANDROID_OPENSLES + #include + #include + #include #include "native/juce_android_OpenSL.cpp" #endif #if JUCE_USE_ANDROID_OBOE + #if JUCE_USE_ANDROID_OPENSLES + #error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES" + #endif + + JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter", + "-Wzero-as-null-pointer-constant", + "-Winconsistent-missing-destructor-override", + "-Wshadow-field-in-constructor", + "-Wshadow-field") + #include + JUCE_END_IGNORE_WARNINGS_GCC_LIKE + #include "native/juce_android_Oboe.cpp" #endif #endif @@ -259,3 +221,11 @@ namespace juce bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; } } #endif + +#include "audio_io/juce_AudioDeviceManager.cpp" +#include "audio_io/juce_AudioIODevice.cpp" +#include "audio_io/juce_AudioIODeviceType.cpp" +#include "midi_io/juce_MidiMessageCollector.cpp" +#include "midi_io/juce_MidiDevices.cpp" +#include "sources/juce_AudioSourcePlayer.cpp" +#include "sources/juce_AudioTransportSource.cpp" diff --git a/modules/juce_audio_devices/juce_audio_devices.h b/modules/juce_audio_devices/juce_audio_devices.h index 1977cfba73..fe58ae69e2 100644 --- a/modules/juce_audio_devices/juce_audio_devices.h +++ b/modules/juce_audio_devices/juce_audio_devices.h @@ -88,21 +88,12 @@ #endif /** Config: JUCE_WASAPI - Enables WASAPI audio devices (Windows Vista and above). See also the - JUCE_WASAPI_EXCLUSIVE flag. + Enables WASAPI audio devices (Windows Vista and above). */ #ifndef JUCE_WASAPI #define JUCE_WASAPI 1 #endif -/** Config: JUCE_WASAPI_EXCLUSIVE - Enables WASAPI audio devices in exclusive mode (Windows Vista and above). -*/ -#ifndef JUCE_WASAPI_EXCLUSIVE - #define JUCE_WASAPI_EXCLUSIVE 0 -#endif - - /** Config: JUCE_DIRECTSOUND Enables DirectSound audio (MS Windows only). */ @@ -174,6 +165,19 @@ //============================================================================== #include "midi_io/juce_MidiDevices.h" #include "midi_io/juce_MidiMessageCollector.h" + +/** Available modes for the WASAPI audio device. + + Pass one of these to the AudioIODeviceType::createAudioIODeviceType_WASAPI() + method to create a WASAPI AudioIODeviceType object in this mode. +*/ +enum class WASAPIDeviceMode +{ + shared, + exclusive, + sharedLowLatency +}; + #include "audio_io/juce_AudioIODevice.h" #include "audio_io/juce_AudioIODeviceType.h" #include "audio_io/juce_SystemAudioVolume.h" diff --git a/modules/juce_audio_devices/native/juce_android_Audio.cpp b/modules/juce_audio_devices/native/juce_android_Audio.cpp index 37aeede8eb..7953988250 100644 --- a/modules/juce_audio_devices/native/juce_android_Audio.cpp +++ b/modules/juce_audio_devices/native/juce_android_Audio.cpp @@ -478,19 +478,4 @@ private: extern bool isOboeAvailable(); extern bool isOpenSLAvailable(); -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() -{ - #if JUCE_USE_ANDROID_OBOE - if (isOboeAvailable()) - return nullptr; - #endif - - #if JUCE_USE_ANDROID_OPENSLES - if (isOpenSLAvailable()) - return nullptr; - #endif - - return new AndroidAudioIODeviceType(); -} - } // namespace juce diff --git a/modules/juce_audio_devices/native/juce_android_Oboe.cpp b/modules/juce_audio_devices/native/juce_android_Oboe.cpp index d3d7c23704..31f1cfd94c 100644 --- a/modules/juce_audio_devices/native/juce_android_Oboe.cpp +++ b/modules/juce_audio_devices/native/juce_android_Oboe.cpp @@ -1310,15 +1310,8 @@ public: const char* const OboeAudioIODevice::oboeTypeName = "Android Oboe"; - -//============================================================================== bool isOboeAvailable() { return OboeAudioIODeviceType::isOboeAvailable(); } -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() -{ - return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr; -} - //============================================================================== class OboeRealtimeThread : private oboe::AudioStreamCallback { diff --git a/modules/juce_audio_devices/native/juce_android_OpenSL.cpp b/modules/juce_audio_devices/native/juce_android_OpenSL.cpp index 01b986c863..2bb38b4064 100644 --- a/modules/juce_audio_devices/native/juce_android_OpenSL.cpp +++ b/modules/juce_audio_devices/native/juce_android_OpenSL.cpp @@ -1120,11 +1120,6 @@ const char* const OpenSLAudioIODevice::openSLTypeName = "Android OpenSL"; //============================================================================== bool isOpenSLAvailable() { return OpenSLAudioDeviceType::isOpenSLAvailable(); } -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() -{ - return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr; -} - //============================================================================== class SLRealtimeThread { diff --git a/modules/juce_audio_devices/native/juce_ios_Audio.cpp b/modules/juce_audio_devices/native/juce_ios_Audio.cpp index 05399ada3a..7de777d3d2 100644 --- a/modules/juce_audio_devices/native/juce_ios_Audio.cpp +++ b/modules/juce_audio_devices/native/juce_ios_Audio.cpp @@ -1431,12 +1431,6 @@ void iOSAudioIODeviceType::handleAsyncUpdate() callDeviceChangeListeners(); } -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() -{ - return new iOSAudioIODeviceType(); -} - //============================================================================== AudioSessionHolder::AudioSessionHolder() { nativeSession = [[iOSAudioSessionNative alloc] init: this]; } AudioSessionHolder::~AudioSessionHolder() { [nativeSession release]; } diff --git a/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index e1830bc524..323fe13e40 100644 --- a/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -1299,9 +1299,4 @@ AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices() return new ALSAAudioIODeviceType (false, "ALSA"); } -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() -{ - return createAudioIODeviceType_ALSA_PCMDevices(); -} - } // namespace juce diff --git a/modules/juce_audio_devices/native/juce_linux_Bela.cpp b/modules/juce_audio_devices/native/juce_linux_Bela.cpp index c4843f4618..c2dba98304 100644 --- a/modules/juce_audio_devices/native/juce_linux_Bela.cpp +++ b/modules/juce_audio_devices/native/juce_linux_Bela.cpp @@ -521,12 +521,6 @@ struct BelaAudioIODeviceType : public AudioIODeviceType JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODeviceType) }; -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() -{ - return new BelaAudioIODeviceType(); -} - //============================================================================== MidiInput::MidiInput (const String& deviceName, const String& deviceID) : deviceInfo (deviceName, deviceID) diff --git a/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp b/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp index 33758a4063..90a8afb002 100644 --- a/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp +++ b/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp @@ -667,10 +667,4 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType) }; -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() -{ - return new JackAudioIODeviceType(); -} - } // namespace juce diff --git a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp index e9f3b19c76..646aae7c7e 100644 --- a/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp +++ b/modules/juce_audio_devices/native/juce_mac_CoreAudio.cpp @@ -2234,12 +2234,6 @@ private: }; -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() -{ - return new CoreAudioClasses::CoreAudioIODeviceType(); -} - #undef JUCE_COREAUDIOLOG } // namespace juce diff --git a/modules/juce_audio_devices/native/juce_win32_ASIO.cpp b/modules/juce_audio_devices/native/juce_win32_ASIO.cpp index c20efbf246..a9b7a676c7 100644 --- a/modules/juce_audio_devices/native/juce_win32_ASIO.cpp +++ b/modules/juce_audio_devices/native/juce_win32_ASIO.cpp @@ -1640,9 +1640,4 @@ void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType* type) type->sendDeviceChangeToListeners(); } -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() -{ - return new ASIOAudioIODeviceType(); -} - } // namespace juce diff --git a/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp b/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp index 81ff6edcbb..742b0209e6 100644 --- a/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp +++ b/modules/juce_audio_devices/native/juce_win32_DirectSound.cpp @@ -1282,10 +1282,4 @@ private: JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType) }; -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() -{ - return new DSoundAudioIODeviceType(); -} - } // namespace juce diff --git a/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp b/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp index cf922be902..acabe33b3b 100644 --- a/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp +++ b/modules/juce_audio_devices/native/juce_win32_WASAPI.cpp @@ -208,6 +208,29 @@ enum AUDCLNT_SHAREMODE AUDCLNT_SHAREMODE_EXCLUSIVE }; +enum AUDIO_STREAM_CATEGORY +{ + AudioCategory_Other = 0, + AudioCategory_ForegroundOnlyMedia, + AudioCategory_BackgroundCapableMedia, + AudioCategory_Communications, + AudioCategory_Alerts, + AudioCategory_SoundEffects, + AudioCategory_GameEffects, + AudioCategory_GameMedia, + AudioCategory_GameChat, + AudioCategory_Speech, + AudioCategory_Movie, + AudioCategory_Media +}; + +struct AudioClientProperties +{ + UINT32 cbSize; + BOOL bIsOffload; + AUDIO_STREAM_CATEGORY eCategory; +}; + JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") { JUCE_COMCALL Initialize (AUDCLNT_SHAREMODE, DWORD, REFERENCE_TIME, REFERENCE_TIME, const WAVEFORMATEX*, LPCGUID) = 0; @@ -224,6 +247,20 @@ JUCE_IUNKNOWNCLASS (IAudioClient, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") JUCE_COMCALL GetService (REFIID, void**) = 0; }; +JUCE_COMCLASS (IAudioClient2, "726778CD-F60A-4eda-82DE-E47610CD78AA") : public IAudioClient +{ + JUCE_COMCALL IsOffloadCapable (AUDIO_STREAM_CATEGORY, BOOL*) = 0; + JUCE_COMCALL SetClientProperties (const AudioClientProperties*) = 0; + JUCE_COMCALL GetBufferSizeLimits (const WAVEFORMATEX*, BOOL, REFERENCE_TIME*, REFERENCE_TIME*) = 0; +}; + +JUCE_COMCLASS (IAudioClient3, "1CB9AD4C-DBFA-4c32-B178-C2F568A703B2") : public IAudioClient2 +{ + JUCE_COMCALL GetSharedModeEnginePeriod (const WAVEFORMATEX*, UINT32*, UINT32*, UINT32*, UINT32*) = 0; + JUCE_COMCALL GetCurrentSharedModeEnginePeriod (WAVEFORMATEX**, UINT32*) = 0; + JUCE_COMCALL InitializeSharedAudioStream (DWORD, UINT32, const WAVEFORMATEX*, LPCGUID) = 0; +}; + JUCE_IUNKNOWNCLASS (IAudioCaptureClient, "C8ADBD64-E71E-48a0-A4DE-185C395CD317") { JUCE_COMCALL GetBuffer (BYTE**, UINT32*, DWORD*, UINT64*, UINT64*) = 0; @@ -322,7 +359,7 @@ String getDeviceID (IMMDevice* device) return s; } -EDataFlow getDataFlow (const ComSmartPtr& device) +static EDataFlow getDataFlow (const ComSmartPtr& device) { EDataFlow flow = eRender; ComSmartPtr endPoint; @@ -332,88 +369,64 @@ EDataFlow getDataFlow (const ComSmartPtr& device) return flow; } -int refTimeToSamples (const REFERENCE_TIME& t, double sampleRate) noexcept +static int refTimeToSamples (const REFERENCE_TIME& t, double sampleRate) noexcept { return roundToInt (sampleRate * ((double) t) * 0.0000001); } -REFERENCE_TIME samplesToRefTime (int numSamples, double sampleRate) noexcept +static REFERENCE_TIME samplesToRefTime (int numSamples, double sampleRate) noexcept { return (REFERENCE_TIME) ((numSamples * 10000.0 * 1000.0 / sampleRate) + 0.5); } -void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcept +static void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcept { memcpy (&dest, src, src->wFormatTag == WAVE_FORMAT_EXTENSIBLE ? sizeof (WAVEFORMATEXTENSIBLE) : sizeof (WAVEFORMATEX)); } +static bool isExclusiveMode (WASAPIDeviceMode deviceMode) noexcept +{ + return deviceMode == WASAPIDeviceMode::exclusive; +} + +static bool isLowLatencyMode (WASAPIDeviceMode deviceMode) noexcept +{ + return deviceMode == WASAPIDeviceMode::sharedLowLatency; +} + +static bool supportsSampleRateConversion (WASAPIDeviceMode deviceMode) noexcept +{ + return deviceMode == WASAPIDeviceMode::shared; +} + //============================================================================== class WASAPIDeviceBase { public: - WASAPIDeviceBase (const ComSmartPtr& d, bool exclusiveMode) - : device (d), useExclusiveMode (exclusiveMode) + WASAPIDeviceBase (const ComSmartPtr& d, WASAPIDeviceMode mode) + : device (d), + deviceMode (mode) { clientEvent = CreateEvent (nullptr, false, false, nullptr); ComSmartPtr tempClient (createClient()); + if (tempClient == nullptr) return; - REFERENCE_TIME defaultPeriod, minPeriod; - if (! check (tempClient->GetDevicePeriod (&defaultPeriod, &minPeriod))) - return; - - WAVEFORMATEX* mixFormat = nullptr; - if (! check (tempClient->GetMixFormat (&mixFormat))) - return; - WAVEFORMATEXTENSIBLE format; - copyWavFormat (format, mixFormat); - CoTaskMemFree (mixFormat); + + if (! getClientMixFormat (tempClient, format)) + return; actualNumChannels = numChannels = format.Format.nChannels; defaultSampleRate = format.Format.nSamplesPerSec; - minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate); - defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate); + rates.addUsingDefaultSort (defaultSampleRate); mixFormatChannelMask = format.dwChannelMask; - rates.addUsingDefaultSort (defaultSampleRate); - - if (useExclusiveMode - && findSupportedFormat (tempClient, defaultSampleRate, format.dwChannelMask, format)) - { - // Got a format that is supported by the device so we can ask what sample rates are supported (in whatever format) - } - - for (auto rate : { 8000, 11025, 16000, 22050, 32000, - 44100, 48000, 88200, 96000, 176400, - 192000, 352800, 384000, 705600, 768000 }) - { - if (rates.contains (rate)) - continue; - - format.Format.nSamplesPerSec = (DWORD) rate; - format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8); - - WAVEFORMATEX* nearestFormat = nullptr; - - if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE - : AUDCLNT_SHAREMODE_SHARED, - (WAVEFORMATEX*) &format, - useExclusiveMode ? nullptr - : (WAVEFORMATEX**) &nearestFormat))) - { - if (nearestFormat != nullptr) - rate = nearestFormat->nSamplesPerSec; - - if (! rates.contains (rate)) - rates.addUsingDefaultSort (rate); - } - - CoTaskMemFree (nearestFormat); - } + querySupportedBufferSizes (format, tempClient); + querySupportedSampleRates (format, tempClient); } virtual ~WASAPIDeviceBase() @@ -498,11 +511,14 @@ public: //============================================================================== ComSmartPtr device; ComSmartPtr client; + + WASAPIDeviceMode deviceMode; + double sampleRate = 0, defaultSampleRate = 0; int numChannels = 0, actualNumChannels = 0; int minBufferSize = 0, defaultBufferSize = 0, latencySamples = 0; + int lowLatencyBufferSizeMultiple = 0, lowLatencyMaxBufferSize = 0; DWORD mixFormatChannelMask = 0; - const bool useExclusiveMode; Array rates; HANDLE clientEvent = {}; BigInteger channels; @@ -593,6 +609,94 @@ private: return newClient; } + static bool getClientMixFormat (ComSmartPtr& client, WAVEFORMATEXTENSIBLE& format) + { + WAVEFORMATEX* mixFormat = nullptr; + + if (! check (client->GetMixFormat (&mixFormat))) + return false; + + copyWavFormat (format, mixFormat); + CoTaskMemFree (mixFormat); + + return true; + } + + static ComSmartPtr getClientAsVersion3 (ComSmartPtr& clientVersion1) + { + ComSmartPtr newClient; + + if (clientVersion1 != nullptr) + clientVersion1.QueryInterface (__uuidof (IAudioClient3), newClient); + + return newClient; + } + + //============================================================================== + void querySupportedBufferSizes (WAVEFORMATEXTENSIBLE format, ComSmartPtr& audioClient) + { + if (isLowLatencyMode (deviceMode)) + { + if (auto audioClient3 = getClientAsVersion3 (audioClient)) + { + UINT32 defaultPeriod = 0, fundamentalPeriod = 0, minPeriod = 0, maxPeriod = 0; + + if (check (audioClient3->GetSharedModeEnginePeriod ((WAVEFORMATEX*) &format, + &defaultPeriod, + &fundamentalPeriod, + &minPeriod, + &maxPeriod))) + { + minBufferSize = minPeriod; + defaultBufferSize = defaultPeriod; + lowLatencyMaxBufferSize = maxPeriod; + lowLatencyBufferSizeMultiple = fundamentalPeriod; + } + } + } + else + { + REFERENCE_TIME defaultPeriod, minPeriod; + + if (! check (audioClient->GetDevicePeriod (&defaultPeriod, &minPeriod))) + return; + + minBufferSize = refTimeToSamples (minPeriod, defaultSampleRate); + defaultBufferSize = refTimeToSamples (defaultPeriod, defaultSampleRate); + } + } + + void querySupportedSampleRates (WAVEFORMATEXTENSIBLE format, ComSmartPtr& audioClient) + { + for (auto rate : { 8000, 11025, 16000, 22050, 32000, + 44100, 48000, 88200, 96000, 176400, + 192000, 352800, 384000, 705600, 768000 }) + { + if (rates.contains (rate)) + continue; + + format.Format.nSamplesPerSec = (DWORD) rate; + format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8); + + WAVEFORMATEX* nearestFormat = nullptr; + + if (SUCCEEDED (audioClient->IsFormatSupported (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE + : AUDCLNT_SHAREMODE_SHARED, + (WAVEFORMATEX*) &format, + isExclusiveMode (deviceMode) ? nullptr + : (WAVEFORMATEX**) &nearestFormat))) + { + if (nearestFormat != nullptr) + rate = nearestFormat->nSamplesPerSec; + + if (! rates.contains (rate)) + rates.addUsingDefaultSort (rate); + } + + CoTaskMemFree (nearestFormat); + } + } + struct AudioSampleFormat { bool useFloat; @@ -626,11 +730,11 @@ private: WAVEFORMATEXTENSIBLE* nearestFormat = nullptr; - HRESULT hr = clientToUse->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE - : AUDCLNT_SHAREMODE_SHARED, + HRESULT hr = clientToUse->IsFormatSupported (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE + : AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*) &format, - useExclusiveMode ? nullptr - : (WAVEFORMATEX**) &nearestFormat); + isExclusiveMode (deviceMode) ? nullptr + : (WAVEFORMATEX**) &nearestFormat); logFailure (hr); if (hr == S_FALSE @@ -642,7 +746,7 @@ private: } CoTaskMemFree (nearestFormat); - return check (hr); + return hr == S_OK; } bool findSupportedFormat (IAudioClient* clientToUse, double newSampleRate, @@ -666,55 +770,88 @@ private: return false; } + DWORD getStreamFlags() + { + DWORD streamFlags = 0x40000; /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/ + + if (supportsSampleRateConversion (deviceMode)) + streamFlags |= (0x80000000 /*AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM*/ + | 0x8000000); /*AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY*/ + + return streamFlags; + } + + bool initialiseLowLatencyClient (int bufferSizeSamples, WAVEFORMATEXTENSIBLE format) + { + if (auto audioClient3 = getClientAsVersion3 (client)) + return check (audioClient3->InitializeSharedAudioStream (getStreamFlags(), + bufferSizeSamples, + (WAVEFORMATEX*) &format, + nullptr)); + + return false; + } + + bool initialiseStandardClient (int bufferSizeSamples, WAVEFORMATEXTENSIBLE format) + { + REFERENCE_TIME defaultPeriod = 0, minPeriod = 0; + + check (client->GetDevicePeriod (&defaultPeriod, &minPeriod)); + + if (isExclusiveMode (deviceMode) && bufferSizeSamples > 0) + defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec)); + + for (;;) + { + GUID session; + auto hr = client->Initialize (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE + : AUDCLNT_SHAREMODE_SHARED, + getStreamFlags(), + defaultPeriod, + isExclusiveMode (deviceMode) ? defaultPeriod : 0, + (WAVEFORMATEX*) &format, + &session); + + if (check (hr)) + 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 = samplesToRefTime (numFrames, format.Format.nSamplesPerSec); + } + + return false; + } + bool tryInitialisingWithBufferSize (int bufferSizeSamples) { WAVEFORMATEXTENSIBLE format; if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format)) { - REFERENCE_TIME defaultPeriod = 0, minPeriod = 0; + auto isInitialised = isLowLatencyMode (deviceMode) ? initialiseLowLatencyClient (bufferSizeSamples, format) + : initialiseStandardClient (bufferSizeSamples, format); - check (client->GetDevicePeriod (&defaultPeriod, &minPeriod)); - - if (useExclusiveMode && bufferSizeSamples > 0) - defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec)); - - for (;;) + if (isInitialised) { - GUID session; - HRESULT hr = client->Initialize (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED, - 0x40000 /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/ - | 0x80000000 /*AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM*/ - | 0x08000000 /*AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY*/, - defaultPeriod, - useExclusiveMode ? defaultPeriod : 0, - (WAVEFORMATEX*) &format, - &session); + actualNumChannels = format.Format.nChannels; + const bool isFloat = format.Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE && format.SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + bytesPerSample = format.Format.wBitsPerSample / 8; + bytesPerFrame = format.Format.nBlockAlign; - 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; - bytesPerFrame = format.Format.nBlockAlign; + updateFormat (isFloat); - 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 = samplesToRefTime (numFrames, format.Format.nSamplesPerSec); + return true; } } @@ -728,8 +865,8 @@ private: class WASAPIInputDevice : public WASAPIDeviceBase { public: - WASAPIInputDevice (const ComSmartPtr& d, bool exclusiveMode) - : WASAPIDeviceBase (d, exclusiveMode) + WASAPIInputDevice (const ComSmartPtr& d, WASAPIDeviceMode mode) + : WASAPIDeviceBase (d, mode) { } @@ -891,8 +1028,8 @@ private: class WASAPIOutputDevice : public WASAPIDeviceBase { public: - WASAPIOutputDevice (const ComSmartPtr& d, bool exclusiveMode) - : WASAPIDeviceBase (d, exclusiveMode) + WASAPIOutputDevice (const ComSmartPtr& d, WASAPIDeviceMode mode) + : WASAPIDeviceBase (d, mode) { } @@ -950,7 +1087,7 @@ public: if (numChannels <= 0) return 0; - if (! useExclusiveMode) + if (! isExclusiveMode (deviceMode)) { UINT32 padding = 0; @@ -972,7 +1109,7 @@ public: while (bufferSize > 0) { // This is needed in order not to drop any input data if the output device endpoint buffer was full - if ((! useExclusiveMode) && inputDevice != nullptr + if ((! isExclusiveMode (deviceMode)) && inputDevice != nullptr && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) inputDevice->handleDeviceBuffer(); @@ -987,7 +1124,7 @@ public: break; } - if (useExclusiveMode && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT) + if (isExclusiveMode (deviceMode) && WaitForSingleObject (clientEvent, 1000) == WAIT_TIMEOUT) break; uint8* outputData = nullptr; @@ -1021,12 +1158,12 @@ public: const String& typeName, const String& outputDeviceID, const String& inputDeviceID, - bool exclusiveMode) + WASAPIDeviceMode mode) : AudioIODevice (deviceName, typeName), Thread ("JUCE WASAPI"), outputDeviceId (outputDeviceID), inputDeviceId (inputDeviceID), - useExclusiveMode (exclusiveMode) + deviceMode (mode) { } @@ -1050,14 +1187,27 @@ public: if (inputDevice != nullptr && outputDevice != nullptr) { defaultSampleRate = jmin (inputDevice->defaultSampleRate, outputDevice->defaultSampleRate); - minBufferSize = jmin (inputDevice->minBufferSize, outputDevice->minBufferSize); + minBufferSize = jmax (inputDevice->minBufferSize, outputDevice->minBufferSize); defaultBufferSize = jmax (inputDevice->defaultBufferSize, outputDevice->defaultBufferSize); + if (isLowLatencyMode (deviceMode)) + { + lowLatencyMaxBufferSize = jmin (inputDevice->lowLatencyMaxBufferSize, outputDevice->lowLatencyMaxBufferSize); + lowLatencyBufferSizeMultiple = jmax (inputDevice->lowLatencyBufferSizeMultiple, outputDevice->lowLatencyBufferSizeMultiple); + } + sampleRates.addArray (inputDevice->rates); - for (auto r : outputDevice->rates) - if (! sampleRates.contains (r)) - sampleRates.addUsingDefaultSort (r); + if (supportsSampleRateConversion (deviceMode)) + { + for (auto r : outputDevice->rates) + if (! sampleRates.contains (r)) + sampleRates.addUsingDefaultSort (r); + } + else + { + sampleRates.removeValuesNotIn (outputDevice->rates); + } } else { @@ -1067,6 +1217,13 @@ public: defaultSampleRate = d->defaultSampleRate; minBufferSize = d->minBufferSize; defaultBufferSize = d->defaultBufferSize; + + if (isLowLatencyMode (deviceMode)) + { + lowLatencyMaxBufferSize = d->lowLatencyMaxBufferSize; + lowLatencyBufferSizeMultiple = d->lowLatencyBufferSizeMultiple; + } + sampleRates = d->rates; } @@ -1076,13 +1233,28 @@ public: if (minBufferSize != defaultBufferSize) bufferSizes.addUsingDefaultSort (minBufferSize); - int n = 64; - for (int i = 0; i < 40; ++i) + if (isLowLatencyMode (deviceMode)) { - if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n)) - bufferSizes.addUsingDefaultSort (n); + auto size = minBufferSize; - n += (n < 512) ? 32 : (n < 1024 ? 64 : 128); + while (size < lowLatencyMaxBufferSize) + { + size += lowLatencyBufferSizeMultiple; + + if (! bufferSizes.contains (size)) + bufferSizes.addUsingDefaultSort (size); + } + } + else + { + int n = 64; + for (int i = 0; i < 40; ++i) + { + if (n >= minBufferSize && n <= 2048 && ! bufferSizes.contains (n)) + bufferSizes.addUsingDefaultSort (n); + + n += (n < 512) ? 32 : (n < 1024 ? 64 : 128); + } } return true; @@ -1157,7 +1329,7 @@ public: return lastError; } - if (useExclusiveMode) + if (isExclusiveMode (deviceMode)) { // This is to make sure that the callback uses actualBufferSize in case of exclusive mode if (inputDevice != nullptr && outputDevice != nullptr && inputDevice->actualBufferSize != outputDevice->actualBufferSize) @@ -1323,7 +1495,7 @@ public: } else { - if (useExclusiveMode && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) + if (isExclusiveMode (deviceMode) && WaitForSingleObject (inputDevice->clientEvent, 0) == WAIT_OBJECT_0) inputDevice->handleDeviceBuffer(); } @@ -1373,9 +1545,10 @@ private: // Device stats... std::unique_ptr inputDevice; std::unique_ptr outputDevice; - const bool useExclusiveMode; + WASAPIDeviceMode deviceMode; double defaultSampleRate = 0; int minBufferSize = 0, defaultBufferSize = 0; + int lowLatencyMaxBufferSize = 0, lowLatencyBufferSizeMultiple = 0; int latencyIn = 0, latencyOut = 0; Array sampleRates; Array bufferSizes; @@ -1425,9 +1598,9 @@ private: auto flow = getDataFlow (device); if (deviceId == inputDeviceId && flow == eCapture) - inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode)); + inputDevice.reset (new WASAPIInputDevice (device, deviceMode)); else if (deviceId == outputDeviceId && flow == eRender) - outputDevice.reset (new WASAPIOutputDevice (device, useExclusiveMode)); + outputDevice.reset (new WASAPIOutputDevice (device, deviceMode)); } return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk())) @@ -1484,10 +1657,10 @@ class WASAPIAudioIODeviceType : public AudioIODeviceType, private DeviceChangeDetector { public: - WASAPIAudioIODeviceType (bool exclusive) - : AudioIODeviceType (exclusive ? "Windows Audio (Exclusive Mode)" : "Windows Audio"), + WASAPIAudioIODeviceType (WASAPIDeviceMode mode) + : AudioIODeviceType (getDeviceTypename (mode)), DeviceChangeDetector (L"Windows Audio"), - exclusiveMode (exclusive) + deviceMode (mode) { } @@ -1555,7 +1728,7 @@ public: getTypeName(), outputDeviceIds [outputIndex], inputDeviceIds [inputIndex], - exclusiveMode)); + deviceMode)); if (! device->initialise()) device = nullptr; @@ -1569,7 +1742,7 @@ public: StringArray inputDeviceNames, inputDeviceIds; private: - const bool exclusiveMode; + WASAPIDeviceMode deviceMode; bool hasScanned = false; ComSmartPtr enumerator; @@ -1724,6 +1897,17 @@ private: callDeviceChangeListeners(); } + //============================================================================== + static String getDeviceTypename (WASAPIDeviceMode mode) + { + if (mode == WASAPIDeviceMode::shared) return "Windows Audio"; + if (mode == WASAPIDeviceMode::sharedLowLatency) return "Windows Audio (Low Latency Mode)"; + if (mode == WASAPIDeviceMode::exclusive) return "Windows Audio (Exclusive Mode)"; + + jassertfalse; + return {}; + } + //============================================================================== JUCE_DECLARE_WEAK_REFERENCEABLE (WASAPIAudioIODeviceType) JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType) @@ -1782,19 +1966,6 @@ struct MMDeviceMasterVolume } -//============================================================================== -AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode) -{ - #if ! JUCE_WASAPI_EXCLUSIVE - if (exclusiveMode) - return nullptr; - #endif - - return SystemStats::getOperatingSystemType() >= SystemStats::WinVista - ? new WasapiClasses::WASAPIAudioIODeviceType (exclusiveMode) - : nullptr; -} - //============================================================================== #define JUCE_SYSTEMAUDIOVOL_IMPLEMENTED 1 float JUCE_CALLTYPE SystemAudioVolume::getGain() { return WasapiClasses::MMDeviceMasterVolume().getGain(); }