mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Windows: Added support for IAudioClient3 shared stream low latency mode in the WASAPI audio device, removed JUCE_WASAPI_EXCLUSIVE config flag and refactored AudioIODeviceType creation
This commit is contained in:
parent
b01e9276ba
commit
6195a5ab60
17 changed files with 482 additions and 316 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -177,8 +177,9 @@ static void addIfNotNull (OwnedArray<AudioIODeviceType>& list, AudioIODeviceType
|
|||
|
||||
void AudioDeviceManager::createAudioDeviceTypes (OwnedArray<AudioIODeviceType>& 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());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <AudioToolbox/AudioToolbox.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
|
@ -64,10 +69,18 @@
|
|||
#import <CoreMIDI/MIDINetworkSession.h>
|
||||
#endif
|
||||
|
||||
#include "native/juce_ios_Audio.cpp"
|
||||
#include "native/juce_mac_CoreMidi.cpp"
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_WINDOWS
|
||||
#if JUCE_WASAPI
|
||||
#include <mmreg.h>
|
||||
#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 <iasiodrv.h>
|
||||
#include "native/juce_win32_ASIO.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -128,6 +144,7 @@
|
|||
just set the JUCE_ALSA flag to 0.
|
||||
*/
|
||||
#include <alsa/asoundlib.h>
|
||||
#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 <jack/jack.h>
|
||||
#include "native/juce_linux_JackAudio.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_BELA
|
||||
|
|
@ -149,17 +167,29 @@
|
|||
*/
|
||||
#include <Bela.h>
|
||||
#include <Midi.h>
|
||||
#include "native/juce_linux_Bela.cpp"
|
||||
#endif
|
||||
|
||||
#undef SIZEOF
|
||||
|
||||
#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"
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES || JUCE_USE_ANDROID_OBOE
|
||||
#include "native/juce_android_HighPerformanceAudioHelpers.h"
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#include <SLES/OpenSLES_AndroidConfiguration.h>
|
||||
#include "native/juce_android_OpenSL.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_ANDROID_OBOE
|
||||
|
|
@ -174,75 +204,7 @@
|
|||
"-Wshadow-field")
|
||||
#include <oboe/Oboe.h>
|
||||
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
|
||||
#include "native/juce_linux_Midi.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
#include "native/juce_android_Audio.cpp"
|
||||
#include "native/juce_android_Midi.cpp"
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES || JUCE_USE_ANDROID_OBOE
|
||||
#include "native/juce_android_HighPerformanceAudioHelpers.h"
|
||||
|
||||
#if JUCE_USE_ANDROID_OPENSLES
|
||||
#include "native/juce_android_OpenSL.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_ANDROID_OBOE
|
||||
#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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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]; }
|
||||
|
|
|
|||
|
|
@ -1299,9 +1299,4 @@ AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices()
|
|||
return new ALSAAudioIODeviceType (false, "ALSA");
|
||||
}
|
||||
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA()
|
||||
{
|
||||
return createAudioIODeviceType_ALSA_PCMDevices();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -667,10 +667,4 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK()
|
||||
{
|
||||
return new JackAudioIODeviceType();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -2234,12 +2234,6 @@ private:
|
|||
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio()
|
||||
{
|
||||
return new CoreAudioClasses::CoreAudioIODeviceType();
|
||||
}
|
||||
|
||||
#undef JUCE_COREAUDIOLOG
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -1640,9 +1640,4 @@ void sendASIODeviceChangeToListeners (ASIOAudioIODeviceType* type)
|
|||
type->sendDeviceChangeToListeners();
|
||||
}
|
||||
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO()
|
||||
{
|
||||
return new ASIOAudioIODeviceType();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -1282,10 +1282,4 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DSoundAudioIODeviceType)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound()
|
||||
{
|
||||
return new DSoundAudioIODeviceType();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -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<IMMDevice>& device)
|
||||
static EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& device)
|
||||
{
|
||||
EDataFlow flow = eRender;
|
||||
ComSmartPtr<IMMEndpoint> endPoint;
|
||||
|
|
@ -332,88 +369,64 @@ EDataFlow getDataFlow (const ComSmartPtr<IMMDevice>& 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<IMMDevice>& d, bool exclusiveMode)
|
||||
: device (d), useExclusiveMode (exclusiveMode)
|
||||
WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, WASAPIDeviceMode mode)
|
||||
: device (d),
|
||||
deviceMode (mode)
|
||||
{
|
||||
clientEvent = CreateEvent (nullptr, false, false, nullptr);
|
||||
|
||||
ComSmartPtr<IAudioClient> 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<IMMDevice> device;
|
||||
ComSmartPtr<IAudioClient> 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<double> rates;
|
||||
HANDLE clientEvent = {};
|
||||
BigInteger channels;
|
||||
|
|
@ -593,6 +609,94 @@ private:
|
|||
return newClient;
|
||||
}
|
||||
|
||||
static bool getClientMixFormat (ComSmartPtr<IAudioClient>& client, WAVEFORMATEXTENSIBLE& format)
|
||||
{
|
||||
WAVEFORMATEX* mixFormat = nullptr;
|
||||
|
||||
if (! check (client->GetMixFormat (&mixFormat)))
|
||||
return false;
|
||||
|
||||
copyWavFormat (format, mixFormat);
|
||||
CoTaskMemFree (mixFormat);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static ComSmartPtr<IAudioClient3> getClientAsVersion3 (ComSmartPtr<IAudioClient>& clientVersion1)
|
||||
{
|
||||
ComSmartPtr<IAudioClient3> newClient;
|
||||
|
||||
if (clientVersion1 != nullptr)
|
||||
clientVersion1.QueryInterface (__uuidof (IAudioClient3), newClient);
|
||||
|
||||
return newClient;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void querySupportedBufferSizes (WAVEFORMATEXTENSIBLE format, ComSmartPtr<IAudioClient>& 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<IAudioClient>& 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,10 +730,10 @@ private:
|
|||
|
||||
WAVEFORMATEXTENSIBLE* nearestFormat = nullptr;
|
||||
|
||||
HRESULT hr = clientToUse->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE
|
||||
HRESULT hr = clientToUse->IsFormatSupported (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE
|
||||
: AUDCLNT_SHAREMODE_SHARED,
|
||||
(WAVEFORMATEX*) &format,
|
||||
useExclusiveMode ? nullptr
|
||||
isExclusiveMode (deviceMode) ? nullptr
|
||||
: (WAVEFORMATEX**) &nearestFormat);
|
||||
logFailure (hr);
|
||||
|
||||
|
|
@ -642,7 +746,7 @@ private:
|
|||
}
|
||||
|
||||
CoTaskMemFree (nearestFormat);
|
||||
return check (hr);
|
||||
return hr == S_OK;
|
||||
}
|
||||
|
||||
bool findSupportedFormat (IAudioClient* clientToUse, double newSampleRate,
|
||||
|
|
@ -666,41 +770,50 @@ private:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool tryInitialisingWithBufferSize (int bufferSizeSamples)
|
||||
DWORD getStreamFlags()
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE format;
|
||||
DWORD streamFlags = 0x40000; /*AUDCLNT_STREAMFLAGS_EVENTCALLBACK*/
|
||||
|
||||
if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format))
|
||||
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 (useExclusiveMode && bufferSizeSamples > 0)
|
||||
if (isExclusiveMode (deviceMode) && bufferSizeSamples > 0)
|
||||
defaultPeriod = jmax (minPeriod, samplesToRefTime (bufferSizeSamples, format.Format.nSamplesPerSec));
|
||||
|
||||
for (;;)
|
||||
{
|
||||
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*/,
|
||||
auto hr = client->Initialize (isExclusiveMode (deviceMode) ? AUDCLNT_SHAREMODE_EXCLUSIVE
|
||||
: AUDCLNT_SHAREMODE_SHARED,
|
||||
getStreamFlags(),
|
||||
defaultPeriod,
|
||||
useExclusiveMode ? defaultPeriod : 0,
|
||||
isExclusiveMode (deviceMode) ? defaultPeriod : 0,
|
||||
(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;
|
||||
bytesPerFrame = format.Format.nBlockAlign;
|
||||
|
||||
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
|
||||
|
|
@ -716,6 +829,30 @@ private:
|
|||
|
||||
defaultPeriod = samplesToRefTime (numFrames, format.Format.nSamplesPerSec);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tryInitialisingWithBufferSize (int bufferSizeSamples)
|
||||
{
|
||||
WAVEFORMATEXTENSIBLE format;
|
||||
|
||||
if (findSupportedFormat (client, sampleRate, mixFormatChannelMask, format))
|
||||
{
|
||||
auto isInitialised = isLowLatencyMode (deviceMode) ? initialiseLowLatencyClient (bufferSizeSamples, format)
|
||||
: initialiseStandardClient (bufferSizeSamples, format);
|
||||
|
||||
if (isInitialised)
|
||||
{
|
||||
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);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -728,8 +865,8 @@ private:
|
|||
class WASAPIInputDevice : public WASAPIDeviceBase
|
||||
{
|
||||
public:
|
||||
WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
|
||||
: WASAPIDeviceBase (d, exclusiveMode)
|
||||
WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, WASAPIDeviceMode mode)
|
||||
: WASAPIDeviceBase (d, mode)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -891,8 +1028,8 @@ private:
|
|||
class WASAPIOutputDevice : public WASAPIDeviceBase
|
||||
{
|
||||
public:
|
||||
WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
|
||||
: WASAPIDeviceBase (d, exclusiveMode)
|
||||
WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& 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,16 +1187,29 @@ 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);
|
||||
|
||||
if (supportsSampleRateConversion (deviceMode))
|
||||
{
|
||||
for (auto r : outputDevice->rates)
|
||||
if (! sampleRates.contains (r))
|
||||
sampleRates.addUsingDefaultSort (r);
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleRates.removeValuesNotIn (outputDevice->rates);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto* d = inputDevice != nullptr ? static_cast<WASAPIDeviceBase*> (inputDevice.get())
|
||||
: static_cast<WASAPIDeviceBase*> (outputDevice.get());
|
||||
|
|
@ -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,6 +1233,20 @@ public:
|
|||
if (minBufferSize != defaultBufferSize)
|
||||
bufferSizes.addUsingDefaultSort (minBufferSize);
|
||||
|
||||
if (isLowLatencyMode (deviceMode))
|
||||
{
|
||||
auto size = minBufferSize;
|
||||
|
||||
while (size < lowLatencyMaxBufferSize)
|
||||
{
|
||||
size += lowLatencyBufferSizeMultiple;
|
||||
|
||||
if (! bufferSizes.contains (size))
|
||||
bufferSizes.addUsingDefaultSort (size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int n = 64;
|
||||
for (int i = 0; i < 40; ++i)
|
||||
{
|
||||
|
|
@ -1084,6 +1255,7 @@ public:
|
|||
|
||||
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<WASAPIInputDevice> inputDevice;
|
||||
std::unique_ptr<WASAPIOutputDevice> 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<double> sampleRates;
|
||||
Array<int> 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<IMMDeviceEnumerator> 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(); }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue