summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c')
-rw-r--r--3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c8848
1 files changed, 4804 insertions, 4044 deletions
diff --git a/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c b/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c
index 3990043efd7..dcbef04db20 100644
--- a/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c
+++ b/3rdparty/portaudio/src/hostapi/wasapi/pa_win_wasapi.c
@@ -1,9 +1,10 @@
/*
* Portable Audio I/O Library WASAPI implementation
- * Copyright (c) 2006-2010 David Viens, Dmitry Kostjuchenko
+ * Copyright (c) 2006-2010 David Viens
+ * Copyright (c) 2010-2023 Dmitry Kostjuchenko
*
* Based on the Open Source API proposed by Ross Bencina
- * Copyright (c) 1999-2002 Ross Bencina, Phil Burk
+ * Copyright (c) 1999-2019 Ross Bencina, Phil Burk
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
@@ -43,36 +44,61 @@
*/
#include <windows.h>
+#include <stddef.h>
#include <stdio.h>
#include <process.h>
#include <assert.h>
+// Max device count (if defined) causes max constant device count in the device list that
+// enables PaWasapi_UpdateDeviceList() API and makes it possible to update WASAPI list dynamically
+#ifndef PA_WASAPI_MAX_CONST_DEVICE_COUNT
+ #define PA_WASAPI_MAX_CONST_DEVICE_COUNT 0 // Force basic behavior by defining 0 if not defined by user
+#endif
+
+// Fallback from Event to the Polling method in case if latency is higher than 21.33ms, as it allows to use
+// 100% of CPU inside the PA's callback.
+// Note: Some USB DAC drivers are buggy when Polling method is forced in Exclusive mode, audio output becomes
+// unstable with a lot of interruptions, therefore this define is optional. The default behavior is to
+// not change the Event mode to Polling and use the mode which user provided.
+//#define PA_WASAPI_FORCE_POLL_IF_LARGE_BUFFER
+
+//! Poll mode time slots logging.
+//#define PA_WASAPI_LOG_TIME_SLOTS
+
// WinRT
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
- #define PA_WINRT
- #define INITGUID
+ #define PA_WINRT
+ #define INITGUID
#endif
// WASAPI
+// using adjustments for MinGW build from @mgeier/MXE
+// https://github.com/mxe/mxe/commit/f4bbc45682f021948bdaefd9fd476e2a04c4740f
#include <mmreg.h> // must be before other Wasapi headers
-#if defined(_MSC_VER) && (_MSC_VER >= 1400) || (defined(__MINGW64_VERSION_MAJOR) && (__MINGW64_VERSION_MAJOR >= 4))
- #include <avrt.h>
- #define COBJMACROS
- #include <audioclient.h>
- #include <endpointvolume.h>
- #define INITGUID // Avoid additional linkage of static libs, excessive code will be optimized out by the compiler
- #include <mmdeviceapi.h>
-#if defined(_MSC_VER) && (_MSC_VER >= 1400)
- #include <functiondiscoverykeys.h>
-#else
- #include <functiondiscoverykeys_devpkey.h>
+#if defined(_MSC_VER) && (_MSC_VER >= 1400) || defined(__MINGW64_VERSION_MAJOR)
+ #include <avrt.h>
+ #define COBJMACROS
+ #include <audioclient.h>
+ #include <endpointvolume.h>
+ #define INITGUID // Avoid additional linkage of static libs, excessive code will be optimized out by the compiler
+#ifndef _MSC_VER
+ #include <functiondiscoverykeys_devpkey.h>
+#endif
+ #include <functiondiscoverykeys.h>
+ #include <mmdeviceapi.h>
+ #include <devicetopology.h> // Used to get IKsJackDescription interface
+ #undef INITGUID
+// Visual Studio 2010 does not support the inline keyword
+#if (_MSC_VER <= 1600)
+ #define inline _inline
#endif
- #include <devicetopology.h> // Used to get IKsJackDescription interface
- #undef INITGUID
#endif
#ifndef __MWERKS__
- #include <malloc.h>
- #include <memory.h>
+ #include <malloc.h>
+ #include <memory.h>
+#endif
+#ifndef PA_WINRT
+ #include <mmsystem.h>
#endif
#include "pa_util.h"
@@ -81,19 +107,32 @@
#include "pa_stream.h"
#include "pa_cpuload.h"
#include "pa_process.h"
-#include "pa_win_wasapi.h"
#include "pa_debugprint.h"
#include "pa_ringbuffer.h"
+#include "pa_win_version.h"
#include "pa_win_coinitialize.h"
+#include "pa_win_wasapi.h"
+
+#if !defined(NTDDI_VERSION) || (defined(__GNUC__) && (__GNUC__ <= 6) && !defined(__MINGW64__))
-#if !defined(NTDDI_VERSION)
-
#undef WINVER
#undef _WIN32_WINNT
#define WINVER 0x0600 // VISTA
- #define _WIN32_WINNT WINVER
+ #define _WIN32_WINNT WINVER
- #ifndef _AVRT_ //<< fix MinGW dummy compile by defining missing type: AVRT_PRIORITY
+ #ifndef WINAPI
+ #define WINAPI __stdcall
+ #endif
+
+ #ifndef __unaligned
+ #define __unaligned
+ #endif
+
+ #ifndef __C89_NAMELESS
+ #define __C89_NAMELESS
+ #endif
+
+ #ifndef _AVRT_ //<< fix MinGW dummy compile by defining missing type: AVRT_PRIORITY
typedef enum _AVRT_PRIORITY
{
AVRT_PRIORITY_LOW = -1,
@@ -101,16 +140,18 @@
AVRT_PRIORITY_HIGH,
AVRT_PRIORITY_CRITICAL
} AVRT_PRIORITY, *PAVRT_PRIORITY;
- #endif
+ #endif
- #include <basetyps.h> // << for IID/CLSID
+ #include <basetyps.h> // << for IID/CLSID
#include <rpcsal.h>
#include <sal.h>
- #ifndef __LPCGUID_DEFINED__
- #define __LPCGUID_DEFINED__
- typedef const GUID *LPCGUID;
- #endif
+ #ifndef __LPCGUID_DEFINED__
+ #define __LPCGUID_DEFINED__
+ typedef const GUID *LPCGUID;
+ #endif
+ typedef GUID IID;
+ typedef GUID CLSID;
#ifndef PROPERTYKEY_DEFINED
#define PROPERTYKEY_DEFINED
@@ -118,7 +159,7 @@
{
GUID fmtid;
DWORD pid;
- } PROPERTYKEY;
+ } PROPERTYKEY;
#endif
#ifdef __midl_proxy
@@ -129,32 +170,37 @@
#ifdef WIN64
#include <wtypes.h>
- typedef LONG NTSTATUS;
#define FASTCALL
#include <oleidl.h>
#include <objidl.h>
- #else
- typedef struct _BYTE_BLOB
- {
- unsigned long clSize;
- unsigned char abData[ 1 ];
- } BYTE_BLOB;
- typedef /* [unique] */ __RPC_unique_pointer BYTE_BLOB *UP_BYTE_BLOB;
+ #else
+ #ifndef _BLOB_DEFINED
+ typedef struct _BYTE_BLOB
+ {
+ unsigned long clSize;
+ unsigned char abData[ 1 ];
+ } BYTE_BLOB;
+ typedef /* [unique] */ __RPC_unique_pointer BYTE_BLOB *UP_BYTE_BLOB;
+ #endif
typedef LONGLONG REFERENCE_TIME;
#define NONAMELESSUNION
#endif
-
+
+ #ifndef NT_SUCCESS
+ typedef LONG NTSTATUS;
+ #endif
+
#ifndef WAVE_FORMAT_IEEE_FLOAT
#define WAVE_FORMAT_IEEE_FLOAT 0x0003 // 32-bit floating-point
- #endif
-
+ #endif
+
#ifndef __MINGW_EXTENSION
#if defined(__GNUC__) || defined(__GNUG__)
#define __MINGW_EXTENSION __extension__
#else
#define __MINGW_EXTENSION
#endif
- #endif
+ #endif
#include <sdkddkver.h>
#include <propkeydef.h>
@@ -164,7 +210,7 @@
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <functiondiscoverykeys.h>
- #include <devicetopology.h> // Used to get IKsJackDescription interface
+ #include <devicetopology.h> // Used to get IKsJackDescription interface
#undef INITGUID
#endif // NTDDI_VERSION
@@ -172,32 +218,32 @@
// Missing declarations for WinRT
#ifdef PA_WINRT
- #define DEVICE_STATE_ACTIVE 0x00000001
-
- typedef enum _EDataFlow
- {
- eRender = 0,
- eCapture = ( eRender + 1 ) ,
- eAll = ( eCapture + 1 ) ,
- EDataFlow_enum_count = ( eAll + 1 )
- }
- EDataFlow;
-
- typedef enum _EndpointFormFactor
- {
- RemoteNetworkDevice = 0,
- Speakers = ( RemoteNetworkDevice + 1 ) ,
- LineLevel = ( Speakers + 1 ) ,
- Headphones = ( LineLevel + 1 ) ,
- Microphone = ( Headphones + 1 ) ,
- Headset = ( Microphone + 1 ) ,
- Handset = ( Headset + 1 ) ,
- UnknownDigitalPassthrough = ( Handset + 1 ) ,
- SPDIF = ( UnknownDigitalPassthrough + 1 ) ,
- HDMI = ( SPDIF + 1 ) ,
- UnknownFormFactor = ( HDMI + 1 )
- }
- EndpointFormFactor;
+ #define DEVICE_STATE_ACTIVE 0x00000001
+
+ typedef enum _EDataFlow
+ {
+ eRender = 0,
+ eCapture = ( eRender + 1 ) ,
+ eAll = ( eCapture + 1 ) ,
+ EDataFlow_enum_count = ( eAll + 1 )
+ }
+ EDataFlow;
+
+ typedef enum _EndpointFormFactor
+ {
+ RemoteNetworkDevice = 0,
+ Speakers = ( RemoteNetworkDevice + 1 ) ,
+ LineLevel = ( Speakers + 1 ) ,
+ Headphones = ( LineLevel + 1 ) ,
+ Microphone = ( Headphones + 1 ) ,
+ Headset = ( Microphone + 1 ) ,
+ Handset = ( Headset + 1 ) ,
+ UnknownDigitalPassthrough = ( Handset + 1 ) ,
+ SPDIF = ( UnknownDigitalPassthrough + 1 ) ,
+ HDMI = ( SPDIF + 1 ) ,
+ UnknownFormFactor = ( HDMI + 1 )
+ }
+ EndpointFormFactor;
#endif
@@ -217,6 +263,8 @@
PA_DEFINE_IID(IAudioClient, 1cb9ad4c, dbfa, 4c32, b1, 78, c2, f5, 68, a7, 03, b2);
// "726778CD-F60A-4EDA-82DE-E47610CD78AA"
PA_DEFINE_IID(IAudioClient2, 726778cd, f60a, 4eda, 82, de, e4, 76, 10, cd, 78, aa);
+// "7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42"
+PA_DEFINE_IID(IAudioClient3, 7ed4ee07, 8e67, 4cd4, 8c, 1a, 2b, 7a, 59, 87, ad, 42);
// "1BE09788-6894-4089-8586-9A2A6C265AC5"
PA_DEFINE_IID(IMMEndpoint, 1be09788, 6894, 4089, 85, 86, 9a, 2a, 6c, 26, 5a, c5);
// "A95664D2-9614-4F35-A746-DE8DB63617E6"
@@ -235,21 +283,38 @@ PA_DEFINE_IID(IPart, AE2DE0E4, 5BCA, 4F2D, aa, 46, 5d, 13, f8, fd
PA_DEFINE_IID(IKsJackDescription, 4509F757, 2D46, 4637, 8e, 62, ce, 7d, b9, 44, f5, 7b);
// Media formats:
-__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_ADPCM, 0x00000002, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
-__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
+__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
+__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_ADPCM, 0x00000002, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
+__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
+__DEFINE_GUID(pa_KSDATAFORMAT_SUBTYPE_IEC61937_PCM, 0x00000000, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 );
+
+#ifndef _WAVEFORMATEXTENSIBLE_IEC61937_
+ #define _WAVEFORMATEXTENSIBLE_IEC61937_
+typedef struct {
+ WAVEFORMATEXTENSIBLE FormatExt;
+ DWORD dwEncodedSamplesPerSec;
+ DWORD dwEncodedChannelCount;
+ DWORD dwAverageBytesPerSec;
+} WAVEFORMATEXTENSIBLE_IEC61937, *PWAVEFORMATEXTENSIBLE_IEC61937;
+#endif // !_WAVEFORMATEXTENSIBLE_IEC61937_
+
+typedef union _WAVEFORMATEXTENSIBLE_UNION
+{
+ WAVEFORMATEXTENSIBLE ext;
+ WAVEFORMATEXTENSIBLE_IEC61937 iec61937;
+} WAVEFORMATEXTENSIBLE_UNION;
#ifdef __IAudioClient2_INTERFACE_DEFINED__
-typedef enum _pa_AUDCLNT_STREAMOPTIONS {
- pa_AUDCLNT_STREAMOPTIONS_NONE = 0x00,
- pa_AUDCLNT_STREAMOPTIONS_RAW = 0x01,
- pa_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT = 0x02
+typedef enum _pa_AUDCLNT_STREAMOPTIONS {
+ pa_AUDCLNT_STREAMOPTIONS_NONE = 0x00,
+ pa_AUDCLNT_STREAMOPTIONS_RAW = 0x01,
+ pa_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT = 0x02
} pa_AUDCLNT_STREAMOPTIONS;
typedef struct _pa_AudioClientProperties {
- UINT32 cbSize;
- BOOL bIsOffload;
- AUDIO_STREAM_CATEGORY eCategory;
- pa_AUDCLNT_STREAMOPTIONS Options;
+ UINT32 cbSize;
+ BOOL bIsOffload;
+ AUDIO_STREAM_CATEGORY eCategory;
+ pa_AUDCLNT_STREAMOPTIONS Options;
} pa_AudioClientProperties;
#define PA_AUDIOCLIENTPROPERTIES_SIZE_CATEGORY (sizeof(pa_AudioClientProperties) - sizeof(pa_AUDCLNT_STREAMOPTIONS))
#define PA_AUDIOCLIENTPROPERTIES_SIZE_OPTIONS sizeof(pa_AudioClientProperties)
@@ -257,31 +322,43 @@ typedef struct _pa_AudioClientProperties {
/* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */
#if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
- #define CREATE_THREAD(PROC) (HANDLE)_beginthreadex( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
- #define PA_THREAD_FUNC static unsigned WINAPI
- #define PA_THREAD_ID unsigned
+ #define CREATE_THREAD(PROC) (HANDLE)_beginthreadex( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
+ #define PA_THREAD_FUNC static unsigned WINAPI
+ #define PA_THREAD_ID unsigned
#else
- #define CREATE_THREAD(PROC) CreateThread( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
- #define PA_THREAD_FUNC static DWORD WINAPI
- #define PA_THREAD_ID DWORD
+ #define CREATE_THREAD(PROC) CreateThread( NULL, 0, (PROC), stream, 0, &stream->dwThreadId )
+ #define PA_THREAD_FUNC static DWORD WINAPI
+ #define PA_THREAD_ID DWORD
#endif
// Thread function forward decl.
PA_THREAD_FUNC ProcThreadEvent(void *param);
PA_THREAD_FUNC ProcThreadPoll(void *param);
-// Availabe from Windows 7
+// Error codes (available since Windows 7)
#ifndef AUDCLNT_E_BUFFER_ERROR
- #define AUDCLNT_E_BUFFER_ERROR AUDCLNT_ERR(0x018)
+ #define AUDCLNT_E_BUFFER_ERROR AUDCLNT_ERR(0x018)
#endif
#ifndef AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
- #define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019)
+ #define AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED AUDCLNT_ERR(0x019)
#endif
#ifndef AUDCLNT_E_INVALID_DEVICE_PERIOD
- #define AUDCLNT_E_INVALID_DEVICE_PERIOD AUDCLNT_ERR(0x020)
+ #define AUDCLNT_E_INVALID_DEVICE_PERIOD AUDCLNT_ERR(0x020)
#endif
-#define MAX_STR_LEN 512
+// Stream flags (available since Windows 7)
+#ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
+ #define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
+#endif
+#ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
+ #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
+#endif
+
+#define PA_WASAPI_DEVICE_ID_LEN 256
+#define PA_WASAPI_DEVICE_NAME_LEN 128
+#ifdef PA_WINRT
+ #define PA_WASAPI_DEVICE_MAX_COUNT 16
+#endif
enum { S_INPUT = 0, S_OUTPUT = 1, S_COUNT = 2, S_FULLDUPLEX = 0 };
@@ -309,11 +386,12 @@ enum { WASAPI_PACKETS_PER_INPUT_BUFFER = 6 };
#define SAFE_CLOSE(h) if ((h) != NULL) { CloseHandle((h)); (h) = NULL; }
#define SAFE_RELEASE(punk) if ((punk) != NULL) { (punk)->lpVtbl->Release((punk)); (punk) = NULL; }
+#define SAFE_ADDREF(punk) if ((punk) != NULL) { (punk)->lpVtbl->AddRef((punk)); }
// Mixer function
-typedef void (*MixMonoToStereoF) (void *__to, void *__from, UINT32 count);
+typedef void (*MixMonoToStereoF) (void *__to, const void *__from, UINT32 count);
-// AVRT is the new "multimedia schedulling stuff"
+// AVRT is the new "multimedia scheduling stuff"
#ifndef PA_WINRT
typedef BOOL (WINAPI *FAvRtCreateThreadOrderingGroup) (PHANDLE,PLARGE_INTEGER,GUID*,PLARGE_INTEGER);
typedef BOOL (WINAPI *FAvRtDeleteThreadOrderingGroup) (HANDLE);
@@ -398,10 +476,10 @@ typedef struct PaWasapiDeviceInfo
IMMDevice *device;
#endif
- // from GetId
- WCHAR szDeviceID[MAX_STR_LEN];
+ // device Id
+ WCHAR deviceId[PA_WASAPI_DEVICE_ID_LEN];
- // from GetState
+ // from GetState
DWORD state;
// Fields filled from IAudioDevice (_prior_ to Initialize)
@@ -409,20 +487,26 @@ typedef struct PaWasapiDeviceInfo
REFERENCE_TIME DefaultDevicePeriod;
REFERENCE_TIME MinimumDevicePeriod;
- // Default format (setup through Control Panel by user)
- WAVEFORMATEXTENSIBLE DefaultFormat;
+ // Default format (setup through Control Panel by user)
+ WAVEFORMATEXTENSIBLE DefaultFormat;
+
+ // Mix format (internal format used by WASAPI audio engine)
+ WAVEFORMATEXTENSIBLE MixFormat;
// Fields filled from IMMEndpoint'sGetDataFlow
EDataFlow flow;
- // Formfactor
- EndpointFormFactor formFactor;
+ // Form-factor
+ EndpointFormFactor formFactor;
+
+ // Loopback state (TRUE if device acts as loopback)
+ BOOL loopBack;
}
PaWasapiDeviceInfo;
// ------------------------------------------------------------------------------------------
/* PaWasapiHostApiRepresentation - host api datastructure specific to this implementation */
-typedef struct
+typedef struct PaWasapiHostApiRepresentation
{
PaUtilHostApiRepresentation inheritedHostApiRep;
PaUtilStreamInterface callbackStreamInterface;
@@ -434,21 +518,13 @@ typedef struct
PaWinUtilComInitializationResult comInitializationResult;
- //in case we later need the synch
-#ifndef PA_WINRT
- IMMDeviceEnumerator *enumerator;
-#endif
-
- //this is the REAL number of devices, whether they are usefull to PA or not!
+ // this is the REAL number of devices, whether they are useful to PA or not!
UINT32 deviceCount;
- WCHAR defaultRenderer [MAX_STR_LEN];
- WCHAR defaultCapturer [MAX_STR_LEN];
-
PaWasapiDeviceInfo *devInfo;
- // Is true when WOW64 Vista/7 Workaround is needed
- BOOL useWOW64Workaround;
+ // is TRUE when WOW64 Vista/7 Workaround is needed
+ BOOL useWOW64Workaround;
}
PaWasapiHostApiRepresentation;
@@ -456,14 +532,14 @@ PaWasapiHostApiRepresentation;
/* PaWasapiAudioClientParams - audio client parameters */
typedef struct PaWasapiAudioClientParams
{
- PaWasapiDeviceInfo *device_info;
- PaStreamParameters stream_params;
- PaWasapiStreamInfo wasapi_params;
- UINT32 frames_per_buffer;
- double sample_rate;
- BOOL blocking;
- BOOL full_duplex;
- BOOL wow64_workaround;
+ PaWasapiDeviceInfo *device_info;
+ PaStreamParameters stream_params;
+ PaWasapiStreamInfo wasapi_params;
+ UINT32 frames_per_buffer;
+ double sample_rate;
+ BOOL blocking;
+ BOOL full_duplex;
+ BOOL wow64_workaround;
}
PaWasapiAudioClientParams;
@@ -473,34 +549,34 @@ typedef struct PaWasapiSubStream
{
IAudioClient *clientParent;
#ifndef PA_WINRT
- IStream *clientStream;
+ IStream *clientStream;
#endif
- IAudioClient *clientProc;
+ IAudioClient *clientProc;
- WAVEFORMATEXTENSIBLE wavex;
+ WAVEFORMATEXTENSIBLE_UNION wavexu;
UINT32 bufferSize;
REFERENCE_TIME deviceLatency;
REFERENCE_TIME period;
- double latencySeconds;
- UINT32 framesPerHostCallback;
- AUDCLNT_SHAREMODE shareMode;
- UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...
- UINT32 flags;
- PaWasapiAudioClientParams params; //!< parameters
-
- // Buffers
- UINT32 buffers; //!< number of buffers used (from host side)
- UINT32 framesPerBuffer; //!< number of frames per 1 buffer
- BOOL userBufferAndHostMatch;
-
- // Used for Mono >> Stereo workaround, if driver does not support it
- // (in Exclusive mode WASAPI usually refuses to operate with Mono (1-ch)
- void *monoBuffer; //!< pointer to buffer
- UINT32 monoBufferSize; //!< buffer size in bytes
- MixMonoToStereoF monoMixer; //!< pointer to mixer function
-
- PaUtilRingBuffer *tailBuffer; //!< buffer with trailing sample for blocking mode operations (only for Input)
- void *tailBufferMemory; //!< tail buffer memory region
+ double latencySeconds;
+ UINT32 framesPerHostCallback;
+ AUDCLNT_SHAREMODE shareMode;
+ UINT32 streamFlags; // AUDCLNT_STREAMFLAGS_EVENTCALLBACK, ...
+ UINT32 flags;
+ PaWasapiAudioClientParams params; //!< parameters
+
+ // Buffers
+ UINT32 buffers; //!< number of buffers used (from host side)
+ UINT32 framesPerBuffer; //!< number of frames per 1 buffer
+ BOOL userBufferAndHostMatch;
+
+ // Used for Mono >> Stereo workaround, if driver does not support it
+ // (in Exclusive mode WASAPI usually refuses to operate with Mono (1-ch)
+ void *monoBuffer; //!< pointer to buffer
+ UINT32 monoBufferSize; //!< buffer size in bytes
+ MixMonoToStereoF monoMixer; //!< pointer to mixer function
+
+ PaUtilRingBuffer *tailBuffer; //!< buffer with trailing sample for blocking mode operations (only for Input)
+ void *tailBufferMemory; //!< tail buffer memory region
}
PaWasapiSubStream;
@@ -516,61 +592,65 @@ PaWasapiHostProcessor;
// ------------------------------------------------------------------------------------------
typedef struct PaWasapiStream
{
- /* IMPLEMENT ME: rename this */
PaUtilStreamRepresentation streamRepresentation;
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
PaUtilBufferProcessor bufferProcessor;
// input
- PaWasapiSubStream in;
+ PaWasapiSubStream in;
IAudioCaptureClient *captureClientParent;
#ifndef PA_WINRT
- IStream *captureClientStream;
+ IStream *captureClientStream;
#endif
- IAudioCaptureClient *captureClient;
- IAudioEndpointVolume *inVol;
+ IAudioCaptureClient *captureClient;
- // output
- PaWasapiSubStream out;
+ // output
+ PaWasapiSubStream out;
IAudioRenderClient *renderClientParent;
#ifndef PA_WINRT
- IStream *renderClientStream;
+ IStream *renderClientStream;
#endif
- IAudioRenderClient *renderClient;
- IAudioEndpointVolume *outVol;
+ IAudioRenderClient *renderClient;
- // event handles for event-driven processing mode
- HANDLE event[S_COUNT];
+ // Event handles for event-driven processing mode
+ HANDLE event[S_COUNT];
- // buffer mode
- PaUtilHostBufferSizeMode bufferMode;
+ // Buffer mode
+ PaUtilHostBufferSizeMode bufferMode;
- // must be volatile to avoid race condition on user query while
- // thread is being started
- volatile BOOL running;
+ // Stream state: active (can be reset inside the processing thread,
+ // to avoid reading incorrected cached state)
+ volatile BOOL isActive;
+
+ // Stream state: stopped (triggered by user only via external API)
+ BOOL isStopped;
PA_THREAD_ID dwThreadId;
HANDLE hThread;
- HANDLE hCloseRequest;
- HANDLE hThreadStart; //!< signalled by thread on start
- HANDLE hThreadExit; //!< signalled by thread on exit
- HANDLE hBlockingOpStreamRD;
- HANDLE hBlockingOpStreamWR;
+ HANDLE hCloseRequest;
+ HANDLE hThreadStart; // signalled by thread on start
+ HANDLE hThreadExit; // signalled by thread on exit
+ HANDLE hBlockingOpStreamRD;
+ HANDLE hBlockingOpStreamWR;
// Host callback Output overrider
- PaWasapiHostProcessor hostProcessOverrideOutput;
+ PaWasapiHostProcessor hostProcessOverrideOutput;
// Host callback Input overrider
- PaWasapiHostProcessor hostProcessOverrideInput;
+ PaWasapiHostProcessor hostProcessOverrideInput;
+
+ // Defines blocking/callback interface used
+ BOOL isBlocking;
- // Defines blocking/callback interface used
- BOOL bBlocking;
+ // Av Task (MM thread management)
+ HANDLE hAvTask;
- // Av Task (MM thread management)
- HANDLE hAvTask;
+ // Thread priority level
+ PaWasapiThreadPriority nThreadPriority;
- // Thread priority level
- PaWasapiThreadPriority nThreadPriority;
+ // State handler
+ PaWasapiStreamStateCallback fnStateHandler;
+ void *pStateHandlerUserData;
}
PaWasapiStream;
@@ -582,117 +662,277 @@ static HRESULT UnmarshalStreamComPointers(PaWasapiStream *stream);
static void ReleaseUnmarshaledSubComPointers(PaWasapiSubStream *substream);
static void ReleaseUnmarshaledComPointers(PaWasapiStream *stream);
-// Local stream methods
+// Local methods
static void _StreamOnStop(PaWasapiStream *stream);
-static void _StreamFinish(PaWasapiStream *stream);
static void _StreamCleanup(PaWasapiStream *stream);
static HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available);
static HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available);
-static void *PaWasapi_ReallocateMemory(void *ptr, size_t size);
+static void *PaWasapi_ReallocateMemory(void *prev, size_t size);
static void PaWasapi_FreeMemory(void *ptr);
+static PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *fmtext);
-// Local statics
+// WinRT (UWP) device list
+#ifdef PA_WINRT
+typedef struct PaWasapiWinrtDeviceInfo
+{
+ WCHAR id[PA_WASAPI_DEVICE_ID_LEN];
+ WCHAR name[PA_WASAPI_DEVICE_NAME_LEN];
+ EndpointFormFactor formFactor;
+}
+PaWasapiWinrtDeviceInfo;
+typedef struct PaWasapiWinrtDeviceListRole
+{
+ WCHAR defaultId[PA_WASAPI_DEVICE_ID_LEN];
+ PaWasapiWinrtDeviceInfo devices[PA_WASAPI_DEVICE_MAX_COUNT];
+ UINT32 deviceCount;
+}
+PaWasapiWinrtDeviceListRole;
+typedef struct PaWasapiWinrtDeviceList
+{
+ PaWasapiWinrtDeviceListRole render;
+ PaWasapiWinrtDeviceListRole capture;
+}
+PaWasapiWinrtDeviceList;
+static PaWasapiWinrtDeviceList g_DeviceListInfo = { 0 };
+#endif
+
+// WinRT (UWP) device list context
+#ifdef PA_WINRT
+typedef struct PaWasapiWinrtDeviceListContextEntry
+{
+ PaWasapiWinrtDeviceInfo *info;
+ EDataFlow flow;
+}
+PaWasapiWinrtDeviceListContextEntry;
+typedef struct PaWasapiWinrtDeviceListContext
+{
+ PaWasapiWinrtDeviceListContextEntry devices[PA_WASAPI_DEVICE_MAX_COUNT * 2];
+}
+PaWasapiWinrtDeviceListContext;
+#endif
// ------------------------------------------------------------------------------------------
#define LogHostError(HRES) __LogHostError(HRES, __FUNCTION__, __FILE__, __LINE__)
-static HRESULT __LogHostError(HRESULT res, const char *func, const char *file, int line)
+static HRESULT __LogHostError(const HRESULT res, const char *func, const char *file, int line)
{
const char *text = NULL;
switch (res)
- {
- case S_OK: return res;
- case E_POINTER :text ="E_POINTER"; break;
- case E_INVALIDARG :text ="E_INVALIDARG"; break;
-
- case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break;
- case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;
- case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
- case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;
- case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break;
- case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;
- case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break;
- case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
- case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break;
- case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break;
- case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
- case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;
- case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
- case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
- case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
- case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
- case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
- case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
- case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
- case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
- case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
- case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;
- case AUDCLNT_E_BUFFER_ERROR :text ="AUDCLNT_E_BUFFER_ERROR"; break;
- case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break;
- case AUDCLNT_E_INVALID_DEVICE_PERIOD :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break;
-
- case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break;
- case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
- case AUDCLNT_S_POSITION_STALLED :text ="AUDCLNT_S_POSITION_STALLED"; break;
-
- // other windows common errors:
- case CO_E_NOTINITIALIZED :text ="CO_E_NOTINITIALIZED: you must call CoInitialize() before Pa_OpenStream()"; break;
-
- default:
- text = "UNKNOWN ERROR";
- }
- PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", res, text, func, file, line));
- PA_SKELETON_SET_LAST_HOST_ERROR(res, text);
- return res;
+ {
+ case S_OK: return res;
+ case E_POINTER :text ="E_POINTER"; break;
+ case E_INVALIDARG :text ="E_INVALIDARG"; break;
+
+ case AUDCLNT_E_NOT_INITIALIZED :text ="AUDCLNT_E_NOT_INITIALIZED"; break;
+ case AUDCLNT_E_ALREADY_INITIALIZED :text ="AUDCLNT_E_ALREADY_INITIALIZED"; break;
+ case AUDCLNT_E_WRONG_ENDPOINT_TYPE :text ="AUDCLNT_E_WRONG_ENDPOINT_TYPE"; break;
+ case AUDCLNT_E_DEVICE_INVALIDATED :text ="AUDCLNT_E_DEVICE_INVALIDATED"; break;
+ case AUDCLNT_E_NOT_STOPPED :text ="AUDCLNT_E_NOT_STOPPED"; break;
+ case AUDCLNT_E_BUFFER_TOO_LARGE :text ="AUDCLNT_E_BUFFER_TOO_LARGE"; break;
+ case AUDCLNT_E_OUT_OF_ORDER :text ="AUDCLNT_E_OUT_OF_ORDER"; break;
+ case AUDCLNT_E_UNSUPPORTED_FORMAT :text ="AUDCLNT_E_UNSUPPORTED_FORMAT"; break;
+ case AUDCLNT_E_INVALID_SIZE :text ="AUDCLNT_E_INVALID_SIZE"; break;
+ case AUDCLNT_E_DEVICE_IN_USE :text ="AUDCLNT_E_DEVICE_IN_USE"; break;
+ case AUDCLNT_E_BUFFER_OPERATION_PENDING :text ="AUDCLNT_E_BUFFER_OPERATION_PENDING"; break;
+ case AUDCLNT_E_THREAD_NOT_REGISTERED :text ="AUDCLNT_E_THREAD_NOT_REGISTERED"; break;
+ case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED :text ="AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED"; break;
+ case AUDCLNT_E_ENDPOINT_CREATE_FAILED :text ="AUDCLNT_E_ENDPOINT_CREATE_FAILED"; break;
+ case AUDCLNT_E_SERVICE_NOT_RUNNING :text ="AUDCLNT_E_SERVICE_NOT_RUNNING"; break;
+ case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED :text ="AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED"; break;
+ case AUDCLNT_E_EXCLUSIVE_MODE_ONLY :text ="AUDCLNT_E_EXCLUSIVE_MODE_ONLY"; break;
+ case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL :text ="AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"; break;
+ case AUDCLNT_E_EVENTHANDLE_NOT_SET :text ="AUDCLNT_E_EVENTHANDLE_NOT_SET"; break;
+ case AUDCLNT_E_INCORRECT_BUFFER_SIZE :text ="AUDCLNT_E_INCORRECT_BUFFER_SIZE"; break;
+ case AUDCLNT_E_BUFFER_SIZE_ERROR :text ="AUDCLNT_E_BUFFER_SIZE_ERROR"; break;
+ case AUDCLNT_E_CPUUSAGE_EXCEEDED :text ="AUDCLNT_E_CPUUSAGE_EXCEEDED"; break;
+ case AUDCLNT_E_BUFFER_ERROR :text ="AUDCLNT_E_BUFFER_ERROR"; break;
+ case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED :text ="AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED"; break;
+ case AUDCLNT_E_INVALID_DEVICE_PERIOD :text ="AUDCLNT_E_INVALID_DEVICE_PERIOD"; break;
+
+#ifdef AUDCLNT_E_INVALID_STREAM_FLAG
+ case AUDCLNT_E_INVALID_STREAM_FLAG :text ="AUDCLNT_E_INVALID_STREAM_FLAG"; break;
+#endif
+#ifdef AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE
+ case AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE :text ="AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE"; break;
+#endif
+#ifdef AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES
+ case AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES :text ="AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES"; break;
+#endif
+#ifdef AUDCLNT_E_OFFLOAD_MODE_ONLY
+ case AUDCLNT_E_OFFLOAD_MODE_ONLY :text ="AUDCLNT_E_OFFLOAD_MODE_ONLY"; break;
+#endif
+#ifdef AUDCLNT_E_NONOFFLOAD_MODE_ONLY
+ case AUDCLNT_E_NONOFFLOAD_MODE_ONLY :text ="AUDCLNT_E_NONOFFLOAD_MODE_ONLY"; break;
+#endif
+#ifdef AUDCLNT_E_RESOURCES_INVALIDATED
+ case AUDCLNT_E_RESOURCES_INVALIDATED :text ="AUDCLNT_E_RESOURCES_INVALIDATED"; break;
+#endif
+#ifdef AUDCLNT_E_RAW_MODE_UNSUPPORTED
+ case AUDCLNT_E_RAW_MODE_UNSUPPORTED :text ="AUDCLNT_E_RAW_MODE_UNSUPPORTED"; break;
+#endif
+#ifdef AUDCLNT_E_ENGINE_PERIODICITY_LOCKED
+ case AUDCLNT_E_ENGINE_PERIODICITY_LOCKED :text ="AUDCLNT_E_ENGINE_PERIODICITY_LOCKED"; break;
+#endif
+#ifdef AUDCLNT_E_ENGINE_FORMAT_LOCKED
+ case AUDCLNT_E_ENGINE_FORMAT_LOCKED :text ="AUDCLNT_E_ENGINE_FORMAT_LOCKED"; break;
+#endif
+
+ case AUDCLNT_S_BUFFER_EMPTY :text ="AUDCLNT_S_BUFFER_EMPTY"; break;
+ case AUDCLNT_S_THREAD_ALREADY_REGISTERED :text ="AUDCLNT_S_THREAD_ALREADY_REGISTERED"; break;
+ case AUDCLNT_S_POSITION_STALLED :text ="AUDCLNT_S_POSITION_STALLED"; break;
+
+ // other windows common errors:
+ case CO_E_NOTINITIALIZED :text ="CO_E_NOTINITIALIZED: you must call CoInitialize() before Pa_OpenStream()"; break;
+
+ default:
+ text = "UNKNOWN ERROR";
+ }
+ PRINT(("WASAPI ERROR HRESULT: 0x%X : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", res, text, func, file, line));
+#ifndef PA_ENABLE_DEBUG_OUTPUT
+ (void)func; (void)file; (void)line;
+#endif
+ PA_SKELETON_SET_LAST_HOST_ERROR(res, text);
+ return res;
}
// ------------------------------------------------------------------------------------------
#define LogPaError(PAERR) __LogPaError(PAERR, __FUNCTION__, __FILE__, __LINE__)
static PaError __LogPaError(PaError err, const char *func, const char *file, int line)
{
- if (err == paNoError)
- return err;
- PRINT(("WASAPI ERROR PAERROR: %i : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", err, Pa_GetErrorText(err), func, file, line));
- return err;
+ if (err == paNoError)
+ return err;
+
+ PRINT(("WASAPI ERROR PAERROR: %i : %s\n [FUNCTION: %s FILE: %s {LINE: %d}]\n", err, Pa_GetErrorText(err), func, file, line));
+#ifndef PA_ENABLE_DEBUG_OUTPUT
+ (void)func; (void)file; (void)line;
+#endif
+ return err;
}
// ------------------------------------------------------------------------------------------
/*! \class ThreadSleepScheduler
Allows to emulate thread sleep of less than 1 millisecond under Windows. Scheduler
- calculates number of times the thread must run untill next sleep of 1 millisecond.
- It does not make thread sleeping for real number of microseconds but rather controls
- how many of imaginary microseconds the thread task can allow thread to sleep.
+ calculates number of times the thread must run until next sleep of 1 millisecond.
+ It does not make thread sleeping for real number of microseconds but rather controls
+ how many of imaginary microseconds the thread task can allow thread to sleep.
*/
typedef struct ThreadIdleScheduler
{
- UINT32 m_idle_microseconds; //!< number of microseconds to sleep
- UINT32 m_next_sleep; //!< next sleep round
- UINT32 m_i; //!< current round iterator position
- UINT32 m_resolution; //!< resolution in number of milliseconds
+ UINT32 m_idle_microseconds; //!< number of microseconds to sleep
+ UINT32 m_next_sleep; //!< next sleep round
+ UINT32 m_i; //!< current round iterator position
+ UINT32 m_resolution; //!< resolution in number of milliseconds
}
ThreadIdleScheduler;
+
//! Setup scheduler.
static void ThreadIdleScheduler_Setup(ThreadIdleScheduler *sched, UINT32 resolution, UINT32 microseconds)
{
- assert(microseconds != 0);
- assert(resolution != 0);
- assert((resolution * 1000) >= microseconds);
+ assert(microseconds != 0);
+ assert(resolution != 0);
+ assert((resolution * 1000) >= microseconds);
- memset(sched, 0, sizeof(*sched));
+ memset(sched, 0, sizeof(*sched));
- sched->m_idle_microseconds = microseconds;
- sched->m_resolution = resolution;
- sched->m_next_sleep = (resolution * 1000) / microseconds;
+ sched->m_idle_microseconds = microseconds;
+ sched->m_resolution = resolution;
+ sched->m_next_sleep = (resolution * 1000) / microseconds;
}
+
//! Iterate and check if can sleep.
-static UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched)
+static inline UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched)
+{
+ // advance and check if thread can sleep
+ if (++sched->m_i == sched->m_next_sleep)
+ {
+ sched->m_i = 0;
+ return sched->m_resolution;
+ }
+ return 0;
+}
+
+// ------------------------------------------------------------------------------------------
+typedef struct _SystemTimer
+{
+ INT32 granularity;
+
+} SystemTimer;
+static LARGE_INTEGER g_SystemTimerFrequency;
+static BOOL g_SystemTimerUseQpc = FALSE;
+
+//! Set granularity of the system timer.
+static BOOL SystemTimer_SetGranularity(SystemTimer *timer, UINT32 granularity)
+{
+#ifndef PA_WINRT
+ TIMECAPS caps;
+
+ timer->granularity = granularity;
+
+ if (timeGetDevCaps(&caps, sizeof(caps)) == MMSYSERR_NOERROR)
+ {
+ if (timer->granularity < (INT32)caps.wPeriodMin)
+ timer->granularity = (INT32)caps.wPeriodMin;
+ }
+
+ if (timeBeginPeriod(timer->granularity) != TIMERR_NOERROR)
+ {
+ PRINT(("SetSystemTimer: timeBeginPeriod(1) failed!\n"));
+
+ timer->granularity = 10;
+ return FALSE;
+ }
+#else
+ (void)granularity;
+
+ // UWP does not support increase of the timer precision change and thus calling WaitForSingleObject with anything
+ // below 10 milliseconds will cause underruns for input and output stream.
+ timer->granularity = 10;
+#endif
+
+ return TRUE;
+}
+
+//! Restore granularity of the system timer.
+static void SystemTimer_RestoreGranularity(SystemTimer *timer)
{
- // advance and check if thread can sleep
- if (++ sched->m_i == sched->m_next_sleep)
- {
- sched->m_i = 0;
- return sched->m_resolution;
- }
- return 0;
+#ifndef PA_WINRT
+ if (timer->granularity != 0)
+ {
+ if (timeEndPeriod(timer->granularity) != TIMERR_NOERROR)
+ {
+ PRINT(("RestoreSystemTimer: timeEndPeriod(1) failed!\n"));
+ }
+ }
+#else
+ (void)timer;
+#endif
+}
+
+//! Initialize high-resolution time getter.
+static void SystemTimer_InitializeTimeGetter()
+{
+ g_SystemTimerUseQpc = QueryPerformanceFrequency(&g_SystemTimerFrequency);
+}
+
+//! Get high-resolution time in milliseconds (using QPC by default).
+static inline LONGLONG SystemTimer_GetTime(SystemTimer *timer)
+{
+ (void)timer;
+
+ // QPC: https://docs.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps
+ if (g_SystemTimerUseQpc)
+ {
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+ return (now.QuadPart * 1000LL) / g_SystemTimerFrequency.QuadPart;
+ }
+ else
+ {
+ #ifdef PA_WINRT
+ return GetTickCount64();
+ #else
+ return timeGetTime();
+ #endif
+ }
}
// ------------------------------------------------------------------------------------------
@@ -701,7 +941,7 @@ static UINT32 ThreadIdleScheduler_NextSleep(ThreadIdleScheduler *sched)
// 1 nano = 0.000000001 seconds
//100 nano = 0.0000001 seconds
//100 nano = 0.0001 milliseconds
- return ((double)ref)*0.0001;
+ return ((double)ref) * 0.0001;
}*/
// ------------------------------------------------------------------------------------------
@@ -710,7 +950,7 @@ static double nano100ToSeconds(REFERENCE_TIME ref)
// 1 nano = 0.000000001 seconds
//100 nano = 0.0000001 seconds
//100 nano = 0.0001 milliseconds
- return ((double)ref)*0.0000001;
+ return ((double)ref) * 0.0000001;
}
// ------------------------------------------------------------------------------------------
@@ -719,7 +959,7 @@ static double nano100ToSeconds(REFERENCE_TIME ref)
// 1 nano = 0.000000001 seconds
//100 nano = 0.0000001 seconds
//100 nano = 0.0001 milliseconds
- return (REFERENCE_TIME)(ref/0.0001);
+ return (REFERENCE_TIME)(ref / 0.0001);
}*/
// ------------------------------------------------------------------------------------------
@@ -728,51 +968,56 @@ static REFERENCE_TIME SecondsTonano100(double ref)
// 1 nano = 0.000000001 seconds
//100 nano = 0.0000001 seconds
//100 nano = 0.0001 milliseconds
- return (REFERENCE_TIME)(ref/0.0000001);
+ return (REFERENCE_TIME)(ref / 0.0000001);
}
// ------------------------------------------------------------------------------------------
// Makes Hns period from frames and sample rate
static REFERENCE_TIME MakeHnsPeriod(UINT32 nFrames, DWORD nSamplesPerSec)
{
- return (REFERENCE_TIME)((10000.0 * 1000 / nSamplesPerSec * nFrames) + 0.5);
+ return (REFERENCE_TIME)((10000.0 * 1000 / nSamplesPerSec * nFrames) + 0.5);
}
// ------------------------------------------------------------------------------------------
// Converts PaSampleFormat to bits per sample value
+// Note: paCustomFormat stands for 8.24 format (24-bits inside 32-bit containers)
static WORD PaSampleFormatToBitsPerSample(PaSampleFormat format_id)
{
- switch (format_id & ~paNonInterleaved)
- {
- case paFloat32:
- case paInt32: return 32;
- case paInt24: return 24;
- case paInt16: return 16;
- case paInt8:
- case paUInt8: return 8;
- }
- return 0;
+ switch (format_id & ~paNonInterleaved)
+ {
+ case paFloat32:
+ case paInt32: return 32;
+ case paCustomFormat:
+ case paInt24: return 24;
+ case paInt16: return 16;
+ case paInt8:
+ case paUInt8: return 8;
+ }
+ return 0;
}
// ------------------------------------------------------------------------------------------
-// Converts PaSampleFormat to bits per sample value
-/*static WORD PaSampleFormatToBytesPerSample(PaSampleFormat format_id)
+// Convert PaSampleFormat to valid sample format for I/O, e.g. if paCustomFormat is specified
+// it will be converted to paInt32, other formats pass through
+// Note: paCustomFormat stands for 8.24 format (24-bits inside 32-bit containers)
+static PaSampleFormat GetSampleFormatForIO(PaSampleFormat format_id)
{
- return PaSampleFormatToBitsPerSample(format_id) >> 3; // 'bits/8'
-}*/
+ return ((format_id & ~paNonInterleaved) == paCustomFormat ?
+ (paInt32 | (format_id & paNonInterleaved ? paNonInterleaved : 0)) : format_id);
+}
// ------------------------------------------------------------------------------------------
// Converts Hns period into number of frames
static UINT32 MakeFramesFromHns(REFERENCE_TIME hnsPeriod, UINT32 nSamplesPerSec)
{
- UINT32 nFrames = (UINT32)( // frames =
- 1.0 * hnsPeriod * // hns *
- nSamplesPerSec / // (frames / s) /
- 1000 / // (ms / s) /
- 10000 // (hns / s) /
- + 0.5 // rounding
+ UINT32 nFrames = (UINT32)( // frames =
+ 1.0 * hnsPeriod * // hns *
+ nSamplesPerSec / // (frames / s) /
+ 1000 / // (ms / s) /
+ 10000 // (hns / s) /
+ + 0.5 // rounding
);
- return nFrames;
+ return nFrames;
}
// Aligning function type
@@ -782,84 +1027,93 @@ typedef UINT32 (*ALIGN_FUNC) (UINT32 v, UINT32 align);
// Aligns 'v' backwards
static UINT32 ALIGN_BWD(UINT32 v, UINT32 align)
{
- return ((v - (align ? v % align : 0)));
+ return ((v - (align ? v % align : 0)));
}
// ------------------------------------------------------------------------------------------
// Aligns 'v' forward
static UINT32 ALIGN_FWD(UINT32 v, UINT32 align)
{
- UINT32 remainder = (align ? (v % align) : 0);
- if (remainder == 0)
- return v;
- return v + (align - remainder);
+ UINT32 remainder = (align ? (v % align) : 0);
+ if (remainder == 0)
+ return v;
+ return v + (align - remainder);
}
// ------------------------------------------------------------------------------------------
// Get next value power of 2
-UINT32 ALIGN_NEXT_POW2(UINT32 v)
+static UINT32 ALIGN_NEXT_POW2(UINT32 v)
{
- UINT32 v2 = 1;
- while (v > (v2 <<= 1)) { }
- v = v2;
- return v;
+ UINT32 v2 = 1;
+ while (v > (v2 <<= 1)) { }
+ v = v2;
+ return v;
}
// ------------------------------------------------------------------------------------------
// Aligns WASAPI buffer to 128 byte packet boundary. HD Audio will fail to play if buffer
// is misaligned. This problem was solved in Windows 7 were AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED
// is thrown although we must align for Vista anyway.
-static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nSamplesPerSec, UINT32 nBlockAlign,
- ALIGN_FUNC pAlignFunc)
+static UINT32 AlignFramesPerBuffer(UINT32 nFrames, UINT32 nBlockAlign, ALIGN_FUNC pAlignFunc)
{
#define HDA_PACKET_SIZE (128)
- long frame_bytes = nFrames * nBlockAlign;
- long packets;
- (void)nSamplesPerSec;
+ UINT32 bytes = nFrames * nBlockAlign;
+ UINT32 packets;
- // align to packet size
- frame_bytes = pAlignFunc(frame_bytes, HDA_PACKET_SIZE); // use ALIGN_FWD if bigger but safer period is more desired
+ // align to a HD Audio packet size
+ bytes = pAlignFunc(bytes, HDA_PACKET_SIZE);
- // atlest 1 frame must be available
- if (frame_bytes < HDA_PACKET_SIZE)
- frame_bytes = HDA_PACKET_SIZE;
+ // atlest 1 frame must be available
+ if (bytes < HDA_PACKET_SIZE)
+ bytes = HDA_PACKET_SIZE;
- nFrames = frame_bytes / nBlockAlign;
- packets = frame_bytes / HDA_PACKET_SIZE;
+ packets = bytes / HDA_PACKET_SIZE;
+ bytes = packets * HDA_PACKET_SIZE;
+ nFrames = bytes / nBlockAlign;
- frame_bytes = packets * HDA_PACKET_SIZE;
- nFrames = frame_bytes / nBlockAlign;
+ // WASAPI frames are always aligned to at least 8
+ nFrames = ALIGN_FWD(nFrames, 8);
- return nFrames;
+ return nFrames;
#undef HDA_PACKET_SIZE
}
// ------------------------------------------------------------------------------------------
-static UINT32 GetFramesSleepTime(UINT32 nFrames, UINT32 nSamplesPerSec)
+static UINT32 GetFramesSleepTime(REFERENCE_TIME nFrames, REFERENCE_TIME nSamplesPerSec)
{
- REFERENCE_TIME nDuration;
- if (nSamplesPerSec == 0)
- return 0;
-#define REFTIMES_PER_SEC 10000000
-#define REFTIMES_PER_MILLISEC 10000
- // Calculate the actual duration of the allocated buffer.
- nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);
- return (UINT32)(nDuration/REFTIMES_PER_MILLISEC/2);
+ REFERENCE_TIME nDuration;
+ if (nSamplesPerSec == 0)
+ return 0;
+
+#define REFTIMES_PER_SEC 10000000LL
+#define REFTIMES_PER_MILLISEC 10000LL
+
+ // Calculate the actual duration of the allocated buffer.
+ nDuration = (REFTIMES_PER_SEC * nFrames) / nSamplesPerSec;
+ return (UINT32)(nDuration / REFTIMES_PER_MILLISEC);
+
+#undef REFTIMES_PER_SEC
+#undef REFTIMES_PER_MILLISEC
}
// ------------------------------------------------------------------------------------------
-static UINT32 GetFramesSleepTimeMicroseconds(UINT32 nFrames, UINT32 nSamplesPerSec)
+static UINT32 GetFramesSleepTimeMicroseconds(REFERENCE_TIME nFrames, REFERENCE_TIME nSamplesPerSec)
{
- REFERENCE_TIME nDuration;
- if (nSamplesPerSec == 0)
- return 0;
-#define REFTIMES_PER_SEC 10000000
-#define REFTIMES_PER_MILLISEC 10000
- // Calculate the actual duration of the allocated buffer.
- nDuration = (REFERENCE_TIME)((double)REFTIMES_PER_SEC * nFrames / nSamplesPerSec);
- return (UINT32)(nDuration/10/2);
+ REFERENCE_TIME nDuration;
+ if (nSamplesPerSec == 0)
+ return 0;
+
+#define REFTIMES_PER_SEC 10000000LL
+#define REFTIMES_PER_MILLISEC 10000LL
+
+ // Calculate the actual duration of the allocated buffer.
+ nDuration = (REFTIMES_PER_SEC * nFrames) / nSamplesPerSec;
+ return (UINT32)(nDuration / 10);
+
+#undef REFTIMES_PER_SEC
+#undef REFTIMES_PER_MILLISEC
}
// ------------------------------------------------------------------------------------------
@@ -874,15 +1128,15 @@ static BOOL SetupAVRT()
_GetProc(pAvRtDeleteThreadOrderingGroup, FAvRtDeleteThreadOrderingGroup, "AvRtDeleteThreadOrderingGroup");
_GetProc(pAvRtWaitOnThreadOrderingGroup, FAvRtWaitOnThreadOrderingGroup, "AvRtWaitOnThreadOrderingGroup");
_GetProc(pAvSetMmThreadCharacteristics, FAvSetMmThreadCharacteristics, "AvSetMmThreadCharacteristicsA");
- _GetProc(pAvRevertMmThreadCharacteristics,FAvRevertMmThreadCharacteristics,"AvRevertMmThreadCharacteristics");
+ _GetProc(pAvRevertMmThreadCharacteristics,FAvRevertMmThreadCharacteristics,"AvRevertMmThreadCharacteristics");
_GetProc(pAvSetMmThreadPriority, FAvSetMmThreadPriority, "AvSetMmThreadPriority");
- return pAvRtCreateThreadOrderingGroup &&
- pAvRtDeleteThreadOrderingGroup &&
- pAvRtWaitOnThreadOrderingGroup &&
- pAvSetMmThreadCharacteristics &&
- pAvRevertMmThreadCharacteristics &&
- pAvSetMmThreadPriority;
+ return pAvRtCreateThreadOrderingGroup &&
+ pAvRtDeleteThreadOrderingGroup &&
+ pAvRtWaitOnThreadOrderingGroup &&
+ pAvSetMmThreadCharacteristics &&
+ pAvRevertMmThreadCharacteristics &&
+ pAvSetMmThreadPriority;
}
#endif
@@ -890,9 +1144,9 @@ static BOOL SetupAVRT()
static void CloseAVRT()
{
#ifndef PA_WINRT
- if (hDInputDLL != NULL)
- FreeLibrary(hDInputDLL);
- hDInputDLL = NULL;
+ if (hDInputDLL != NULL)
+ FreeLibrary(hDInputDLL);
+ hDInputDLL = NULL;
#endif
}
@@ -901,10 +1155,10 @@ static BOOL IsWow64()
{
#ifndef PA_WINRT
- // http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx
+ // http://msdn.microsoft.com/en-us/library/ms684139(VS.85).aspx
- typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
- LPFN_ISWOW64PROCESS fnIsWow64Process;
+ typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
+ LPFN_ISWOW64PROCESS fnIsWow64Process;
BOOL bIsWow64 = FALSE;
@@ -916,398 +1170,322 @@ static BOOL IsWow64()
GetModuleHandleA("kernel32"), "IsWow64Process");
if (fnIsWow64Process == NULL)
- return FALSE;
+ return FALSE;
if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
- return FALSE;
+ return FALSE;
return bIsWow64;
#else
- return FALSE;
-
-#endif
-}
-
-// ------------------------------------------------------------------------------------------
-typedef enum EWindowsVersion
-{
- WINDOWS_UNKNOWN = 0,
- WINDOWS_VISTA_SERVER2008,
- WINDOWS_7_SERVER2008R2,
- WINDOWS_8_SERVER2012,
- WINDOWS_8_1_SERVER2012R2,
- WINDOWS_10_SERVER2016,
- WINDOWS_FUTURE
-}
-EWindowsVersion;
-// Alternative way for checking Windows version (allows to check version on Windows 8.1 and up)
-#ifndef PA_WINRT
-static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
-{
- typedef ULONGLONG (NTAPI *LPFN_VERSETCONDITIONMASK)(ULONGLONG ConditionMask, DWORD TypeMask, BYTE Condition);
- typedef BOOL (WINAPI *LPFN_VERIFYVERSIONINFO)(LPOSVERSIONINFOEXA lpVersionInformation, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
-
- LPFN_VERSETCONDITIONMASK fnVerSetConditionMask;
- LPFN_VERIFYVERSIONINFO fnVerifyVersionInfo;
- OSVERSIONINFOEXA osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
- DWORDLONG dwlConditionMask;
+ return FALSE;
- fnVerSetConditionMask = (LPFN_VERSETCONDITIONMASK)GetProcAddress(GetModuleHandleA("kernel32"), "VerSetConditionMask");
- fnVerifyVersionInfo = (LPFN_VERIFYVERSIONINFO)GetProcAddress(GetModuleHandleA("kernel32"), "VerifyVersionInfoA");
-
- if ((fnVerSetConditionMask == NULL) || (fnVerifyVersionInfo == NULL))
- return FALSE;
-
- dwlConditionMask = fnVerSetConditionMask(
- fnVerSetConditionMask(
- fnVerSetConditionMask(
- 0, VER_MAJORVERSION, VER_GREATER_EQUAL),
- VER_MINORVERSION, VER_GREATER_EQUAL),
- VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
-
- osvi.dwMajorVersion = wMajorVersion;
- osvi.dwMinorVersion = wMinorVersion;
- osvi.wServicePackMajor = wServicePackMajor;
-
- return (fnVerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE);
-}
-#endif
-// Get Windows version
-static EWindowsVersion GetWindowsVersion()
-{
-#ifndef PA_WINRT
- static EWindowsVersion version = WINDOWS_UNKNOWN;
-
- if (version == WINDOWS_UNKNOWN)
- {
- DWORD dwVersion = 0;
- DWORD dwMajorVersion = 0;
- DWORD dwMinorVersion = 0;
- DWORD dwBuild = 0;
-
- typedef DWORD (WINAPI *LPFN_GETVERSION)(VOID);
- LPFN_GETVERSION fnGetVersion;
-
- fnGetVersion = (LPFN_GETVERSION)GetProcAddress(GetModuleHandleA("kernel32"), "GetVersion");
- if (fnGetVersion != NULL)
- {
- PRINT(("WASAPI: getting Windows version with GetVersion()\n"));
-
- dwVersion = fnGetVersion();
-
- // Get the Windows version
- dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
- dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
-
- // Get the build number
- if (dwVersion < 0x80000000)
- dwBuild = (DWORD)(HIWORD(dwVersion));
-
- switch (dwMajorVersion)
- {
- case 0:
- case 1:
- case 2:
- case 3:
- case 4:
- case 5:
- break; // skip lower
- case 6:
- switch (dwMinorVersion)
- {
- case 0: version = WINDOWS_VISTA_SERVER2008; break;
- case 1: version = WINDOWS_7_SERVER2008R2; break;
- case 2: version = WINDOWS_8_SERVER2012; break;
- case 3: version = WINDOWS_8_1_SERVER2012R2; break;
- default: version = WINDOWS_FUTURE; break;
- }
- break;
- case 10:
- switch (dwMinorVersion)
- {
- case 0: version = WINDOWS_10_SERVER2016; break;
- default: version = WINDOWS_FUTURE; break;
- }
- break;
- default:
- version = WINDOWS_FUTURE;
- break;
- }
- }
- else
- {
- PRINT(("WASAPI: getting Windows version with VerifyVersionInfo()\n"));
-
- if (IsWindowsVersionOrGreater(10, 0, 0))
- version = WINDOWS_10_SERVER2016;
- else
- if (IsWindowsVersionOrGreater(6, 3, 0))
- version = WINDOWS_8_1_SERVER2012R2;
- else
- if (IsWindowsVersionOrGreater(6, 2, 0))
- version = WINDOWS_8_SERVER2012;
- else
- if (IsWindowsVersionOrGreater(6, 1, 0))
- version = WINDOWS_7_SERVER2008R2;
- else
- if (IsWindowsVersionOrGreater(6, 0, 0))
- version = WINDOWS_VISTA_SERVER2008;
- else
- version = WINDOWS_FUTURE;
- }
-
- PRINT(("WASAPI: Windows version = %d\n", version));
- }
-
- return version;
-#else
- return WINDOWS_8_SERVER2012;
#endif
}
-
// ------------------------------------------------------------------------------------------
static BOOL UseWOW64Workaround()
{
- // note: WOW64 bug is common to Windows Vista x64, thus we fall back to safe Poll-driven
- // method. Windows 7 x64 seems has WOW64 bug fixed.
+ // note: WOW64 bug is common to Windows Vista x64, thus we fall back to safe Poll-driven
+ // method. Windows 7 x64 seems has WOW64 bug fixed.
- return (IsWow64() && (GetWindowsVersion() == WINDOWS_VISTA_SERVER2008));
+ return (IsWow64() && (PaWinUtil_GetOsVersion() == paOsVersionWindowsVistaServer2008));
}
// ------------------------------------------------------------------------------------------
static UINT32 GetAudioClientVersion()
{
- if (GetWindowsVersion() >= WINDOWS_10_SERVER2016)
- return 3;
- else
- if (GetWindowsVersion() >= WINDOWS_8_SERVER2012)
- return 2;
+ PaOsVersion version = PaWinUtil_GetOsVersion();
- return 1;
+ if (version >= paOsVersionWindows10Server2016)
+ return 3;
+ else
+ if (version >= paOsVersionWindows8Server2012)
+ return 2;
+
+ return 1;
}
// ------------------------------------------------------------------------------------------
static const IID *GetAudioClientIID()
{
- static const IID *cli_iid = NULL;
- if (cli_iid == NULL)
- {
- UINT32 cli_version = GetAudioClientVersion();
- if (cli_version <= 1)
- {
- cli_iid = &pa_IID_IAudioClient;
- }
- else
- {
- switch (cli_version)
- {
- case 3: cli_iid = &pa_IID_IAudioClient2; cli_version = 2; break; // use IAudioClient2 for Windows 10+ until IAudioClient3 functions are required
- default: cli_iid = &pa_IID_IAudioClient2; cli_version = 2; break;
- }
- }
+ static const IID *cli_iid = NULL;
+ if (cli_iid == NULL)
+ {
+ UINT32 cli_version = GetAudioClientVersion();
+ switch (cli_version)
+ {
+ case 3: cli_iid = &pa_IID_IAudioClient3; break;
+ case 2: cli_iid = &pa_IID_IAudioClient2; break;
+ default: cli_iid = &pa_IID_IAudioClient; break;
+ }
- PRINT(("WASAPI: IAudioClient version = %d\n", cli_version));
- }
+ PRINT(("WASAPI: IAudioClient version = %d\n", cli_version));
+ }
- return cli_iid;
+ return cli_iid;
}
// ------------------------------------------------------------------------------------------
-typedef enum EMixerDir { MIX_DIR__1TO2, MIX_DIR__2TO1, MIX_DIR__2TO1_L } EMixerDir;
+typedef enum EMixDirection
+{
+ MIX_DIR__1TO2, //!< mix one channel to L and R
+ MIX_DIR__2TO1, //!< mix L and R channels to one channel
+ MIX_DIR__2TO1_L //!< mix only L channel (of total 2 channels) to one channel
+}
+EMixDirection;
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(TYPE)\
- TYPE * __restrict to = __to;\
- TYPE * __restrict from = __from;\
- TYPE * __restrict end = from + count;\
- while (from != end)\
- {\
- *to ++ = *from;\
- *to ++ = *from;\
- ++ from;\
- }
+ TYPE * __restrict to = (TYPE *)__to;\
+ const TYPE * __restrict from = (const TYPE *)__from;\
+ const TYPE * __restrict end = from + count;\
+ while (from != end)\
+ {\
+ to[0] = to[1] = *from ++;\
+ to += 2;\
+ }
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(TYPE)\
- TYPE * __restrict to = (TYPE *)__to;\
- TYPE * __restrict from = (TYPE *)__from;\
- TYPE * __restrict end = to + count;\
- while (to != end)\
- {\
- *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\
- from += 2;\
- }
+ TYPE * __restrict to = (TYPE *)__to;\
+ const TYPE * __restrict from = (const TYPE *)__from;\
+ const TYPE * __restrict end = to + count;\
+ while (to != end)\
+ {\
+ *to ++ = (TYPE)((float)(from[0] + from[1]) * 0.5f);\
+ from += 2;\
+ }
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(TYPE)\
- TYPE * __restrict to = (TYPE *)__to;\
- TYPE * __restrict from = (TYPE *)__from;\
- TYPE * __restrict end = to + count;\
- while (to != end)\
- {\
- *to ++ = (TYPE)(((INT32)from[0] + (INT32)from[1]) >> 1);\
- from += 2;\
- }
+ TYPE * __restrict to = (TYPE *)__to;\
+ const TYPE * __restrict from = (const TYPE *)__from;\
+ const TYPE * __restrict end = to + count;\
+ while (to != end)\
+ {\
+ *to ++ = (TYPE)(((INT32)from[0] + (INT32)from[1]) >> 1);\
+ from += 2;\
+ }
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(TYPE)\
- TYPE * __restrict to = (TYPE *)__to;\
- TYPE * __restrict from = (TYPE *)__from;\
- TYPE * __restrict end = to + count;\
- while (to != end)\
- {\
- *to ++ = (TYPE)(((INT64)from[0] + (INT64)from[1]) >> 1);\
- from += 2;\
- }
+ TYPE * __restrict to = (TYPE *)__to;\
+ const TYPE * __restrict from = (const TYPE *)__from;\
+ const TYPE * __restrict end = to + count;\
+ while (to != end)\
+ {\
+ *to ++ = (TYPE)(((INT64)from[0] + (INT64)from[1]) >> 1);\
+ from += 2;\
+ }
// ------------------------------------------------------------------------------------------
#define _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(TYPE)\
- TYPE * __restrict to = (TYPE *)__to;\
- TYPE * __restrict from = (TYPE *)__from;\
- TYPE * __restrict end = to + count;\
- while (to != end)\
- {\
- *to ++ = from[0];\
- from += 2;\
- }
-
-// ------------------------------------------------------------------------------------------
-static void _MixMonoToStereo_1TO2_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(BYTE); }
-static void _MixMonoToStereo_1TO2_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(short); }
-static void _MixMonoToStereo_1TO2_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); /* !!! int24 data is contained in 32-bit containers*/ }
-static void _MixMonoToStereo_1TO2_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); }
-static void _MixMonoToStereo_1TO2_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(float); }
-
-// ------------------------------------------------------------------------------------------
-static void _MixMonoToStereo_2TO1_8(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(BYTE); }
-static void _MixMonoToStereo_2TO1_16(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(short); }
-static void _MixMonoToStereo_2TO1_24(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(int); /* !!! int24 data is contained in 32-bit containers*/ }
-static void _MixMonoToStereo_2TO1_32(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(int); }
-static void _MixMonoToStereo_2TO1_32f(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(float); }
-
-// ------------------------------------------------------------------------------------------
-static void _MixMonoToStereo_2TO1_8_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(BYTE); }
-static void _MixMonoToStereo_2TO1_16_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(short); }
-static void _MixMonoToStereo_2TO1_24_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); /* !!! int24 data is contained in 32-bit containers*/ }
-static void _MixMonoToStereo_2TO1_32_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); }
-static void _MixMonoToStereo_2TO1_32f_L(void *__to, void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(float); }
-
-// ------------------------------------------------------------------------------------------
-static MixMonoToStereoF _GetMonoToStereoMixer(PaSampleFormat format, EMixerDir dir)
-{
- switch (dir)
- {
- case MIX_DIR__1TO2:
- switch (format & ~paNonInterleaved)
- {
- case paUInt8: return _MixMonoToStereo_1TO2_8;
- case paInt16: return _MixMonoToStereo_1TO2_16;
- case paInt24: return _MixMonoToStereo_1TO2_24;
- case paInt32: return _MixMonoToStereo_1TO2_32;
- case paFloat32: return _MixMonoToStereo_1TO2_32f;
- }
- break;
-
- case MIX_DIR__2TO1:
- switch (format & ~paNonInterleaved)
- {
- case paUInt8: return _MixMonoToStereo_2TO1_8;
- case paInt16: return _MixMonoToStereo_2TO1_16;
- case paInt24: return _MixMonoToStereo_2TO1_24;
- case paInt32: return _MixMonoToStereo_2TO1_32;
- case paFloat32: return _MixMonoToStereo_2TO1_32f;
- }
- break;
-
- case MIX_DIR__2TO1_L:
- switch (format & ~paNonInterleaved)
- {
- case paUInt8: return _MixMonoToStereo_2TO1_8_L;
- case paInt16: return _MixMonoToStereo_2TO1_16_L;
- case paInt24: return _MixMonoToStereo_2TO1_24_L;
- case paInt32: return _MixMonoToStereo_2TO1_32_L;
- case paFloat32: return _MixMonoToStereo_2TO1_32f_L;
- }
- break;
- }
-
- return NULL;
+ TYPE * __restrict to = (TYPE *)__to;\
+ const TYPE * __restrict from = (const TYPE *)__from;\
+ const TYPE * __restrict end = to + count;\
+ while (to != end)\
+ {\
+ *to ++ = from[0];\
+ from += 2;\
+ }
+
+// ------------------------------------------------------------------------------------------
+static void _MixMonoToStereo_1TO2_8(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(BYTE); }
+static void _MixMonoToStereo_1TO2_16(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(short); }
+static void _MixMonoToStereo_1TO2_8_24(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); /* !!! int24 data is contained in 32-bit containers*/ }
+static void _MixMonoToStereo_1TO2_32(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(int); }
+static void _MixMonoToStereo_1TO2_32f(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_1_TO_2(float); }
+static void _MixMonoToStereo_1TO2_24(void *__to, const void *__from, UINT32 count)
+{
+ const UCHAR * __restrict from = (const UCHAR *)__from;
+ UCHAR * __restrict to = (UCHAR *)__to;
+ const UCHAR * __restrict end = to + (count * (2 * 3));
+
+ while (to != end)
+ {
+ to[0] = to[3] = from[0];
+ to[1] = to[4] = from[1];
+ to[2] = to[5] = from[2];
+
+ from += 3;
+ to += (2 * 3);
+ }
+}
+
+// ------------------------------------------------------------------------------------------
+static void _MixMonoToStereo_2TO1_8(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(BYTE); }
+static void _MixMonoToStereo_2TO1_16(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(short); }
+static void _MixMonoToStereo_2TO1_8_24(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT32(int); /* !!! int24 data is contained in 32-bit containers*/ }
+static void _MixMonoToStereo_2TO1_32(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_INT64(int); }
+static void _MixMonoToStereo_2TO1_32f(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_FLT32(float); }
+static void _MixMonoToStereo_2TO1_24(void *__to, const void *__from, UINT32 count)
+{
+ const UCHAR * __restrict from = (const UCHAR *)__from;
+ UCHAR * __restrict to = (UCHAR *)__to;
+ const UCHAR * __restrict end = to + (count * 3);
+ PaInt32 tempL, tempR, tempM;
+
+ while (to != end)
+ {
+ tempL = (((PaInt32)from[0]) << 8);
+ tempL = tempL | (((PaInt32)from[1]) << 16);
+ tempL = tempL | (((PaInt32)from[2]) << 24);
+
+ tempR = (((PaInt32)from[3]) << 8);
+ tempR = tempR | (((PaInt32)from[4]) << 16);
+ tempR = tempR | (((PaInt32)from[5]) << 24);
+
+ tempM = (tempL + tempR) >> 1;
+
+ to[0] = (UCHAR)(tempM >> 8);
+ to[1] = (UCHAR)(tempM >> 16);
+ to[2] = (UCHAR)(tempM >> 24);
+
+ from += (2 * 3);
+ to += 3;
+ }
+}
+
+// ------------------------------------------------------------------------------------------
+static void _MixMonoToStereo_2TO1_8_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(BYTE); }
+static void _MixMonoToStereo_2TO1_16_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(short); }
+static void _MixMonoToStereo_2TO1_8_24_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); /* !!! int24 data is contained in 32-bit containers*/ }
+static void _MixMonoToStereo_2TO1_32_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(int); }
+static void _MixMonoToStereo_2TO1_32f_L(void *__to, const void *__from, UINT32 count) { _WASAPI_MONO_TO_STEREO_MIXER_2_TO_1_L(float); }
+static void _MixMonoToStereo_2TO1_24_L(void *__to, const void *__from, UINT32 count)
+{
+ const UCHAR * __restrict from = (const UCHAR *)__from;
+ UCHAR * __restrict to = (UCHAR *)__to;
+ const UCHAR * __restrict end = to + (count * 3);
+
+ while (to != end)
+ {
+ to[0] = from[0];
+ to[1] = from[1];
+ to[2] = from[2];
+
+ from += (2 * 3);
+ to += 3;
+ }
+}
+
+// ------------------------------------------------------------------------------------------
+static MixMonoToStereoF GetMonoToStereoMixer(const WAVEFORMATEXTENSIBLE *fmtext, EMixDirection dir)
+{
+ PaSampleFormat format = WaveToPaFormat(fmtext);
+
+ switch (dir)
+ {
+ case MIX_DIR__1TO2:
+ switch (format & ~paNonInterleaved)
+ {
+ case paUInt8: return _MixMonoToStereo_1TO2_8;
+ case paInt16: return _MixMonoToStereo_1TO2_16;
+ case paInt24: return (fmtext->Format.wBitsPerSample == 32 ? _MixMonoToStereo_1TO2_8_24 : _MixMonoToStereo_1TO2_24);
+ case paInt32: return _MixMonoToStereo_1TO2_32;
+ case paFloat32: return _MixMonoToStereo_1TO2_32f;
+ }
+ break;
+
+ case MIX_DIR__2TO1:
+ switch (format & ~paNonInterleaved)
+ {
+ case paUInt8: return _MixMonoToStereo_2TO1_8;
+ case paInt16: return _MixMonoToStereo_2TO1_16;
+ case paInt24: return (fmtext->Format.wBitsPerSample == 32 ? _MixMonoToStereo_2TO1_8_24 : _MixMonoToStereo_2TO1_24);
+ case paInt32: return _MixMonoToStereo_2TO1_32;
+ case paFloat32: return _MixMonoToStereo_2TO1_32f;
+ }
+ break;
+
+ case MIX_DIR__2TO1_L:
+ switch (format & ~paNonInterleaved)
+ {
+ case paUInt8: return _MixMonoToStereo_2TO1_8_L;
+ case paInt16: return _MixMonoToStereo_2TO1_16_L;
+ case paInt24: return (fmtext->Format.wBitsPerSample == 32 ? _MixMonoToStereo_2TO1_8_24_L : _MixMonoToStereo_2TO1_24_L);
+ case paInt32: return _MixMonoToStereo_2TO1_32_L;
+ case paFloat32: return _MixMonoToStereo_2TO1_32f_L;
+ }
+ break;
+ }
+
+ return NULL;
}
// ------------------------------------------------------------------------------------------
#ifdef PA_WINRT
typedef struct PaActivateAudioInterfaceCompletionHandler
{
- IActivateAudioInterfaceCompletionHandler parent;
- volatile LONG refs;
- volatile LONG done;
- struct
- {
- const IID *iid;
- void **obj;
- }
- in;
- struct
- {
- HRESULT hr;
- }
- out;
+ IActivateAudioInterfaceCompletionHandler parent;
+ volatile LONG refs;
+ volatile LONG done;
+ struct
+ {
+ const IID *iid;
+ void **obj;
+ }
+ in;
+ struct
+ {
+ HRESULT hr;
+ }
+ out;
}
PaActivateAudioInterfaceCompletionHandler;
-static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_QueryInterface)(
+static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_QueryInterface)(
IActivateAudioInterfaceCompletionHandler *This, REFIID riid, void **ppvObject)
{
- PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
+ PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
- // From MSDN:
- // "The IAgileObject interface is a marker interface that indicates that an object
- // is free threaded and can be called from any apartment."
- if (IsEqualIID(riid, &IID_IUnknown) ||
- IsEqualIID(riid, &IID_IAgileObject))
- {
- IActivateAudioInterfaceCompletionHandler_AddRef((IActivateAudioInterfaceCompletionHandler *)handler);
- (*ppvObject) = handler;
- return S_OK;
- }
+ // From MSDN:
+ // "The IAgileObject interface is a marker interface that indicates that an object
+ // is free threaded and can be called from any apartment."
+ if (IsEqualIID(riid, &IID_IUnknown) ||
+ IsEqualIID(riid, &IID_IAgileObject))
+ {
+ IActivateAudioInterfaceCompletionHandler_AddRef((IActivateAudioInterfaceCompletionHandler *)handler);
+ (*ppvObject) = handler;
+ return S_OK;
+ }
- return E_NOINTERFACE;
+ return E_NOINTERFACE;
}
-
-static ULONG (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_AddRef)(
+
+static ULONG (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_AddRef)(
IActivateAudioInterfaceCompletionHandler *This)
{
- PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
+ PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
- return InterlockedIncrement(&handler->refs);
+ return InterlockedIncrement(&handler->refs);
}
-
-static ULONG (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_Release)(
+
+static ULONG (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_Release)(
IActivateAudioInterfaceCompletionHandler *This)
{
- PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
- ULONG refs;
+ PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
+ ULONG refs;
- if ((refs = InterlockedDecrement(&handler->refs)) == 0)
- {
- PaUtil_FreeMemory(handler->parent.lpVtbl);
- PaUtil_FreeMemory(handler);
- }
+ if ((refs = InterlockedDecrement(&handler->refs)) == 0)
+ {
+ PaUtil_FreeMemory(handler->parent.lpVtbl);
+ PaUtil_FreeMemory(handler);
+ }
- return refs;
+ return refs;
}
-
-static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_ActivateCompleted)(
+
+static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_ActivateCompleted)(
IActivateAudioInterfaceCompletionHandler *This, IActivateAudioInterfaceAsyncOperation *activateOperation)
{
- PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
+ PaActivateAudioInterfaceCompletionHandler *handler = (PaActivateAudioInterfaceCompletionHandler *)This;
HRESULT hr = S_OK;
HRESULT hrActivateResult = S_OK;
IUnknown *punkAudioInterface = NULL;
-
+
// Check for a successful activation result
hr = IActivateAudioInterfaceAsyncOperation_GetActivateResult(activateOperation, &hrActivateResult, &punkAudioInterface);
if (SUCCEEDED(hr) && SUCCEEDED(hrActivateResult))
@@ -1316,504 +1494,859 @@ static HRESULT (STDMETHODCALLTYPE PaActivateAudioInterfaceCompletionHandler_Acti
IUnknown_QueryInterface(punkAudioInterface, handler->in.iid, handler->in.obj);
if ((*handler->in.obj) == NULL)
hrActivateResult = E_FAIL;
- }
- SAFE_RELEASE(punkAudioInterface);
-
- if (SUCCEEDED(hr))
- handler->out.hr = hrActivateResult;
- else
- handler->out.hr = hr;
-
- // Got client object, stop busy waiting in ActivateAudioInterface
- InterlockedExchange(&handler->done, TRUE);
-
- return hr;
-}
-
-static IActivateAudioInterfaceCompletionHandler *CreateActivateAudioInterfaceCompletionHandler(const IID *iid, void **obj)
-{
- PaActivateAudioInterfaceCompletionHandler *handler = PaUtil_AllocateMemory(sizeof(PaActivateAudioInterfaceCompletionHandler));
- ZeroMemory(handler, sizeof(*handler));
- handler->parent.lpVtbl = PaUtil_AllocateMemory(sizeof(*handler->parent.lpVtbl));
- handler->parent.lpVtbl->QueryInterface = &PaActivateAudioInterfaceCompletionHandler_QueryInterface;
- handler->parent.lpVtbl->AddRef = &PaActivateAudioInterfaceCompletionHandler_AddRef;
- handler->parent.lpVtbl->Release = &PaActivateAudioInterfaceCompletionHandler_Release;
- handler->parent.lpVtbl->ActivateCompleted = &PaActivateAudioInterfaceCompletionHandler_ActivateCompleted;
- handler->refs = 1;
- handler->in.iid = iid;
- handler->in.obj = obj;
- return (IActivateAudioInterfaceCompletionHandler *)handler;
+ }
+ SAFE_RELEASE(punkAudioInterface);
+
+ if (SUCCEEDED(hr))
+ handler->out.hr = hrActivateResult;
+ else
+ handler->out.hr = hr;
+
+ // Got client object, stop busy waiting in ActivateAudioInterface
+ InterlockedExchange(&handler->done, TRUE);
+
+ return hr;
+}
+
+static IActivateAudioInterfaceCompletionHandler *CreateActivateAudioInterfaceCompletionHandler(const IID *iid, void **client)
+{
+ PaActivateAudioInterfaceCompletionHandler *handler = PaUtil_AllocateZeroInitializedMemory(sizeof(PaActivateAudioInterfaceCompletionHandler));
+
+ memset(handler, 0, sizeof(*handler));
+
+ handler->parent.lpVtbl = PaUtil_AllocateZeroInitializedMemory(sizeof(*handler->parent.lpVtbl));
+ handler->parent.lpVtbl->QueryInterface = &PaActivateAudioInterfaceCompletionHandler_QueryInterface;
+ handler->parent.lpVtbl->AddRef = &PaActivateAudioInterfaceCompletionHandler_AddRef;
+ handler->parent.lpVtbl->Release = &PaActivateAudioInterfaceCompletionHandler_Release;
+ handler->parent.lpVtbl->ActivateCompleted = &PaActivateAudioInterfaceCompletionHandler_ActivateCompleted;
+ handler->refs = 1;
+ handler->in.iid = iid;
+ handler->in.obj = client;
+
+ return (IActivateAudioInterfaceCompletionHandler *)handler;
}
#endif
// ------------------------------------------------------------------------------------------
#ifdef PA_WINRT
-static HRESULT ActivateAudioInterface_WINRT(const PaWasapiDeviceInfo *deviceInfo, const IID *iid, void **obj)
-{
-#define PA_WASAPI_DEVICE_PATH_LEN 64
-
- PaError result = paNoError;
- HRESULT hr = S_OK;
- IActivateAudioInterfaceAsyncOperation *asyncOp = NULL;
- IActivateAudioInterfaceCompletionHandler *handler = CreateActivateAudioInterfaceCompletionHandler(iid, obj);
- PaActivateAudioInterfaceCompletionHandler *handlerImpl = (PaActivateAudioInterfaceCompletionHandler *)handler;
- OLECHAR devicePath[PA_WASAPI_DEVICE_PATH_LEN] = { 0 };
-
- // Get device path in form L"{DEVICE_GUID}"
- switch (deviceInfo->flow)
- {
- case eRender:
- StringFromGUID2(&DEVINTERFACE_AUDIO_RENDER, devicePath, PA_WASAPI_DEVICE_PATH_LEN - 1);
- break;
- case eCapture:
- StringFromGUID2(&DEVINTERFACE_AUDIO_CAPTURE, devicePath, PA_WASAPI_DEVICE_PATH_LEN - 1);
- break;
- default:
- return S_FALSE;
- }
-
- // Async operation will call back to IActivateAudioInterfaceCompletionHandler::ActivateCompleted
- // which must be an agile interface implementation
- hr = ActivateAudioInterfaceAsync(devicePath, iid, NULL, handler, &asyncOp);
+static HRESULT WinRT_GetDefaultDeviceId(WCHAR *deviceId, UINT32 deviceIdMax, EDataFlow flow)
+{
+ switch (flow)
+ {
+ case eRender:
+ if (g_DeviceListInfo.render.defaultId[0] != 0)
+ wcsncpy_s(deviceId, deviceIdMax, g_DeviceListInfo.render.defaultId, wcslen(g_DeviceListInfo.render.defaultId));
+ else
+ StringFromGUID2(&DEVINTERFACE_AUDIO_RENDER, deviceId, deviceIdMax);
+ break;
+ case eCapture:
+ if (g_DeviceListInfo.capture.defaultId[0] != 0)
+ wcsncpy_s(deviceId, deviceIdMax, g_DeviceListInfo.capture.defaultId, wcslen(g_DeviceListInfo.capture.defaultId));
+ else
+ StringFromGUID2(&DEVINTERFACE_AUDIO_CAPTURE, deviceId, deviceIdMax);
+ break;
+ default:
+ return S_FALSE;
+ }
+
+ return S_OK;
+}
+#endif
+
+// ------------------------------------------------------------------------------------------
+#ifdef PA_WINRT
+static HRESULT WinRT_ActivateAudioInterface(const WCHAR *deviceId, const IID *iid, void **client)
+{
+ PaError result = paNoError;
+ HRESULT hr = S_OK;
+ IActivateAudioInterfaceAsyncOperation *asyncOp = NULL;
+ IActivateAudioInterfaceCompletionHandler *handler = CreateActivateAudioInterfaceCompletionHandler(iid, client);
+ PaActivateAudioInterfaceCompletionHandler *handlerImpl = (PaActivateAudioInterfaceCompletionHandler *)handler;
+ UINT32 sleepToggle = 0;
+
+ // Async operation will call back to IActivateAudioInterfaceCompletionHandler::ActivateCompleted
+ // which must be an agile interface implementation
+ hr = ActivateAudioInterfaceAsync(deviceId, iid, NULL, handler, &asyncOp);
IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- // Wait in busy loop for async operation to complete
- // Use Interlocked API here to ensure that ->done variable is read every time through the loop
- while (SUCCEEDED(hr) && !InterlockedOr(&handlerImpl->done, 0))
- {
- Sleep(1);
- }
+ // Wait in busy loop for async operation to complete
+ // Use Interlocked API here to ensure that ->done variable is read every time through the loop
+ while (SUCCEEDED(hr) && !InterlockedOr(&handlerImpl->done, 0))
+ {
+ Sleep(sleepToggle ^= 1);
+ }
- hr = handlerImpl->out.hr;
+ hr = handlerImpl->out.hr;
error:
- SAFE_RELEASE(asyncOp);
- SAFE_RELEASE(handler);
+ SAFE_RELEASE(asyncOp);
+ SAFE_RELEASE(handler);
return hr;
-
-#undef PA_WASAPI_DEVICE_PATH_LEN
}
#endif
// ------------------------------------------------------------------------------------------
-static HRESULT ActivateAudioInterface(const PaWasapiDeviceInfo *deviceInfo, IAudioClient **client)
+static HRESULT ActivateAudioInterface(const PaWasapiDeviceInfo *deviceInfo, const PaWasapiStreamInfo *streamInfo,
+ IAudioClient **client)
{
+ HRESULT hr;
+
#ifndef PA_WINRT
- return IMMDevice_Activate(deviceInfo->device, GetAudioClientIID(), CLSCTX_ALL, NULL, (void **)client);
+ if (FAILED(hr = IMMDevice_Activate(deviceInfo->device, GetAudioClientIID(), CLSCTX_ALL, NULL, (void **)client)))
+ return hr;
#else
- return ActivateAudioInterface_WINRT(deviceInfo, GetAudioClientIID(), (void **)client);
+ if (FAILED(hr = WinRT_ActivateAudioInterface(deviceInfo->deviceId, GetAudioClientIID(), (void **)client)))
+ return hr;
+#endif
+
+ // Set audio client options (applicable only to IAudioClient2+): options may affect the audio format
+ // support by IAudioClient implementation and therefore we should set them before GetClosestFormat()
+ // in order to correctly match the requested format
+#ifdef __IAudioClient2_INTERFACE_DEFINED__
+ if ((streamInfo != NULL) && (GetAudioClientVersion() >= 2))
+ {
+ pa_AudioClientProperties audioProps = { 0 };
+ audioProps.cbSize = sizeof(pa_AudioClientProperties);
+ audioProps.bIsOffload = FALSE;
+ audioProps.eCategory = (AUDIO_STREAM_CATEGORY)streamInfo->streamCategory;
+ switch (streamInfo->streamOption)
+ {
+ case eStreamOptionRaw:
+ if (PaWinUtil_GetOsVersion() >= paOsVersionWindows8_1Server2012R2)
+ audioProps.Options = pa_AUDCLNT_STREAMOPTIONS_RAW;
+ break;
+ case eStreamOptionMatchFormat:
+ if (PaWinUtil_GetOsVersion() >= paOsVersionWindows10Server2016)
+ audioProps.Options = pa_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;
+ break;
+ }
+
+ if (FAILED(hr = IAudioClient2_SetClientProperties((IAudioClient2 *)(*client), (AudioClientProperties *)&audioProps)))
+ {
+ PRINT(("WASAPI: IAudioClient2_SetClientProperties(IsOffload = %d, Category = %d, Options = %d) failed\n", audioProps.bIsOffload, audioProps.eCategory, audioProps.Options));
+ LogHostError(hr);
+ }
+ else
+ {
+ PRINT(("WASAPI: IAudioClient2 set properties: IsOffload = %d, Category = %d, Options = %d\n", audioProps.bIsOffload, audioProps.eCategory, audioProps.Options));
+ }
+ }
#endif
+
+ return S_OK;
}
// ------------------------------------------------------------------------------------------
#ifdef PA_WINRT
+// Windows 10 SDK 10.0.15063.0 has SignalObjectAndWait defined again (unlike in 10.0.14393.0 and lower)
+#if !defined(WDK_NTDDI_VERSION) || (WDK_NTDDI_VERSION < NTDDI_WIN10_RS2)
static DWORD SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds, BOOL bAlertable)
{
- SetEvent(hObjectToSignal);
- return WaitForSingleObjectEx(hObjectToWaitOn, dwMilliseconds, bAlertable);
+ SetEvent(hObjectToSignal);
+ return WaitForSingleObjectEx(hObjectToWaitOn, dwMilliseconds, bAlertable);
}
#endif
+#endif
// ------------------------------------------------------------------------------------------
-PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+static void NotifyStateChanged(PaWasapiStream *stream, UINT32 flags, HRESULT hr)
{
- PaError result = paNoError;
- PaWasapiHostApiRepresentation *paWasapi;
- PaDeviceInfo *deviceInfoArray;
- HRESULT hr = S_OK;
- UINT i;
-#ifndef PA_WINRT
- IMMDeviceCollection* pEndPoints = NULL;
-#else
- WAVEFORMATEX *mixFormat;
+ if (stream->fnStateHandler == NULL)
+ return;
+
+ if (FAILED(hr))
+ flags |= paWasapiStreamStateError;
+
+ stream->fnStateHandler((PaStream *)stream, flags, hr, stream->pStateHandlerUserData);
+}
+
+// ------------------------------------------------------------------------------------------
+static void FillBaseDeviceInfo(PaDeviceInfo *deviceInfo, PaHostApiIndex hostApiIndex)
+{
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+}
+
+// ------------------------------------------------------------------------------------------
+static PaError FillInactiveDeviceInfo(PaWasapiHostApiRepresentation *paWasapi, PaDeviceInfo *deviceInfo)
+{
+ if (deviceInfo->name == NULL)
+ deviceInfo->name = (char *)PaUtil_GroupAllocateZeroInitializedMemory(paWasapi->allocations, 1);
+
+ if (deviceInfo->name != NULL)
+ {
+ ((char *)deviceInfo->name)[0] = 0;
+ }
+ else
+ return paInsufficientMemory;
+
+ return paNoError;
+}
+
+// ------------------------------------------------------------------------------------------
+static PaError FillDeviceInfo(PaWasapiHostApiRepresentation *paWasapi, void *pEndPoints, INT32 index, const WCHAR *defaultRenderId,
+ const WCHAR *defaultCaptureId, PaDeviceInfo *deviceInfo, PaWasapiDeviceInfo *wasapiDeviceInfo
+#ifdef PA_WINRT
+ , PaWasapiWinrtDeviceListContext *deviceListContext
+#endif
+)
+{
+ HRESULT hr;
+ PaError result;
+ PaUtilHostApiRepresentation *hostApi = (PaUtilHostApiRepresentation *)paWasapi;
+#ifdef PA_WINRT
+ PaWasapiWinrtDeviceListContextEntry *listEntry = &deviceListContext->devices[index];
+ (void)pEndPoints;
+ (void)defaultRenderId;
+ (void)defaultCaptureId;
#endif
#ifndef PA_WINRT
- if (!SetupAVRT())
- {
- PRINT(("WASAPI: No AVRT! (not VISTA?)"));
- return paNoError;
+ hr = IMMDeviceCollection_Item((IMMDeviceCollection *)pEndPoints, index, &wasapiDeviceInfo->device);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ // Get device Id
+ {
+ WCHAR *deviceId;
+
+ hr = IMMDevice_GetId(wasapiDeviceInfo->device, &deviceId);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ wcsncpy(wasapiDeviceInfo->deviceId, deviceId, PA_WASAPI_DEVICE_ID_LEN - 1);
+ CoTaskMemFree(deviceId);
}
-#endif
- paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateMemory( sizeof(PaWasapiHostApiRepresentation) );
- if (paWasapi == NULL)
- {
+ // Get state of the device
+ hr = IMMDevice_GetState(wasapiDeviceInfo->device, &wasapiDeviceInfo->state);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ if (wasapiDeviceInfo->state != DEVICE_STATE_ACTIVE)
+ {
+ PRINT(("WASAPI device: %d is not currently available (state:%d)\n", index, wasapiDeviceInfo->state));
+ }
+
+ // Get basic device info
+ {
+ IPropertyStore *pProperty;
+ IMMEndpoint *endpoint;
+ PROPVARIANT value;
+
+ hr = IMMDevice_OpenPropertyStore(wasapiDeviceInfo->device, STGM_READ, &pProperty);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ // "Friendly" Name
+ {
+ PropVariantInit(&value);
+
+ hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ if ((deviceInfo->name = (char *)PaUtil_GroupAllocateZeroInitializedMemory(paWasapi->allocations, PA_WASAPI_DEVICE_NAME_LEN)) == NULL)
+ {
+ result = paInsufficientMemory;
+ PropVariantClear(&value);
+ goto error;
+ }
+ if (value.pwszVal)
+ WideCharToMultiByte(CP_UTF8, 0, value.pwszVal, (INT32)wcslen(value.pwszVal), (char *)deviceInfo->name, PA_WASAPI_DEVICE_NAME_LEN - 1, 0, 0);
+ else
+ _snprintf((char *)deviceInfo->name, PA_WASAPI_DEVICE_NAME_LEN - 1, "baddev%d", index);
+
+ PropVariantClear(&value);
+
+ PA_DEBUG(("WASAPI:%d| name[%s]\n", index, deviceInfo->name));
+ }
+
+ // Default format
+ {
+ PropVariantInit(&value);
+
+ hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ memcpy(&wasapiDeviceInfo->DefaultFormat, value.blob.pBlobData, min(sizeof(wasapiDeviceInfo->DefaultFormat), value.blob.cbSize));
+
+ PropVariantClear(&value);
+ }
+
+ // Form factor
+ {
+ PropVariantInit(&value);
+
+ hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ // set
+ #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION)
+ // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1)));
+ UINT v;
+ memcpy(&v, (((WORD *)&value.wReserved3) + 1), sizeof(v));
+ wasapiDeviceInfo->formFactor = (EndpointFormFactor)v;
+ #else
+ wasapiDeviceInfo->formFactor = (EndpointFormFactor)value.uintVal;
+ #endif
+
+ PA_DEBUG(("WASAPI:%d| form-factor[%d]\n", index, wasapiDeviceInfo->formFactor));
+
+ PropVariantClear(&value);
+ }
+
+ // Data flow (Renderer or Capture)
+ hr = IMMDevice_QueryInterface(wasapiDeviceInfo->device, &pa_IID_IMMEndpoint, (void **)&endpoint);
+ if (SUCCEEDED(hr))
+ {
+ hr = IMMEndpoint_GetDataFlow(endpoint, &wasapiDeviceInfo->flow);
+ SAFE_RELEASE(endpoint);
+ }
+
+ SAFE_RELEASE(pProperty);
+ }
+#else
+ // Set device Id
+ wcsncpy(wasapiDeviceInfo->deviceId, listEntry->info->id, PA_WASAPI_DEVICE_ID_LEN - 1);
+
+ // Set device name
+ if ((deviceInfo->name = (char *)PaUtil_GroupAllocateZeroInitializedMemory(paWasapi->allocations, PA_WASAPI_DEVICE_NAME_LEN)) == NULL)
+ {
result = paInsufficientMemory;
goto error;
}
-
- memset( paWasapi, 0, sizeof(PaWasapiHostApiRepresentation) ); /* ensure all fields are zeroed. especially paWasapi->allocations */
+ ((char *)deviceInfo->name)[0] = 0;
+ if (listEntry->info->name[0] != 0)
+ WideCharToMultiByte(CP_UTF8, 0, listEntry->info->name, (INT32)wcslen(listEntry->info->name), (char *)deviceInfo->name, PA_WASAPI_DEVICE_NAME_LEN - 1, 0, 0);
+ if (deviceInfo->name[0] == 0) // fallback if WideCharToMultiByte is failed, or listEntry is nameless
+ _snprintf((char *)deviceInfo->name, PA_WASAPI_DEVICE_NAME_LEN - 1, "WASAPI_%s:%d", (listEntry->flow == eRender ? "Output" : "Input"), index);
+
+ // Form-factor
+ wasapiDeviceInfo->formFactor = listEntry->info->formFactor;
+
+ // Set data flow
+ wasapiDeviceInfo->flow = listEntry->flow;
+#endif
- result = PaWinUtil_CoInitialize( paWASAPI, &paWasapi->comInitializationResult );
- if( result != paNoError )
+ // Set default Output/Input devices
+ if ((defaultRenderId != NULL) && (wcsncmp(wasapiDeviceInfo->deviceId, defaultRenderId, PA_WASAPI_DEVICE_NAME_LEN - 1) == 0))
+ hostApi->info.defaultOutputDevice = index;
+ if ((defaultCaptureId != NULL) && (wcsncmp(wasapiDeviceInfo->deviceId, defaultCaptureId, PA_WASAPI_DEVICE_NAME_LEN - 1) == 0))
+ hostApi->info.defaultInputDevice = index;
+
+ // Get a temporary IAudioClient for more details
{
- goto error;
+ IAudioClient *tmpClient;
+ WAVEFORMATEX *mixFormat;
+
+ hr = ActivateAudioInterface(wasapiDeviceInfo, NULL, &tmpClient);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+
+ // Get latency
+ hr = IAudioClient_GetDevicePeriod(tmpClient, &wasapiDeviceInfo->DefaultDevicePeriod, &wasapiDeviceInfo->MinimumDevicePeriod);
+ if (FAILED(hr))
+ {
+ PA_DEBUG(("WASAPI:%d| failed getting min/default periods by IAudioClient::GetDevicePeriod() with error[%08X], will use 30000/100000 hns\n", index, (UINT32)hr));
+
+ // assign WASAPI common values
+ wasapiDeviceInfo->DefaultDevicePeriod = 100000;
+ wasapiDeviceInfo->MinimumDevicePeriod = 30000;
+
+ // ignore error, let continue further without failing with paInternalError
+ hr = S_OK;
+ }
+
+ // Get mix format
+ hr = IAudioClient_GetMixFormat(tmpClient, &mixFormat);
+ if (SUCCEEDED(hr))
+ {
+ memcpy(&wasapiDeviceInfo->MixFormat, mixFormat, min(sizeof(wasapiDeviceInfo->MixFormat), (sizeof(*mixFormat) + mixFormat->cbSize)));
+ CoTaskMemFree(mixFormat);
+ }
+
+ // Register WINRT device
+ #ifdef PA_WINRT
+ if (SUCCEEDED(hr))
+ {
+ // Set state
+ wasapiDeviceInfo->state = DEVICE_STATE_ACTIVE;
+
+ // Default format (Shared mode) is always a mix format
+ wasapiDeviceInfo->DefaultFormat = wasapiDeviceInfo->MixFormat;
+ }
+ #endif
+
+ // Release tmp client
+ SAFE_RELEASE(tmpClient);
+
+ if (hr != S_OK)
+ {
+ //davidv: this happened with my hardware, previously for that same device in DirectSound:
+ //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
+ //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat
+ LogHostError(hr);
+ result = paInternalError;
+ goto error;
+ }
}
- paWasapi->allocations = PaUtil_CreateAllocationGroup();
- if (paWasapi->allocations == NULL)
- {
- result = paInsufficientMemory;
+ // Fill basic device data
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = 0;
+ deviceInfo->defaultSampleRate = wasapiDeviceInfo->MixFormat.Format.nSamplesPerSec;
+ switch (wasapiDeviceInfo->flow)
+ {
+ case eRender: {
+ deviceInfo->maxOutputChannels = wasapiDeviceInfo->MixFormat.Format.nChannels;
+ deviceInfo->defaultHighOutputLatency = nano100ToSeconds(wasapiDeviceInfo->DefaultDevicePeriod);
+ deviceInfo->defaultLowOutputLatency = nano100ToSeconds(wasapiDeviceInfo->MinimumDevicePeriod);
+ PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", index, (UINT32)deviceInfo->defaultSampleRate,
+ deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));
+ break;}
+ case eCapture: {
+ deviceInfo->maxInputChannels = wasapiDeviceInfo->MixFormat.Format.nChannels;
+ deviceInfo->defaultHighInputLatency = nano100ToSeconds(wasapiDeviceInfo->DefaultDevicePeriod);
+ deviceInfo->defaultLowInputLatency = nano100ToSeconds(wasapiDeviceInfo->MinimumDevicePeriod);
+ PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", index, (UINT32)deviceInfo->defaultSampleRate,
+ deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency));
+ break; }
+ default:
+ PRINT(("WASAPI:%d| bad Data Flow!\n", index));
+ result = paInternalError;
goto error;
}
- *hostApi = &paWasapi->inheritedHostApiRep;
- (*hostApi)->info.structVersion = 1;
- (*hostApi)->info.type = paWASAPI;
- (*hostApi)->info.name = "Windows WASAPI";
- (*hostApi)->info.deviceCount = 0;
- (*hostApi)->info.defaultInputDevice = paNoDevice;
- (*hostApi)->info.defaultOutputDevice = paNoDevice;
+ return paNoError;
+
+error:
+
+ PRINT(("WASAPI: failed filling device info for device index[%d] - error[%d|%s]\n", index, result, Pa_GetErrorText(result)));
+
+ return result;
+}
+
+// ------------------------------------------------------------------------------------------
+static PaDeviceInfo *AllocateDeviceListMemory(PaWasapiHostApiRepresentation *paWasapi, UINT32 deviceCount)
+{
+ PaUtilHostApiRepresentation *hostApi = (PaUtilHostApiRepresentation *)paWasapi;
+ PaDeviceInfo *deviceInfoArray = NULL;
+
+ if ((paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_GroupAllocateZeroInitializedMemory(paWasapi->allocations,
+ sizeof(PaWasapiDeviceInfo) * deviceCount)) == NULL)
+ {
+ return NULL;
+ }
+ /* NOTE: we depend on all paWasapi->devInfo elements being zero-initialized */
+
+ if (deviceCount != 0)
+ {
+ UINT32 i;
+ #if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0)
+ if (deviceCount < PA_WASAPI_MAX_CONST_DEVICE_COUNT)
+ deviceCount = PA_WASAPI_MAX_CONST_DEVICE_COUNT;
+ #endif
+
+ if ((hostApi->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateZeroInitializedMemory(paWasapi->allocations,
+ sizeof(PaDeviceInfo *) * deviceCount)) == NULL)
+ {
+ return NULL;
+ }
+ for (i = 0; i < deviceCount; ++i)
+ hostApi->deviceInfos[i] = NULL;
+
+ // Allocate all device info structs in a contiguous block
+ if ((deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateZeroInitializedMemory(paWasapi->allocations,
+ sizeof(PaDeviceInfo) * deviceCount)) == NULL)
+ {
+ return NULL;
+ }
+ /* NOTE: we depend on all deviceInfoArray elements being zero-initialized */
+ }
+
+ return deviceInfoArray;
+}
+
+// ------------------------------------------------------------------------------------------
+#ifndef PA_WINRT
+static UINT32 GetDeviceListDeviceCount(IMMDeviceCollection *pEndPoints, EDataFlow filterFlow)
+{
+ HRESULT hr;
+ UINT32 deviceCount;
+ IMMDevice *device;
+ IMMEndpoint *endpoint;
+ EDataFlow flow;
+ UINT32 ret = 0;
+
+ hr = IMMDeviceCollection_GetCount(pEndPoints, &deviceCount);
+ IF_FAILED_JUMP(hr, error);
+
+ for (UINT32 i = 0; i < deviceCount; ++i)
+ {
+ hr = IMMDeviceCollection_Item((IMMDeviceCollection *)pEndPoints, i, &device);
+ IF_FAILED_JUMP(hr, error);
+
+ if (SUCCEEDED(hr = IMMDevice_QueryInterface(device, &pa_IID_IMMEndpoint, (void **)&endpoint)))
+ {
+ if (SUCCEEDED(hr = IMMEndpoint_GetDataFlow(endpoint, &flow)))
+ ret += (flow == filterFlow);
+
+ SAFE_RELEASE(endpoint);
+ }
+
+ SAFE_RELEASE(device);
+ }
+
+ return ret;
+
+error:
+
+ return 0;
+}
+#else
+static UINT32 GetDeviceListDeviceCount(const PaWasapiHostApiRepresentation *paWasapi,
+ const PaWasapiWinrtDeviceListContext *deviceListContext, EDataFlow filterFlow)
+{
+ UINT32 i, ret = 0;
+
+ for (i = 0; i < paWasapi->deviceCount; ++i)
+ ret += (deviceListContext->devices[i].flow == filterFlow);
+
+ return ret;
+}
+#endif
+
+// ------------------------------------------------------------------------------------------
+static BOOL FillLooopbackDeviceInfo(PaWasapiHostApiRepresentation *paWasapi, PaDeviceInfo *loopbackDeviceInfo,
+ PaWasapiDeviceInfo *loopbackWasapiInfo, const PaDeviceInfo *deviceInfo, const PaWasapiDeviceInfo *wasapiInfo)
+{
+// Loopback device name identificator
+// note: Some projects depend on loopback device detection by device name, do not change!
+#define PA_WASAPI_LOOPBACK_NAME_IDENTIFICATOR "[Loopback]"
+
+ memcpy(loopbackDeviceInfo, deviceInfo, sizeof(*loopbackDeviceInfo));
+ memcpy(loopbackWasapiInfo, wasapiInfo, sizeof(*loopbackWasapiInfo));
+
+ // Append loopback device name identificator to the device name to provide possibility to find
+ // loopback device by its name for some external projects
+ loopbackDeviceInfo->name = (char *)PaUtil_GroupAllocateZeroInitializedMemory(paWasapi->allocations, PA_WASAPI_DEVICE_NAME_LEN + 1);
+ if (loopbackDeviceInfo->name == NULL)
+ return FALSE;
+ _snprintf((char *)loopbackDeviceInfo->name, PA_WASAPI_DEVICE_NAME_LEN - 1,
+ "%s " PA_WASAPI_LOOPBACK_NAME_IDENTIFICATOR, deviceInfo->name);
+
+ // Acquire ref to the device as it is already referenced as render (output) device
+ // to avoid duplicate release in ReleaseWasapiDeviceInfoList()
+#ifndef PA_WINRT
+ SAFE_ADDREF(loopbackWasapiInfo->device);
+#endif
+
+ // Mark as loopback device
+ loopbackWasapiInfo->loopBack = TRUE;
+
+ // Input is the reverse of Output
+ loopbackDeviceInfo->maxInputChannels = deviceInfo->maxOutputChannels;
+ loopbackDeviceInfo->defaultHighInputLatency = deviceInfo->defaultHighOutputLatency;
+ loopbackDeviceInfo->defaultLowInputLatency = deviceInfo->defaultLowOutputLatency;
+ loopbackDeviceInfo->maxOutputChannels = 0;
+ loopbackDeviceInfo->defaultHighOutputLatency = 0;
+ loopbackDeviceInfo->defaultLowOutputLatency = 0;
+
+ return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------
+static PaError CreateDeviceList(PaWasapiHostApiRepresentation *paWasapi, PaHostApiIndex hostApiIndex)
+{
+ PaUtilHostApiRepresentation *hostApi = (PaUtilHostApiRepresentation *)paWasapi;
+ PaError result = paNoError;
+ PaDeviceInfo *deviceInfoArray = NULL;
+ UINT32 i, j, loopbackDevices;
+ WCHAR *defaultRenderId = NULL;
+ WCHAR *defaultCaptureId = NULL;
+#ifndef PA_WINRT
+ HRESULT hr;
+ IMMDeviceCollection *pEndPoints = NULL;
+ IMMDeviceEnumerator *pEnumerator = NULL;
+#else
+ void *pEndPoints = NULL;
+ IAudioClient *tmpClient;
+ PaWasapiWinrtDeviceListContext deviceListContext = { 0 };
+ PaWasapiWinrtDeviceInfo defaultRender = { 0 };
+ PaWasapiWinrtDeviceInfo defaultCapture = { 0 };
+#endif
+
+ // Make sure device list empty
+ if ((paWasapi->deviceCount != 0) || (hostApi->info.deviceCount != 0))
+ return paInternalError;
#ifndef PA_WINRT
- paWasapi->enumerator = NULL;
hr = CoCreateInstance(&pa_CLSID_IMMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER,
- &pa_IID_IMMDeviceEnumerator, (void **)&paWasapi->enumerator);
-
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ &pa_IID_IMMDeviceEnumerator, (void **)&pEnumerator);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- // getting default device ids in the eMultimedia "role"
+ // Get default render and capture devices
{
+ IMMDevice *device;
+
+ hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eRender, eMultimedia, &device);
+ if (hr != S_OK)
{
- IMMDevice *defaultRenderer = NULL;
- hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eRender, eMultimedia, &defaultRenderer);
- if (hr != S_OK)
- {
- if (hr != E_NOTFOUND) {
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- }
- }
- else
- {
- WCHAR *pszDeviceId = NULL;
- hr = IMMDevice_GetId(defaultRenderer, &pszDeviceId);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- wcsncpy(paWasapi->defaultRenderer, pszDeviceId, MAX_STR_LEN-1);
- CoTaskMemFree(pszDeviceId);
- IMMDevice_Release(defaultRenderer);
- }
+ if (hr != E_NOTFOUND)
+ {
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ }
+ }
+ else
+ {
+ hr = IMMDevice_GetId(device, &defaultRenderId);
+ IMMDevice_Release(device);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
}
+ hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(pEnumerator, eCapture, eMultimedia, &device);
+ if (hr != S_OK)
{
- IMMDevice *defaultCapturer = NULL;
- hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(paWasapi->enumerator, eCapture, eMultimedia, &defaultCapturer);
- if (hr != S_OK)
- {
- if (hr != E_NOTFOUND) {
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- }
- }
- else
- {
- WCHAR *pszDeviceId = NULL;
- hr = IMMDevice_GetId(defaultCapturer, &pszDeviceId);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- wcsncpy(paWasapi->defaultCapturer, pszDeviceId, MAX_STR_LEN-1);
- CoTaskMemFree(pszDeviceId);
- IMMDevice_Release(defaultCapturer);
- }
+ if (hr != E_NOTFOUND)
+ {
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ }
+ }
+ else
+ {
+ hr = IMMDevice_GetId(device, &defaultCaptureId);
+ IMMDevice_Release(device);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
}
}
- hr = IMMDeviceEnumerator_EnumAudioEndpoints(paWasapi->enumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ // Get all currently active devices
+ hr = IMMDeviceEnumerator_EnumAudioEndpoints(pEnumerator, eAll, DEVICE_STATE_ACTIVE, &pEndPoints);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ // Get device count
hr = IMMDeviceCollection_GetCount(pEndPoints, &paWasapi->deviceCount);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ // Get loopback device count (e.g. all renders)
+ loopbackDevices = GetDeviceListDeviceCount(pEndPoints, eRender);
#else
- paWasapi->deviceCount = 2;
+ WinRT_GetDefaultDeviceId(defaultRender.id, STATIC_ARRAY_SIZE(defaultRender.id) - 1, eRender);
+ defaultRenderId = defaultRender.id;
+
+ WinRT_GetDefaultDeviceId(defaultCapture.id, STATIC_ARRAY_SIZE(defaultCapture.id) - 1, eCapture);
+ defaultCaptureId = defaultCapture.id;
+
+ if (g_DeviceListInfo.render.deviceCount == 0)
+ {
+ if (SUCCEEDED(WinRT_ActivateAudioInterface(defaultRenderId, GetAudioClientIID(), &tmpClient)))
+ {
+ deviceListContext.devices[paWasapi->deviceCount].info = &defaultRender;
+ deviceListContext.devices[paWasapi->deviceCount].flow = eRender;
+ paWasapi->deviceCount++;
+
+ SAFE_RELEASE(tmpClient);
+ }
+ }
+ else
+ {
+ for (i = 0; i < g_DeviceListInfo.render.deviceCount; ++i)
+ {
+ deviceListContext.devices[paWasapi->deviceCount].info = &g_DeviceListInfo.render.devices[i];
+ deviceListContext.devices[paWasapi->deviceCount].flow = eRender;
+ paWasapi->deviceCount++;
+ }
+ }
+
+ if (g_DeviceListInfo.capture.deviceCount == 0)
+ {
+ if (SUCCEEDED(WinRT_ActivateAudioInterface(defaultCaptureId, GetAudioClientIID(), &tmpClient)))
+ {
+ deviceListContext.devices[paWasapi->deviceCount].info = &defaultCapture;
+ deviceListContext.devices[paWasapi->deviceCount].flow = eCapture;
+ paWasapi->deviceCount++;
+
+ SAFE_RELEASE(tmpClient);
+ }
+ }
+ else
+ {
+ for (i = 0; i < g_DeviceListInfo.capture.deviceCount; ++i)
+ {
+ deviceListContext.devices[paWasapi->deviceCount].info = &g_DeviceListInfo.capture.devices[i];
+ deviceListContext.devices[paWasapi->deviceCount].flow = eCapture;
+ paWasapi->deviceCount++;
+ }
+ }
+
+ // Get loopback device count (e.g. all renders)
+ loopbackDevices = GetDeviceListDeviceCount(paWasapi, &deviceListContext, eRender);
#endif
- paWasapi->devInfo = (PaWasapiDeviceInfo *)PaUtil_AllocateMemory(sizeof(PaWasapiDeviceInfo) * paWasapi->deviceCount);
- if (paWasapi->devInfo == NULL)
- {
+ // Allocate memory for the device list
+ if ((paWasapi->deviceCount != 0) &&
+ ((deviceInfoArray = AllocateDeviceListMemory(paWasapi, paWasapi->deviceCount + loopbackDevices)) == NULL))
+ {
result = paInsufficientMemory;
goto error;
}
- for (i = 0; i < paWasapi->deviceCount; ++i)
- memset(&paWasapi->devInfo[i], 0, sizeof(PaWasapiDeviceInfo));
- if (paWasapi->deviceCount > 0)
+ // Fill WASAPI device info
+ for (i = 0, j = 0; i < paWasapi->deviceCount; ++i)
{
- (*hostApi)->deviceInfos = (PaDeviceInfo **)PaUtil_GroupAllocateMemory(
- paWasapi->allocations, sizeof(PaDeviceInfo *) * paWasapi->deviceCount);
- if ((*hostApi)->deviceInfos == NULL)
- {
- result = paInsufficientMemory;
- goto error;
- }
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
+ PaWasapiDeviceInfo *wasapiInfo = &paWasapi->devInfo[i];
- /* allocate all device info structs in a contiguous block */
- deviceInfoArray = (PaDeviceInfo *)PaUtil_GroupAllocateMemory(
- paWasapi->allocations, sizeof(PaDeviceInfo) * paWasapi->deviceCount);
- if (deviceInfoArray == NULL)
- {
- result = paInsufficientMemory;
- goto error;
- }
+ PA_DEBUG(("WASAPI: device idx: %02d\n", i));
+ PA_DEBUG(("WASAPI: ---------------\n"));
+
+ FillBaseDeviceInfo(deviceInfo, hostApiIndex);
- for (i = 0; i < paWasapi->deviceCount; ++i)
- {
- PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- deviceInfo->structVersion = 2;
- deviceInfo->hostApi = hostApiIndex;
+ if ((result = FillDeviceInfo(paWasapi, pEndPoints, i, defaultRenderId, defaultCaptureId,
+ deviceInfo, wasapiInfo
+ #ifdef PA_WINRT
+ , &deviceListContext
+ #endif
+ )) != paNoError)
+ {
+ // Faulty device is made inactive
+ if ((result = FillInactiveDeviceInfo(paWasapi, deviceInfo)) != paNoError)
+ goto error;
+ }
- PA_DEBUG(("WASAPI: device idx: %02d\n", i));
- PA_DEBUG(("WASAPI: ---------------\n"));
+ hostApi->deviceInfos[i] = deviceInfo;
+ ++hostApi->info.deviceCount;
- #ifndef PA_WINRT
- hr = IMMDeviceCollection_Item(pEndPoints, i, &paWasapi->devInfo[i].device);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ // Add loopback device for the render device
+ if ((paWasapi->devInfo[i].flow == eRender) && (j < loopbackDevices))
+ {
+ // Add loopback device to the end of the device list
+ UINT32 loopbackIndex = paWasapi->deviceCount + j++;
+ assert(loopbackIndex < (paWasapi->deviceCount + loopbackDevices));
+ PaDeviceInfo *loopbackDeviceInfo = &deviceInfoArray[loopbackIndex];
+ PaWasapiDeviceInfo *loopbackWasapiInfo = &paWasapi->devInfo[loopbackIndex];
- // getting ID
+ if (!FillLooopbackDeviceInfo(paWasapi, loopbackDeviceInfo, loopbackWasapiInfo, deviceInfo, wasapiInfo))
{
- WCHAR *pszDeviceId = NULL;
- hr = IMMDevice_GetId(paWasapi->devInfo[i].device, &pszDeviceId);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hr, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- wcsncpy(paWasapi->devInfo[i].szDeviceID, pszDeviceId, MAX_STR_LEN-1);
- CoTaskMemFree(pszDeviceId);
-
- if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultCapturer) == 0)
- {// we found the default input!
- (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
- }
- if (lstrcmpW(paWasapi->devInfo[i].szDeviceID, paWasapi->defaultRenderer) == 0)
- {// we found the default output!
- (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
- }
+ result = paInsufficientMemory;
+ goto error;
}
- hr = IMMDevice_GetState(paWasapi->devInfo[i].device, &paWasapi->devInfo[i].state);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ hostApi->deviceInfos[loopbackIndex] = loopbackDeviceInfo;
+ ++hostApi->info.deviceCount;
+ }
+ }
- if (paWasapi->devInfo[i].state != DEVICE_STATE_ACTIVE)
- {
- PRINT(("WASAPI device: %d is not currently available (state:%d)\n", i, paWasapi->devInfo[i].state));
- }
+ // Resize device list to accomodate inserted loopback devices
+ paWasapi->deviceCount += loopbackDevices;
- {
- IPropertyStore *pProperty;
- hr = IMMDevice_OpenPropertyStore(paWasapi->devInfo[i].device, STGM_READ, &pProperty);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
+ // Fill the remaining slots with inactive device info
+#if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0)
+ if ((hostApi->info.deviceCount != 0) && (hostApi->info.deviceCount < PA_WASAPI_MAX_CONST_DEVICE_COUNT))
+ {
+ for (i = hostApi->info.deviceCount; i < PA_WASAPI_MAX_CONST_DEVICE_COUNT; ++i)
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
- // "Friendly" Name
- {
- char *deviceName;
- PROPVARIANT value;
- PropVariantInit(&value);
- hr = IPropertyStore_GetValue(pProperty, &PKEY_Device_FriendlyName, &value);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- deviceInfo->name = NULL;
- deviceName = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1);
- if (deviceName == NULL)
- {
- result = paInsufficientMemory;
- goto error;
- }
- if (value.pwszVal)
- WideCharToMultiByte(CP_UTF8, 0, value.pwszVal, (int)wcslen(value.pwszVal), deviceName, MAX_STR_LEN - 1, 0, 0);
- else
- _snprintf(deviceName, MAX_STR_LEN - 1, "baddev%d", i);
- deviceInfo->name = deviceName;
- PropVariantClear(&value);
- PA_DEBUG(("WASAPI:%d| name[%s]\n", i, deviceInfo->name));
- }
+ FillBaseDeviceInfo(deviceInfo, hostApiIndex);
- // Default format
- {
- PROPVARIANT value;
- PropVariantInit(&value);
- hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEngine_DeviceFormat, &value);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- memcpy(&paWasapi->devInfo[i].DefaultFormat, value.blob.pBlobData, min(sizeof(paWasapi->devInfo[i].DefaultFormat), value.blob.cbSize));
- // cleanup
- PropVariantClear(&value);
- }
+ if ((result = FillInactiveDeviceInfo(paWasapi, deviceInfo)) != paNoError)
+ goto error;
- // Formfactor
- {
- PROPVARIANT value;
- PropVariantInit(&value);
- hr = IPropertyStore_GetValue(pProperty, &PKEY_AudioEndpoint_FormFactor, &value);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
- // set
- #if defined(DUMMYUNIONNAME) && defined(NONAMELESSUNION)
- // avoid breaking strict-aliasing rules in such line: (EndpointFormFactor)(*((UINT *)(((WORD *)&value.wReserved3)+1)));
- UINT v;
- memcpy(&v, (((WORD *)&value.wReserved3)+1), sizeof(v));
- paWasapi->devInfo[i].formFactor = (EndpointFormFactor)v;
- #else
- paWasapi->devInfo[i].formFactor = (EndpointFormFactor)value.uintVal;
- #endif
- PA_DEBUG(("WASAPI:%d| form-factor[%d]\n", i, paWasapi->devInfo[i].formFactor));
- // cleanup
- PropVariantClear(&value);
- }
+ hostApi->deviceInfos[i] = deviceInfo;
+ ++hostApi->info.deviceCount;
+ }
+ }
+#endif
- SAFE_RELEASE(pProperty);
- }
-
- // Endpoint data
- {
- IMMEndpoint *endpoint = NULL;
- hr = IMMDevice_QueryInterface(paWasapi->devInfo[i].device, &pa_IID_IMMEndpoint, (void **)&endpoint);
- if (SUCCEEDED(hr))
- {
- hr = IMMEndpoint_GetDataFlow(endpoint, &paWasapi->devInfo[i].flow);
- SAFE_RELEASE(endpoint);
- }
- }
- #endif
+ // Clear any non-fatal errors
+ result = paNoError;
- // Getting a temporary IAudioClient for more fields
- // we make sure NOT to call Initialize yet!
- {
- #ifdef PA_WINRT
- // Set flow as ActivateAudioInterface depends on it and selects corresponding
- // direction for the Audio Client
- paWasapi->devInfo[i].flow = (i == 0 ? eRender : eCapture);
- #endif
-
- // Create temp Audio Client instance to query additional details
- IAudioClient *tmpClient = NULL;
- hr = ActivateAudioInterface(&paWasapi->devInfo[i], &tmpClient);
- // We need to set the result to a value otherwise we will return paNoError
- // [IF_FAILED_JUMP(hResult, error);]
- IF_FAILED_INTERNAL_ERROR_JUMP(hr, result, error);
-
- // Get latency
- hr = IAudioClient_GetDevicePeriod(tmpClient,
- &paWasapi->devInfo[i].DefaultDevicePeriod,
- &paWasapi->devInfo[i].MinimumDevicePeriod);
- if (FAILED(hr))
- {
- PA_DEBUG(("WASAPI:%d| failed getting min/default periods by IAudioClient::GetDevicePeriod() with error[%08X], will use 30000/100000 hns\n", i, (UINT32)hr));
-
- // assign WASAPI common values
- paWasapi->devInfo[i].DefaultDevicePeriod = 100000;
- paWasapi->devInfo[i].MinimumDevicePeriod = 30000;
-
- // ignore error, let continue further without failing with paInternalError
- hr = S_OK;
- }
-
- #ifdef PA_WINRT
- // Get mix format which will treat as default device format
- hr = IAudioClient_GetMixFormat(tmpClient, &mixFormat);
- if (SUCCEEDED(hr))
- {
- // Default device
- if (i == 0)
- (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
- else
- (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
-
- // State
- paWasapi->devInfo[i].state = DEVICE_STATE_ACTIVE;
-
- // Default format
- memcpy(&paWasapi->devInfo[i].DefaultFormat, mixFormat, min(sizeof(paWasapi->devInfo[i].DefaultFormat), sizeof(*mixFormat)));
- CoTaskMemFree(mixFormat);
-
- // Form-factor
- paWasapi->devInfo[i].formFactor = UnknownFormFactor;
-
- // Name
- deviceInfo->name = (char *)PaUtil_GroupAllocateMemory(paWasapi->allocations, MAX_STR_LEN + 1);
- if (deviceInfo->name == NULL)
- {
- SAFE_RELEASE(tmpClient);
- result = paInsufficientMemory;
- goto error;
- }
- _snprintf((char *)deviceInfo->name, MAX_STR_LEN - 1, "WASAPI_%s:%d", (i == 0 ? "Output" : "Input"), i);
- PA_DEBUG(("WASAPI:%d| name[%s]\n", i, deviceInfo->name));
- }
- #endif
-
- // Release tmp client
- SAFE_RELEASE(tmpClient);
-
- if (hr != S_OK)
- {
- //davidv: this happened with my hardware, previously for that same device in DirectSound:
- //Digital Output (Realtek AC'97 Audio)'s GUID: {0x38f2cf50,0x7b4c,0x4740,0x86,0xeb,0xd4,0x38,0x66,0xd8,0xc8, 0x9f}
- //so something must be _really_ wrong with this device, TODO handle this better. We kind of need GetMixFormat
- LogHostError(hr);
- // We need to set the result to a value otherwise we will return paNoError
- result = paInternalError;
- goto error;
- }
- }
-
- // we can now fill in portaudio device data
- deviceInfo->maxInputChannels = 0;
- deviceInfo->maxOutputChannels = 0;
- deviceInfo->defaultSampleRate = paWasapi->devInfo[i].DefaultFormat.Format.nSamplesPerSec;
- switch (paWasapi->devInfo[i].flow)
- {
- case eRender: {
- deviceInfo->maxOutputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
- deviceInfo->defaultHighOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
- deviceInfo->defaultLowOutputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
- PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
- deviceInfo->maxOutputChannels, (float)deviceInfo->defaultHighOutputLatency, (float)deviceInfo->defaultLowOutputLatency));
- break;}
- case eCapture: {
- deviceInfo->maxInputChannels = paWasapi->devInfo[i].DefaultFormat.Format.nChannels;
- deviceInfo->defaultHighInputLatency = nano100ToSeconds(paWasapi->devInfo[i].DefaultDevicePeriod);
- deviceInfo->defaultLowInputLatency = nano100ToSeconds(paWasapi->devInfo[i].MinimumDevicePeriod);
- PA_DEBUG(("WASAPI:%d| def.SR[%d] max.CH[%d] latency{hi[%f] lo[%f]}\n", i, (UINT32)deviceInfo->defaultSampleRate,
- deviceInfo->maxInputChannels, (float)deviceInfo->defaultHighInputLatency, (float)deviceInfo->defaultLowInputLatency));
- break; }
- default:
- PRINT(("WASAPI:%d| bad Data Flow!\n", i));
- // We need to set the result to a value otherwise we will return paNoError
- result = paInternalError;
- //continue; // do not skip from list, allow to initialize
- break;
- }
+ PRINT(("WASAPI: device list ok - found %d devices, %d loopback devices\n", paWasapi->deviceCount, loopbackDevices));
- (*hostApi)->deviceInfos[i] = deviceInfo;
- ++(*hostApi)->info.deviceCount;
- }
+done:
+
+#ifndef PA_WINRT
+ CoTaskMemFree(defaultRenderId);
+ CoTaskMemFree(defaultCaptureId);
+ SAFE_RELEASE(pEndPoints);
+ SAFE_RELEASE(pEnumerator);
+#endif
+
+ return result;
+
+error:
+
+ // Safety if error was not set so that we do not think initialize was a success
+ if (result == paNoError)
+ result = paInternalError;
+
+ PRINT(("WASAPI: failed to create device list - error[%d|%s]\n", result, Pa_GetErrorText(result)));
+
+ goto done;
+}
+
+// ------------------------------------------------------------------------------------------
+PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result;
+ PaWasapiHostApiRepresentation *paWasapi;
+
+#ifndef PA_WINRT
+ // Fail safely for any Windows version below Windows Vista
+ if (PaWinUtil_GetOsVersion() < paOsVersionWindowsVistaServer2008)
+ {
+ PRINT(("WASAPI: Unsupported Windows version!\n"));
+ return paNoError;
}
- (*hostApi)->Terminate = Terminate;
- (*hostApi)->OpenStream = OpenStream;
- (*hostApi)->IsFormatSupported = IsFormatSupported;
+ if (!SetupAVRT())
+ {
+ PRINT(("WASAPI: avrt.dll missing! Windows integrity broken?\n"));
+ return paNoError;
+ }
+#endif
+
+ paWasapi = (PaWasapiHostApiRepresentation *)PaUtil_AllocateZeroInitializedMemory(sizeof(PaWasapiHostApiRepresentation));
+ if (paWasapi == NULL)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* NOTE: we depend on PaUtil_AllocateZeroInitializedMemory() ensuring that all
+ fields are set to zero. especially paWasapi->allocations */
+
+ // Initialize COM subsystem
+ result = PaWinUtil_CoInitialize(paWASAPI, &paWasapi->comInitializationResult);
+ if (result != paNoError)
+ goto error;
+
+ // Create memory group
+ paWasapi->allocations = PaUtil_CreateAllocationGroup();
+ if (paWasapi->allocations == NULL)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ // Fill basic interface info
+ *hostApi = &paWasapi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paWASAPI;
+ (*hostApi)->info.name = "Windows WASAPI";
+ (*hostApi)->info.deviceCount = 0;
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ // Fill the device list
+ if ((result = CreateDeviceList(paWasapi, hostApiIndex)) != paNoError)
+ goto error;
+
+ // Detect if platform workaround is required
+ paWasapi->useWOW64Workaround = UseWOW64Workaround();
+
+ // Initialize time getter
+ SystemTimer_InitializeTimeGetter();
PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
@@ -1826,537 +2359,680 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
GetStreamTime, PaUtil_DummyGetCpuLoad,
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
-
- // findout if platform workaround is required
- paWasapi->useWOW64Workaround = UseWOW64Workaround();
-
-#ifndef PA_WINRT
- SAFE_RELEASE(pEndPoints);
-#endif
-
- PRINT(("WASAPI: initialized ok\n"));
+ PRINT(("WASAPI: initialized ok\n"));
return paNoError;
error:
- PRINT(("WASAPI: failed %s error[%d|%s]\n", __FUNCTION__, result, Pa_GetErrorText(result)));
+ PRINT(("WASAPI: failed %s error[%d|%s]\n", __FUNCTION__, result, Pa_GetErrorText(result)));
-#ifndef PA_WINRT
- SAFE_RELEASE(pEndPoints);
-#endif
+ Terminate((PaUtilHostApiRepresentation *)paWasapi);
- Terminate((PaUtilHostApiRepresentation *)paWasapi);
+ return result;
+}
- // Safety if error was not set so that we do not think initialize was a success
- if (result == paNoError) {
- result = paInternalError;
- }
+// ------------------------------------------------------------------------------------------
+static void ReleaseWasapiDeviceInfoList( PaWasapiHostApiRepresentation *paWasapi )
+{
+ UINT32 i;
- return result;
+ // Release device info bound objects
+ for (i = 0; i < paWasapi->deviceCount; ++i)
+ {
+ #ifndef PA_WINRT
+ SAFE_RELEASE(paWasapi->devInfo[i].device);
+ #endif
+ }
+
+ // Free device info
+ if (paWasapi->allocations != NULL)
+ PaUtil_GroupFreeMemory(paWasapi->allocations, paWasapi->devInfo);
+
+ // Be ready for a device list reinitialization and if its creation is failed pointers must not be dangling
+ paWasapi->devInfo = NULL;
+ paWasapi->deviceCount = 0;
}
// ------------------------------------------------------------------------------------------
static void Terminate( PaUtilHostApiRepresentation *hostApi )
{
- UINT i;
PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
- if (paWasapi == NULL)
- return;
+ if (paWasapi == NULL)
+ return;
- // Release IMMDeviceEnumerator
-#ifndef PA_WINRT
- SAFE_RELEASE(paWasapi->enumerator);
-#endif
+ // Release device list
+ ReleaseWasapiDeviceInfoList(paWasapi);
- // Release device info bound objects and device info itself
- for (i = 0; i < paWasapi->deviceCount; ++i)
- {
- PaWasapiDeviceInfo *info = &paWasapi->devInfo[i];
- #ifndef PA_WINRT
- SAFE_RELEASE(info->device);
- #else
- (void)info;
- #endif
- }
- PaUtil_FreeMemory(paWasapi->devInfo);
-
- if (paWasapi->allocations)
- {
+ // Free allocations and memory group itself
+ if (paWasapi->allocations != NULL)
+ {
PaUtil_FreeAllAllocations(paWasapi->allocations);
PaUtil_DestroyAllocationGroup(paWasapi->allocations);
}
- PaWinUtil_CoUninitialize( paWASAPI, &paWasapi->comInitializationResult );
+ // Release COM subsystem
+ PaWinUtil_CoUninitialize(paWASAPI, &paWasapi->comInitializationResult);
+ // Free API representation
PaUtil_FreeMemory(paWasapi);
- // Close AVRT
- CloseAVRT();
+ // Close AVRT
+ CloseAVRT();
}
// ------------------------------------------------------------------------------------------
-static PaWasapiHostApiRepresentation *_GetHostApi(PaError *_error)
+static PaWasapiHostApiRepresentation *_GetHostApi(PaError *ret)
{
- PaError error;
+ PaError error;
+ PaUtilHostApiRepresentation *pApi;
- PaUtilHostApiRepresentation *pApi;
- if ((error = PaUtil_GetHostApiRepresentation(&pApi, paWASAPI)) != paNoError)
- {
- if (_error != NULL)
- (*_error) = error;
+ if ((error = PaUtil_GetHostApiRepresentation(&pApi, paWASAPI)) != paNoError)
+ {
+ if (ret != NULL)
+ (*ret) = error;
- return NULL;
- }
- return (PaWasapiHostApiRepresentation *)pApi;
+ return NULL;
+ }
+
+ return (PaWasapiHostApiRepresentation *)pApi;
}
// ------------------------------------------------------------------------------------------
-int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int nFormatSize, PaDeviceIndex nDevice )
+static PaError UpdateDeviceList()
{
- PaError ret;
- PaWasapiHostApiRepresentation *paWasapi;
- UINT32 size;
- PaDeviceIndex index;
+ int i;
+ PaError ret;
+ PaWasapiHostApiRepresentation *paWasapi;
+ PaUtilHostApiRepresentation *hostApi;
- if (pFormat == NULL)
- return paBadBufferPtr;
- if (nFormatSize <= 0)
- return paBufferTooSmall;
+ // Get API
+ hostApi = (PaUtilHostApiRepresentation *)(paWasapi = _GetHostApi(&ret));
+ if (paWasapi == NULL)
+ return paNotInitialized;
- // Get API
- paWasapi = _GetHostApi(&ret);
- if (paWasapi == NULL)
- return ret;
+ // Make sure initialized properly
+ if (paWasapi->allocations == NULL)
+ return paNotInitialized;
- // Get device index
- ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
- if (ret != paNoError)
+ // Release WASAPI internal device info list
+ ReleaseWasapiDeviceInfoList(paWasapi);
+
+ // Release external device info list
+ if (hostApi->deviceInfos != NULL)
+ {
+ for (i = 0; i < hostApi->info.deviceCount; ++i)
+ {
+ PaUtil_GroupFreeMemory(paWasapi->allocations, (void *)hostApi->deviceInfos[i]->name);
+ }
+ PaUtil_GroupFreeMemory(paWasapi->allocations, hostApi->deviceInfos[0]);
+ PaUtil_GroupFreeMemory(paWasapi->allocations, hostApi->deviceInfos);
+
+ // Be ready for a device list reinitialization and if its creation is failed pointers must not be dangling
+ hostApi->deviceInfos = NULL;
+ hostApi->info.deviceCount = 0;
+ hostApi->info.defaultInputDevice = paNoDevice;
+ hostApi->info.defaultOutputDevice = paNoDevice;
+ }
+
+ // Fill possibly updated device list
+ if ((ret = CreateDeviceList(paWasapi, Pa_HostApiTypeIdToHostApiIndex(paWASAPI))) != paNoError)
+ return ret;
+
+ return paNoError;
+}
+
+// ------------------------------------------------------------------------------------------
+PaError PaWasapi_UpdateDeviceList()
+{
+#if defined(PA_WASAPI_MAX_CONST_DEVICE_COUNT) && (PA_WASAPI_MAX_CONST_DEVICE_COUNT > 0)
+ return UpdateDeviceList();
+#else
+ return paInternalError;
+#endif
+}
+
+// ------------------------------------------------------------------------------------------
+int PaWasapi_GetDeviceCurrentFormat( PaStream *pStream, void *pFormat, unsigned int formatSize, int bOutput )
+{
+ UINT32 size;
+ WAVEFORMATEXTENSIBLE *format;
+
+ PaWasapiStream *stream = (PaWasapiStream *)pStream;
+ if (stream == NULL)
+ return paBadStreamPtr;
+
+ format = (bOutput == TRUE ? &stream->out.wavexu.ext : &stream->in.wavexu.ext);
+
+ size = min(formatSize, (UINT32)sizeof(*format));
+ memcpy(pFormat, format, size);
+
+ return size;
+}
+
+// ------------------------------------------------------------------------------------------
+static PaError _GetWasapiDeviceInfoByDeviceIndex( PaWasapiDeviceInfo **info, PaDeviceIndex device )
+{
+ PaError ret;
+ PaDeviceIndex index;
+
+ // Get API
+ PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
+ if (paWasapi == NULL)
+ return paNotInitialized;
+
+ // Get device index
+ if ((ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, device, &paWasapi->inheritedHostApiRep)) != paNoError)
+ return ret;
+
+ // Validate index
+ if ((UINT32)index >= paWasapi->deviceCount)
+ return paInvalidDevice;
+
+ (*info) = &paWasapi->devInfo[ index ];
+
+ return paNoError;
+}
+
+// ------------------------------------------------------------------------------------------
+int PaWasapi_GetDeviceDefaultFormat( void *pFormat, unsigned int formatSize, PaDeviceIndex device )
+{
+ PaError ret;
+ PaWasapiDeviceInfo *deviceInfo;
+ UINT32 size;
+
+ if (pFormat == NULL)
+ return paBadBufferPtr;
+ if (formatSize <= 0)
+ return paBufferTooSmall;
+
+ if ((ret = _GetWasapiDeviceInfoByDeviceIndex(&deviceInfo, device)) != paNoError)
+ return ret;
+
+ size = min(formatSize, (UINT32)sizeof(deviceInfo->DefaultFormat));
+ memcpy(pFormat, &deviceInfo->DefaultFormat, size);
+
+ return size;
+}
+
+// ------------------------------------------------------------------------------------------
+int PaWasapi_GetDeviceMixFormat( void *pFormat, unsigned int formatSize, PaDeviceIndex device )
+{
+ PaError ret;
+ PaWasapiDeviceInfo *deviceInfo;
+ UINT32 size;
+
+ if (pFormat == NULL)
+ return paBadBufferPtr;
+ if (formatSize <= 0)
+ return paBufferTooSmall;
+
+ if ((ret = _GetWasapiDeviceInfoByDeviceIndex(&deviceInfo, device)) != paNoError)
return ret;
- // Validate index
- if ((UINT32)index >= paWasapi->deviceCount)
- return paInvalidDevice;
+ size = min(formatSize, (UINT32)sizeof(deviceInfo->MixFormat));
+ memcpy(pFormat, &deviceInfo->MixFormat, size);
+
+ return size;
+}
+
+// ------------------------------------------------------------------------------------------
+int PaWasapi_GetDeviceRole( PaDeviceIndex device )
+{
+ PaError ret;
+ PaWasapiDeviceInfo *deviceInfo;
+
+ if ((ret = _GetWasapiDeviceInfoByDeviceIndex(&deviceInfo, device)) != paNoError)
+ return ret;
+
+ return deviceInfo->formFactor;
+}
+
+// ------------------------------------------------------------------------------------------
+PaError PaWasapi_GetIMMDevice( PaDeviceIndex device, void **pIMMDevice )
+{
+#ifndef PA_WINRT
+ PaError ret;
+ PaWasapiDeviceInfo *deviceInfo;
+
+ if (pIMMDevice == NULL)
+ return paBadBufferPtr;
+
+ if ((ret = _GetWasapiDeviceInfoByDeviceIndex(&deviceInfo, device)) != paNoError)
+ return ret;
- size = min(nFormatSize, (UINT32)sizeof(paWasapi->devInfo[ index ].DefaultFormat));
- memcpy(pFormat, &paWasapi->devInfo[ index ].DefaultFormat, size);
+ (*pIMMDevice) = deviceInfo->device;
- return size;
+ return paNoError;
+#else
+ (void)device;
+ (void)pIMMDevice;
+ return paIncompatibleStreamHostApi;
+#endif
}
// ------------------------------------------------------------------------------------------
-int PaWasapi_GetDeviceRole( PaDeviceIndex nDevice )
+int PaWasapi_IsLoopback( PaDeviceIndex device )
{
- PaError ret;
- PaDeviceIndex index;
+ PaError ret;
+ PaDeviceIndex index;
- // Get API
- PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
- if (paWasapi == NULL)
- return paNotInitialized;
+ // Get API
+ PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
+ if (paWasapi == NULL)
+ return paNotInitialized;
- // Get device index
- ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
+ // Get device index
+ ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, device, &paWasapi->inheritedHostApiRep);
if (ret != paNoError)
return ret;
- // Validate index
- if ((UINT32)index >= paWasapi->deviceCount)
- return paInvalidDevice;
+ // Validate index
+ if ((UINT32)index >= paWasapi->deviceCount)
+ return paInvalidDevice;
- return paWasapi->devInfo[ index ].formFactor;
+ return paWasapi->devInfo[ index ].loopBack;
}
// ------------------------------------------------------------------------------------------
-PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput )
+PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *pInput, unsigned int *pOutput )
{
PaWasapiStream *stream = (PaWasapiStream *)pStream;
- if (stream == NULL)
- return paBadStreamPtr;
+ if (stream == NULL)
+ return paBadStreamPtr;
- if (nInput != NULL)
- (*nInput) = stream->in.framesPerHostCallback;
+ if (pInput != NULL)
+ (*pInput) = stream->in.framesPerHostCallback;
- if (nOutput != NULL)
- (*nOutput) = stream->out.framesPerHostCallback;
+ if (pOutput != NULL)
+ (*pOutput) = stream->out.framesPerHostCallback;
- return paNoError;
+ return paNoError;
}
// ------------------------------------------------------------------------------------------
static void LogWAVEFORMATEXTENSIBLE(const WAVEFORMATEXTENSIBLE *in)
{
const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
- switch (old->wFormatTag)
- {
- case WAVE_FORMAT_EXTENSIBLE: {
-
- PRINT(("wFormatTag =WAVE_FORMAT_EXTENSIBLE\n"));
-
- if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
- {
- PRINT(("SubFormat =KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
- }
- else
- if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
- {
- PRINT(("SubFormat =KSDATAFORMAT_SUBTYPE_PCM\n"));
- }
- else
- {
- PRINT(("SubFormat =CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",
- in->SubFormat.Data1,
- in->SubFormat.Data2,
- in->SubFormat.Data3,
- (int)in->SubFormat.Data4[0],
- (int)in->SubFormat.Data4[1],
- (int)in->SubFormat.Data4[2],
- (int)in->SubFormat.Data4[3],
- (int)in->SubFormat.Data4[4],
- (int)in->SubFormat.Data4[5],
- (int)in->SubFormat.Data4[6],
- (int)in->SubFormat.Data4[7]));
- }
- PRINT(("Samples.wValidBitsPerSample =%d\n", in->Samples.wValidBitsPerSample));
- PRINT(("dwChannelMask =0x%X\n",in->dwChannelMask));
-
- break; }
-
- case WAVE_FORMAT_PCM: PRINT(("wFormatTag =WAVE_FORMAT_PCM\n")); break;
- case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag =WAVE_FORMAT_IEEE_FLOAT\n")); break;
- default:
- PRINT(("wFormatTag =UNKNOWN(%d)\n",old->wFormatTag)); break;
- }
-
- PRINT(("nChannels =%d\n",old->nChannels));
- PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));
- PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));
- PRINT(("nBlockAlign =%d\n",old->nBlockAlign));
- PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));
- PRINT(("cbSize =%d\n",old->cbSize));
-}
-
-// ------------------------------------------------------------------------------------------
-static PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *in)
-{
- const WAVEFORMATEX *old = (WAVEFORMATEX *)in;
-
switch (old->wFormatTag)
- {
+ {
case WAVE_FORMAT_EXTENSIBLE: {
+
+ PRINT(("wFormatTag =WAVE_FORMAT_EXTENSIBLE\n"));
+
if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
- {
- if (in->Samples.wValidBitsPerSample == 32)
+ {
+ PRINT(("SubFormat =KSDATAFORMAT_SUBTYPE_IEEE_FLOAT\n"));
+ }
+ else
+ if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
+ {
+ PRINT(("SubFormat =KSDATAFORMAT_SUBTYPE_PCM\n"));
+ }
+ else
+ {
+ PRINT(("SubFormat =CUSTOM GUID{%d:%d:%d:%d%d%d%d%d%d%d%d}\n",
+ in->SubFormat.Data1,
+ in->SubFormat.Data2,
+ in->SubFormat.Data3,
+ (int)in->SubFormat.Data4[0],
+ (int)in->SubFormat.Data4[1],
+ (int)in->SubFormat.Data4[2],
+ (int)in->SubFormat.Data4[3],
+ (int)in->SubFormat.Data4[4],
+ (int)in->SubFormat.Data4[5],
+ (int)in->SubFormat.Data4[6],
+ (int)in->SubFormat.Data4[7]));
+ }
+ PRINT(("Samples.wValidBitsPerSample =%d\n", in->Samples.wValidBitsPerSample));
+ PRINT(("dwChannelMask =0x%X\n",in->dwChannelMask));
+
+ break; }
+
+ case WAVE_FORMAT_PCM: PRINT(("wFormatTag =WAVE_FORMAT_PCM\n")); break;
+ case WAVE_FORMAT_IEEE_FLOAT: PRINT(("wFormatTag =WAVE_FORMAT_IEEE_FLOAT\n")); break;
+ default:
+ PRINT(("wFormatTag =UNKNOWN(%d)\n",old->wFormatTag)); break;
+ }
+
+ PRINT(("nChannels =%d\n",old->nChannels));
+ PRINT(("nSamplesPerSec =%d\n",old->nSamplesPerSec));
+ PRINT(("nAvgBytesPerSec=%d\n",old->nAvgBytesPerSec));
+ PRINT(("nBlockAlign =%d\n",old->nBlockAlign));
+ PRINT(("wBitsPerSample =%d\n",old->wBitsPerSample));
+ PRINT(("cbSize =%d\n",old->cbSize));
+}
+
+// ------------------------------------------------------------------------------------------
+PaSampleFormat WaveToPaFormat(const WAVEFORMATEXTENSIBLE *fmtext)
+{
+ switch (fmtext->Format.wFormatTag)
+ {
+ case WAVE_FORMAT_EXTENSIBLE: {
+ if (IsEqualGUID(&fmtext->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+ {
+ if (fmtext->Samples.wValidBitsPerSample == 32)
return paFloat32;
}
else
- if (IsEqualGUID(&in->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
- {
- switch (old->wBitsPerSample)
- {
- case 32: return paInt32;
- case 24: return paInt24;
- case 8: return paUInt8;
- case 16: return paInt16;
+ if (IsEqualGUID(&fmtext->SubFormat, &pa_KSDATAFORMAT_SUBTYPE_PCM))
+ {
+ switch (fmtext->Format.wBitsPerSample)
+ {
+ case 32: return paInt32;
+ case 24: return paInt24;
+ case 16: return paInt16;
+ case 8: return paUInt8;
}
}
- break; }
+ else
+ // KSDATAFORMAT_SUBTYPE_IEC61937_PCM is a naked format GUID which has different Data1 and Data2
+ // depending on the passthrough format, for the possible values refer Microsoft documentation
+ // "Representing Formats for IEC 61937 Transmissions". Here we check if Data3 and Data4 match
+ // assuming that if they do match then we have KSDATAFORMAT_SUBTYPE_IEC61937_XXX format.
+ // Currently PA WASAPI is using KSDATAFORMAT_SUBTYPE_IEEE_FLOAT and KSDATAFORMAT_SUBTYPE_PCM
+ // therefore this check is reliable in this way.
+ if (!memcmp(&fmtext->SubFormat.Data3, &pa_KSDATAFORMAT_SUBTYPE_IEC61937_PCM.Data3,
+ (sizeof(GUID) - offsetof(GUID, Data3))))
+ {
+ return paInt16;
+ }
+ break; }
case WAVE_FORMAT_IEEE_FLOAT:
- return paFloat32;
+ return paFloat32;
case WAVE_FORMAT_PCM: {
- switch (old->wBitsPerSample)
- {
- case 32: return paInt32;
- case 24: return paInt24;
- case 8: return paUInt8;
- case 16: return paInt16;
+ switch (fmtext->Format.wBitsPerSample)
+ {
+ case 32: return paInt32;
+ case 24: return paInt24;
+ case 16: return paInt16;
+ case 8: return paUInt8;
}
- break; }
+ break; }
}
return paCustomFormat;
}
// ------------------------------------------------------------------------------------------
-static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE *wavex, const PaStreamParameters *params,
- double sampleRate)
+static PaError MakeWaveFormatFromParams(WAVEFORMATEXTENSIBLE_UNION *wavexu, const PaStreamParameters *params,
+ double sampleRate, BOOL packedOnly)
{
- WORD bitsPerSample;
- WAVEFORMATEX *old;
- DWORD channelMask = 0;
- PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo;
-
- // Get user assigned channel mask
- if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask))
- channelMask = streamInfo->channelMask;
+ WORD bitsPerSample;
+ WAVEFORMATEX *old;
+ DWORD channelMask = 0;
+ BOOL useExtensible = (params->channelCount > 2); // format is always forced for >2 channels format
+ PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)params->hostApiSpecificStreamInfo;
+ WAVEFORMATEXTENSIBLE *wavex = (WAVEFORMATEXTENSIBLE *)wavexu;
+ BOOL passthroughMode = ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiPassthrough));
+
+ // Convert PaSampleFormat to valid data bits
+ if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
+ return paSampleFormatNotSupported;
+
+ // Use user assigned channel mask
+ if ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiUseChannelMask))
+ {
+ channelMask = streamInfo->channelMask;
+ useExtensible = TRUE;
+ }
- // Convert PaSampleFormat to bits per sample
- if ((bitsPerSample = PaSampleFormatToBitsPerSample(params->sampleFormat)) == 0)
- return paSampleFormatNotSupported;
+ memset(wavexu, 0, sizeof(*wavexu));
- memset(wavex, 0, sizeof(*wavex));
+ old = (WAVEFORMATEX *)wavex;
+ old->nChannels = (WORD)params->channelCount;
+ old->nSamplesPerSec = (DWORD)sampleRate;
+ old->wBitsPerSample = bitsPerSample;
- old = (WAVEFORMATEX *)wavex;
- old->nChannels = (WORD)params->channelCount;
- old->nSamplesPerSec = (DWORD)sampleRate;
- if ((old->wBitsPerSample = bitsPerSample) > 16)
- {
- old->wBitsPerSample = 32; // 20 or 24 bits must go in 32 bit containers (ints)
- }
- old->nBlockAlign = (old->nChannels * (old->wBitsPerSample/8));
- old->nAvgBytesPerSec = (old->nSamplesPerSec * old->nBlockAlign);
+ // according to MSDN for WAVEFORMATEX structure for WAVE_FORMAT_PCM:
+ // "If wFormatTag is WAVE_FORMAT_PCM, then wBitsPerSample should be equal to 8 or 16."
+ if ((bitsPerSample != 8) && (bitsPerSample != 16))
+ {
+ // Normally 20 or 24 bits must go in 32 bit containers (ints) but in Exclusive mode some devices require
+ // packed version of the format, e.g. for example 24-bit in 3-bytes
+ old->wBitsPerSample = (packedOnly ? bitsPerSample : 32);
+ useExtensible = TRUE;
+ }
// WAVEFORMATEX
- if ((params->channelCount <= 2) && ((bitsPerSample == 16) || (bitsPerSample == 8)))
- {
- old->cbSize = 0;
- old->wFormatTag = WAVE_FORMAT_PCM;
+ if (!useExtensible)
+ {
+ old->wFormatTag = WAVE_FORMAT_PCM;
}
// WAVEFORMATEXTENSIBLE
else
- {
+ {
old->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- old->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
+ old->cbSize = (passthroughMode ? (sizeof(WAVEFORMATEXTENSIBLE_IEC61937) - sizeof(WAVEFORMATEX))
+ : (sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX)));
+
+ if (passthroughMode)
+ {
+ WAVEFORMATEXTENSIBLE_IEC61937 *wavex_61937 = (WAVEFORMATEXTENSIBLE_IEC61937 *)wavexu;
+
+ wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEC61937_PCM;
+ wavex->SubFormat.Data1 = (streamInfo->passthrough.formatId >> 16);
+ wavex->SubFormat.Data2 = (USHORT)(streamInfo->passthrough.formatId & 0x0000FFFF);
- if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
+ wavex_61937->dwEncodedSamplesPerSec = streamInfo->passthrough.encodedSamplesPerSec;
+ wavex_61937->dwEncodedChannelCount = streamInfo->passthrough.encodedChannelCount;
+ wavex_61937->dwAverageBytesPerSec = streamInfo->passthrough.averageBytesPerSec;
+ }
+ else if ((params->sampleFormat & ~paNonInterleaved) == paFloat32)
+ {
wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ }
else
+ {
wavex->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
+ }
+
+ wavex->Samples.wValidBitsPerSample = bitsPerSample;
- wavex->Samples.wValidBitsPerSample = bitsPerSample; //no extra padding!
-
- // Set channel mask
- if (channelMask != 0)
- {
- wavex->dwChannelMask = channelMask;
- }
- else
- {
- switch (params->channelCount)
- {
- case 1: wavex->dwChannelMask = PAWIN_SPEAKER_MONO; break;
- case 2: wavex->dwChannelMask = PAWIN_SPEAKER_STEREO; break;
- case 3: wavex->dwChannelMask = PAWIN_SPEAKER_STEREO|SPEAKER_LOW_FREQUENCY; break;
- case 4: wavex->dwChannelMask = PAWIN_SPEAKER_QUAD; break;
- case 5: wavex->dwChannelMask = PAWIN_SPEAKER_QUAD|SPEAKER_LOW_FREQUENCY; break;
+ // Set channel mask
+ if (channelMask != 0)
+ {
+ wavex->dwChannelMask = channelMask;
+ }
+ else
+ {
+ switch (params->channelCount)
+ {
+ case 1: wavex->dwChannelMask = PAWIN_SPEAKER_MONO; break;
+ case 2: wavex->dwChannelMask = PAWIN_SPEAKER_STEREO; break;
+ case 3: wavex->dwChannelMask = PAWIN_SPEAKER_STEREO|SPEAKER_LOW_FREQUENCY; break;
+ case 4: wavex->dwChannelMask = PAWIN_SPEAKER_QUAD; break;
+ case 5: wavex->dwChannelMask = PAWIN_SPEAKER_QUAD|SPEAKER_LOW_FREQUENCY; break;
#ifdef PAWIN_SPEAKER_5POINT1_SURROUND
- case 6: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1_SURROUND; break;
+ case 6: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1_SURROUND; break;
#else
- case 6: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1; break;
+ case 6: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1; break;
#endif
#ifdef PAWIN_SPEAKER_5POINT1_SURROUND
- case 7: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1_SURROUND|SPEAKER_BACK_CENTER; break;
+ case 7: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1_SURROUND|SPEAKER_BACK_CENTER; break;
#else
- case 7: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1|SPEAKER_BACK_CENTER; break;
-#endif
+ case 7: wavex->dwChannelMask = PAWIN_SPEAKER_5POINT1|SPEAKER_BACK_CENTER; break;
+#endif
#ifdef PAWIN_SPEAKER_7POINT1_SURROUND
- case 8: wavex->dwChannelMask = PAWIN_SPEAKER_7POINT1_SURROUND; break;
+ case 8: wavex->dwChannelMask = PAWIN_SPEAKER_7POINT1_SURROUND; break;
#else
- case 8: wavex->dwChannelMask = PAWIN_SPEAKER_7POINT1; break;
+ case 8: wavex->dwChannelMask = PAWIN_SPEAKER_7POINT1; break;
#endif
- default: wavex->dwChannelMask = 0;
- }
- }
- }
+ default: wavex->dwChannelMask = 0;
+ }
+ }
+ }
+
+ old->nBlockAlign = old->nChannels * (old->wBitsPerSample / 8);
+ old->nAvgBytesPerSec = old->nSamplesPerSec * old->nBlockAlign;
+
return paNoError;
}
// ------------------------------------------------------------------------------------------
-/*static void wasapiFillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
+static HRESULT GetAlternativeSampleFormatExclusive(IAudioClient *client, double sampleRate,
+ const PaStreamParameters *params, WAVEFORMATEXTENSIBLE_UNION *outWavexU, BOOL packedSampleFormatOnly)
{
- PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
- PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
- PA_DEBUG(( "chanelCount = %d\n", channelCount ));
-
- pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
- pwfext->Format.nChannels = (WORD)channelCount;
- pwfext->Format.nSamplesPerSec = (DWORD)sampleRate;
- if(channelCount == 1)
- pwfext->dwChannelMask = PAWIN_SPEAKER_DIRECTOUT;
- else
- pwfext->dwChannelMask = PAWIN_SPEAKER_STEREO;
- if(sampleFormat == paFloat32)
+ HRESULT hr = !S_OK;
+ AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
+ WAVEFORMATEXTENSIBLE_UNION testFormat;
+ PaStreamParameters testParams;
+ int i;
+ static const PaSampleFormat bestToWorst[] = { paInt32, paInt24, paFloat32, paInt16 };
+
+ // Try combination Stereo (2 channels) and then we will use our custom mono-stereo mixer
+ if (params->channelCount == 1)
{
- pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
- pwfext->Format.wBitsPerSample = 32;
- pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
- pwfext->Samples.wValidBitsPerSample = 32;
- pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ testParams = (*params);
+ testParams.channelCount = 2;
+
+ if (MakeWaveFormatFromParams(&testFormat, &testParams, sampleRate, packedSampleFormatOnly) == paNoError)
+ {
+ if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.ext.Format, NULL)) == S_OK)
+ {
+ (*outWavexU) = testFormat;
+ return hr;
+ }
+ }
+
+ // Try selecting suitable sample type
+ for (i = 0; i < STATIC_ARRAY_SIZE(bestToWorst); ++i)
+ {
+ testParams.sampleFormat = bestToWorst[i];
+
+ if (MakeWaveFormatFromParams(&testFormat, &testParams, sampleRate, packedSampleFormatOnly) == paNoError)
+ {
+ if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.ext.Format, NULL)) == S_OK)
+ {
+ (*outWavexU) = testFormat;
+ return hr;
+ }
+ }
+ }
}
- else if(sampleFormat == paInt32)
+
+ // Try selecting suitable sample type
+ testParams = (*params);
+ for (i = 0; i < STATIC_ARRAY_SIZE(bestToWorst); ++i)
{
- pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
- pwfext->Format.wBitsPerSample = 32;
- pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
- pwfext->Samples.wValidBitsPerSample = 32;
- pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
+ testParams.sampleFormat = bestToWorst[i];
+
+ if (MakeWaveFormatFromParams(&testFormat, &testParams, sampleRate, packedSampleFormatOnly) == paNoError)
+ {
+ if ((hr = IAudioClient_IsFormatSupported(client, shareMode, &testFormat.ext.Format, NULL)) == S_OK)
+ {
+ (*outWavexU) = testFormat;
+ return hr;
+ }
+ }
}
- else if(sampleFormat == paInt24)
+
+ return hr;
+}
+
+// ------------------------------------------------------------------------------------------
+static PaError GetClosestFormat(IAudioClient *client, double sampleRate, const PaStreamParameters *_params,
+ AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE_UNION *outWavexU, BOOL output)
+{
+ PaWasapiStreamInfo *streamInfo = (PaWasapiStreamInfo *)_params->hostApiSpecificStreamInfo;
+ WAVEFORMATEX *sharedClosestMatch = NULL;
+ HRESULT hr = !S_OK;
+ PaStreamParameters params = (*_params);
+ const BOOL explicitFormat = (streamInfo != NULL) && ((streamInfo->flags & paWinWasapiExplicitSampleFormat) == paWinWasapiExplicitSampleFormat);
+ WAVEFORMATEXTENSIBLE *outWavex = (WAVEFORMATEXTENSIBLE *)outWavexU;
+ (void)output;
+
+ /* It was not noticed that 24-bit Input producing no output while device accepts this format.
+ To fix this issue let's ask for 32-bits and let PA converters convert host 32-bit data
+ to 24-bit for user-space. The bug concerns Vista, if Windows 7 supports 24-bits for Input
+ please report to PortAudio developers to exclude Windows 7.
+ */
+ /*if ((params.sampleFormat == paInt24) && (output == FALSE))
+ params.sampleFormat = paFloat32;*/ // <<< The silence was due to missing Int32_To_Int24_Dither implementation
+
+ // Try standard approach, e.g. if data is > 16 bits it will be packed into 32-bit containers
+ MakeWaveFormatFromParams(outWavexU, &params, sampleRate, FALSE);
+
+ // If built-in PCM converter requested then shared mode format will always succeed
+ if ((PaWinUtil_GetOsVersion() >= paOsVersionWindows7Server2008R2) &&
+ (shareMode == AUDCLNT_SHAREMODE_SHARED) &&
+ ((streamInfo != NULL) && (streamInfo->flags & paWinWasapiAutoConvert)))
+ return paFormatIsSupported;
+
+ hr = IAudioClient_IsFormatSupported(client, shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
+
+ // Exclusive mode can require packed format for some devices
+ if ((hr != S_OK) && (shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE))
{
- pwfext->Format.nBlockAlign = (WORD)(channelCount * 4);
- pwfext->Format.wBitsPerSample = 32; // 24-bit in 32-bit int container
- pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
- pwfext->Samples.wValidBitsPerSample = 24;
- pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
+ // Enforce packed only format, e.g. data bits will not be packed into 32-bit containers in any case
+ MakeWaveFormatFromParams(outWavexU, &params, sampleRate, TRUE);
+ hr = IAudioClient_IsFormatSupported(client, shareMode, &outWavex->Format, NULL);
}
- else if(sampleFormat == paInt16)
+
+ if (hr == S_OK)
{
- pwfext->Format.nBlockAlign = (WORD)(channelCount * 2);
- pwfext->Format.wBitsPerSample = 16;
- pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
- pwfext->Samples.wValidBitsPerSample = 16;
- pwfext->SubFormat = pa_KSDATAFORMAT_SUBTYPE_PCM;
+ return paFormatIsSupported;
}
- pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
-}*/
+ else
+ if (sharedClosestMatch != NULL)
+ {
+ WORD bitsPerSample;
-// ------------------------------------------------------------------------------------------
-static PaError GetClosestFormat(IAudioClient *myClient, double sampleRate,
- const PaStreamParameters *_params, AUDCLNT_SHAREMODE shareMode, WAVEFORMATEXTENSIBLE *outWavex,
- BOOL output)
-{
- PaError answer = paInvalidSampleRate;
- WAVEFORMATEX *sharedClosestMatch = NULL;
- HRESULT hr = !S_OK;
- PaStreamParameters params = (*_params);
- (void)output;
+ if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+ memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE));
+ else
+ memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX));
+
+ CoTaskMemFree(sharedClosestMatch);
+ sharedClosestMatch = NULL;
- /* It was not noticed that 24-bit Input producing no output while device accepts this format.
- To fix this issue let's ask for 32-bits and let PA converters convert host 32-bit data
- to 24-bit for user-space. The bug concerns Vista, if Windows 7 supports 24-bits for Input
- please report to PortAudio developers to exclude Windows 7.
- */
- /*if ((params.sampleFormat == paInt24) && (output == FALSE))
- params.sampleFormat = paFloat32;*/ // <<< The silence was due to missing Int32_To_Int24_Dither implementation
+ // Validate SampleRate
+ if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec)
+ return paInvalidSampleRate;
+
+ // Validate Channel count
+ if ((WORD)params.channelCount != outWavex->Format.nChannels)
+ {
+ // If mono, then driver does not support 1 channel, we use internal workaround
+ // of tiny software mixing functionality, e.g. we provide to user buffer 1 channel
+ // but then mix into 2 for device buffer
+ if ((params.channelCount == 1) && (outWavex->Format.nChannels == 2))
+ return paFormatIsSupported;
+ else
+ return paInvalidChannelCount;
+ }
- MakeWaveFormatFromParams(outWavex, &params, sampleRate);
+ // Validate Sample format
+ if ((bitsPerSample = PaSampleFormatToBitsPerSample(params.sampleFormat)) == 0)
+ return paSampleFormatNotSupported;
- hr = IAudioClient_IsFormatSupported(myClient, shareMode, &outWavex->Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
- if (hr == S_OK)
- answer = paFormatIsSupported;
+ // Accepted format
+ return paFormatIsSupported;
+ }
else
- if (sharedClosestMatch)
- {
- WORD bitsPerSample;
- WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)sharedClosestMatch;
-
- GUID subf_guid = GUID_NULL;
- if (sharedClosestMatch->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
- {
- memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEXTENSIBLE));
- subf_guid = ext->SubFormat;
- }
- else
- memcpy(outWavex, sharedClosestMatch, sizeof(WAVEFORMATEX));
+ if ((shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) && !explicitFormat)
+ {
+ // Try standard approach, e.g. if data is > 16 bits it will be packed into 32-bit containers
+ if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, &params, outWavexU, FALSE)) == S_OK)
+ return paFormatIsSupported;
- CoTaskMemFree(sharedClosestMatch);
+ // Enforce packed only format, e.g. data bits will not be packed into 32-bit containers in any case
+ if ((hr = GetAlternativeSampleFormatExclusive(client, sampleRate, &params, outWavexU, TRUE)) == S_OK)
+ return paFormatIsSupported;
- // Make supported by default
- answer = paFormatIsSupported;
-
- // Validate SampleRate
- if ((DWORD)sampleRate != outWavex->Format.nSamplesPerSec)
- return paInvalidSampleRate;
-
- // Validate Channel count
- if ((WORD)params.channelCount != outWavex->Format.nChannels)
- {
- // If mono, then driver does not support 1 channel, we use internal workaround
- // of tiny software mixing functionality, e.g. we provide to user buffer 1 channel
- // but then mix into 2 for device buffer
- if ((params.channelCount == 1) && (outWavex->Format.nChannels == 2))
- return paFormatIsSupported;
- else
- return paInvalidChannelCount;
- }
-
- // Validate Sample format
- if ((bitsPerSample = PaSampleFormatToBitsPerSample(params.sampleFormat)) == 0)
- return paSampleFormatNotSupported;
-
- // Validate Sample format: bit size (WASAPI does not limit 'bit size')
- //if (bitsPerSample != outWavex->Format.wBitsPerSample)
- // return paSampleFormatNotSupported;
-
- // Validate Sample format: paFloat32 (WASAPI does not limit 'bit type')
- //if ((params->sampleFormat == paFloat32) && (subf_guid != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
- // return paSampleFormatNotSupported;
-
- // Validate Sample format: paInt32 (WASAPI does not limit 'bit type')
- //if ((params->sampleFormat == paInt32) && (subf_guid != KSDATAFORMAT_SUBTYPE_PCM))
- // return paSampleFormatNotSupported;
- }
- else
- {
- static const int BestToWorst[] = { paFloat32, paInt24, paInt16 };
- int i;
-
- // Try combination stereo and we will use built-in mono-stereo mixer then
- if (params.channelCount == 1)
- {
- WAVEFORMATEXTENSIBLE stereo = { 0 };
-
- PaStreamParameters stereo_params = params;
- stereo_params.channelCount = 2;
-
- MakeWaveFormatFromParams(&stereo, &stereo_params, sampleRate);
-
- hr = IAudioClient_IsFormatSupported(myClient, shareMode, &stereo.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
- if (hr == S_OK)
- {
- memcpy(outWavex, &stereo, sizeof(WAVEFORMATEXTENSIBLE));
- CoTaskMemFree(sharedClosestMatch);
- return (answer = paFormatIsSupported);
- }
-
- // Try selecting suitable sample type
- for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)
- {
- WAVEFORMATEXTENSIBLE sample = { 0 };
-
- PaStreamParameters sample_params = stereo_params;
- sample_params.sampleFormat = BestToWorst[i];
-
- MakeWaveFormatFromParams(&sample, &sample_params, sampleRate);
-
- hr = IAudioClient_IsFormatSupported(myClient, shareMode, &sample.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
- if (hr == S_OK)
- {
- memcpy(outWavex, &sample, sizeof(WAVEFORMATEXTENSIBLE));
- CoTaskMemFree(sharedClosestMatch);
- return (answer = paFormatIsSupported);
- }
- }
- }
-
- // Try selecting suitable sample type
- for (i = 0; i < STATIC_ARRAY_SIZE(BestToWorst); ++i)
- {
- WAVEFORMATEXTENSIBLE spfmt = { 0 };
-
- PaStreamParameters spfmt_params = params;
- spfmt_params.sampleFormat = BestToWorst[i];
-
- MakeWaveFormatFromParams(&spfmt, &spfmt_params, sampleRate);
-
- hr = IAudioClient_IsFormatSupported(myClient, shareMode, &spfmt.Format, (shareMode == AUDCLNT_SHAREMODE_SHARED ? &sharedClosestMatch : NULL));
- if (hr == S_OK)
- {
- memcpy(outWavex, &spfmt, sizeof(WAVEFORMATEXTENSIBLE));
- CoTaskMemFree(sharedClosestMatch);
- answer = paFormatIsSupported;
- break;
- }
- }
-
- // Nothing helped
- LogHostError(hr);
- }
-
- return answer;
+ // Log failure
+ LogHostError(hr);
+ }
+ else
+ {
+ // Exclusive mode and requested strict format, WASAPI did not accept this sample format
+ LogHostError(hr);
+ }
+
+ return paInvalidSampleRate;
}
// ------------------------------------------------------------------------------------------
@@ -2365,17 +3041,18 @@ static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *outputParameters,
double sampleRate)
{
- if (hostApi == NULL)
- return paHostApiNotFound;
- if ((UINT32)sampleRate == 0)
- return paInvalidSampleRate;
+ if (hostApi == NULL)
+ return paHostApiNotFound;
+ if ((UINT32)sampleRate == 0)
+ return paInvalidSampleRate;
- if (inputParameters != NULL)
+ if (inputParameters != NULL)
{
/* all standard sample formats are supported by the buffer adapter,
this implementation doesn't support any custom sample formats */
- if (inputParameters->sampleFormat & paCustomFormat)
- return paSampleFormatNotSupported;
+ // Note: paCustomFormat is now 8.24 (24-bits in 32-bit containers)
+ //if (inputParameters->sampleFormat & paCustomFormat)
+ // return paSampleFormatNotSupported;
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
@@ -2388,15 +3065,15 @@ static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
/* validate inputStreamInfo */
if (inputParameters->hostApiSpecificStreamInfo)
- {
- PaWasapiStreamInfo *inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
- if ((inputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
- (inputStreamInfo->version != 1) ||
+ {
+ PaWasapiStreamInfo *inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
+ if ((inputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
+ (inputStreamInfo->version != 1) ||
(inputStreamInfo->hostApiType != paWASAPI))
- {
- return paIncompatibleHostApiSpecificStreamInfo;
- }
- }
+ {
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ }
return paNoError;
}
@@ -2405,8 +3082,9 @@ static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
{
/* all standard sample formats are supported by the buffer adapter,
this implementation doesn't support any custom sample formats */
- if (outputParameters->sampleFormat & paCustomFormat)
- return paSampleFormatNotSupported;
+ // Note: paCustomFormat is now 8.24 (24-bits in 32-bit containers)
+ //if (outputParameters->sampleFormat & paCustomFormat)
+ // return paSampleFormatNotSupported;
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
@@ -2420,19 +3098,19 @@ static PaError IsStreamParamsValid(struct PaUtilHostApiRepresentation *hostApi,
/* validate outputStreamInfo */
if(outputParameters->hostApiSpecificStreamInfo)
{
- PaWasapiStreamInfo *outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
- if ((outputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
- (outputStreamInfo->version != 1) ||
+ PaWasapiStreamInfo *outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
+ if ((outputStreamInfo->size != sizeof(PaWasapiStreamInfo)) ||
+ (outputStreamInfo->version != 1) ||
(outputStreamInfo->hostApiType != paWASAPI))
- {
- return paIncompatibleHostApiSpecificStreamInfo;
- }
+ {
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
}
- return paNoError;
+ return paNoError;
}
- return (inputParameters || outputParameters ? paNoError : paInternalError);
+ return (inputParameters || outputParameters ? paNoError : paInternalError);
}
// ------------------------------------------------------------------------------------------
@@ -2441,509 +3119,510 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *outputParameters,
double sampleRate )
{
- IAudioClient *tmpClient = NULL;
- PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
- PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
+ IAudioClient *tmpClient = NULL;
+ PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
+ PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
- // Validate PaStreamParameters
- PaError error;
- if ((error = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
- return error;
+ // Validate PaStreamParameters
+ PaError error;
+ if ((error = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
+ return error;
if (inputParameters != NULL)
{
- WAVEFORMATEXTENSIBLE wavex;
- HRESULT hr;
- PaError answer;
- AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
- inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
+ WAVEFORMATEXTENSIBLE_UNION wavex;
+ HRESULT hr;
+ PaError answer;
+ AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
+ inputStreamInfo = (PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo;
- if (inputStreamInfo && (inputStreamInfo->flags & paWinWasapiExclusive))
- shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
+ if (inputStreamInfo && (inputStreamInfo->flags & paWinWasapiExclusive))
+ shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
- hr = ActivateAudioInterface(&paWasapi->devInfo[inputParameters->device], &tmpClient);
- if (hr != S_OK)
- {
- LogHostError(hr);
- return paInvalidDevice;
- }
+ hr = ActivateAudioInterface(&paWasapi->devInfo[inputParameters->device], inputStreamInfo, &tmpClient);
+ if (hr != S_OK)
+ {
+ LogHostError(hr);
+ return paInvalidDevice;
+ }
- answer = GetClosestFormat(tmpClient, sampleRate, inputParameters, shareMode, &wavex, FALSE);
- SAFE_RELEASE(tmpClient);
+ answer = GetClosestFormat(tmpClient, sampleRate, inputParameters, shareMode, &wavex, FALSE);
+ SAFE_RELEASE(tmpClient);
- if (answer != paFormatIsSupported)
- return answer;
+ if (answer != paFormatIsSupported)
+ return answer;
}
if (outputParameters != NULL)
{
- HRESULT hr;
- WAVEFORMATEXTENSIBLE wavex;
- PaError answer;
- AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
+ HRESULT hr;
+ WAVEFORMATEXTENSIBLE_UNION wavex;
+ PaError answer;
+ AUDCLNT_SHAREMODE shareMode = AUDCLNT_SHAREMODE_SHARED;
outputStreamInfo = (PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo;
- if (outputStreamInfo && (outputStreamInfo->flags & paWinWasapiExclusive))
- shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
+ if (outputStreamInfo && (outputStreamInfo->flags & paWinWasapiExclusive))
+ shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
- hr = ActivateAudioInterface(&paWasapi->devInfo[outputParameters->device], &tmpClient);
- if (hr != S_OK)
- {
- LogHostError(hr);
- return paInvalidDevice;
- }
+ hr = ActivateAudioInterface(&paWasapi->devInfo[outputParameters->device], outputStreamInfo, &tmpClient);
+ if (hr != S_OK)
+ {
+ LogHostError(hr);
+ return paInvalidDevice;
+ }
- answer = GetClosestFormat(tmpClient, sampleRate, outputParameters, shareMode, &wavex, TRUE);
- SAFE_RELEASE(tmpClient);
+ answer = GetClosestFormat(tmpClient, sampleRate, outputParameters, shareMode, &wavex, TRUE);
+ SAFE_RELEASE(tmpClient);
- if (answer != paFormatIsSupported)
- return answer;
+ if (answer != paFormatIsSupported)
+ return answer;
}
return paFormatIsSupported;
}
// ------------------------------------------------------------------------------------------
-static PaUint32 PaUtil_GetFramesPerHostBuffer(PaUint32 userFramesPerBuffer, PaTime suggestedLatency, double sampleRate, PaUint32 TimerJitterMs)
+static UINT32 _CalculateFramesPerHostBuffer(UINT32 userFramesPerBuffer, PaTime suggestedLatency, double sampleRate)
{
- PaUint32 frames = userFramesPerBuffer + max( userFramesPerBuffer, (PaUint32)(suggestedLatency * sampleRate) );
- frames += (PaUint32)((sampleRate * 0.001) * TimerJitterMs);
- return frames;
+ // Note: WASAPI tends to enforce double buffering by itself therefore enforcing double buffering
+ // on PA side looks as an excessive measure.
+ const BOOL doubleBuffering = TRUE;
+
+ return userFramesPerBuffer + max((doubleBuffering ? userFramesPerBuffer : 0),
+ (UINT32)(suggestedLatency * sampleRate));
}
// ------------------------------------------------------------------------------------------
-static void _RecalculateBuffersCount(PaWasapiSubStream *sub, UINT32 userFramesPerBuffer, UINT32 framesPerLatency, BOOL fullDuplex)
+static void _RecalculateBuffersCount(PaWasapiSubStream *sub, UINT32 userFramesPerBuffer, UINT32 framesPerLatency,
+ BOOL fullDuplex, BOOL output)
{
- // Count buffers (must be at least 1)
- sub->buffers = (userFramesPerBuffer ? framesPerLatency / userFramesPerBuffer : 0);
- if (sub->buffers == 0)
- sub->buffers = 1;
-
- // Determine amount of buffers used:
- // - Full-duplex mode will lead to period difference, thus only 1.
- // - Input mode, only 1, as WASAPI allows extraction of only 1 packet.
- // - For Shared mode we use double buffering.
- if ((sub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) || fullDuplex)
- {
- // Exclusive mode does not allow >1 buffers be used for Event interface, e.g. GetBuffer
- // call must acquire max buffer size and it all must be processed.
- if (sub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)
- sub->userBufferAndHostMatch = 1;
-
- // Use paUtilBoundedHostBufferSize because exclusive mode will starve and produce
- // bad quality of audio
- sub->buffers = 1;
- }
+ // Count buffers (must be at least 1)
+ sub->buffers = (userFramesPerBuffer != 0 ? framesPerLatency / userFramesPerBuffer : 1);
+ if (sub->buffers == 0)
+ sub->buffers = 1;
+
+ // Determine number of buffers used:
+ // - Full-duplex mode will lead to period difference, thus only 1
+ // - Input mode, only 1, as WASAPI allows extraction of only 1 packet
+ // - For Shared mode we use double buffering (see _CalculateFramesPerHostBuffer)
+ if ((sub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) || fullDuplex)
+ {
+ BOOL eventMode = ((sub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == AUDCLNT_STREAMFLAGS_EVENTCALLBACK);
+
+ // Exclusive mode does not allow >1 buffers be used for Event interface, e.g. GetBuffer
+ // call must acquire max buffer size and it all must be processed.
+ if (eventMode)
+ sub->userBufferAndHostMatch = TRUE;
+
+ // Full-duplex or Event mode: prefer paUtilBoundedHostBufferSize because exclusive mode will starve
+ // and produce glitchy audio
+ // Output Polling mode: prefer paUtilFixedHostBufferSize (buffers != 1) for polling mode is it allows
+ // to consume user data by fixed size data chunks and thus lowers memory movement (less CPU usage)
+ if (fullDuplex || eventMode || !output)
+ sub->buffers = 1;
+ }
}
// ------------------------------------------------------------------------------------------
-static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerLatency,
- ALIGN_FUNC pAlignFunc)
+static void _CalculateAlignedPeriod(PaWasapiSubStream *pSub, UINT32 *nFramesPerLatency, ALIGN_FUNC pAlignFunc)
{
- // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.
- // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will
- // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary
- // for Exclusive mode only! when audio data is feeded directly to hardware.
- if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
- {
- (*nFramesPerLatency) = AlignFramesPerBuffer((*nFramesPerLatency),
- pSub->wavex.Format.nSamplesPerSec, pSub->wavex.Format.nBlockAlign, pAlignFunc);
- }
+ // Align frames to HD Audio packet size of 128 bytes for Exclusive mode only.
+ // Not aligning on Windows Vista will cause Event timeout, although Windows 7 will
+ // return AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED error to realign buffer. Aligning is necessary
+ // for Exclusive mode only! when audio data is fed directly to hardware.
+ if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
+ {
+ (*nFramesPerLatency) = AlignFramesPerBuffer((*nFramesPerLatency),
+ pSub->wavexu.ext.Format.nBlockAlign, pAlignFunc);
+ }
- // Calculate period
- pSub->period = MakeHnsPeriod((*nFramesPerLatency), pSub->wavex.Format.nSamplesPerSec);
+ // Calculate period
+ pSub->period = MakeHnsPeriod((*nFramesPerLatency), pSub->wavexu.ext.Format.nSamplesPerSec);
}
// ------------------------------------------------------------------------------------------
-static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSub, BOOL output, PaError *pa_error)
+static void _CalculatePeriodicity(PaWasapiSubStream *pSub, BOOL output, REFERENCE_TIME *periodicity)
{
- PaError error;
- HRESULT hr;
+ // Note: according to Microsoft docs for IAudioClient::Initialize we can set periodicity of the buffer
+ // only for Exclusive mode. By setting periodicity almost equal to the user buffer frames we can
+ // achieve high quality (less glitchy) low-latency audio.
+ if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
+ {
+ const PaWasapiDeviceInfo *pInfo = pSub->params.device_info;
+
+ // By default periodicity equals to the full buffer (legacy PA WASAPI's behavior)
+ (*periodicity) = pSub->period;
+
+ // Try make buffer ready for I/O once we request the buffer readiness for it. Only Polling mode
+ // because for Event mode buffer size and periodicity must be equal according to Microsoft
+ // documentation for IAudioClient::Initialize.
+ //
+ // TO-DO: try spread to capture and full-duplex cases (not tested and therefore disabled)
+ //
+ if (((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0) &&
+ (output && !pSub->params.full_duplex))
+ {
+ UINT32 alignedFrames;
+ REFERENCE_TIME userPeriodicity;
- const PaWasapiDeviceInfo *pInfo = pSub->params.device_info;
- const PaStreamParameters *params = &pSub->params.stream_params;
- UINT32 framesPerLatency = pSub->params.frames_per_buffer;
- double sampleRate = pSub->params.sample_rate;
- //BOOL blocking = pSub->params.blocking;
- BOOL fullDuplex = pSub->params.full_duplex;
+ // Align frames backwards, so device will likely make buffer read ready when we are ready
+ // to read it (our scheduling will wait for amount of millisoconds of frames_per_buffer)
+ alignedFrames = AlignFramesPerBuffer(pSub->params.frames_per_buffer,
+ pSub->wavexu.ext.Format.nBlockAlign, ALIGN_BWD);
- const UINT32 userFramesPerBuffer = framesPerLatency;
- IAudioClient *audioClient = NULL;
+ userPeriodicity = MakeHnsPeriod(alignedFrames, pSub->wavexu.ext.Format.nSamplesPerSec);
- // Assume default failure due to some reason
- (*pa_error) = paInvalidDevice;
+ // Must not be larger than buffer size
+ if (userPeriodicity > pSub->period)
+ userPeriodicity = pSub->period;
+
+ // Must not be smaller than minimum supported by the device
+ if (userPeriodicity < pInfo->MinimumDevicePeriod)
+ userPeriodicity = pInfo->MinimumDevicePeriod;
+
+ (*periodicity) = userPeriodicity;
+ }
+ }
+ else
+ (*periodicity) = 0;
+}
- // Validate parameters
+// ------------------------------------------------------------------------------------------
+static HRESULT CreateAudioClient(PaWasapiStream *pStream, PaWasapiSubStream *pSub, BOOL output, PaError *pa_error)
+{
+ PaError error;
+ HRESULT hr;
+ const PaWasapiDeviceInfo *pInfo = pSub->params.device_info;
+ const PaStreamParameters *params = &pSub->params.stream_params;
+ const double sampleRate = pSub->params.sample_rate;
+ const BOOL fullDuplex = pSub->params.full_duplex;
+ const UINT32 userFramesPerBuffer = pSub->params.frames_per_buffer;
+ UINT32 framesPerLatency = userFramesPerBuffer;
+ IAudioClient *audioClient = NULL;
+ REFERENCE_TIME eventPeriodicity = 0;
+
+ // Assume default failure due to some reason
+ (*pa_error) = paInvalidDevice;
+
+ // Validate parameters
if (!pSub || !pInfo || !params)
- {
- (*pa_error) = paBadStreamPtr;
+ {
+ (*pa_error) = paBadStreamPtr;
return E_POINTER;
- }
- if ((UINT32)sampleRate == 0)
- {
- (*pa_error) = paInvalidSampleRate;
+ }
+ if ((UINT32)sampleRate == 0)
+ {
+ (*pa_error) = paInvalidSampleRate;
return E_INVALIDARG;
- }
+ }
// Get the audio client
- hr = ActivateAudioInterface(pInfo, &audioClient);
- if (hr != S_OK)
- {
- (*pa_error) = paInsufficientMemory;
- LogHostError(hr);
- goto done;
- }
-
- // Get closest format
- if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)
- {
- (*pa_error) = error;
- LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
- goto done; // fail, format not supported
- }
-
- // Check for Mono <<>> Stereo workaround
- if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))
- {
- /*if (blocking)
- {
- LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
- goto done; // fail, blocking mode not supported
- }*/
-
- // select mixer
- pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
- if (pSub->monoMixer == NULL)
- {
- (*pa_error) = paInvalidChannelCount;
- LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
- goto done; // fail, no mixer for format
- }
- }
-
-#if 0
- // Add suggestd latency
- framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);
-#else
- // Calculate host buffer size
- if ((pSub->shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE) &&
- (!pSub->streamFlags || ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
- {
- framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer,
- params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,
- (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);
- }
- else
- {
- REFERENCE_TIME overall;
-
- // Work 1:1 with user buffer (only polling allows to use >1)
- framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavex.Format.nSamplesPerSec);
-
- // Use Polling if overall latency is > 5ms as it allows to use 100% CPU in a callback,
- // or user specified latency parameter
- overall = MakeHnsPeriod(framesPerLatency, pSub->wavex.Format.nSamplesPerSec);
- if ((overall >= (106667*2)/*21.33ms*/) || ((INT32)(params->suggestedLatency*100000.0) != 0/*0.01 msec granularity*/))
- {
- framesPerLatency = PaUtil_GetFramesPerHostBuffer(userFramesPerBuffer,
- params->suggestedLatency, pSub->wavex.Format.nSamplesPerSec, 0/*,
- (streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? 0 : 1)*/);
-
- // Use Polling interface
- pSub->streamFlags &= ~AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
- PRINT(("WASAPI: CreateAudioClient: forcing POLL mode\n"));
- }
- }
-#endif
+ if (FAILED(hr = ActivateAudioInterface(pInfo, &pSub->params.wasapi_params, &audioClient)))
+ {
+ (*pa_error) = paInsufficientMemory;
+ LogHostError(hr);
+ goto done;
+ }
+
+ // Get closest format
+ if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavexu, output)) != paFormatIsSupported)
+ {
+ (*pa_error) = error;
+ LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
+ goto done; // fail, format not supported
+ }
+
+ // Check for Mono <<>> Stereo workaround
+ if ((params->channelCount == 1) && (pSub->wavexu.ext.Format.nChannels == 2))
+ {
+ // select mixer
+ pSub->monoMixer = GetMonoToStereoMixer(&pSub->wavexu.ext, (output ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
+ if (pSub->monoMixer == NULL)
+ {
+ (*pa_error) = paInvalidChannelCount;
+ LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
+ goto done; // fail, no mixer for format
+ }
+ }
+
+ // Calculate host buffer size
+ if ((pSub->shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE) &&
+ (!pSub->streamFlags || ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
+ {
+ framesPerLatency = _CalculateFramesPerHostBuffer(userFramesPerBuffer, params->suggestedLatency,
+ pSub->wavexu.ext.Format.nSamplesPerSec);
+ }
+ else
+ {
+ #ifdef PA_WASAPI_FORCE_POLL_IF_LARGE_BUFFER
+ REFERENCE_TIME overall;
+ #endif
+
+ // Work 1:1 with user buffer (only polling allows to use >1)
+ framesPerLatency += MakeFramesFromHns(SecondsTonano100(params->suggestedLatency), pSub->wavexu.ext.Format.nSamplesPerSec);
- // For full-duplex output resize buffer to be the same as for input
- if (output && fullDuplex)
- framesPerLatency = pStream->in.framesPerHostCallback;
-
- // Avoid 0 frames
- if (framesPerLatency == 0)
- framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavex.Format.nSamplesPerSec);
-
- // Exclusive Input stream renders data in 6 packets, we must set then the size of
- // single packet, total buffer size, e.g. required latency will be PacketSize * 6
- if (!output && (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE))
- {
- // Do it only for Polling mode
- if ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)
- {
- framesPerLatency /= WASAPI_PACKETS_PER_INPUT_BUFFER;
- }
- }
-
- // Calculate aligned period
- _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
-
- /*! Enforce min/max period for device in Shared mode to avoid bad audio quality.
+ // Force Polling if overall latency is >= 21.33ms as it allows to use 100% CPU in a callback,
+ // or user specified latency parameter.
+ #ifdef PA_WASAPI_FORCE_POLL_IF_LARGE_BUFFER
+ overall = MakeHnsPeriod(framesPerLatency, pSub->wavexu.ext.Format.nSamplesPerSec);
+ if (overall >= (106667 * 2)/*21.33ms*/)
+ {
+ framesPerLatency = _CalculateFramesPerHostBuffer(userFramesPerBuffer, params->suggestedLatency,
+ pSub->wavexu.Format.nSamplesPerSec);
+
+ // Use Polling interface
+ pSub->streamFlags &= ~AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
+ PRINT(("WASAPI: CreateAudioClient: forcing POLL mode\n"));
+ }
+ #endif
+ }
+
+ // For full-duplex output resize buffer to be the same as for input
+ if (output && fullDuplex)
+ framesPerLatency = pStream->in.framesPerHostCallback;
+
+ // Avoid 0 frames
+ if (framesPerLatency == 0)
+ framesPerLatency = MakeFramesFromHns(pInfo->DefaultDevicePeriod, pSub->wavexu.ext.Format.nSamplesPerSec);
+
+ // Exclusive Input stream renders data in 6 packets, we must set then the size of
+ // single packet, total buffer size, e.g. required latency will be PacketSize * 6
+ if (!output && (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE))
+ {
+ // Do it only for Polling mode
+ if ((pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)
+ framesPerLatency /= WASAPI_PACKETS_PER_INPUT_BUFFER;
+ }
+
+ // Calculate aligned period
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
+
+ /*! Enforce min/max period for device in Shared mode to avoid bad audio quality.
Avoid doing so for Exclusive mode as alignment will suffer.
- */
- if (pSub->shareMode == AUDCLNT_SHAREMODE_SHARED)
- {
- if (pSub->period < pInfo->DefaultDevicePeriod)
- {
- pSub->period = pInfo->DefaultDevicePeriod;
- // Recalculate aligned period
- framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
- _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
- }
- }
- else
- {
- if (pSub->period < pInfo->MinimumDevicePeriod)
- {
- pSub->period = pInfo->MinimumDevicePeriod;
- // Recalculate aligned period
- framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
- _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_FWD);
- }
- }
-
- /*! Windows 7 does not allow to set latency lower than minimal device period and will
- return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista we enforce the same behavior
- manually for unified behavior on all platforms.
- */
- {
- /*! AUDCLNT_E_BUFFER_SIZE_ERROR: Applies to Windows 7 and later.
- Indicates that the buffer duration value requested by an exclusive-mode client is
- out of range. The requested duration value for pull mode must not be greater than
- 500 milliseconds; for push mode the duration value must not be greater than 2 seconds.
- */
- if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
- {
- static const REFERENCE_TIME MAX_BUFFER_EVENT_DURATION = 500 * 10000;
- static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION = 2000 * 10000;
-
- if (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) // pull mode, max 500ms
- {
- if (pSub->period > MAX_BUFFER_EVENT_DURATION)
- {
- pSub->period = MAX_BUFFER_EVENT_DURATION;
- // Recalculate aligned period
- framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
- _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
- }
- }
- else // push mode, max 2000ms
- {
- if (pSub->period > MAX_BUFFER_POLL_DURATION)
- {
- pSub->period = MAX_BUFFER_POLL_DURATION;
- // Recalculate aligned period
- framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
- _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
- }
- }
- }
- }
-
- // Set Raw mode (applicable only to IAudioClient2)
-#ifdef __IAudioClient2_INTERFACE_DEFINED__
- if (GetAudioClientVersion() >= 2)
- {
- pa_AudioClientProperties audioProps = { 0 };
- audioProps.cbSize = sizeof(pa_AudioClientProperties);
- audioProps.bIsOffload = FALSE;
- audioProps.eCategory = (AUDIO_STREAM_CATEGORY)pSub->params.wasapi_params.streamCategory;
- switch (pSub->params.wasapi_params.streamOption)
- {
- case eStreamOptionRaw:
- if (GetWindowsVersion() >= WINDOWS_8_1_SERVER2012R2)
- audioProps.Options = pa_AUDCLNT_STREAMOPTIONS_RAW;
- break;
- case eStreamOptionMatchFormat:
- if (GetWindowsVersion() >= WINDOWS_10_SERVER2016)
- audioProps.Options = pa_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;
- break;
- }
-
- hr = IAudioClient2_SetClientProperties((IAudioClient2 *)audioClient, (AudioClientProperties *)&audioProps);
- if (hr != S_OK)
- {
- PRINT(("WASAPI: IAudioClient2_SetClientProperties(Category = %d, Options = %d) failed with error = %08X\n", audioProps.eCategory, audioProps.Options, (UINT32)hr));
- }
- else
- {
- PRINT(("WASAPI: IAudioClient2 set properties: IsOffload = %d, Category = %d, Options = %d\n", audioProps.bIsOffload, audioProps.eCategory, audioProps.Options));
- }
- }
-#endif
+ */
+ if (pSub->shareMode == AUDCLNT_SHAREMODE_SHARED)
+ {
+ if (pSub->period < pInfo->DefaultDevicePeriod)
+ {
+ pSub->period = pInfo->DefaultDevicePeriod;
+
+ // Recalculate aligned period
+ framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
+ }
+ }
+ else
+ {
+ if (pSub->period < pInfo->MinimumDevicePeriod)
+ {
+ pSub->period = pInfo->MinimumDevicePeriod;
+
+ // Recalculate aligned period
+ framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_FWD);
+ }
+ }
+
+ /*! Windows 7 does not allow to set latency lower than minimal device period and will
+ return error: AUDCLNT_E_INVALID_DEVICE_PERIOD. Under Vista we enforce the same behavior
+ manually for unified behavior on all platforms.
+ */
+ {
+ /*! AUDCLNT_E_BUFFER_SIZE_ERROR: Applies to Windows 7 and later.
+ Indicates that the buffer duration value requested by an exclusive-mode client is
+ out of range. The requested duration value for pull mode must not be greater than
+ 500 milliseconds; for push mode the duration value must not be greater than 2 seconds.
+ */
+ if (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE)
+ {
+ static const REFERENCE_TIME MAX_BUFFER_EVENT_DURATION = 500 * 10000;
+ static const REFERENCE_TIME MAX_BUFFER_POLL_DURATION = 2000 * 10000;
+
+ // Pull mode, max 500ms
+ if (pSub->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)
+ {
+ if (pSub->period > MAX_BUFFER_EVENT_DURATION)
+ {
+ pSub->period = MAX_BUFFER_EVENT_DURATION;
+
+ // Recalculate aligned period
+ framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
+ }
+ }
+ // Push mode, max 2000ms
+ else
+ {
+ if (pSub->period > MAX_BUFFER_POLL_DURATION)
+ {
+ pSub->period = MAX_BUFFER_POLL_DURATION;
- // Open the stream and associate it with an audio session
+ // Recalculate aligned period
+ framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
+ }
+ }
+ }
+ }
+
+ // Set device scheduling period (always 0 in Shared mode according to Microsoft docs)
+ _CalculatePeriodicity(pSub, output, &eventPeriodicity);
+
+ // Open the stream and associate it with an audio session
hr = IAudioClient_Initialize(audioClient,
pSub->shareMode,
pSub->streamFlags,
- pSub->period,
- (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
- &pSub->wavex.Format,
+ pSub->period,
+ eventPeriodicity,
+ &pSub->wavexu.ext.Format,
NULL);
- /*! WASAPI is tricky on large device buffer, sometimes 2000ms can be allocated sometimes
- less. There is no known guaranteed level thus we make subsequent tries by decreasing
- buffer by 100ms per try.
- */
- while ((hr == E_OUTOFMEMORY) && (pSub->period > (100 * 10000)))
- {
- PRINT(("WASAPI: CreateAudioClient: decreasing buffer size to %d milliseconds\n", (pSub->period / 10000)));
+ // [Output only] Check if buffer size is the one we requested in Exclusive mode, for UAC1 USB DACs WASAPI
+ // can allocate internal buffer equal to 8 times of pSub->period that has to be corrected in order to match
+ // the requested latency
+ if (output && SUCCEEDED(hr) && (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE))
+ {
+ UINT32 maxBufferFrames;
- // Decrease by 100ms and try again
- pSub->period -= (100 * 10000);
+ if (FAILED(hr = IAudioClient_GetBufferSize(audioClient, &maxBufferFrames)))
+ {
+ (*pa_error) = paInvalidDevice;
+ LogHostError(hr);
+ goto done;
+ }
- // Recalculate aligned period
- framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec);
- _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
+ // For Exclusive mode for UAC1 devices maxBufferFrames may be framesPerLatency * 8 but check any difference
+ // to be able to guarantee the latency user requested and also resulted framesPerLatency may be bigger than
+ // 2 seconds that will cause audio client not operational (GetCurrentPadding() will return always 0)
+ if (maxBufferFrames >= (framesPerLatency * 2))
+ {
+ UINT32 ratio = maxBufferFrames / framesPerLatency;
- // Release the previous allocations
- SAFE_RELEASE(audioClient);
+ PRINT(("WASAPI: CreateAudioClient: detected %d times larger buffer than requested, correct to match user latency\n", ratio));
- // Create a new audio client
- hr = ActivateAudioInterface(pInfo, &audioClient);
- if (hr != S_OK)
- {
- (*pa_error) = paInsufficientMemory;
- LogHostError(hr);
- goto done;
- }
-
- // Open the stream and associate it with an audio session
- hr = IAudioClient_Initialize(audioClient,
- pSub->shareMode,
- pSub->streamFlags,
- pSub->period,
- (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
- &pSub->wavex.Format,
- NULL);
- }
-
- /*! WASAPI buffer size failure. Fallback to using default size.
- */
- if (hr == AUDCLNT_E_BUFFER_SIZE_ERROR)
- {
- // Use default
- pSub->period = pInfo->DefaultDevicePeriod;
-
- PRINT(("WASAPI: CreateAudioClient: correcting buffer size to device default\n"));
+ // Get new aligned frames lowered by calculated ratio
+ framesPerLatency = MakeFramesFromHns(pSub->period / ratio, pSub->wavexu.ext.Format.nSamplesPerSec);
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
+
+ // Make sure we are not below the minimum period
+ if (pSub->period < pInfo->MinimumDevicePeriod)
+ pSub->period = pInfo->MinimumDevicePeriod;
+
+ // Release previous client
+ SAFE_RELEASE(audioClient);
+
+ // Create a new audio client
+ if (FAILED(hr = ActivateAudioInterface(pInfo, &pSub->params.wasapi_params, &audioClient)))
+ {
+ (*pa_error) = paInsufficientMemory;
+ LogHostError(hr);
+ goto done;
+ }
+
+ // Set device scheduling period (always 0 in Shared mode according to Microsoft docs)
+ _CalculatePeriodicity(pSub, output, &eventPeriodicity);
+
+ // Open the stream and associate it with an audio session
+ hr = IAudioClient_Initialize(audioClient,
+ pSub->shareMode,
+ pSub->streamFlags,
+ pSub->period,
+ eventPeriodicity,
+ &pSub->wavexu.ext.Format,
+ NULL);
+ }
+ }
+
+ /*! WASAPI is tricky on large device buffer, sometimes 2000ms can be allocated sometimes
+ less. There is no known guaranteed level thus we make subsequent tries by decreasing
+ buffer by 100ms per try.
+ */
+ while ((hr == E_OUTOFMEMORY) && (pSub->period > (100 * 10000)))
+ {
+ PRINT(("WASAPI: CreateAudioClient: decreasing buffer size to %d milliseconds\n", (pSub->period / 10000)));
+
+ // Decrease by 100ms and try again
+ pSub->period -= (100 * 10000);
+
+ // Recalculate aligned period
+ framesPerLatency = MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec);
+ _CalculateAlignedPeriod(pSub, &framesPerLatency, ALIGN_BWD);
// Release the previous allocations
SAFE_RELEASE(audioClient);
// Create a new audio client
- hr = ActivateAudioInterface(pInfo, &audioClient);
- if (hr != S_OK)
- {
- (*pa_error) = paInsufficientMemory;
- LogHostError(hr);
- goto done;
- }
-
- // Open the stream and associate it with an audio session
- hr = IAudioClient_Initialize(audioClient,
- pSub->shareMode,
- pSub->streamFlags,
- pSub->period,
- (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
- &pSub->wavex.Format,
- NULL);
- }
-
- /*! If the requested buffer size is not aligned. Can be triggered by Windows 7 and up.
- Should not be be triggered ever as we do align buffers always with _CalculateAlignedPeriod.
- */
- if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
- {
- UINT32 frames = 0;
-
- // Get the next aligned frame
- hr = IAudioClient_GetBufferSize(audioClient, &frames);
- if (hr != S_OK)
- {
- (*pa_error) = paInvalidDevice;
- LogHostError(hr);
- goto done;
- }
-
- PRINT(("WASAPI: CreateAudioClient: aligning buffer size to % frames\n", frames));
+ if (FAILED(hr = ActivateAudioInterface(pInfo, &pSub->params.wasapi_params, &audioClient)))
+ {
+ (*pa_error) = paInsufficientMemory;
+ LogHostError(hr);
+ goto done;
+ }
+
+ // Set device scheduling period (always 0 in Shared mode according to Microsoft docs)
+ _CalculatePeriodicity(pSub, output, &eventPeriodicity);
+
+ // Open the stream and associate it with an audio session
+ hr = IAudioClient_Initialize(audioClient,
+ pSub->shareMode,
+ pSub->streamFlags,
+ pSub->period,
+ eventPeriodicity,
+ &pSub->wavexu.ext.Format,
+ NULL);
+ }
+
+ /*! WASAPI buffer size or alignment failure. Fallback to using default size and alignment.
+ */
+ if ((hr == AUDCLNT_E_BUFFER_SIZE_ERROR) || (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED))
+ {
+ // Use default
+ pSub->period = pInfo->DefaultDevicePeriod;
+
+ PRINT(("WASAPI: CreateAudioClient: correcting buffer size/alignment to device default\n"));
// Release the previous allocations
SAFE_RELEASE(audioClient);
// Create a new audio client
- hr = ActivateAudioInterface(pInfo, &audioClient);
- if (hr != S_OK)
- {
- (*pa_error) = paInsufficientMemory;
- LogHostError(hr);
- goto done;
- }
-
- // Get closest format
- if ((error = GetClosestFormat(audioClient, sampleRate, params, pSub->shareMode, &pSub->wavex, output)) != paFormatIsSupported)
- {
- (*pa_error) = error;
- LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT); // fail, format not supported
- goto done;
- }
-
- // Check for Mono >> Stereo workaround
- if ((params->channelCount == 1) && (pSub->wavex.Format.nChannels == 2))
- {
- /*if (blocking)
- {
- LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
- goto done; // fail, blocking mode not supported
- }*/
-
- // Select mixer
- pSub->monoMixer = _GetMonoToStereoMixer(WaveToPaFormat(&pSub->wavex), (pInfo->flow == eRender ? MIX_DIR__1TO2 : MIX_DIR__2TO1_L));
- if (pSub->monoMixer == NULL)
- {
- (*pa_error) = paInvalidChannelCount;
- LogHostError(hr = AUDCLNT_E_UNSUPPORTED_FORMAT);
- goto done; // fail, no mixer for format
- }
- }
-
- // Calculate period
- pSub->period = MakeHnsPeriod(frames, pSub->wavex.Format.nSamplesPerSec);
+ if (FAILED(hr = ActivateAudioInterface(pInfo, &pSub->params.wasapi_params, &audioClient)))
+ {
+ (*pa_error) = paInsufficientMemory;
+ LogHostError(hr);
+ goto done;
+ }
+
+ // Set device scheduling period (always 0 in Shared mode according to Microsoft docs)
+ _CalculatePeriodicity(pSub, output, &eventPeriodicity);
// Open the stream and associate it with an audio session
hr = IAudioClient_Initialize(audioClient,
pSub->shareMode,
pSub->streamFlags,
- pSub->period,
- (pSub->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? pSub->period : 0),
- &pSub->wavex.Format,
+ pSub->period,
+ eventPeriodicity,
+ &pSub->wavexu.ext.Format,
NULL);
- if (hr != S_OK)
- {
- (*pa_error) = paInvalidDevice;
- LogHostError(hr);
- goto done;
- }
}
- else
- if (hr != S_OK)
+
+ // Error has no workaround, fail completely
+ if (FAILED(hr))
{
- (*pa_error) = paInvalidDevice;
- LogHostError(hr);
- goto done;
+ (*pa_error) = paInvalidDevice;
+ LogHostError(hr);
+ goto done;
}
// Set client
- pSub->clientParent = audioClient;
+ pSub->clientParent = audioClient;
IAudioClient_AddRef(pSub->clientParent);
- // Recalculate buffers count
- _RecalculateBuffersCount(pSub,
- userFramesPerBuffer,
- MakeFramesFromHns(pSub->period, pSub->wavex.Format.nSamplesPerSec),
- fullDuplex);
+ // Recalculate buffers count
+ _RecalculateBuffersCount(pSub, userFramesPerBuffer, MakeFramesFromHns(pSub->period, pSub->wavexu.ext.Format.nSamplesPerSec),
+ fullDuplex, output);
- // No error, client is succesfully created
- (*pa_error) = paNoError;
+ // No error, client is successfully created
+ (*pa_error) = paNoError;
done:
@@ -2953,147 +3632,70 @@ done:
}
// ------------------------------------------------------------------------------------------
-static PaError ActivateAudioClientOutput(PaWasapiStream *stream)
+static PaError ActivateAudioClient(PaWasapiStream *stream, BOOL output)
{
- HRESULT hr;
- PaError result;
-
- UINT32 maxBufferSize = 0;
- PaTime buffer_latency = 0;
- UINT32 framesPerBuffer = stream->out.params.frames_per_buffer;
+ HRESULT hr;
+ PaError result;
+ UINT32 maxBufferSize;
+ PaTime bufferLatency;
+ PaWasapiSubStream *subStream = (output ? &stream->out : &stream->in);
- // Create Audio client
- hr = CreateAudioClient(stream, &stream->out, TRUE, &result);
- if (hr != S_OK)
- {
- LogPaError(result);
- goto error;
+ // Create Audio client
+ if (FAILED(hr = CreateAudioClient(stream, subStream, output, &result)))
+ {
+ LogPaError(result);
+ goto error;
}
- LogWAVEFORMATEXTENSIBLE(&stream->out.wavex);
-
- // Activate volume
- stream->outVol = NULL;
- /*hr = info->device->Activate(
- __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
- (void**)&stream->outVol);
- if (hr != S_OK)
- return paInvalidDevice;*/
+ LogWAVEFORMATEXTENSIBLE(&subStream->wavexu.ext);
// Get max possible buffer size to check if it is not less than that we request
- hr = IAudioClient_GetBufferSize(stream->out.clientParent, &maxBufferSize);
- if (hr != S_OK)
- {
- LogHostError(hr);
- LogPaError(result = paInvalidDevice);
- goto error;
- }
-
- // Correct buffer to max size if it maxed out result of GetBufferSize
- stream->out.bufferSize = maxBufferSize;
-
- // Get interface latency (actually uneeded as we calculate latency from the size
- // of maxBufferSize).
- hr = IAudioClient_GetStreamLatency(stream->out.clientParent, &stream->out.deviceLatency);
- if (hr != S_OK)
- {
- LogHostError(hr);
- LogPaError(result = paInvalidDevice);
- goto error;
- }
- //stream->out.latencySeconds = nano100ToSeconds(stream->out.deviceLatency);
-
- // Number of frames that are required at each period
- stream->out.framesPerHostCallback = maxBufferSize;
-
- // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
- stream->out.framesPerBuffer =
- (stream->out.userBufferAndHostMatch ? stream->out.framesPerHostCallback : framesPerBuffer);
-
- // Calculate buffer latency
- buffer_latency = (PaTime)maxBufferSize / stream->out.wavex.Format.nSamplesPerSec;
-
- // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
- stream->out.latencySeconds = buffer_latency;
-
- PRINT(("WASAPI::OpenStream(output): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->out.framesPerHostCallback, (float)(stream->out.latencySeconds*1000.0f), (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->out.params.wow64_workaround ? "YES" : "NO"), (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
-
- return paNoError;
-
-error:
-
- return result;
-}
-
-// ------------------------------------------------------------------------------------------
-static PaError ActivateAudioClientInput(PaWasapiStream *stream)
-{
- HRESULT hr;
- PaError result;
-
- UINT32 maxBufferSize = 0;
- PaTime buffer_latency = 0;
- UINT32 framesPerBuffer = stream->in.params.frames_per_buffer;
-
- // Create Audio client
- hr = CreateAudioClient(stream, &stream->in, FALSE, &result);
- if (hr != S_OK)
- {
- LogPaError(result);
- goto error;
+ if (FAILED(hr = IAudioClient_GetBufferSize(subStream->clientParent, &maxBufferSize)))
+ {
+ LogHostError(hr);
+ LogPaError(result = paInvalidDevice);
+ goto error;
}
- LogWAVEFORMATEXTENSIBLE(&stream->in.wavex);
-
- // Create volume mgr
- stream->inVol = NULL;
- /*hr = info->device->Activate(
- __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL,
- (void**)&stream->inVol);
- if (hr != S_OK)
- return paInvalidDevice;*/
-
- // Get max possible buffer size to check if it is not less than that we request
- hr = IAudioClient_GetBufferSize(stream->in.clientParent, &maxBufferSize);
- if (hr != S_OK)
- {
- LogHostError(hr);
- LogPaError(result = paInvalidDevice);
- goto error;
- }
// Correct buffer to max size if it maxed out result of GetBufferSize
- stream->in.bufferSize = maxBufferSize;
+ subStream->bufferSize = maxBufferSize;
- // Get interface latency (actually uneeded as we calculate latency from the size
- // of maxBufferSize).
- hr = IAudioClient_GetStreamLatency(stream->in.clientParent, &stream->in.deviceLatency);
- if (hr != S_OK)
- {
- LogHostError(hr);
- LogPaError(result = paInvalidDevice);
- goto error;
- }
- //stream->in.latencySeconds = nano100ToSeconds(stream->in.deviceLatency);
+ if (!output)
+ {
+ // Get interface latency (actually unneeded as we calculate latency from the size of maxBufferSize)
+ if (FAILED(hr = IAudioClient_GetStreamLatency(stream->in.clientParent, &stream->in.deviceLatency)))
+ {
+ LogHostError(hr);
+ LogPaError(result = paInvalidDevice);
+ goto error;
+ }
+ //stream->in.latencySeconds = nano100ToSeconds(stream->in.deviceLatency);
+ }
// Number of frames that are required at each period
- stream->in.framesPerHostCallback = maxBufferSize;
+ subStream->framesPerHostCallback = maxBufferSize;
- // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
- stream->in.framesPerBuffer =
- (stream->in.userBufferAndHostMatch ? stream->in.framesPerHostCallback : framesPerBuffer);
+ // Calculate frames per single buffer, if buffers > 1 then always framesPerBuffer
+ subStream->framesPerBuffer = (subStream->userBufferAndHostMatch ?
+ subStream->framesPerHostCallback : subStream->params.frames_per_buffer);
- // Calculate buffer latency
- buffer_latency = (PaTime)maxBufferSize / stream->in.wavex.Format.nSamplesPerSec;
+ // Calculate buffer latency
+ bufferLatency = (PaTime)maxBufferSize / subStream->wavexu.ext.Format.nSamplesPerSec;
- // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
- stream->in.latencySeconds = buffer_latency;
+ // Append buffer latency to interface latency in shared mode (see GetStreamLatency notes)
+ subStream->latencySeconds = bufferLatency;
- PRINT(("WASAPI::OpenStream(input): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n", (UINT32)framesPerBuffer, (UINT32)stream->in.framesPerHostCallback, (float)(stream->in.latencySeconds*1000.0f), (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"), (stream->in.params.wow64_workaround ? "YES" : "NO"), (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
+ PRINT(("WASAPI::OpenStream(%s): framesPerUser[ %d ] framesPerHost[ %d ] latency[ %.02fms ] exclusive[ %s ] wow64_fix[ %s ] mode[ %s ]\n",
+ (output ? "output" : "input"),
+ subStream->params.frames_per_buffer, subStream->framesPerHostCallback, (subStream->latencySeconds * 1000),
+ (subStream->shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? "YES" : "NO"),
+ (subStream->params.wow64_workaround ? "YES" : "NO"),
+ (subStream->streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK ? "EVENT" : "POLL")));
- return paNoError;
+ return paNoError;
error:
- return result;
+ return result;
}
// ------------------------------------------------------------------------------------------
@@ -3108,131 +3710,149 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
void *userData )
{
PaError result = paNoError;
- HRESULT hr;
+ HRESULT hr;
PaWasapiHostApiRepresentation *paWasapi = (PaWasapiHostApiRepresentation*)hostApi;
PaWasapiStream *stream = NULL;
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
- PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
- PaWasapiDeviceInfo *info = NULL;
- ULONG framesPerHostCallback;
- PaUtilHostBufferSizeMode bufferMode;
- const BOOL fullDuplex = ((inputParameters != NULL) && (outputParameters != NULL));
+ PaWasapiStreamInfo *inputStreamInfo = NULL, *outputStreamInfo = NULL;
+ PaWasapiDeviceInfo *info = NULL;
+ ULONG framesPerHostCallback;
+ PaUtilHostBufferSizeMode bufferMode;
+ const BOOL fullDuplex = ((inputParameters != NULL) && (outputParameters != NULL));
+ BOOL useInputBufferProcessor = (inputParameters != NULL), useOutputBufferProcessor = (outputParameters != NULL);
- // validate PaStreamParameters
- if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
- return LogPaError(result);
+ // validate PaStreamParameters
+ if ((result = IsStreamParamsValid(hostApi, inputParameters, outputParameters, sampleRate)) != paNoError)
+ return LogPaError(result);
// Validate platform specific flags
if ((streamFlags & paPlatformSpecificFlags) != 0)
- {
- LogPaError(result = paInvalidFlag); /* unexpected platform specific flag */
- goto error;
- }
-
- // Allocate memory for PaWasapiStream
- if ((stream = (PaWasapiStream *)PaUtil_AllocateMemory(sizeof(PaWasapiStream))) == NULL)
- {
+ {
+ LogPaError(result = paInvalidFlag); /* unexpected platform specific flag */
+ goto error;
+ }
+
+ // Allocate memory for PaWasapiStream
+ if ((stream = (PaWasapiStream *)PaUtil_AllocateZeroInitializedMemory(sizeof(PaWasapiStream))) == NULL)
+ {
LogPaError(result = paInsufficientMemory);
goto error;
}
- // Default thread priority is Audio: for exclusive mode we will use Pro Audio.
- stream->nThreadPriority = eThreadPriorityAudio;
-
- // Set default number of frames: paFramesPerBufferUnspecified
- if (framesPerBuffer == paFramesPerBufferUnspecified)
- {
- UINT32 framesPerBufferIn = 0, framesPerBufferOut = 0;
- if (inputParameters != NULL)
- {
- info = &paWasapi->devInfo[inputParameters->device];
- framesPerBufferIn = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
- }
- if (outputParameters != NULL)
- {
- info = &paWasapi->devInfo[outputParameters->device];
- framesPerBufferOut = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
- }
- // choosing maximum default size
- framesPerBuffer = max(framesPerBufferIn, framesPerBufferOut);
- }
- if (framesPerBuffer == 0)
- framesPerBuffer = ((UINT32)sampleRate / 100) * 2;
-
- // Try create device: Input
- if (inputParameters != NULL)
+ // Set stream state
+ stream->isActive = FALSE;
+ stream->isStopped = TRUE;
+
+ // Default thread priority is Audio: for exclusive mode we will use Pro Audio.
+ stream->nThreadPriority = eThreadPriorityAudio;
+
+ // Set default number of frames: paFramesPerBufferUnspecified
+ if (framesPerBuffer == paFramesPerBufferUnspecified)
+ {
+ UINT32 framesPerBufferIn = 0, framesPerBufferOut = 0;
+ if (inputParameters != NULL)
+ {
+ info = &paWasapi->devInfo[inputParameters->device];
+ framesPerBufferIn = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
+ }
+ if (outputParameters != NULL)
+ {
+ info = &paWasapi->devInfo[outputParameters->device];
+ framesPerBufferOut = MakeFramesFromHns(info->DefaultDevicePeriod, (UINT32)sampleRate);
+ }
+ // choosing maximum default size
+ framesPerBuffer = max(framesPerBufferIn, framesPerBufferOut);
+ }
+ if (framesPerBuffer == 0)
+ framesPerBuffer = ((UINT32)sampleRate / 100) * 2;
+
+ // Try create device: Input
+ if (inputParameters != NULL)
{
inputChannelCount = inputParameters->channelCount;
- inputSampleFormat = inputParameters->sampleFormat;
+ inputSampleFormat = GetSampleFormatForIO(inputParameters->sampleFormat);
info = &paWasapi->devInfo[inputParameters->device];
- // default Shared Mode
- stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED;
-
- // PaWasapiStreamInfo
- if (inputParameters->hostApiSpecificStreamInfo != NULL)
- {
- memcpy(&stream->in.params.wasapi_params, inputParameters->hostApiSpecificStreamInfo, min(sizeof(stream->in.params.wasapi_params), ((PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo)->size));
- stream->in.params.wasapi_params.size = sizeof(stream->in.params.wasapi_params);
-
- stream->in.params.stream_params.hostApiSpecificStreamInfo = &stream->in.params.wasapi_params;
- inputStreamInfo = &stream->in.params.wasapi_params;
-
- stream->in.flags = inputStreamInfo->flags;
-
- // Exclusive Mode
- if (inputStreamInfo->flags & paWinWasapiExclusive)
- {
- // Boost thread priority
- stream->nThreadPriority = eThreadPriorityProAudio;
- // Make Exclusive
- stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
- }
-
- // explicit thread priority level
- if (inputStreamInfo->flags & paWinWasapiThreadPriority)
- {
- if ((inputStreamInfo->threadPriority > eThreadPriorityNone) &&
- (inputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
- stream->nThreadPriority = inputStreamInfo->threadPriority;
- }
- }
-
- // Choose processing mode
- stream->in.streamFlags = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);
- if (paWasapi->useWOW64Workaround)
- stream->in.streamFlags = 0; // polling interface
- else
- if (streamCallback == NULL)
- stream->in.streamFlags = 0; // polling interface
- else
- if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))
- stream->in.streamFlags = 0; // polling interface
- else
- if (fullDuplex)
- stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also
-
- // Fill parameters for Audio Client creation
- stream->in.params.device_info = info;
- stream->in.params.stream_params = (*inputParameters);
- stream->in.params.frames_per_buffer = framesPerBuffer;
- stream->in.params.sample_rate = sampleRate;
- stream->in.params.blocking = (streamCallback == NULL);
- stream->in.params.full_duplex = fullDuplex;
- stream->in.params.wow64_workaround = paWasapi->useWOW64Workaround;
-
- // Create and activate audio client
- hr = ActivateAudioClientInput(stream);
- if (hr != S_OK)
- {
- LogPaError(result = paInvalidDevice);
- goto error;
+ // default Shared Mode
+ stream->in.shareMode = AUDCLNT_SHAREMODE_SHARED;
+
+ // PaWasapiStreamInfo
+ if (inputParameters->hostApiSpecificStreamInfo != NULL)
+ {
+ memcpy(&stream->in.params.wasapi_params, inputParameters->hostApiSpecificStreamInfo, min(sizeof(stream->in.params.wasapi_params), ((PaWasapiStreamInfo *)inputParameters->hostApiSpecificStreamInfo)->size));
+ stream->in.params.wasapi_params.size = sizeof(stream->in.params.wasapi_params);
+
+ stream->in.params.stream_params.hostApiSpecificStreamInfo = &stream->in.params.wasapi_params;
+ inputStreamInfo = &stream->in.params.wasapi_params;
+
+ stream->in.flags = inputStreamInfo->flags;
+
+ // Exclusive Mode
+ if (inputStreamInfo->flags & paWinWasapiExclusive)
+ {
+ // Boost thread priority
+ stream->nThreadPriority = eThreadPriorityProAudio;
+ // Make Exclusive
+ stream->in.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
+ }
+
+ // explicit thread priority level
+ if (inputStreamInfo->flags & paWinWasapiThreadPriority)
+ {
+ if ((inputStreamInfo->threadPriority > eThreadPriorityNone) &&
+ (inputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
+ stream->nThreadPriority = inputStreamInfo->threadPriority;
+ }
+
+ // redirect processing to custom user callback, ignore PA buffer processor
+ useInputBufferProcessor = !(inputStreamInfo->flags & paWinWasapiRedirectHostProcessor);
}
- // Get closest format
- hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->in.wavex), inputSampleFormat );
+ // Choose processing mode
+ stream->in.streamFlags = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);
+ if (paWasapi->useWOW64Workaround)
+ stream->in.streamFlags = 0; // polling interface
+ else
+ if (streamCallback == NULL)
+ stream->in.streamFlags = 0; // polling interface
+ else
+ if ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiPolling))
+ stream->in.streamFlags = 0; // polling interface
+ else
+ if (fullDuplex)
+ stream->in.streamFlags = 0; // polling interface is implemented for full-duplex mode also
+
+ // Use built-in PCM converter (channel count and sample rate) if requested
+ if ((PaWinUtil_GetOsVersion() >= paOsVersionWindows7Server2008R2) &&
+ (stream->in.shareMode == AUDCLNT_SHAREMODE_SHARED) &&
+ ((inputStreamInfo != NULL) && (inputStreamInfo->flags & paWinWasapiAutoConvert)))
+ stream->in.streamFlags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
+
+ // Loopback device is a real output device, so we open stream with AUDCLNT_STREAMFLAGS_LOOPBACK
+ // flag to make it work as input device
+ if (info->loopBack)
+ stream->in.streamFlags |= AUDCLNT_STREAMFLAGS_LOOPBACK;
+
+ // Fill parameters for Audio Client creation
+ stream->in.params.device_info = info;
+ stream->in.params.stream_params = (*inputParameters);
+ stream->in.params.frames_per_buffer = framesPerBuffer;
+ stream->in.params.sample_rate = sampleRate;
+ stream->in.params.blocking = (streamCallback == NULL);
+ stream->in.params.full_duplex = fullDuplex;
+ stream->in.params.wow64_workaround = paWasapi->useWOW64Workaround;
+
+ // Create and activate audio client
+ if ((result = ActivateAudioClient(stream, FALSE)) != paNoError)
+ {
+ LogPaError(result);
+ goto error;
+ }
+
+ // Get closest format
+ hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat(WaveToPaFormat(&stream->in.wavexu.ext), inputSampleFormat);
// Set user-side custom host processor
if ((inputStreamInfo != NULL) &&
@@ -3242,124 +3862,130 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->hostProcessOverrideInput.userData = userData;
}
- // Only get IAudioCaptureClient input once here instead of getting it at multiple places based on the use
- hr = IAudioClient_GetService(stream->in.clientParent, &pa_IID_IAudioCaptureClient, (void **)&stream->captureClientParent);
- if (hr != S_OK)
- {
- LogHostError(hr);
- LogPaError(result = paUnanticipatedHostError);
- goto error;
- }
-
- // Create ring buffer for blocking mode (It is needed because we fetch Input packets, not frames,
- // and thus we have to save partial packet if such remains unread)
- if (stream->in.params.blocking == TRUE)
- {
- UINT32 bufferFrames = ALIGN_NEXT_POW2((stream->in.framesPerHostCallback / WASAPI_PACKETS_PER_INPUT_BUFFER) * 2);
- UINT32 frameSize = stream->in.wavex.Format.nBlockAlign;
-
- // buffer
- if ((stream->in.tailBuffer = PaUtil_AllocateMemory(sizeof(PaUtilRingBuffer))) == NULL)
- {
- LogPaError(result = paInsufficientMemory);
- goto error;
- }
- memset(stream->in.tailBuffer, 0, sizeof(PaUtilRingBuffer));
-
- // buffer memory region
- stream->in.tailBufferMemory = PaUtil_AllocateMemory(frameSize * bufferFrames);
- if (stream->in.tailBufferMemory == NULL)
- {
- LogPaError(result = paInsufficientMemory);
- goto error;
- }
-
- // initialize
- if (PaUtil_InitializeRingBuffer(stream->in.tailBuffer, frameSize, bufferFrames, stream->in.tailBufferMemory) != 0)
- {
- LogPaError(result = paInternalError);
- goto error;
- }
- }
- }
+ // Only get IAudioCaptureClient input once here instead of getting it at multiple places based on the use
+ if (FAILED(hr = IAudioClient_GetService(stream->in.clientParent, &pa_IID_IAudioCaptureClient, (void **)&stream->captureClientParent)))
+ {
+ LogHostError(hr);
+ LogPaError(result = paUnanticipatedHostError);
+ goto error;
+ }
+
+ // Create ring buffer for blocking mode (It is needed because we fetch Input packets, not frames,
+ // and thus we have to save partial packet if such remains unread)
+ if (stream->in.params.blocking == TRUE)
+ {
+ UINT32 bufferFrames = ALIGN_NEXT_POW2((stream->in.framesPerHostCallback / WASAPI_PACKETS_PER_INPUT_BUFFER) * 2);
+ UINT32 frameSize = stream->in.wavexu.ext.Format.nBlockAlign;
+
+ // buffer
+ if ((stream->in.tailBuffer = PaUtil_AllocateZeroInitializedMemory(sizeof(PaUtilRingBuffer))) == NULL)
+ {
+ LogPaError(result = paInsufficientMemory);
+ goto error;
+ }
+
+ // buffer memory region
+ stream->in.tailBufferMemory = PaUtil_AllocateZeroInitializedMemory(frameSize * bufferFrames);
+ if (stream->in.tailBufferMemory == NULL)
+ {
+ LogPaError(result = paInsufficientMemory);
+ goto error;
+ }
+
+ // initialize
+ if (PaUtil_InitializeRingBuffer(stream->in.tailBuffer, frameSize, bufferFrames, stream->in.tailBufferMemory) != 0)
+ {
+ LogPaError(result = paInternalError);
+ goto error;
+ }
+ }
+ }
else
{
inputChannelCount = 0;
- inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress 'uninitialised var' warnings. */
+ inputSampleFormat = hostInputSampleFormat = paInt16; /* Suppress 'uninitialised var' warnings. */
}
- // Try create device: Output
+ // Try create device: Output
if (outputParameters != NULL)
{
outputChannelCount = outputParameters->channelCount;
- outputSampleFormat = outputParameters->sampleFormat;
- info = &paWasapi->devInfo[outputParameters->device];
-
- // default Shared Mode
- stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED;
-
- // set PaWasapiStreamInfo
- if (outputParameters->hostApiSpecificStreamInfo != NULL)
- {
- memcpy(&stream->out.params.wasapi_params, outputParameters->hostApiSpecificStreamInfo, min(sizeof(stream->out.params.wasapi_params), ((PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo)->size));
- stream->out.params.wasapi_params.size = sizeof(stream->out.params.wasapi_params);
-
- stream->out.params.stream_params.hostApiSpecificStreamInfo = &stream->out.params.wasapi_params;
- outputStreamInfo = &stream->out.params.wasapi_params;
-
- stream->out.flags = outputStreamInfo->flags;
-
- // Exclusive Mode
- if (outputStreamInfo->flags & paWinWasapiExclusive)
- {
- // Boost thread priority
- stream->nThreadPriority = eThreadPriorityProAudio;
- // Make Exclusive
- stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
- }
-
- // explicit thread priority level
- if (outputStreamInfo->flags & paWinWasapiThreadPriority)
- {
- if ((outputStreamInfo->threadPriority > eThreadPriorityNone) &&
- (outputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
- stream->nThreadPriority = outputStreamInfo->threadPriority;
- }
- }
-
- // Choose processing mode
- stream->out.streamFlags = (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);
- if (paWasapi->useWOW64Workaround)
- stream->out.streamFlags = 0; // polling interface
- else
- if (streamCallback == NULL)
- stream->out.streamFlags = 0; // polling interface
- else
- if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))
- stream->out.streamFlags = 0; // polling interface
- else
- if (fullDuplex)
- stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also
-
- // Fill parameters for Audio Client creation
- stream->out.params.device_info = info;
- stream->out.params.stream_params = (*outputParameters);
- stream->out.params.frames_per_buffer = framesPerBuffer;
- stream->out.params.sample_rate = sampleRate;
- stream->out.params.blocking = (streamCallback == NULL);
- stream->out.params.full_duplex = fullDuplex;
- stream->out.params.wow64_workaround = paWasapi->useWOW64Workaround;
-
- // Create and activate audio client
- hr = ActivateAudioClientOutput(stream);
- if (hr != S_OK)
- {
- LogPaError(result = paInvalidDevice);
- goto error;
+ outputSampleFormat = GetSampleFormatForIO(outputParameters->sampleFormat);
+ info = &paWasapi->devInfo[outputParameters->device];
+
+ // default Shared Mode
+ stream->out.shareMode = AUDCLNT_SHAREMODE_SHARED;
+
+ // set PaWasapiStreamInfo
+ if (outputParameters->hostApiSpecificStreamInfo != NULL)
+ {
+ memcpy(&stream->out.params.wasapi_params, outputParameters->hostApiSpecificStreamInfo, min(sizeof(stream->out.params.wasapi_params), ((PaWasapiStreamInfo *)outputParameters->hostApiSpecificStreamInfo)->size));
+ stream->out.params.wasapi_params.size = sizeof(stream->out.params.wasapi_params);
+
+ stream->out.params.stream_params.hostApiSpecificStreamInfo = &stream->out.params.wasapi_params;
+ outputStreamInfo = &stream->out.params.wasapi_params;
+
+ stream->out.flags = outputStreamInfo->flags;
+
+ // Exclusive Mode
+ if (outputStreamInfo->flags & paWinWasapiExclusive)
+ {
+ // Boost thread priority
+ stream->nThreadPriority = eThreadPriorityProAudio;
+ // Make Exclusive
+ stream->out.shareMode = AUDCLNT_SHAREMODE_EXCLUSIVE;
+ }
+
+ // explicit thread priority level
+ if (outputStreamInfo->flags & paWinWasapiThreadPriority)
+ {
+ if ((outputStreamInfo->threadPriority > eThreadPriorityNone) &&
+ (outputStreamInfo->threadPriority <= eThreadPriorityWindowManager))
+ stream->nThreadPriority = outputStreamInfo->threadPriority;
+ }
+
+ // redirect processing to custom user callback, ignore PA buffer processor
+ useOutputBufferProcessor = !(outputStreamInfo->flags & paWinWasapiRedirectHostProcessor);
+ }
+
+ // Choose processing mode
+ stream->out.streamFlags = (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE ? AUDCLNT_STREAMFLAGS_EVENTCALLBACK : 0);
+ if (paWasapi->useWOW64Workaround)
+ stream->out.streamFlags = 0; // polling interface
+ else
+ if (streamCallback == NULL)
+ stream->out.streamFlags = 0; // polling interface
+ else
+ if ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiPolling))
+ stream->out.streamFlags = 0; // polling interface
+ else
+ if (fullDuplex)
+ stream->out.streamFlags = 0; // polling interface is implemented for full-duplex mode also
+
+ // Use built-in PCM converter (channel count and sample rate) if requested
+ if ((PaWinUtil_GetOsVersion() >= paOsVersionWindows7Server2008R2) &&
+ (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED) &&
+ ((outputStreamInfo != NULL) && (outputStreamInfo->flags & paWinWasapiAutoConvert)))
+ stream->out.streamFlags |= (AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY);
+
+ // Fill parameters for Audio Client creation
+ stream->out.params.device_info = info;
+ stream->out.params.stream_params = (*outputParameters);
+ stream->out.params.frames_per_buffer = framesPerBuffer;
+ stream->out.params.sample_rate = sampleRate;
+ stream->out.params.blocking = (streamCallback == NULL);
+ stream->out.params.full_duplex = fullDuplex;
+ stream->out.params.wow64_workaround = paWasapi->useWOW64Workaround;
+
+ // Create and activate audio client
+ if ((result = ActivateAudioClient(stream, TRUE)) != paNoError)
+ {
+ LogPaError(result);
+ goto error;
}
- // Get closest format
- hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( WaveToPaFormat(&stream->out.wavex), outputSampleFormat );
+ // Get closest format
+ hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat(WaveToPaFormat(&stream->out.wavexu.ext), outputSampleFormat);
// Set user-side custom host processor
if ((outputStreamInfo != NULL) &&
@@ -3369,135 +3995,137 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->hostProcessOverrideOutput.userData = userData;
}
- // Only get IAudioCaptureClient output once here instead of getting it at multiple places based on the use
- hr = IAudioClient_GetService(stream->out.clientParent, &pa_IID_IAudioRenderClient, (void **)&stream->renderClientParent);
- if (hr != S_OK)
- {
- LogHostError(hr);
- LogPaError(result = paUnanticipatedHostError);
- goto error;
- }
- }
+ // Only get IAudioCaptureClient output once here instead of getting it at multiple places based on the use
+ if (FAILED(hr = IAudioClient_GetService(stream->out.clientParent, &pa_IID_IAudioRenderClient, (void **)&stream->renderClientParent)))
+ {
+ LogHostError(hr);
+ LogPaError(result = paUnanticipatedHostError);
+ goto error;
+ }
+ }
else
{
outputChannelCount = 0;
- outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
- }
-
- // log full-duplex
- if (fullDuplex)
- PRINT(("WASAPI::OpenStream: full-duplex mode\n"));
-
- // paWinWasapiPolling must be on/or not on both streams
- if ((inputParameters != NULL) && (outputParameters != NULL))
- {
- if ((inputStreamInfo != NULL) && (outputStreamInfo != NULL))
- {
- if (((inputStreamInfo->flags & paWinWasapiPolling) &&
- !(outputStreamInfo->flags & paWinWasapiPolling))
- ||
- (!(inputStreamInfo->flags & paWinWasapiPolling) &&
- (outputStreamInfo->flags & paWinWasapiPolling)))
- {
- LogPaError(result = paInvalidFlag);
- goto error;
- }
- }
- }
-
- // Initialize stream representation
+ outputSampleFormat = hostOutputSampleFormat = paInt16; /* Suppress 'uninitialized var' warnings. */
+ }
+
+ // log full-duplex
+ if (fullDuplex)
+ PRINT(("WASAPI::OpenStream: full-duplex mode\n"));
+
+ // paWinWasapiPolling must be on/or not on both streams
+ if ((inputParameters != NULL) && (outputParameters != NULL))
+ {
+ if ((inputStreamInfo != NULL) && (outputStreamInfo != NULL))
+ {
+ if (((inputStreamInfo->flags & paWinWasapiPolling) &&
+ !(outputStreamInfo->flags & paWinWasapiPolling))
+ ||
+ (!(inputStreamInfo->flags & paWinWasapiPolling) &&
+ (outputStreamInfo->flags & paWinWasapiPolling)))
+ {
+ LogPaError(result = paInvalidFlag);
+ goto error;
+ }
+ }
+ }
+
+ // Initialize stream representation
if (streamCallback)
{
- stream->bBlocking = FALSE;
+ stream->isBlocking = FALSE;
PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
&paWasapi->callbackStreamInterface,
- streamCallback, userData);
+ streamCallback, userData);
}
else
{
- stream->bBlocking = TRUE;
+ stream->isBlocking = TRUE;
PaUtil_InitializeStreamRepresentation(&stream->streamRepresentation,
&paWasapi->blockingStreamInterface,
- streamCallback, userData);
+ streamCallback, userData);
}
- // Initialize CPU measurer
+ // Initialize CPU measurer
PaUtil_InitializeCpuLoadMeasurer(&stream->cpuLoadMeasurer, sampleRate);
- if (outputParameters && inputParameters)
- {
- // serious problem #1 - No, Not a problem, especially concerning Exclusive mode.
- // Input device in exclusive mode somehow is getting large buffer always, thus we
- // adjust Output latency to reflect it, thus period will differ but playback will be
- // normal.
- /*if (stream->in.period != stream->out.period)
- {
- PRINT(("WASAPI: OpenStream: period discrepancy\n"));
- LogPaError(result = paBadIODeviceCombination);
- goto error;
- }*/
-
- // serious problem #2 - No, Not a problem, as framesPerHostCallback take into account
- // sample size while it is not a problem for PA full-duplex, we must care of
- // preriod only!
- /*if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback)
- {
- PRINT(("WASAPI: OpenStream: framesPerHostCallback discrepancy\n"));
- goto error;
- }*/
- }
-
- // Calculate frames per host for processor
- framesPerHostCallback = (outputParameters ? stream->out.framesPerBuffer : stream->in.framesPerBuffer);
-
- // Choose correct mode of buffer processing:
- // Exclusive/Shared non paWinWasapiPolling mode: paUtilFixedHostBufferSize - always fixed
- // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary for Exclusive or Full-duplex
- bufferMode = paUtilFixedHostBufferSize;
- if (inputParameters) // !!! WASAPI IAudioCaptureClient::GetBuffer extracts not number of frames but 1 packet, thus we always must adapt
- bufferMode = paUtilBoundedHostBufferSize;
- else
- if (outputParameters)
- {
- if ((stream->out.buffers == 1) &&
- (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
- bufferMode = paUtilBoundedHostBufferSize;
- }
- stream->bufferMode = bufferMode;
+ if (outputParameters && inputParameters)
+ {
+ // serious problem #1 - No, Not a problem, especially concerning Exclusive mode.
+ // Input device in exclusive mode somehow is getting large buffer always, thus we
+ // adjust Output latency to reflect it, thus period will differ but playback will be
+ // normal.
+ /*if (stream->in.period != stream->out.period)
+ {
+ PRINT(("WASAPI: OpenStream: period discrepancy\n"));
+ LogPaError(result = paBadIODeviceCombination);
+ goto error;
+ }*/
+
+ // serious problem #2 - No, Not a problem, as framesPerHostCallback take into account
+ // sample size while it is not a problem for PA full-duplex, we must care of
+ // period only!
+ /*if (stream->out.framesPerHostCallback != stream->in.framesPerHostCallback)
+ {
+ PRINT(("WASAPI: OpenStream: framesPerHostCallback discrepancy\n"));
+ goto error;
+ }*/
+ }
+
+ // Calculate frames per host for processor
+ framesPerHostCallback = (outputParameters ? stream->out.framesPerBuffer : stream->in.framesPerBuffer);
+
+ // Choose correct mode of buffer processing:
+ // Exclusive/Shared non paWinWasapiPolling mode: paUtilFixedHostBufferSize - always fixed
+ // Exclusive/Shared paWinWasapiPolling mode: paUtilBoundedHostBufferSize - may vary for Exclusive or Full-duplex
+ bufferMode = paUtilFixedHostBufferSize;
+ if (inputParameters) // !!! WASAPI IAudioCaptureClient::GetBuffer extracts not number of frames but 1 packet, thus we always must adapt
+ bufferMode = paUtilBoundedHostBufferSize;
+ else
+ if (outputParameters)
+ {
+ if ((stream->out.buffers == 1) &&
+ (!stream->out.streamFlags || ((stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK) == 0)))
+ bufferMode = paUtilBoundedHostBufferSize;
+ }
+ stream->bufferMode = bufferMode;
// Initialize buffer processor
- result = PaUtil_InitializeBufferProcessor(
- &stream->bufferProcessor,
- inputChannelCount,
- inputSampleFormat,
- hostInputSampleFormat,
- outputChannelCount,
- outputSampleFormat,
- hostOutputSampleFormat,
- sampleRate,
- streamFlags,
- framesPerBuffer,
- framesPerHostCallback,
- bufferMode,
- streamCallback,
- userData);
- if (result != paNoError)
- {
- LogPaError(result);
- goto error;
- }
+ if (useInputBufferProcessor || useOutputBufferProcessor)
+ {
+ result = PaUtil_InitializeBufferProcessor(
+ &stream->bufferProcessor,
+ inputChannelCount,
+ inputSampleFormat,
+ hostInputSampleFormat,
+ outputChannelCount,
+ outputSampleFormat,
+ hostOutputSampleFormat,
+ sampleRate,
+ streamFlags,
+ framesPerBuffer,
+ framesPerHostCallback,
+ bufferMode,
+ streamCallback,
+ userData);
+ if (result != paNoError)
+ {
+ LogPaError(result);
+ goto error;
+ }
+ }
- // Set Input latency
+ // Set Input latency
stream->streamRepresentation.streamInfo.inputLatency =
- ((double)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate)
- + ((inputParameters)?stream->in.latencySeconds : 0);
+ (useInputBufferProcessor ? PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate : 0)
+ + (inputParameters != NULL ? stream->in.latencySeconds : 0);
- // Set Output latency
+ // Set Output latency
stream->streamRepresentation.streamInfo.outputLatency =
- ((double)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate)
- + ((outputParameters)?stream->out.latencySeconds : 0);
+ (useOutputBufferProcessor ? PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate : 0)
+ + (outputParameters != NULL ? stream->out.latencySeconds : 0);
- // Set SR
+ // Set SR
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
(*s) = (PaStream *)stream;
@@ -3506,7 +4134,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
error:
if (stream != NULL)
- CloseStream(stream);
+ CloseStream(stream);
return result;
}
@@ -3517,32 +4145,30 @@ static PaError CloseStream( PaStream* s )
PaError result = paNoError;
PaWasapiStream *stream = (PaWasapiStream*)s;
- // abort active stream
- if (IsStreamActive(s))
- {
- result = AbortStream(s);
- }
+ // abort active stream
+ if (IsStreamActive(s))
+ {
+ result = AbortStream(s);
+ }
SAFE_RELEASE(stream->captureClientParent);
SAFE_RELEASE(stream->renderClientParent);
SAFE_RELEASE(stream->out.clientParent);
SAFE_RELEASE(stream->in.clientParent);
- SAFE_RELEASE(stream->inVol);
- SAFE_RELEASE(stream->outVol);
- CloseHandle(stream->event[S_INPUT]);
- CloseHandle(stream->event[S_OUTPUT]);
+ CloseHandle(stream->event[S_INPUT]);
+ CloseHandle(stream->event[S_OUTPUT]);
- _StreamCleanup(stream);
+ _StreamCleanup(stream);
- PaWasapi_FreeMemory(stream->in.monoBuffer);
- PaWasapi_FreeMemory(stream->out.monoBuffer);
+ PaWasapi_FreeMemory(stream->in.monoBuffer);
+ PaWasapi_FreeMemory(stream->out.monoBuffer);
- PaUtil_FreeMemory(stream->in.tailBuffer);
- PaUtil_FreeMemory(stream->in.tailBufferMemory);
+ PaUtil_FreeMemory(stream->in.tailBuffer);
+ PaUtil_FreeMemory(stream->in.tailBufferMemory);
- PaUtil_FreeMemory(stream->out.tailBuffer);
- PaUtil_FreeMemory(stream->out.tailBufferMemory);
+ PaUtil_FreeMemory(stream->out.tailBuffer);
+ PaUtil_FreeMemory(stream->out.tailBufferMemory);
PaUtil_TerminateBufferProcessor(&stream->bufferProcessor);
PaUtil_TerminateStreamRepresentation(&stream->streamRepresentation);
@@ -3552,404 +4178,406 @@ static PaError CloseStream( PaStream* s )
}
// ------------------------------------------------------------------------------------------
-HRESULT UnmarshalSubStreamComPointers(PaWasapiSubStream *substream)
+HRESULT UnmarshalSubStreamComPointers(PaWasapiSubStream *substream)
{
#ifndef PA_WINRT
- HRESULT hResult = S_OK;
- HRESULT hFirstBadResult = S_OK;
- substream->clientProc = NULL;
-
- // IAudioClient
- hResult = CoGetInterfaceAndReleaseStream(substream->clientStream, GetAudioClientIID(), (LPVOID*)&substream->clientProc);
- substream->clientStream = NULL;
- if (hResult != S_OK)
- {
- hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
- }
+ HRESULT hResult = S_OK;
+ HRESULT hFirstBadResult = S_OK;
+ substream->clientProc = NULL;
+
+ // IAudioClient
+ hResult = CoGetInterfaceAndReleaseStream(substream->clientStream, GetAudioClientIID(), (LPVOID*)&substream->clientProc);
+ substream->clientStream = NULL;
+ if (hResult != S_OK)
+ {
+ hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
+ }
- return hFirstBadResult;
+ return hFirstBadResult;
#else
- (void)substream;
- return S_OK;
+ (void)substream;
+ return S_OK;
#endif
}
// ------------------------------------------------------------------------------------------
-HRESULT UnmarshalStreamComPointers(PaWasapiStream *stream)
+HRESULT UnmarshalStreamComPointers(PaWasapiStream *stream)
{
#ifndef PA_WINRT
- HRESULT hResult = S_OK;
- HRESULT hFirstBadResult = S_OK;
- stream->captureClient = NULL;
- stream->renderClient = NULL;
- stream->in.clientProc = NULL;
- stream->out.clientProc = NULL;
-
- if (NULL != stream->in.clientParent)
- {
- // SubStream pointers
- hResult = UnmarshalSubStreamComPointers(&stream->in);
- if (hResult != S_OK)
- {
- hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
- }
-
- // IAudioCaptureClient
- hResult = CoGetInterfaceAndReleaseStream(stream->captureClientStream, &pa_IID_IAudioCaptureClient, (LPVOID*)&stream->captureClient);
- stream->captureClientStream = NULL;
- if (hResult != S_OK)
- {
- hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
- }
- }
-
- if (NULL != stream->out.clientParent)
- {
- // SubStream pointers
- hResult = UnmarshalSubStreamComPointers(&stream->out);
- if (hResult != S_OK)
- {
- hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
- }
-
- // IAudioRenderClient
- hResult = CoGetInterfaceAndReleaseStream(stream->renderClientStream, &pa_IID_IAudioRenderClient, (LPVOID*)&stream->renderClient);
- stream->renderClientStream = NULL;
- if (hResult != S_OK)
- {
- hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
- }
- }
-
- return hFirstBadResult;
+ HRESULT hResult = S_OK;
+ HRESULT hFirstBadResult = S_OK;
+ stream->captureClient = NULL;
+ stream->renderClient = NULL;
+ stream->in.clientProc = NULL;
+ stream->out.clientProc = NULL;
+
+ if (NULL != stream->in.clientParent)
+ {
+ // SubStream pointers
+ hResult = UnmarshalSubStreamComPointers(&stream->in);
+ if (hResult != S_OK)
+ {
+ hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
+ }
+
+ // IAudioCaptureClient
+ hResult = CoGetInterfaceAndReleaseStream(stream->captureClientStream, &pa_IID_IAudioCaptureClient, (LPVOID*)&stream->captureClient);
+ stream->captureClientStream = NULL;
+ if (hResult != S_OK)
+ {
+ hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
+ }
+ }
+
+ if (NULL != stream->out.clientParent)
+ {
+ // SubStream pointers
+ hResult = UnmarshalSubStreamComPointers(&stream->out);
+ if (hResult != S_OK)
+ {
+ hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
+ }
+
+ // IAudioRenderClient
+ hResult = CoGetInterfaceAndReleaseStream(stream->renderClientStream, &pa_IID_IAudioRenderClient, (LPVOID*)&stream->renderClient);
+ stream->renderClientStream = NULL;
+ if (hResult != S_OK)
+ {
+ hFirstBadResult = (hFirstBadResult == S_OK) ? hResult : hFirstBadResult;
+ }
+ }
+
+ return hFirstBadResult;
#else
- if (stream->in.clientParent != NULL)
- {
- stream->in.clientProc = stream->in.clientParent;
- IAudioClient_AddRef(stream->in.clientParent);
- }
-
- if (stream->out.clientParent != NULL)
- {
- stream->out.clientProc = stream->out.clientParent;
- IAudioClient_AddRef(stream->out.clientParent);
- }
-
- if (stream->renderClientParent != NULL)
- {
- stream->renderClient = stream->renderClientParent;
- IAudioRenderClient_AddRef(stream->renderClientParent);
- }
-
- if (stream->captureClientParent != NULL)
- {
- stream->captureClient = stream->captureClientParent;
- IAudioCaptureClient_AddRef(stream->captureClientParent);
- }
-
- return S_OK;
+ if (stream->in.clientParent != NULL)
+ {
+ stream->in.clientProc = stream->in.clientParent;
+ IAudioClient_AddRef(stream->in.clientParent);
+ }
+
+ if (stream->out.clientParent != NULL)
+ {
+ stream->out.clientProc = stream->out.clientParent;
+ IAudioClient_AddRef(stream->out.clientParent);
+ }
+
+ if (stream->renderClientParent != NULL)
+ {
+ stream->renderClient = stream->renderClientParent;
+ IAudioRenderClient_AddRef(stream->renderClientParent);
+ }
+
+ if (stream->captureClientParent != NULL)
+ {
+ stream->captureClient = stream->captureClientParent;
+ IAudioCaptureClient_AddRef(stream->captureClientParent);
+ }
+
+ return S_OK;
#endif
}
// -----------------------------------------------------------------------------------------
-void ReleaseUnmarshaledSubComPointers(PaWasapiSubStream *substream)
+void ReleaseUnmarshaledSubComPointers(PaWasapiSubStream *substream)
{
- SAFE_RELEASE(substream->clientProc);
+ SAFE_RELEASE(substream->clientProc);
}
// -----------------------------------------------------------------------------------------
-void ReleaseUnmarshaledComPointers(PaWasapiStream *stream)
+void ReleaseUnmarshaledComPointers(PaWasapiStream *stream)
{
- // Release AudioClient services first
- SAFE_RELEASE(stream->captureClient);
- SAFE_RELEASE(stream->renderClient);
+ // Release AudioClient services first
+ SAFE_RELEASE(stream->captureClient);
+ SAFE_RELEASE(stream->renderClient);
- // Release AudioClients
- ReleaseUnmarshaledSubComPointers(&stream->in);
- ReleaseUnmarshaledSubComPointers(&stream->out);
+ // Release AudioClients
+ ReleaseUnmarshaledSubComPointers(&stream->in);
+ ReleaseUnmarshaledSubComPointers(&stream->out);
}
// ------------------------------------------------------------------------------------------
-HRESULT MarshalSubStreamComPointers(PaWasapiSubStream *substream)
+HRESULT MarshalSubStreamComPointers(PaWasapiSubStream *substream)
{
#ifndef PA_WINRT
- HRESULT hResult;
- substream->clientStream = NULL;
+ HRESULT hResult;
+ substream->clientStream = NULL;
- // IAudioClient
- hResult = CoMarshalInterThreadInterfaceInStream(GetAudioClientIID(), (LPUNKNOWN)substream->clientParent, &substream->clientStream);
- if (hResult != S_OK)
- goto marshal_sub_error;
+ // IAudioClient
+ hResult = CoMarshalInterThreadInterfaceInStream(GetAudioClientIID(), (LPUNKNOWN)substream->clientParent, &substream->clientStream);
+ if (hResult != S_OK)
+ goto marshal_sub_error;
- return hResult;
+ return hResult;
- // If marshaling error occurred, make sure to release everything.
+ // If marshaling error occurred, make sure to release everything.
marshal_sub_error:
- UnmarshalSubStreamComPointers(substream);
- ReleaseUnmarshaledSubComPointers(substream);
- return hResult;
+ UnmarshalSubStreamComPointers(substream);
+ ReleaseUnmarshaledSubComPointers(substream);
+ return hResult;
#else
- (void)substream;
- return S_OK;
+ (void)substream;
+ return S_OK;
#endif
}
// ------------------------------------------------------------------------------------------
-HRESULT MarshalStreamComPointers(PaWasapiStream *stream)
+HRESULT MarshalStreamComPointers(PaWasapiStream *stream)
{
#ifndef PA_WINRT
- HRESULT hResult = S_OK;
- stream->captureClientStream = NULL;
- stream->in.clientStream = NULL;
- stream->renderClientStream = NULL;
- stream->out.clientStream = NULL;
-
- if (NULL != stream->in.clientParent)
- {
- // SubStream pointers
- hResult = MarshalSubStreamComPointers(&stream->in);
- if (hResult != S_OK)
- goto marshal_error;
-
- // IAudioCaptureClient
- hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioCaptureClient, (LPUNKNOWN)stream->captureClientParent, &stream->captureClientStream);
- if (hResult != S_OK)
- goto marshal_error;
- }
-
- if (NULL != stream->out.clientParent)
- {
- // SubStream pointers
- hResult = MarshalSubStreamComPointers(&stream->out);
- if (hResult != S_OK)
- goto marshal_error;
-
- // IAudioRenderClient
- hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioRenderClient, (LPUNKNOWN)stream->renderClientParent, &stream->renderClientStream);
- if (hResult != S_OK)
- goto marshal_error;
- }
-
- return hResult;
-
- // If marshaling error occurred, make sure to release everything.
+ HRESULT hResult = S_OK;
+ stream->captureClientStream = NULL;
+ stream->in.clientStream = NULL;
+ stream->renderClientStream = NULL;
+ stream->out.clientStream = NULL;
+
+ if (NULL != stream->in.clientParent)
+ {
+ // SubStream pointers
+ hResult = MarshalSubStreamComPointers(&stream->in);
+ if (hResult != S_OK)
+ goto marshal_error;
+
+ // IAudioCaptureClient
+ hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioCaptureClient, (LPUNKNOWN)stream->captureClientParent, &stream->captureClientStream);
+ if (hResult != S_OK)
+ goto marshal_error;
+ }
+
+ if (NULL != stream->out.clientParent)
+ {
+ // SubStream pointers
+ hResult = MarshalSubStreamComPointers(&stream->out);
+ if (hResult != S_OK)
+ goto marshal_error;
+
+ // IAudioRenderClient
+ hResult = CoMarshalInterThreadInterfaceInStream(&pa_IID_IAudioRenderClient, (LPUNKNOWN)stream->renderClientParent, &stream->renderClientStream);
+ if (hResult != S_OK)
+ goto marshal_error;
+ }
+
+ return hResult;
+
+ // If marshaling error occurred, make sure to release everything.
marshal_error:
- UnmarshalStreamComPointers(stream);
- ReleaseUnmarshaledComPointers(stream);
- return hResult;
+ UnmarshalStreamComPointers(stream);
+ ReleaseUnmarshaledComPointers(stream);
+ return hResult;
#else
- (void)stream;
- return S_OK;
+ (void)stream;
+ return S_OK;
#endif
}
// ------------------------------------------------------------------------------------------
static PaError StartStream( PaStream *s )
{
- HRESULT hr;
+ HRESULT hr;
PaWasapiStream *stream = (PaWasapiStream*)s;
- PaError result = paNoError;
+ PaError result = paNoError;
- // check if stream is active already
- if (IsStreamActive(s))
- return paStreamIsNotStopped;
+ // check if stream is active already
+ if (IsStreamActive(s))
+ return paStreamIsNotStopped;
PaUtil_ResetBufferProcessor(&stream->bufferProcessor);
- // Cleanup handles (may be necessary if stream was stopped by itself due to error)
- _StreamCleanup(stream);
-
- // Create close event
- if ((stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
- {
- result = paInsufficientMemory;
- goto start_error;
- }
-
- // Create thread
- if (!stream->bBlocking)
- {
- // Create thread events
- stream->hThreadStart = CreateEvent(NULL, TRUE, FALSE, NULL);
- stream->hThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL);
- if ((stream->hThreadStart == NULL) || (stream->hThreadExit == NULL))
- {
- result = paInsufficientMemory;
- goto start_error;
- }
-
- // Marshal WASAPI interface pointers for safe use in thread created below.
- if ((hr = MarshalStreamComPointers(stream)) != S_OK)
- {
- PRINT(("Failed marshaling stream COM pointers."));
- result = paUnanticipatedHostError;
- goto nonblocking_start_error;
- }
-
- if ((stream->in.clientParent && (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) ||
- (stream->out.clientParent && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)))
- {
- if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)
- {
- PRINT(("Failed creating thread: ProcThreadEvent."));
- result = paUnanticipatedHostError;
- goto nonblocking_start_error;
- }
- }
- else
- {
- if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL)
- {
- PRINT(("Failed creating thread: ProcThreadPoll."));
- result = paUnanticipatedHostError;
- goto nonblocking_start_error;
- }
- }
-
- // Wait for thread to start
- if (WaitForSingleObject(stream->hThreadStart, 60*1000) == WAIT_TIMEOUT)
- {
- PRINT(("Failed starting thread: timeout."));
- result = paUnanticipatedHostError;
- goto nonblocking_start_error;
- }
- }
- else
- {
- // Create blocking operation events (non-signaled event means - blocking operation is pending)
- if (stream->out.clientParent != NULL)
- {
- if ((stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL)
- {
- result = paInsufficientMemory;
- goto start_error;
- }
- }
- if (stream->in.clientParent != NULL)
- {
- if ((stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL)
- {
- result = paInsufficientMemory;
- goto start_error;
- }
- }
-
- // Initialize event & start INPUT stream
- if (stream->in.clientParent != NULL)
- {
- if ((hr = IAudioClient_Start(stream->in.clientParent)) != S_OK)
- {
- LogHostError(hr);
- result = paUnanticipatedHostError;
- goto start_error;
- }
- }
-
- // Initialize event & start OUTPUT stream
- if (stream->out.clientParent != NULL)
- {
- // Start
- if ((hr = IAudioClient_Start(stream->out.clientParent)) != S_OK)
- {
- LogHostError(hr);
- result = paUnanticipatedHostError;
- goto start_error;
- }
- }
-
- // Set parent to working pointers to use shared functions.
- stream->captureClient = stream->captureClientParent;
- stream->renderClient = stream->renderClientParent;
- stream->in.clientProc = stream->in.clientParent;
- stream->out.clientProc = stream->out.clientParent;
-
- // Signal: stream running.
- stream->running = TRUE;
- }
+ // Cleanup handles (may be necessary if stream was stopped by itself due to error)
+ _StreamCleanup(stream);
+
+ // Create close event
+ if ((stream->hCloseRequest = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL)
+ {
+ result = paInsufficientMemory;
+ goto start_error;
+ }
+
+ // Set stream state
+ stream->isActive = TRUE;
+ stream->isStopped = FALSE;
+
+ if (!stream->isBlocking)
+ {
+ // Create thread events
+ stream->hThreadStart = CreateEvent(NULL, TRUE, FALSE, NULL);
+ stream->hThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if ((stream->hThreadStart == NULL) || (stream->hThreadExit == NULL))
+ {
+ result = paInsufficientMemory;
+ goto start_error;
+ }
+
+ // Marshal WASAPI interface pointers for safe use in thread created below.
+ if ((hr = MarshalStreamComPointers(stream)) != S_OK)
+ {
+ PRINT(("Failed marshaling stream COM pointers."));
+ result = paUnanticipatedHostError;
+ goto nonblocking_start_error;
+ }
+
+ if ((stream->in.clientParent && (stream->in.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)) ||
+ (stream->out.clientParent && (stream->out.streamFlags & AUDCLNT_STREAMFLAGS_EVENTCALLBACK)))
+ {
+ if ((stream->hThread = CREATE_THREAD(ProcThreadEvent)) == NULL)
+ {
+ PRINT(("Failed creating thread: ProcThreadEvent."));
+ result = paUnanticipatedHostError;
+ goto nonblocking_start_error;
+ }
+ }
+ else
+ {
+ if ((stream->hThread = CREATE_THREAD(ProcThreadPoll)) == NULL)
+ {
+ PRINT(("Failed creating thread: ProcThreadPoll."));
+ result = paUnanticipatedHostError;
+ goto nonblocking_start_error;
+ }
+ }
+
+ // Wait for thread to start
+ if (WaitForSingleObject(stream->hThreadStart, 60*1000) == WAIT_TIMEOUT)
+ {
+ PRINT(("Failed starting thread: timeout."));
+ result = paUnanticipatedHostError;
+ goto nonblocking_start_error;
+ }
+ }
+ else
+ {
+ // Create blocking operation events (non-signaled event means - blocking operation is pending)
+ if (stream->out.clientParent != NULL)
+ {
+ if ((stream->hBlockingOpStreamWR = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL)
+ {
+ result = paInsufficientMemory;
+ goto start_error;
+ }
+ }
+ if (stream->in.clientParent != NULL)
+ {
+ if ((stream->hBlockingOpStreamRD = CreateEvent(NULL, TRUE, TRUE, NULL)) == NULL)
+ {
+ result = paInsufficientMemory;
+ goto start_error;
+ }
+ }
+
+ // Initialize event & start INPUT stream
+ if (stream->in.clientParent != NULL)
+ {
+ if ((hr = IAudioClient_Start(stream->in.clientParent)) != S_OK)
+ {
+ LogHostError(hr);
+ result = paUnanticipatedHostError;
+ goto start_error;
+ }
+ }
+
+ // Initialize event & start OUTPUT stream
+ if (stream->out.clientParent != NULL)
+ {
+ // Start
+ if ((hr = IAudioClient_Start(stream->out.clientParent)) != S_OK)
+ {
+ LogHostError(hr);
+ result = paUnanticipatedHostError;
+ goto start_error;
+ }
+ }
+
+ // Set parent to working pointers to use shared functions
+ stream->captureClient = stream->captureClientParent;
+ stream->renderClient = stream->renderClientParent;
+ stream->in.clientProc = stream->in.clientParent;
+ stream->out.clientProc = stream->out.clientParent;
+ }
return result;
nonblocking_start_error:
- // Set hThreadExit event to prevent blocking during cleanup
- SetEvent(stream->hThreadExit);
- UnmarshalStreamComPointers(stream);
- ReleaseUnmarshaledComPointers(stream);
+ // Set hThreadExit event to prevent blocking during cleanup
+ SetEvent(stream->hThreadExit);
+ UnmarshalStreamComPointers(stream);
+ ReleaseUnmarshaledComPointers(stream);
start_error:
- StopStream(s);
- return result;
+ StopStream(s);
+ return result;
}
// ------------------------------------------------------------------------------------------
-void _StreamFinish(PaWasapiStream *stream)
+static void StopStreamByUser(PaWasapiStream *stream)
{
- // Issue command to thread to stop processing and wait for thread exit
- if (!stream->bBlocking)
- {
- SignalObjectAndWait(stream->hCloseRequest, stream->hThreadExit, INFINITE, FALSE);
- }
- else
- // Blocking mode does not own thread
- {
- // Signal close event and wait for each of 2 blocking operations to complete
- if (stream->out.clientParent)
- SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE);
- if (stream->out.clientParent)
- SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE);
-
- // Process stop
- _StreamOnStop(stream);
- }
+ // Issue command to thread to stop processing and wait for thread exit
+ if (!stream->isBlocking)
+ {
+ SignalObjectAndWait(stream->hCloseRequest, stream->hThreadExit, INFINITE, FALSE);
+ }
+ else
+ // Blocking mode does not own thread
+ {
+ // Signal close event and wait for each of 2 blocking operations to complete
+ if (stream->out.clientParent)
+ SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamWR, INFINITE, TRUE);
+ if (stream->out.clientParent)
+ SignalObjectAndWait(stream->hCloseRequest, stream->hBlockingOpStreamRD, INFINITE, TRUE);
+
+ // Process stop
+ _StreamOnStop(stream);
+ }
- // Cleanup handles
- _StreamCleanup(stream);
+ // Cleanup handles
+ _StreamCleanup(stream);
- stream->running = FALSE;
+ // Set stream state
+ stream->isActive = FALSE;
+ stream->isStopped = TRUE;
}
// ------------------------------------------------------------------------------------------
void _StreamCleanup(PaWasapiStream *stream)
{
- // Close thread handles to allow restart
- SAFE_CLOSE(stream->hThread);
- SAFE_CLOSE(stream->hThreadStart);
- SAFE_CLOSE(stream->hThreadExit);
- SAFE_CLOSE(stream->hCloseRequest);
- SAFE_CLOSE(stream->hBlockingOpStreamRD);
- SAFE_CLOSE(stream->hBlockingOpStreamWR);
+ // Close thread handles to allow restart
+ SAFE_CLOSE(stream->hThread);
+ SAFE_CLOSE(stream->hThreadStart);
+ SAFE_CLOSE(stream->hThreadExit);
+ SAFE_CLOSE(stream->hCloseRequest);
+ SAFE_CLOSE(stream->hBlockingOpStreamRD);
+ SAFE_CLOSE(stream->hBlockingOpStreamWR);
}
// ------------------------------------------------------------------------------------------
static PaError StopStream( PaStream *s )
{
- // Finish stream
- _StreamFinish((PaWasapiStream *)s);
+ // Finish stream
+ StopStreamByUser((PaWasapiStream *)s);
return paNoError;
}
// ------------------------------------------------------------------------------------------
static PaError AbortStream( PaStream *s )
{
- // Finish stream
- _StreamFinish((PaWasapiStream *)s);
+ // Finish stream
+ StopStreamByUser((PaWasapiStream *)s);
return paNoError;
}
// ------------------------------------------------------------------------------------------
static PaError IsStreamStopped( PaStream *s )
{
- return !((PaWasapiStream *)s)->running;
+ return ((PaWasapiStream *)s)->isStopped;
}
// ------------------------------------------------------------------------------------------
static PaError IsStreamActive( PaStream *s )
{
- return ((PaWasapiStream *)s)->running;
+ return ((PaWasapiStream *)s)->isActive;
}
// ------------------------------------------------------------------------------------------
@@ -3966,40 +4594,52 @@ static PaTime GetStreamTime( PaStream *s )
// ------------------------------------------------------------------------------------------
static double GetStreamCpuLoad( PaStream* s )
{
- return PaUtil_GetCpuLoad(&((PaWasapiStream *)s)->cpuLoadMeasurer);
+ return PaUtil_GetCpuLoad(&((PaWasapiStream *)s)->cpuLoadMeasurer);
}
// ------------------------------------------------------------------------------------------
-static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames )
+static inline BOOL CheckForStopOrWait(PaWasapiStream *stream, UINT32 delay)
{
- PaWasapiStream *stream = (PaWasapiStream*)s;
-
- HRESULT hr = S_OK;
- BYTE *user_buffer = (BYTE *)_buffer;
- BYTE *wasapi_buffer = NULL;
- DWORD flags = 0;
- UINT32 i, available, sleep = 0;
- unsigned long processed;
- ThreadIdleScheduler sched;
+ return (WaitForSingleObject(stream->hCloseRequest, delay) != WAIT_TIMEOUT);
+}
- // validate
- if (!stream->running)
- return paStreamIsStopped;
- if (stream->captureClient == NULL)
- return paBadStreamPtr;
+// ------------------------------------------------------------------------------------------
+static inline BOOL CheckForStop(PaWasapiStream *stream)
+{
+ return CheckForStopOrWait(stream, 0);
+}
- // Notify blocking op has begun
- ResetEvent(stream->hBlockingOpStreamRD);
+// ------------------------------------------------------------------------------------------
+static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames )
+{
+ PaWasapiStream *stream = (PaWasapiStream*)s;
- // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than
- // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting
- ThreadIdleScheduler_Setup(&sched, 1, 250/* microseconds */);
+ HRESULT hr = S_OK;
+ BYTE *user_buffer = (BYTE *)_buffer;
+ BYTE *wasapi_buffer = NULL;
+ DWORD flags = 0;
+ UINT32 i, available, sleep = 0;
+ unsigned long processed;
+ ThreadIdleScheduler sched;
+
+ // validate
+ if (!stream->isActive)
+ return paStreamIsStopped;
+ if (stream->captureClient == NULL)
+ return paBadStreamPtr;
+
+ // Notify blocking op has begun
+ ResetEvent(stream->hBlockingOpStreamRD);
+
+ // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than
+ // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting
+ ThreadIdleScheduler_Setup(&sched, 1, 250/* microseconds */);
// Make a local copy of the user buffer pointer(s), this is necessary
- // because PaUtil_CopyOutput() advances these pointers every time it is called
+ // because PaUtil_CopyOutput() advances these pointers every time it is called
if (!stream->bufferProcessor.userInputIsInterleaved)
{
- user_buffer = (BYTE *)alloca(sizeof(BYTE *) * stream->bufferProcessor.inputChannelCount);
+ user_buffer = (BYTE *)alloca(sizeof(BYTE *) * stream->bufferProcessor.inputChannelCount);
if (user_buffer == NULL)
return paInsufficientMemory;
@@ -4007,145 +4647,145 @@ static PaError ReadStream( PaStream* s, void *_buffer, unsigned long frames )
((BYTE **)user_buffer)[i] = ((BYTE **)_buffer)[i];
}
- // Findout if there are tail frames, flush them all before reading hardware
- if ((available = PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer)) != 0)
- {
- ring_buffer_size_t buf1_size = 0, buf2_size = 0, read, desired;
- void *buf1 = NULL, *buf2 = NULL;
-
- // Limit desired to amount of requested frames
- desired = available;
- if ((UINT32)desired > frames)
- desired = frames;
-
- // Get pointers to read regions
- read = PaUtil_GetRingBufferReadRegions(stream->in.tailBuffer, desired, &buf1, &buf1_size, &buf2, &buf2_size);
-
- if (buf1 != NULL)
- {
- // Register available frames to processor
- PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf1_size);
-
- // Register host buffer pointer to processor
- PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf1, stream->bufferProcessor.inputChannelCount);
-
- // Copy user data to host buffer (with conversion if applicable)
- processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf1_size);
- frames -= processed;
- }
-
- if (buf2 != NULL)
- {
- // Register available frames to processor
- PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf2_size);
-
- // Register host buffer pointer to processor
- PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf2, stream->bufferProcessor.inputChannelCount);
-
- // Copy user data to host buffer (with conversion if applicable)
- processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf2_size);
- frames -= processed;
- }
-
- // Advance
- PaUtil_AdvanceRingBufferReadIndex(stream->in.tailBuffer, read);
- }
-
- // Read hardware
- while (frames != 0)
- {
- // Check if blocking call must be interrupted
- if (WaitForSingleObject(stream->hCloseRequest, sleep) != WAIT_TIMEOUT)
- break;
-
- // Get available frames (must be finding out available frames before call to IAudioCaptureClient_GetBuffer
- // othervise audio glitches will occur inExclusive mode as it seems that WASAPI has some scheduling/
- // processing problems when such busy polling with IAudioCaptureClient_GetBuffer occurs)
- if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK)
- {
- LogHostError(hr);
- return paUnanticipatedHostError;
- }
-
- // Wait for more frames to become available
- if (available == 0)
- {
- // Exclusive mode may require latency of 1 millisecond, thus we shall sleep
- // around 500 microseconds (emulated) to collect packets in time
- if (stream->in.shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE)
- {
- UINT32 sleep_frames = (frames < stream->in.framesPerHostCallback ? frames : stream->in.framesPerHostCallback);
-
- sleep = GetFramesSleepTime(sleep_frames, stream->in.wavex.Format.nSamplesPerSec);
- sleep /= 4; // wait only for 1/4 of the buffer
-
- // WASAPI input provides packets, thus expiring packet will result in bad audio
- // limit waiting time to 2 seconds (will always work for smallest buffer in Shared)
- if (sleep > 2)
- sleep = 2;
-
- // Avoid busy waiting, schedule next 1 millesecond wait
- if (sleep == 0)
- sleep = ThreadIdleScheduler_NextSleep(&sched);
- }
- else
- {
- if ((sleep = ThreadIdleScheduler_NextSleep(&sched)) != 0)
- {
- Sleep(sleep);
- sleep = 0;
- }
- }
-
- continue;
- }
-
- // Get the available data in the shared buffer.
- if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &wasapi_buffer, &available, &flags, NULL, NULL)) != S_OK)
- {
- // Buffer size is too small, waiting
- if (hr != AUDCLNT_S_BUFFER_EMPTY)
- {
- LogHostError(hr);
- goto end;
- }
-
- continue;
- }
-
- // Register available frames to processor
+ // Find out if there are tail frames, flush them all before reading hardware
+ if ((available = PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer)) != 0)
+ {
+ ring_buffer_size_t buf1_size = 0, buf2_size = 0, read, desired;
+ void *buf1 = NULL, *buf2 = NULL;
+
+ // Limit desired to amount of requested frames
+ desired = available;
+ if ((UINT32)desired > frames)
+ desired = frames;
+
+ // Get pointers to read regions
+ read = PaUtil_GetRingBufferReadRegions(stream->in.tailBuffer, desired, &buf1, &buf1_size, &buf2, &buf2_size);
+
+ if (buf1 != NULL)
+ {
+ // Register available frames to processor
+ PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf1_size);
+
+ // Register host buffer pointer to processor
+ PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf1, stream->bufferProcessor.inputChannelCount);
+
+ // Copy user data to host buffer (with conversion if applicable)
+ processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf1_size);
+ frames -= processed;
+ }
+
+ if (buf2 != NULL)
+ {
+ // Register available frames to processor
+ PaUtil_SetInputFrameCount(&stream->bufferProcessor, buf2_size);
+
+ // Register host buffer pointer to processor
+ PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, buf2, stream->bufferProcessor.inputChannelCount);
+
+ // Copy user data to host buffer (with conversion if applicable)
+ processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, buf2_size);
+ frames -= processed;
+ }
+
+ // Advance
+ PaUtil_AdvanceRingBufferReadIndex(stream->in.tailBuffer, read);
+ }
+
+ // Read hardware
+ while (frames != 0)
+ {
+ // Check if blocking call must be interrupted
+ if (CheckForStopOrWait(stream, sleep))
+ break;
+
+ // Get available frames (must be finding out available frames before call to IAudioCaptureClient_GetBuffer
+ // othervise audio glitches will occur inExclusive mode as it seems that WASAPI has some scheduling/
+ // processing problems when such busy polling with IAudioCaptureClient_GetBuffer occurs)
+ if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK)
+ {
+ LogHostError(hr);
+ return paUnanticipatedHostError;
+ }
+
+ // Wait for more frames to become available
+ if (available == 0)
+ {
+ // Exclusive mode may require latency of 1 millisecond, thus we shall sleep
+ // around 500 microseconds (emulated) to collect packets in time
+ if (stream->in.shareMode != AUDCLNT_SHAREMODE_EXCLUSIVE)
+ {
+ UINT32 sleep_frames = (frames < stream->in.framesPerHostCallback ? frames : stream->in.framesPerHostCallback);
+
+ sleep = GetFramesSleepTime(sleep_frames, stream->in.wavexu.ext.Format.nSamplesPerSec);
+ sleep /= 4; // wait only for 1/4 of the buffer
+
+ // WASAPI input provides packets, thus expiring packet will result in bad audio
+ // limit waiting time to 2 seconds (will always work for smallest buffer in Shared)
+ if (sleep > 2)
+ sleep = 2;
+
+ // Avoid busy waiting, schedule next 1 millesecond wait
+ if (sleep == 0)
+ sleep = ThreadIdleScheduler_NextSleep(&sched);
+ }
+ else
+ {
+ if ((sleep = ThreadIdleScheduler_NextSleep(&sched)) != 0)
+ {
+ Sleep(sleep);
+ sleep = 0;
+ }
+ }
+
+ continue;
+ }
+
+ // Get the available data in the shared buffer.
+ if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &wasapi_buffer, &available, &flags, NULL, NULL)) != S_OK)
+ {
+ // Buffer size is too small, waiting
+ if (hr != AUDCLNT_S_BUFFER_EMPTY)
+ {
+ LogHostError(hr);
+ goto end;
+ }
+
+ continue;
+ }
+
+ // Register available frames to processor
PaUtil_SetInputFrameCount(&stream->bufferProcessor, available);
- // Register host buffer pointer to processor
+ // Register host buffer pointer to processor
PaUtil_SetInterleavedInputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.inputChannelCount);
- // Copy user data to host buffer (with conversion if applicable)
- processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, frames);
- frames -= processed;
+ // Copy user data to host buffer (with conversion if applicable)
+ processed = PaUtil_CopyInput(&stream->bufferProcessor, (void **)&user_buffer, frames);
+ frames -= processed;
- // Save tail into buffer
- if ((frames == 0) && (available > processed))
- {
- UINT32 bytes_processed = processed * stream->in.wavex.Format.nBlockAlign;
- UINT32 frames_to_save = available - processed;
+ // Save tail into buffer
+ if ((frames == 0) && (available > processed))
+ {
+ UINT32 bytes_processed = processed * stream->in.wavexu.ext.Format.nBlockAlign;
+ UINT32 frames_to_save = available - processed;
- PaUtil_WriteRingBuffer(stream->in.tailBuffer, wasapi_buffer + bytes_processed, frames_to_save);
- }
+ PaUtil_WriteRingBuffer(stream->in.tailBuffer, wasapi_buffer + bytes_processed, frames_to_save);
+ }
- // Release host buffer
- if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, available)) != S_OK)
- {
- LogHostError(hr);
- goto end;
- }
- }
+ // Release host buffer
+ if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, available)) != S_OK)
+ {
+ LogHostError(hr);
+ goto end;
+ }
+ }
end:
- // Notify blocking op has ended
- SetEvent(stream->hBlockingOpStreamRD);
+ // Notify blocking op has ended
+ SetEvent(stream->hBlockingOpStreamRD);
- return (hr != S_OK ? paUnanticipatedHostError : paNoError);
+ return (hr != S_OK ? paUnanticipatedHostError : paNoError);
}
// ------------------------------------------------------------------------------------------
@@ -4153,29 +4793,28 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long fram
{
PaWasapiStream *stream = (PaWasapiStream*)s;
- //UINT32 frames;
- const BYTE *user_buffer = (const BYTE *)_buffer;
- BYTE *wasapi_buffer;
- HRESULT hr = S_OK;
- UINT32 i, available, sleep = 0;
- unsigned long processed;
- ThreadIdleScheduler sched;
+ const BYTE *user_buffer = (const BYTE *)_buffer;
+ BYTE *wasapi_buffer;
+ HRESULT hr = S_OK;
+ UINT32 i, available, sleep = 0;
+ unsigned long processed;
+ ThreadIdleScheduler sched;
- // validate
- if (!stream->running)
- return paStreamIsStopped;
- if (stream->renderClient == NULL)
- return paBadStreamPtr;
+ // validate
+ if (!stream->isActive)
+ return paStreamIsStopped;
+ if (stream->renderClient == NULL)
+ return paBadStreamPtr;
- // Notify blocking op has begun
- ResetEvent(stream->hBlockingOpStreamWR);
+ // Notify blocking op has begun
+ ResetEvent(stream->hBlockingOpStreamWR);
- // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than
- // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting
- ThreadIdleScheduler_Setup(&sched, 1, 500/* microseconds */);
+ // Use thread scheduling for 500 microseconds (emulated) when wait time for frames is less than
+ // 1 milliseconds, emulation helps to normalize CPU consumption and avoids too busy waiting
+ ThreadIdleScheduler_Setup(&sched, 1, 500/* microseconds */);
// Make a local copy of the user buffer pointer(s), this is necessary
- // because PaUtil_CopyOutput() advances these pointers every time it is called
+ // because PaUtil_CopyOutput() advances these pointers every time it is called
if (!stream->bufferProcessor.userOutputIsInterleaved)
{
user_buffer = (const BYTE *)alloca(sizeof(const BYTE *) * stream->bufferProcessor.outputChannelCount);
@@ -4186,85 +4825,85 @@ static PaError WriteStream( PaStream* s, const void *_buffer, unsigned long fram
((const BYTE **)user_buffer)[i] = ((const BYTE **)_buffer)[i];
}
- // Blocking (potentially, untill 'frames' are consumed) loop
- while (frames != 0)
- {
- // Check if blocking call must be interrupted
- if (WaitForSingleObject(stream->hCloseRequest, sleep) != WAIT_TIMEOUT)
- break;
-
- // Get frames available
- if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK)
- {
- LogHostError(hr);
- goto end;
- }
-
- // Wait for more frames to become available
- if (available == 0)
- {
- UINT32 sleep_frames = (frames < stream->out.framesPerHostCallback ? frames : stream->out.framesPerHostCallback);
-
- sleep = GetFramesSleepTime(sleep_frames, stream->out.wavex.Format.nSamplesPerSec);
- sleep /= 2; // wait only for half of the buffer
-
- // Avoid busy waiting, schedule next 1 millesecond wait
- if (sleep == 0)
- sleep = ThreadIdleScheduler_NextSleep(&sched);
-
- continue;
- }
-
- // Keep in 'frmaes' range
- if (available > frames)
- available = frames;
-
- // Get pointer to host buffer
- if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, available, &wasapi_buffer)) != S_OK)
- {
- // Buffer size is too big, waiting
- if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
- continue;
-
- LogHostError(hr);
- goto end;
- }
-
- // Keep waiting again (on Vista it was noticed that WASAPI could SOMETIMES return NULL pointer
- // to buffer without returning AUDCLNT_E_BUFFER_TOO_LARGE instead)
- if (wasapi_buffer == NULL)
- continue;
-
- // Register available frames to processor
+ // Blocking (potentially, until 'frames' are consumed) loop
+ while (frames != 0)
+ {
+ // Check if blocking call must be interrupted
+ if (CheckForStopOrWait(stream, sleep))
+ break;
+
+ // Get frames available
+ if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK)
+ {
+ LogHostError(hr);
+ goto end;
+ }
+
+ // Wait for more frames to become available
+ if (available == 0)
+ {
+ UINT32 sleep_frames = (frames < stream->out.framesPerHostCallback ? frames : stream->out.framesPerHostCallback);
+
+ sleep = GetFramesSleepTime(sleep_frames, stream->out.wavexu.ext.Format.nSamplesPerSec);
+ sleep /= 2; // wait only for half of the buffer
+
+ // Avoid busy waiting, schedule next 1 millesecond wait
+ if (sleep == 0)
+ sleep = ThreadIdleScheduler_NextSleep(&sched);
+
+ continue;
+ }
+
+ // Keep in 'frames' range
+ if (available > frames)
+ available = frames;
+
+ // Get pointer to host buffer
+ if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, available, &wasapi_buffer)) != S_OK)
+ {
+ // Buffer size is too big, waiting
+ if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
+ continue;
+
+ LogHostError(hr);
+ goto end;
+ }
+
+ // Keep waiting again (on Vista it was noticed that WASAPI could SOMETIMES return NULL pointer
+ // to buffer without returning AUDCLNT_E_BUFFER_TOO_LARGE instead)
+ if (wasapi_buffer == NULL)
+ continue;
+
+ // Register available frames to processor
PaUtil_SetOutputFrameCount(&stream->bufferProcessor, available);
- // Register host buffer pointer to processor
- PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.outputChannelCount);
+ // Register host buffer pointer to processor
+ PaUtil_SetInterleavedOutputChannels(&stream->bufferProcessor, 0, wasapi_buffer, stream->bufferProcessor.outputChannelCount);
- // Copy user data to host buffer (with conversion if applicable), this call will advance
- // pointer 'user_buffer' to consumed portion of data
- processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, frames);
- frames -= processed;
+ // Copy user data to host buffer (with conversion if applicable), this call will advance
+ // pointer 'user_buffer' to consumed portion of data
+ processed = PaUtil_CopyOutput(&stream->bufferProcessor, (const void **)&user_buffer, frames);
+ frames -= processed;
- // Release host buffer
- if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, available, 0)) != S_OK)
- {
- LogHostError(hr);
- goto end;
- }
- }
+ // Release host buffer
+ if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, available, 0)) != S_OK)
+ {
+ LogHostError(hr);
+ goto end;
+ }
+ }
end:
- // Notify blocking op has ended
- SetEvent(stream->hBlockingOpStreamWR);
+ // Notify blocking op has ended
+ SetEvent(stream->hBlockingOpStreamWR);
- return (hr != S_OK ? paUnanticipatedHostError : paNoError);
+ return (hr != S_OK ? paUnanticipatedHostError : paNoError);
}
unsigned long PaUtil_GetOutputFrameCount( PaUtilBufferProcessor* bp )
{
- return bp->hostOutputFrameCount[0];
+ return bp->hostOutputFrameCount[0];
}
// ------------------------------------------------------------------------------------------
@@ -4272,24 +4911,24 @@ static signed long GetStreamReadAvailable( PaStream* s )
{
PaWasapiStream *stream = (PaWasapiStream*)s;
- HRESULT hr;
- UINT32 available = 0;
+ HRESULT hr;
+ UINT32 available = 0;
- // validate
- if (!stream->running)
- return paStreamIsStopped;
- if (stream->captureClient == NULL)
- return paBadStreamPtr;
+ // validate
+ if (!stream->isActive)
+ return paStreamIsStopped;
+ if (stream->captureClient == NULL)
+ return paBadStreamPtr;
- // available in hardware buffer
- if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK)
- {
- LogHostError(hr);
- return paUnanticipatedHostError;
- }
+ // available in hardware buffer
+ if ((hr = _PollGetInputFramesAvailable(stream, &available)) != S_OK)
+ {
+ LogHostError(hr);
+ return paUnanticipatedHostError;
+ }
- // available in software tail buffer
- available += PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer);
+ // available in software tail buffer
+ available += PaUtil_GetRingBufferReadAvailable(stream->in.tailBuffer);
return available;
}
@@ -4298,22 +4937,22 @@ static signed long GetStreamReadAvailable( PaStream* s )
static signed long GetStreamWriteAvailable( PaStream* s )
{
PaWasapiStream *stream = (PaWasapiStream*)s;
- HRESULT hr;
- UINT32 available = 0;
+ HRESULT hr;
+ UINT32 available = 0;
- // validate
- if (!stream->running)
- return paStreamIsStopped;
- if (stream->renderClient == NULL)
- return paBadStreamPtr;
+ // validate
+ if (!stream->isActive)
+ return paStreamIsStopped;
+ if (stream->renderClient == NULL)
+ return paBadStreamPtr;
- if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK)
- {
- LogHostError(hr);
- return paUnanticipatedHostError;
- }
+ if ((hr = _PollGetOutputFramesAvailable(stream, &available)) != S_OK)
+ {
+ LogHostError(hr);
+ return paUnanticipatedHostError;
+ }
- return (signed long)available;
+ return (signed long)available;
}
@@ -4324,42 +4963,42 @@ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
{
PaWasapiStream *stream = (PaWasapiStream*)userData;
PaStreamCallbackTimeInfo timeInfo = {0,0,0};
- PaStreamCallbackFlags flags = 0;
+ PaStreamCallbackFlags flags = 0;
int callbackResult;
unsigned long framesProcessed;
- HRESULT hr;
- UINT32 pending;
+ HRESULT hr;
+ UINT32 pending;
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
/*
- Pa_GetStreamTime:
+ Pa_GetStreamTime:
- generate timing information
- handle buffer slips
*/
- timeInfo.currentTime = PaUtil_GetTime();
- // Query input latency
- if (stream->in.clientProc != NULL)
- {
- PaTime pending_time;
- if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, &pending)) == S_OK)
- pending_time = (PaTime)pending / (PaTime)stream->in.wavex.Format.nSamplesPerSec;
- else
- pending_time = (PaTime)stream->in.latencySeconds;
-
- timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time;
- }
- // Query output current latency
- if (stream->out.clientProc != NULL)
- {
- PaTime pending_time;
- if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &pending)) == S_OK)
- pending_time = (PaTime)pending / (PaTime)stream->out.wavex.Format.nSamplesPerSec;
- else
- pending_time = (PaTime)stream->out.latencySeconds;
-
- timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time;
- }
+ timeInfo.currentTime = PaUtil_GetTime();
+ // Query input latency
+ if (stream->in.clientProc != NULL)
+ {
+ PaTime pending_time;
+ if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, &pending)) == S_OK)
+ pending_time = (PaTime)pending / (PaTime)stream->in.wavexu.ext.Format.nSamplesPerSec;
+ else
+ pending_time = (PaTime)stream->in.latencySeconds;
+
+ timeInfo.inputBufferAdcTime = timeInfo.currentTime + pending_time;
+ }
+ // Query output current latency
+ if (stream->out.clientProc != NULL)
+ {
+ PaTime pending_time;
+ if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &pending)) == S_OK)
+ pending_time = (PaTime)pending / (PaTime)stream->out.wavexu.ext.Format.nSamplesPerSec;
+ else
+ pending_time = (PaTime)stream->out.latencySeconds;
+
+ timeInfo.outputBufferDacTime = timeInfo.currentTime + pending_time;
+ }
/*
If you need to byte swap or shift inputBuffer to convert it into a
@@ -4406,280 +5045,292 @@ static void WaspiHostProcessingLoop( void *inputBuffer, long inputFrames,
host format, do it here.
*/
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
- if (callbackResult == paContinue)
- {
- /* nothing special to do */
- }
- else
- if (callbackResult == paAbort)
- {
- // stop stream
- SetEvent(stream->hCloseRequest);
- }
- else
+ if (callbackResult != paContinue)
{
- // stop stream
+ // schedule stream stop
SetEvent(stream->hCloseRequest);
}
}
// ------------------------------------------------------------------------------------------
-HANDLE MMCSS_activate(const char *name)
-{
#ifndef PA_WINRT
+static PaError MMCSS_activate(PaWasapiThreadPriority nPriorityClass, HANDLE *ret)
+{
+ static const char *mmcs_name[] =
+ {
+ NULL,
+ "Audio",
+ "Capture",
+ "Distribution",
+ "Games",
+ "Playback",
+ "Pro Audio",
+ "Window Manager"
+ };
+
DWORD task_idx = 0;
- HANDLE hTask = pAvSetMmThreadCharacteristics(name, &task_idx);
- if (hTask == NULL)
- {
- PRINT(("WASAPI: AvSetMmThreadCharacteristics failed!\n"));
+ HANDLE hTask;
+
+ if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name))
+ return paUnanticipatedHostError;
+
+ if ((hTask = pAvSetMmThreadCharacteristics(mmcs_name[nPriorityClass], &task_idx)) == NULL)
+ {
+ PRINT(("WASAPI: AvSetMmThreadCharacteristics failed: error[%d]\n", GetLastError()));
+ return paUnanticipatedHostError;
}
/*BOOL priority_ok = pAvSetMmThreadPriority(hTask, AVRT_PRIORITY_NORMAL);
if (priority_ok == FALSE)
- {
+ {
PRINT(("WASAPI: AvSetMmThreadPriority failed!\n"));
}*/
- // debug
+ // debug
{
- int cur_priority = GetThreadPriority(GetCurrentThread());
+ int cur_priority = GetThreadPriority(GetCurrentThread());
DWORD cur_priority_class = GetPriorityClass(GetCurrentProcess());
- PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class));
+ PRINT(("WASAPI: thread[ priority-0x%X class-0x%X ]\n", cur_priority, cur_priority_class));
}
- return hTask;
-#else
- (void)name;
- return NULL;
-#endif
+ (*ret) = hTask;
+ return paNoError;
}
+#endif
// ------------------------------------------------------------------------------------------
-void MMCSS_deactivate(HANDLE hTask)
-{
- if (!hTask)
- return;
-
#ifndef PA_WINRT
- if (pAvRevertMmThreadCharacteristics(hTask) == FALSE)
- {
+static void MMCSS_deactivate(HANDLE hTask)
+{
+ if (pAvRevertMmThreadCharacteristics(hTask) == FALSE)
+ {
PRINT(("WASAPI: AvRevertMmThreadCharacteristics failed!\n"));
}
-#endif
}
+#endif
// ------------------------------------------------------------------------------------------
-PaError PaWasapi_ThreadPriorityBoost(void **hTask, PaWasapiThreadPriority nPriorityClass)
+PaError PaWasapi_ThreadPriorityBoost(void **pTask, PaWasapiThreadPriority priorityClass)
{
- static const char *mmcs_name[] =
- {
- NULL,
- "Audio",
- "Capture",
- "Distribution",
- "Games",
- "Playback",
- "Pro Audio",
- "Window Manager"
- };
- HANDLE task;
+ HANDLE task;
+ PaError ret;
+
+ if (pTask == NULL)
+ return paUnanticipatedHostError;
+
+#ifndef PA_WINRT
+ if ((ret = MMCSS_activate(priorityClass, &task)) != paNoError)
+ return ret;
+#else
+ switch (priorityClass)
+ {
+ case eThreadPriorityAudio:
+ case eThreadPriorityProAudio: {
+
+ // Save previous thread priority
+ intptr_t priority_prev = GetThreadPriority(GetCurrentThread());
+
+ // Try set new thread priority
+ if (SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) == FALSE)
+ return paUnanticipatedHostError;
- if (hTask == NULL)
- return paUnanticipatedHostError;
+ // Memorize prev priority (pretend to be non NULL pointer by adding 0x80000000 mask)
+ task = (HANDLE)(priority_prev | 0x80000000);
- if ((UINT32)nPriorityClass >= STATIC_ARRAY_SIZE(mmcs_name))
- return paUnanticipatedHostError;
+ ret = paNoError;
- task = MMCSS_activate(mmcs_name[nPriorityClass]);
- if (task == NULL)
- return paUnanticipatedHostError;
+ break; }
- (*hTask) = task;
- return paNoError;
+ default:
+ return paUnanticipatedHostError;
+ }
+#endif
+
+ (*pTask) = task;
+ return ret;
}
// ------------------------------------------------------------------------------------------
-PaError PaWasapi_ThreadPriorityRevert(void *hTask)
+PaError PaWasapi_ThreadPriorityRevert(void *pTask)
{
- if (hTask == NULL)
- return paUnanticipatedHostError;
+ if (pTask == NULL)
+ return paUnanticipatedHostError;
- MMCSS_deactivate((HANDLE)hTask);
+#ifndef PA_WINRT
+ MMCSS_deactivate((HANDLE)pTask);
+#else
+ // Revert previous priority by removing 0x80000000 mask
+ if (SetThreadPriority(GetCurrentThread(), (int)((intptr_t)pTask & ~0x80000000)) == FALSE)
+ return paUnanticipatedHostError;
+#endif
- return paNoError;
+ return paNoError;
}
// ------------------------------------------------------------------------------------------
// Described at:
// http://msdn.microsoft.com/en-us/library/dd371387(v=VS.85).aspx
-PaError PaWasapi_GetJackCount(PaDeviceIndex nDevice, int *jcount)
+PaError PaWasapi_GetJackCount(PaDeviceIndex device, int *pJackCount)
{
#ifndef PA_WINRT
- PaError ret;
- HRESULT hr = S_OK;
- PaDeviceIndex index;
+ PaError ret;
+ HRESULT hr = S_OK;
+ PaWasapiDeviceInfo *deviceInfo;
IDeviceTopology *pDeviceTopology = NULL;
IConnector *pConnFrom = NULL;
IConnector *pConnTo = NULL;
IPart *pPart = NULL;
IKsJackDescription *pJackDesc = NULL;
- UINT jackCount = 0;
+ UINT jackCount = 0;
- PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
- if (paWasapi == NULL)
- return paNotInitialized;
+ if (pJackCount == NULL)
+ return paUnanticipatedHostError;
- // Get device index.
- ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
- if (ret != paNoError)
+ if ((ret = _GetWasapiDeviceInfoByDeviceIndex(&deviceInfo, device)) != paNoError)
return ret;
- // Validate index.
- if ((UINT32)index >= paWasapi->deviceCount)
- return paInvalidDevice;
+ // Get the endpoint device's IDeviceTopology interface
+ hr = IMMDevice_Activate(deviceInfo->device, &pa_IID_IDeviceTopology,
+ CLSCTX_INPROC_SERVER, NULL, (void**)&pDeviceTopology);
+ IF_FAILED_JUMP(hr, error);
- // Get the endpoint device's IDeviceTopology interface.
- hr = IMMDevice_Activate(paWasapi->devInfo[index].device, &pa_IID_IDeviceTopology,
- CLSCTX_INPROC_SERVER, NULL, (void**)&pDeviceTopology);
- IF_FAILED_JUMP(hr, error);
+ // The device topology for an endpoint device always contains just one connector (connector number 0)
+ hr = IDeviceTopology_GetConnector(pDeviceTopology, 0, &pConnFrom);
+ IF_FAILED_JUMP(hr, error);
- // The device topology for an endpoint device always contains just one connector (connector number 0).
- hr = IDeviceTopology_GetConnector(pDeviceTopology, 0, &pConnFrom);
- IF_FAILED_JUMP(hr, error);
-
- // Step across the connection to the jack on the adapter.
- hr = IConnector_GetConnectedTo(pConnFrom, &pConnTo);
+ // Step across the connection to the jack on the adapter
+ hr = IConnector_GetConnectedTo(pConnFrom, &pConnTo);
if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
{
- // The adapter device is not currently active.
+ // The adapter device is not currently active
hr = E_NOINTERFACE;
}
- IF_FAILED_JUMP(hr, error);
+ IF_FAILED_JUMP(hr, error);
- // Get the connector's IPart interface.
- hr = IConnector_QueryInterface(pConnTo, &pa_IID_IPart, (void**)&pPart);
- IF_FAILED_JUMP(hr, error);
+ // Get the connector's IPart interface
+ hr = IConnector_QueryInterface(pConnTo, &pa_IID_IPart, (void**)&pPart);
+ IF_FAILED_JUMP(hr, error);
- // Activate the connector's IKsJackDescription interface.
- hr = IPart_Activate(pPart, CLSCTX_INPROC_SERVER, &pa_IID_IKsJackDescription, (void**)&pJackDesc);
- IF_FAILED_JUMP(hr, error);
+ // Activate the connector's IKsJackDescription interface
+ hr = IPart_Activate(pPart, CLSCTX_INPROC_SERVER, &pa_IID_IKsJackDescription, (void**)&pJackDesc);
+ IF_FAILED_JUMP(hr, error);
- // Return jack count for this device.
- hr = IKsJackDescription_GetJackCount(pJackDesc, &jackCount);
- IF_FAILED_JUMP(hr, error);
+ // Return jack count for this device
+ hr = IKsJackDescription_GetJackCount(pJackDesc, &jackCount);
+ IF_FAILED_JUMP(hr, error);
- // Set.
- (*jcount) = jackCount;
+ // Set.
+ (*pJackCount) = jackCount;
- // Ok.
- ret = paNoError;
+ // Ok.
+ ret = paNoError;
error:
- SAFE_RELEASE(pDeviceTopology);
- SAFE_RELEASE(pConnFrom);
- SAFE_RELEASE(pConnTo);
- SAFE_RELEASE(pPart);
- SAFE_RELEASE(pJackDesc);
+ SAFE_RELEASE(pDeviceTopology);
+ SAFE_RELEASE(pConnFrom);
+ SAFE_RELEASE(pConnTo);
+ SAFE_RELEASE(pPart);
+ SAFE_RELEASE(pJackDesc);
- LogHostError(hr);
- return paNoError;
+ LogHostError(hr);
+ return paNoError;
#else
- (void)nDevice;
- (void)jcount;
- return paUnanticipatedHostError;
+ (void)device;
+ (void)pJackCount;
+ return paUnanticipatedHostError;
#endif
}
// ------------------------------------------------------------------------------------------
#ifndef PA_WINRT
-static PaWasapiJackConnectionType ConvertJackConnectionTypeWASAPIToPA(int connType)
+static PaWasapiJackConnectionType _ConvertJackConnectionTypeWASAPIToPA(int connType)
{
- switch (connType)
- {
- case eConnTypeUnknown: return eJackConnTypeUnknown;
+ switch (connType)
+ {
+ case eConnTypeUnknown: return eJackConnTypeUnknown;
#ifdef _KS_
- case eConnType3Point5mm: return eJackConnType3Point5mm;
+ case eConnType3Point5mm: return eJackConnType3Point5mm;
#else
- case eConnTypeEighth: return eJackConnType3Point5mm;
+ case eConnTypeEighth: return eJackConnType3Point5mm;
#endif
- case eConnTypeQuarter: return eJackConnTypeQuarter;
- case eConnTypeAtapiInternal: return eJackConnTypeAtapiInternal;
- case eConnTypeRCA: return eJackConnTypeRCA;
- case eConnTypeOptical: return eJackConnTypeOptical;
- case eConnTypeOtherDigital: return eJackConnTypeOtherDigital;
- case eConnTypeOtherAnalog: return eJackConnTypeOtherAnalog;
- case eConnTypeMultichannelAnalogDIN: return eJackConnTypeMultichannelAnalogDIN;
- case eConnTypeXlrProfessional: return eJackConnTypeXlrProfessional;
- case eConnTypeRJ11Modem: return eJackConnTypeRJ11Modem;
- case eConnTypeCombination: return eJackConnTypeCombination;
- }
- return eJackConnTypeUnknown;
+ case eConnTypeQuarter: return eJackConnTypeQuarter;
+ case eConnTypeAtapiInternal: return eJackConnTypeAtapiInternal;
+ case eConnTypeRCA: return eJackConnTypeRCA;
+ case eConnTypeOptical: return eJackConnTypeOptical;
+ case eConnTypeOtherDigital: return eJackConnTypeOtherDigital;
+ case eConnTypeOtherAnalog: return eJackConnTypeOtherAnalog;
+ case eConnTypeMultichannelAnalogDIN: return eJackConnTypeMultichannelAnalogDIN;
+ case eConnTypeXlrProfessional: return eJackConnTypeXlrProfessional;
+ case eConnTypeRJ11Modem: return eJackConnTypeRJ11Modem;
+ case eConnTypeCombination: return eJackConnTypeCombination;
+ }
+ return eJackConnTypeUnknown;
}
#endif
// ------------------------------------------------------------------------------------------
#ifndef PA_WINRT
-static PaWasapiJackGeoLocation ConvertJackGeoLocationWASAPIToPA(int geoLoc)
-{
- switch (geoLoc)
- {
- case eGeoLocRear: return eJackGeoLocRear;
- case eGeoLocFront: return eJackGeoLocFront;
- case eGeoLocLeft: return eJackGeoLocLeft;
- case eGeoLocRight: return eJackGeoLocRight;
- case eGeoLocTop: return eJackGeoLocTop;
- case eGeoLocBottom: return eJackGeoLocBottom;
+static PaWasapiJackGeoLocation _ConvertJackGeoLocationWASAPIToPA(int geoLoc)
+{
+ switch (geoLoc)
+ {
+ case eGeoLocRear: return eJackGeoLocRear;
+ case eGeoLocFront: return eJackGeoLocFront;
+ case eGeoLocLeft: return eJackGeoLocLeft;
+ case eGeoLocRight: return eJackGeoLocRight;
+ case eGeoLocTop: return eJackGeoLocTop;
+ case eGeoLocBottom: return eJackGeoLocBottom;
#ifdef _KS_
- case eGeoLocRearPanel: return eJackGeoLocRearPanel;
+ case eGeoLocRearPanel: return eJackGeoLocRearPanel;
#else
- case eGeoLocRearOPanel: return eJackGeoLocRearPanel;
+ case eGeoLocRearOPanel: return eJackGeoLocRearPanel;
#endif
- case eGeoLocRiser: return eJackGeoLocRiser;
- case eGeoLocInsideMobileLid: return eJackGeoLocInsideMobileLid;
- case eGeoLocDrivebay: return eJackGeoLocDrivebay;
- case eGeoLocHDMI: return eJackGeoLocHDMI;
- case eGeoLocOutsideMobileLid: return eJackGeoLocOutsideMobileLid;
- case eGeoLocATAPI: return eJackGeoLocATAPI;
- }
- return eJackGeoLocUnk;
+ case eGeoLocRiser: return eJackGeoLocRiser;
+ case eGeoLocInsideMobileLid: return eJackGeoLocInsideMobileLid;
+ case eGeoLocDrivebay: return eJackGeoLocDrivebay;
+ case eGeoLocHDMI: return eJackGeoLocHDMI;
+ case eGeoLocOutsideMobileLid: return eJackGeoLocOutsideMobileLid;
+ case eGeoLocATAPI: return eJackGeoLocATAPI;
+ }
+ return eJackGeoLocUnk;
}
#endif
// ------------------------------------------------------------------------------------------
#ifndef PA_WINRT
-static PaWasapiJackGenLocation ConvertJackGenLocationWASAPIToPA(int genLoc)
+static PaWasapiJackGenLocation _ConvertJackGenLocationWASAPIToPA(int genLoc)
{
- switch (genLoc)
- {
- case eGenLocPrimaryBox: return eJackGenLocPrimaryBox;
- case eGenLocInternal: return eJackGenLocInternal;
+ switch (genLoc)
+ {
+ case eGenLocPrimaryBox: return eJackGenLocPrimaryBox;
+ case eGenLocInternal: return eJackGenLocInternal;
#ifdef _KS_
- case eGenLocSeparate: return eJackGenLocSeparate;
+ case eGenLocSeparate: return eJackGenLocSeparate;
#else
- case eGenLocSeperate: return eJackGenLocSeparate;
+ case eGenLocSeperate: return eJackGenLocSeparate;
#endif
- case eGenLocOther: return eJackGenLocOther;
- }
- return eJackGenLocPrimaryBox;
+ case eGenLocOther: return eJackGenLocOther;
+ }
+ return eJackGenLocPrimaryBox;
}
#endif
// ------------------------------------------------------------------------------------------
#ifndef PA_WINRT
-static PaWasapiJackPortConnection ConvertJackPortConnectionWASAPIToPA(int portConn)
+static PaWasapiJackPortConnection _ConvertJackPortConnectionWASAPIToPA(int portConn)
{
- switch (portConn)
- {
- case ePortConnJack: return eJackPortConnJack;
- case ePortConnIntegratedDevice: return eJackPortConnIntegratedDevice;
- case ePortConnBothIntegratedAndJack:return eJackPortConnBothIntegratedAndJack;
- case ePortConnUnknown: return eJackPortConnUnknown;
- }
- return eJackPortConnJack;
+ switch (portConn)
+ {
+ case ePortConnJack: return eJackPortConnJack;
+ case ePortConnIntegratedDevice: return eJackPortConnIntegratedDevice;
+ case ePortConnBothIntegratedAndJack: return eJackPortConnBothIntegratedAndJack;
+ case ePortConnUnknown: return eJackPortConnUnknown;
+ }
+ return eJackPortConnJack;
}
#endif
@@ -4687,286 +5338,412 @@ static PaWasapiJackPortConnection ConvertJackPortConnectionWASAPIToPA(int portCo
// Described at:
// http://msdn.microsoft.com/en-us/library/dd371387(v=VS.85).aspx
-PaError PaWasapi_GetJackDescription(PaDeviceIndex nDevice, int jindex, PaWasapiJackDescription *pJackDescription)
+PaError PaWasapi_GetJackDescription(PaDeviceIndex device, int jackIndex, PaWasapiJackDescription *pJackDescription)
{
#ifndef PA_WINRT
- PaError ret;
- HRESULT hr = S_OK;
- PaDeviceIndex index;
+ PaError ret;
+ HRESULT hr = S_OK;
+ PaWasapiDeviceInfo *deviceInfo;
IDeviceTopology *pDeviceTopology = NULL;
IConnector *pConnFrom = NULL;
IConnector *pConnTo = NULL;
IPart *pPart = NULL;
IKsJackDescription *pJackDesc = NULL;
- KSJACK_DESCRIPTION jack = { 0 };
+ KSJACK_DESCRIPTION jack = { 0 };
- PaWasapiHostApiRepresentation *paWasapi = _GetHostApi(&ret);
- if (paWasapi == NULL)
- return paNotInitialized;
-
- // Get device index.
- ret = PaUtil_DeviceIndexToHostApiDeviceIndex(&index, nDevice, &paWasapi->inheritedHostApiRep);
- if (ret != paNoError)
+ if ((ret = _GetWasapiDeviceInfoByDeviceIndex(&deviceInfo, device)) != paNoError)
return ret;
- // Validate index.
- if ((UINT32)index >= paWasapi->deviceCount)
- return paInvalidDevice;
-
- // Get the endpoint device's IDeviceTopology interface.
- hr = IMMDevice_Activate(paWasapi->devInfo[index].device, &pa_IID_IDeviceTopology,
- CLSCTX_INPROC_SERVER, NULL, (void**)&pDeviceTopology);
- IF_FAILED_JUMP(hr, error);
+ // Get the endpoint device's IDeviceTopology interface
+ hr = IMMDevice_Activate(deviceInfo->device, &pa_IID_IDeviceTopology,
+ CLSCTX_INPROC_SERVER, NULL, (void**)&pDeviceTopology);
+ IF_FAILED_JUMP(hr, error);
- // The device topology for an endpoint device always contains just one connector (connector number 0).
- hr = IDeviceTopology_GetConnector(pDeviceTopology, 0, &pConnFrom);
- IF_FAILED_JUMP(hr, error);
+ // The device topology for an endpoint device always contains just one connector (connector number 0)
+ hr = IDeviceTopology_GetConnector(pDeviceTopology, 0, &pConnFrom);
+ IF_FAILED_JUMP(hr, error);
- // Step across the connection to the jack on the adapter.
- hr = IConnector_GetConnectedTo(pConnFrom, &pConnTo);
+ // Step across the connection to the jack on the adapter
+ hr = IConnector_GetConnectedTo(pConnFrom, &pConnTo);
if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
{
- // The adapter device is not currently active.
+ // The adapter device is not currently active
hr = E_NOINTERFACE;
}
- IF_FAILED_JUMP(hr, error);
+ IF_FAILED_JUMP(hr, error);
- // Get the connector's IPart interface.
- hr = IConnector_QueryInterface(pConnTo, &pa_IID_IPart, (void**)&pPart);
- IF_FAILED_JUMP(hr, error);
+ // Get the connector's IPart interface
+ hr = IConnector_QueryInterface(pConnTo, &pa_IID_IPart, (void**)&pPart);
+ IF_FAILED_JUMP(hr, error);
- // Activate the connector's IKsJackDescription interface.
- hr = IPart_Activate(pPart, CLSCTX_INPROC_SERVER, &pa_IID_IKsJackDescription, (void**)&pJackDesc);
- IF_FAILED_JUMP(hr, error);
+ // Activate the connector's IKsJackDescription interface
+ hr = IPart_Activate(pPart, CLSCTX_INPROC_SERVER, &pa_IID_IKsJackDescription, (void**)&pJackDesc);
+ IF_FAILED_JUMP(hr, error);
- // Test to return jack description struct for index 0.
- hr = IKsJackDescription_GetJackDescription(pJackDesc, jindex, &jack);
- IF_FAILED_JUMP(hr, error);
+ // Test to return jack description struct for index 0
+ hr = IKsJackDescription_GetJackDescription(pJackDesc, jackIndex, &jack);
+ IF_FAILED_JUMP(hr, error);
- // Convert WASAPI values to PA format.
- pJackDescription->channelMapping = jack.ChannelMapping;
- pJackDescription->color = jack.Color;
- pJackDescription->connectionType = ConvertJackConnectionTypeWASAPIToPA(jack.ConnectionType);
- pJackDescription->genLocation = ConvertJackGenLocationWASAPIToPA(jack.GenLocation);
- pJackDescription->geoLocation = ConvertJackGeoLocationWASAPIToPA(jack.GeoLocation);
- pJackDescription->isConnected = jack.IsConnected;
- pJackDescription->portConnection = ConvertJackPortConnectionWASAPIToPA(jack.PortConnection);
+ // Convert WASAPI values to PA format
+ pJackDescription->channelMapping = jack.ChannelMapping;
+ pJackDescription->color = jack.Color;
+ pJackDescription->connectionType = _ConvertJackConnectionTypeWASAPIToPA(jack.ConnectionType);
+ pJackDescription->genLocation = _ConvertJackGenLocationWASAPIToPA(jack.GenLocation);
+ pJackDescription->geoLocation = _ConvertJackGeoLocationWASAPIToPA(jack.GeoLocation);
+ pJackDescription->isConnected = jack.IsConnected;
+ pJackDescription->portConnection = _ConvertJackPortConnectionWASAPIToPA(jack.PortConnection);
- // Ok.
- ret = paNoError;
+ // Ok
+ ret = paNoError;
error:
- SAFE_RELEASE(pDeviceTopology);
- SAFE_RELEASE(pConnFrom);
- SAFE_RELEASE(pConnTo);
- SAFE_RELEASE(pPart);
- SAFE_RELEASE(pJackDesc);
+ SAFE_RELEASE(pDeviceTopology);
+ SAFE_RELEASE(pConnFrom);
+ SAFE_RELEASE(pConnTo);
+ SAFE_RELEASE(pPart);
+ SAFE_RELEASE(pJackDesc);
- LogHostError(hr);
- return ret;
+ LogHostError(hr);
+ return ret;
#else
- (void)nDevice;
- (void)jindex;
- (void)pJackDescription;
- return paUnanticipatedHostError;
+ (void)device;
+ (void)jackIndex;
+ (void)pJackDescription;
+ return paUnanticipatedHostError;
#endif
}
// ------------------------------------------------------------------------------------------
-HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
+PaError PaWasapi_GetAudioClient(PaStream *pStream, void **pAudioClient, int bOutput)
{
- HRESULT hr;
- UINT32 frames = stream->out.framesPerHostCallback,
- padding = 0;
+ PaWasapiStream *stream = (PaWasapiStream *)pStream;
+ if (stream == NULL)
+ return paBadStreamPtr;
+
+ if (pAudioClient == NULL)
+ return paUnanticipatedHostError;
- (*available) = 0;
+ (*pAudioClient) = (bOutput == TRUE ? stream->out.clientParent : stream->in.clientParent);
- // get read position
- if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding)) != S_OK)
- return LogHostError(hr);
+ return paNoError;
+}
- // get available
- frames -= padding;
+// ------------------------------------------------------------------------------------------
+#ifdef PA_WINRT
+static void CopyNameOrIdString(WCHAR *dst, const UINT32 dstMaxCount, const WCHAR *src)
+{
+ UINT32 i;
+
+ for (i = 0; i < dstMaxCount; ++i)
+ dst[i] = 0;
- // set
- (*available) = frames;
- return hr;
+ if (src != NULL)
+ {
+ for (i = 0; (src[i] != 0) && (i < dstMaxCount); ++i)
+ dst[i] = src[i];
+ }
}
+#endif
// ------------------------------------------------------------------------------------------
-HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
+PaError PaWasapiWinrt_SetDefaultDeviceId( const unsigned short *pId, int bOutput )
{
- HRESULT hr;
+#ifdef PA_WINRT
+ INT32 i;
+ PaWasapiWinrtDeviceListRole *role = (bOutput ? &g_DeviceListInfo.render : &g_DeviceListInfo.capture);
- (*available) = 0;
+ assert(STATIC_ARRAY_SIZE(role->defaultId) == PA_WASAPI_DEVICE_ID_LEN);
- // GetCurrentPadding() has opposite meaning to Output stream
- if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, available)) != S_OK)
- return LogHostError(hr);
+ // Validate Id length
+ if (pId != NULL)
+ {
+ for (i = 0; pId[i] != 0; ++i)
+ {
+ if (i >= PA_WASAPI_DEVICE_ID_LEN)
+ return paBufferTooBig;
+ }
+ }
+
+ // Set Id (or reset to all 0 if NULL is provided)
+ CopyNameOrIdString(role->defaultId, STATIC_ARRAY_SIZE(role->defaultId), pId);
- return hr;
+ return paNoError;
+#else
+ return paIncompatibleStreamHostApi;
+#endif
}
// ------------------------------------------------------------------------------------------
-HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)
+PaError PaWasapiWinrt_PopulateDeviceList( const unsigned short **pId, const unsigned short **pName,
+ const PaWasapiDeviceRole *pRole, unsigned int count, int bOutput )
{
- HRESULT hr;
- BYTE *data = NULL;
+#ifdef PA_WINRT
+ UINT32 i, j;
+ PaWasapiWinrtDeviceListRole *role = (bOutput ? &g_DeviceListInfo.render : &g_DeviceListInfo.capture);
- // Get buffer
- if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK)
- {
- if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED)
- {
- // Using GetCurrentPadding to overcome AUDCLNT_E_BUFFER_TOO_LARGE in
- // shared mode results in no sound in Event-driven mode (MSDN does not
- // document this, or is it WASAPI bug?), thus we better
- // try to acquire buffer next time when GetBuffer allows to do so.
-#if 0
- // Get Read position
- UINT32 padding = 0;
- hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding);
- if (hr != S_OK)
- return LogHostError(hr);
+ memset(&role->devices, 0, sizeof(role->devices));
+ role->deviceCount = 0;
- // Get frames to write
- frames -= padding;
- if (frames == 0)
- return S_OK;
+ if (count == 0)
+ return paNoError;
+ else
+ if (count > PA_WASAPI_DEVICE_MAX_COUNT)
+ return paBufferTooBig;
+
+ // pName or pRole are optional
+ if (pId == NULL)
+ return paInsufficientMemory;
+
+ // Validate Id and Name lengths
+ for (i = 0; i < count; ++i)
+ {
+ const unsigned short *id = pId[i];
+ const unsigned short *name = pName[i];
- if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK)
- return LogHostError(hr);
+ for (j = 0; id[j] != 0; ++j)
+ {
+ if (j >= PA_WASAPI_DEVICE_ID_LEN)
+ return paBufferTooBig;
+ }
+
+ for (j = 0; name[j] != 0; ++j)
+ {
+ if (j >= PA_WASAPI_DEVICE_NAME_LEN)
+ return paBufferTooBig;
+ }
+ }
+
+ // Set Id and Name (or reset to all 0 if NULL is provided)
+ for (i = 0; i < count; ++i)
+ {
+ CopyNameOrIdString(role->devices[i].id, STATIC_ARRAY_SIZE(role->devices[i].id), pId[i]);
+ CopyNameOrIdString(role->devices[i].name, STATIC_ARRAY_SIZE(role->devices[i].name), pName[i]);
+ role->devices[i].formFactor = (pRole != NULL ? (EndpointFormFactor)pRole[i] : UnknownFormFactor);
+
+ // Count device if it has at least the Id
+ role->deviceCount += (role->devices[i].id[0] != 0);
+ }
+
+ return paNoError;
#else
- if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
- return S_OK; // be silent in shared mode, try again next time
+ return paIncompatibleStreamHostApi;
#endif
- }
- else
- return LogHostError(hr);
- }
-
- // Process data
- if (stream->out.monoMixer != NULL)
- {
- // expand buffer
- UINT32 mono_frames_size = frames * (stream->out.wavex.Format.wBitsPerSample / 8);
- if (mono_frames_size > stream->out.monoBufferSize)
- stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
-
- // process
- processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);
-
- // mix 1 to 2 channels
- stream->out.monoMixer(data, stream->out.monoBuffer, frames);
- }
- else
- {
- processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData);
- }
-
- // Release buffer
- if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, frames, 0)) != S_OK)
- LogHostError(hr);
-
- return hr;
-}
-
-// ------------------------------------------------------------------------------------------
-HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)
-{
- HRESULT hr = S_OK;
- UINT32 frames;
- BYTE *data = NULL;
- DWORD flags = 0;
-
- for (;;)
- {
- // Check if blocking call must be interrupted
- if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
- break;
-
- // Findout if any frames available
- frames = 0;
- if ((hr = _PollGetInputFramesAvailable(stream, &frames)) != S_OK)
- return hr;
-
- // Empty/consumed buffer
- if (frames == 0)
- break;
-
- // Get the available data in the shared buffer.
- if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &data, &frames, &flags, NULL, NULL)) != S_OK)
- {
- if (hr == AUDCLNT_S_BUFFER_EMPTY)
- {
- hr = S_OK;
- break; // Empty/consumed buffer
- }
-
- return LogHostError(hr);
- break;
- }
-
- // Detect silence
- // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
- // data = NULL;
-
- // Process data
- if (stream->in.monoMixer != NULL)
- {
- // expand buffer
- UINT32 mono_frames_size = frames * (stream->in.wavex.Format.wBitsPerSample / 8);
- if (mono_frames_size > stream->in.monoBufferSize)
- stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
-
- // mix 1 to 2 channels
- stream->in.monoMixer(stream->in.monoBuffer, data, frames);
-
- // process
- processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);
- }
- else
- {
- processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData);
- }
-
- // Release buffer
- if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, frames)) != S_OK)
- return LogHostError(hr);
-
- //break;
- }
-
- return hr;
+}
+
+// ------------------------------------------------------------------------------------------
+PaError PaWasapi_SetStreamStateHandler( PaStream *pStream, PaWasapiStreamStateCallback fnStateHandler, void *pUserData )
+{
+ PaWasapiStream *stream = (PaWasapiStream *)pStream;
+ if (stream == NULL)
+ return paBadStreamPtr;
+
+ stream->fnStateHandler = fnStateHandler;
+ stream->pStateHandlerUserData = pUserData;
+
+ return paNoError;
+}
+
+// ------------------------------------------------------------------------------------------
+HRESULT _PollGetOutputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
+{
+ HRESULT hr;
+ UINT32 frames = stream->out.framesPerHostCallback,
+ padding;
+
+ (*available) = 0;
+
+ // Get read position
+ if ((hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding)) != S_OK)
+ return LogHostError(hr);
+
+ // Set available frame count
+ (*available) = frames - padding;
+
+ return S_OK;
+}
+
+// ------------------------------------------------------------------------------------------
+HRESULT _PollGetInputFramesAvailable(PaWasapiStream *stream, UINT32 *available)
+{
+ HRESULT hr;
+
+ (*available) = 0;
+
+ // GetCurrentPadding() has opposite meaning to Output stream
+ if ((hr = IAudioClient_GetCurrentPadding(stream->in.clientProc, available)) != S_OK)
+ return LogHostError(hr);
+
+ return S_OK;
+}
+
+// ------------------------------------------------------------------------------------------
+static HRESULT ProcessOutputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor, UINT32 frames)
+{
+ HRESULT hr;
+ BYTE *data;
+
+ // Get buffer
+ if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK)
+ {
+ // Both modes, Shared and Exclusive, can fail with AUDCLNT_E_BUFFER_TOO_LARGE error
+ #if 0
+ if (stream->out.shareMode == AUDCLNT_SHAREMODE_SHARED)
+ {
+ // Using GetCurrentPadding to overcome AUDCLNT_E_BUFFER_TOO_LARGE in
+ // shared mode results in no sound in Event-driven mode (MSDN does not
+ // document this, or is it WASAPI bug?), thus we better
+ // try to acquire buffer next time when GetBuffer allows to do so.
+ #if 0
+ // Get Read position
+ UINT32 padding = 0;
+ hr = IAudioClient_GetCurrentPadding(stream->out.clientProc, &padding);
+ if (hr != S_OK)
+ return LogHostError(hr);
+
+ // Get frames to write
+ frames -= padding;
+ if (frames == 0)
+ return S_OK;
+
+ if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, frames, &data)) != S_OK)
+ return LogHostError(hr);
+ #else
+ if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
+ return S_OK; // be silent in shared mode, try again next time
+ #endif
+ }
+ else
+ return LogHostError(hr);
+ #else
+ if (hr == AUDCLNT_E_BUFFER_TOO_LARGE)
+ return S_OK; // try again next time
+
+ return LogHostError(hr);
+ #endif
+ }
+
+ // Process data
+ if (stream->out.monoMixer != NULL)
+ {
+ // Expand buffer
+ UINT32 monoFrames = frames * (stream->out.wavexu.ext.Format.wBitsPerSample / 8);
+ if (monoFrames > stream->out.monoBufferSize)
+ {
+ stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = monoFrames));
+ if (stream->out.monoBuffer == NULL)
+ return LogHostError(hr = E_OUTOFMEMORY);
+ }
+
+ // Process
+ processor[S_OUTPUT].processor(NULL, 0, (BYTE *)stream->out.monoBuffer, frames, processor[S_OUTPUT].userData);
+
+ // Mix 1 to 2 channels
+ stream->out.monoMixer(data, stream->out.monoBuffer, frames);
+ }
+ else
+ {
+ processor[S_OUTPUT].processor(NULL, 0, data, frames, processor[S_OUTPUT].userData);
+ }
+
+ // Release buffer
+ if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, frames, 0)) != S_OK)
+ return LogHostError(hr);
+
+ return S_OK;
+}
+
+// ------------------------------------------------------------------------------------------
+static HRESULT ProcessInputBuffer(PaWasapiStream *stream, PaWasapiHostProcessor *processor)
+{
+ HRESULT hr;
+ UINT32 frames;
+ BYTE *data;
+ DWORD flags;
+
+ while (!CheckForStop(stream))
+ {
+ // Find out if any frames available
+ if ((hr = _PollGetInputFramesAvailable(stream, &frames)) != S_OK)
+ return hr;
+
+ // Empty/consumed buffer
+ if (frames == 0)
+ break;
+
+ // Get the available data in the shared buffer.
+ if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &data, &frames, &flags, NULL, NULL)) != S_OK)
+ {
+ if (hr == AUDCLNT_S_BUFFER_EMPTY)
+ {
+ hr = S_OK;
+ break; // Empty/consumed buffer
+ }
+
+ return LogHostError(hr);
+ }
+
+ // Detect silence
+ // if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
+ // data = NULL;
+
+ // Process data
+ if (stream->in.monoMixer != NULL)
+ {
+ // expand buffer
+ UINT32 monoFrames = frames * (stream->in.wavexu.ext.Format.wBitsPerSample / 8);
+ if (monoFrames > stream->in.monoBufferSize)
+ {
+ stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = monoFrames));
+ if (stream->in.monoBuffer == NULL)
+ return LogHostError(hr = E_OUTOFMEMORY);
+ }
+
+ // mix 1 to 2 channels
+ stream->in.monoMixer(stream->in.monoBuffer, data, frames);
+
+ // process
+ processor[S_INPUT].processor((BYTE *)stream->in.monoBuffer, frames, NULL, 0, processor[S_INPUT].userData);
+ }
+ else
+ {
+ processor[S_INPUT].processor(data, frames, NULL, 0, processor[S_INPUT].userData);
+ }
+
+ // Release buffer
+ if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, frames)) != S_OK)
+ return LogHostError(hr);
+ }
+
+ return S_OK;
}
// ------------------------------------------------------------------------------------------
void _StreamOnStop(PaWasapiStream *stream)
{
- // Stop INPUT/OUTPUT clients
- if (!stream->bBlocking)
- {
- if (stream->in.clientProc != NULL)
- IAudioClient_Stop(stream->in.clientProc);
- if (stream->out.clientProc != NULL)
- IAudioClient_Stop(stream->out.clientProc);
- }
- else
- {
- if (stream->in.clientParent != NULL)
- IAudioClient_Stop(stream->in.clientParent);
- if (stream->out.clientParent != NULL)
- IAudioClient_Stop(stream->out.clientParent);
- }
-
- // Restore thread priority
- if (stream->hAvTask != NULL)
- {
- PaWasapi_ThreadPriorityRevert(stream->hAvTask);
- stream->hAvTask = NULL;
- }
+ // Stop INPUT/OUTPUT clients
+ if (!stream->isBlocking)
+ {
+ if (stream->in.clientProc != NULL)
+ IAudioClient_Stop(stream->in.clientProc);
+ if (stream->out.clientProc != NULL)
+ IAudioClient_Stop(stream->out.clientProc);
+ }
+ else
+ {
+ if (stream->in.clientParent != NULL)
+ IAudioClient_Stop(stream->in.clientParent);
+ if (stream->out.clientParent != NULL)
+ IAudioClient_Stop(stream->out.clientParent);
+ }
+
+ // Restore thread priority
+ if (stream->hAvTask != NULL)
+ {
+ PaWasapi_ThreadPriorityRevert(stream->hAvTask);
+ stream->hAvTask = NULL;
+ }
// Notify
if (stream->streamRepresentation.streamFinishedCallback != NULL)
@@ -4974,46 +5751,78 @@ void _StreamOnStop(PaWasapiStream *stream)
}
// ------------------------------------------------------------------------------------------
+static BOOL PrepareComPointers(PaWasapiStream *stream, BOOL *threadComInitialized)
+{
+ HRESULT hr;
+
+ /*
+ If COM is already initialized CoInitialize will either return
+ FALSE, or RPC_E_CHANGED_MODE if it was initialized in a different
+ threading mode. In either case we shouldn't consider it an error
+ but we need to be careful to not call CoUninitialize() if
+ RPC_E_CHANGED_MODE was returned.
+ */
+ hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))
+ {
+ PRINT(("WASAPI: failed ProcThreadEvent CoInitialize"));
+ return FALSE;
+ }
+ if (hr != RPC_E_CHANGED_MODE)
+ *threadComInitialized = TRUE;
+
+ // Unmarshal stream pointers for safe COM operation
+ hr = UnmarshalStreamComPointers(stream);
+ if (hr != S_OK)
+ {
+ PRINT(("WASAPI: Error unmarshaling stream COM pointers. HRESULT: %i\n", hr));
+ CoUninitialize();
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+// ------------------------------------------------------------------------------------------
+static void FinishComPointers(PaWasapiStream *stream, BOOL threadComInitialized)
+{
+ // Release unmarshaled COM pointers
+ ReleaseUnmarshaledComPointers(stream);
+
+ // Cleanup COM for this thread
+ if (threadComInitialized == TRUE)
+ CoUninitialize();
+}
+
+// ------------------------------------------------------------------------------------------
PA_THREAD_FUNC ProcThreadEvent(void *param)
{
PaWasapiHostProcessor processor[S_COUNT];
- HRESULT hr;
- DWORD dwResult;
+ HRESULT hr = S_OK;
+ DWORD dwResult;
PaWasapiStream *stream = (PaWasapiStream *)param;
- PaWasapiHostProcessor defaultProcessor;
- BOOL set_event[S_COUNT] = { FALSE, FALSE };
- BOOL bWaitAllEvents = FALSE;
- BOOL bThreadComInitialized = FALSE;
-
- /*
- If COM is already initialized CoInitialize will either return
- FALSE, or RPC_E_CHANGED_MODE if it was initialized in a different
- threading mode. In either case we shouldn't consider it an error
- but we need to be careful to not call CoUninitialize() if
- RPC_E_CHANGED_MODE was returned.
- */
- hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
- if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))
- {
- PRINT(("WASAPI: failed ProcThreadEvent CoInitialize"));
- return (UINT32)paUnanticipatedHostError;
- }
- if (hr != RPC_E_CHANGED_MODE)
- bThreadComInitialized = TRUE;
-
- // Unmarshal stream pointers for safe COM operation
- hr = UnmarshalStreamComPointers(stream);
- if (hr != S_OK) {
- PRINT(("Error unmarshaling stream COM pointers. HRESULT: %i\n", hr));
- goto thread_end;
- }
-
- // Waiting on all events in case of Full-Duplex/Exclusive mode.
- if ((stream->in.clientProc != NULL) && (stream->out.clientProc != NULL))
- {
- bWaitAllEvents = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) &&
- (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE);
- }
+ PaWasapiHostProcessor defaultProcessor;
+ BOOL setEvent[S_COUNT] = { FALSE, FALSE };
+ BOOL waitAllEvents = FALSE;
+ BOOL threadComInitialized = FALSE;
+ SystemTimer timer;
+
+ // Notify: WASAPI-specific stream state
+ NotifyStateChanged(stream, paWasapiStreamStateThreadPrepare, ERROR_SUCCESS);
+
+ // Prepare COM pointers
+ if (!PrepareComPointers(stream, &threadComInitialized))
+ return (UINT32)paUnanticipatedHostError;
+
+ // Request fine (1 ms) granularity of the system timer functions for precise operation of waitable timers
+ SystemTimer_SetGranularity(&timer, 1);
+
+ // Waiting on all events in case of Full-Duplex/Exclusive mode.
+ if ((stream->in.clientProc != NULL) && (stream->out.clientProc != NULL))
+ {
+ waitAllEvents = (stream->in.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) &&
+ (stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE);
+ }
// Setup data processors
defaultProcessor.processor = WaspiHostProcessingLoop;
@@ -5021,243 +5830,298 @@ PA_THREAD_FUNC ProcThreadEvent(void *param)
processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
- // Boost thread priority
- PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
-
- // Create events
- if (stream->event[S_OUTPUT] == NULL)
- {
- stream->event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
- set_event[S_OUTPUT] = TRUE;
- }
- if (stream->event[S_INPUT] == NULL)
- {
- stream->event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
- set_event[S_INPUT] = TRUE;
- }
- if ((stream->event[S_OUTPUT] == NULL) || (stream->event[S_INPUT] == NULL))
- {
- PRINT(("WASAPI Thread: failed creating Input/Output event handle\n"));
- goto thread_error;
- }
-
- // Initialize event & start INPUT stream
- if (stream->in.clientProc)
- {
- // Create & set handle
- if (set_event[S_INPUT])
- {
- if ((hr = IAudioClient_SetEventHandle(stream->in.clientProc, stream->event[S_INPUT])) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
- }
-
- // Start
- if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
- }
-
- // Initialize event & start OUTPUT stream
- if (stream->out.clientProc)
- {
- // Create & set handle
- if (set_event[S_OUTPUT])
- {
- if ((hr = IAudioClient_SetEventHandle(stream->out.clientProc, stream->event[S_OUTPUT])) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
- }
-
- // Preload buffer before start
- if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
-
- // Start
- if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
-
- }
-
- // Signal: stream running
- stream->running = TRUE;
-
- // Notify: thread started
- SetEvent(stream->hThreadStart);
-
- // Processing Loop
- for (;;)
- {
- // 10 sec timeout (on timeout stream will auto-stop when processed by WAIT_TIMEOUT case)
- dwResult = WaitForMultipleObjects(S_COUNT, stream->event, bWaitAllEvents, 10*1000);
-
- // Check for close event (after wait for buffers to avoid any calls to user
- // callback when hCloseRequest was set)
- if (WaitForSingleObject(stream->hCloseRequest, 0) != WAIT_TIMEOUT)
- break;
-
- // Process S_INPUT/S_OUTPUT
- switch (dwResult)
- {
- case WAIT_TIMEOUT: {
- PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n"));
- goto thread_end;
- break; }
-
- // Input stream
- case WAIT_OBJECT_0 + S_INPUT: {
+ // Boost thread priority
+ PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
+
+ // Create events
+ if (stream->event[S_OUTPUT] == NULL)
+ {
+ stream->event[S_OUTPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ setEvent[S_OUTPUT] = TRUE;
+ }
+ if (stream->event[S_INPUT] == NULL)
+ {
+ stream->event[S_INPUT] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ setEvent[S_INPUT] = TRUE;
+ }
+ if ((stream->event[S_OUTPUT] == NULL) || (stream->event[S_INPUT] == NULL))
+ {
+ PRINT(("WASAPI Thread: failed creating Input/Output event handle\n"));
+ goto thread_error;
+ }
+
+ // Signal: stream active (reconfirm)
+ stream->isActive = TRUE;
+
+ // Notify: thread started
+ SetEvent(stream->hThreadStart);
+
+ // Initialize event & start INPUT stream
+ if (stream->in.clientProc)
+ {
+ // Create & set handle
+ if (setEvent[S_INPUT])
+ {
+ if ((hr = IAudioClient_SetEventHandle(stream->in.clientProc, stream->event[S_INPUT])) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+ }
+
+ // Start
+ if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+ }
+
+ // Initialize event & start OUTPUT stream
+ if (stream->out.clientProc)
+ {
+ // Create & set handle
+ if (setEvent[S_OUTPUT])
+ {
+ if ((hr = IAudioClient_SetEventHandle(stream->out.clientProc, stream->event[S_OUTPUT])) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+ }
+
+ // Preload buffer before start
+ if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+
+ // Start
+ if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+
+ }
+
+ // Notify: WASAPI-specific stream state
+ NotifyStateChanged(stream, paWasapiStreamStateThreadStart, ERROR_SUCCESS);
+
+ // Processing Loop
+ for (;;)
+ {
+ // 10 sec timeout (on timeout stream will auto-stop when processed by WAIT_TIMEOUT case)
+ dwResult = WaitForMultipleObjects(S_COUNT, stream->event, waitAllEvents, 10*1000);
+
+ // Check for close event (after wait for buffers to avoid any calls to user
+ // callback when hCloseRequest was set)
+ if (CheckForStop(stream))
+ break;
+
+ // Process S_INPUT/S_OUTPUT
+ switch (dwResult)
+ {
+ case WAIT_TIMEOUT: {
+ PRINT(("WASAPI Thread: WAIT_TIMEOUT - probably bad audio driver or Vista x64 bug: use paWinWasapiPolling instead\n"));
+ goto thread_end;
+ break; }
+
+ // Input stream
+ case WAIT_OBJECT_0 + S_INPUT: {
if (stream->captureClient == NULL)
break;
- if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
+ if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
- break; }
+ break; }
- // Output stream
- case WAIT_OBJECT_0 + S_OUTPUT: {
+ // Output stream
+ case WAIT_OBJECT_0 + S_OUTPUT: {
if (stream->renderClient == NULL)
break;
- if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
+ if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
- break; }
- }
- }
+ break; }
+ }
+ }
thread_end:
- // Process stop
- _StreamOnStop(stream);
+ // Process stop
+ _StreamOnStop(stream);
- // Release unmarshaled COM pointers
- ReleaseUnmarshaledComPointers(stream);
+ // Release unmarshaled COM pointers
+ FinishComPointers(stream, threadComInitialized);
- // Cleanup COM for this thread
- if (bThreadComInitialized == TRUE)
- CoUninitialize();
+ // Restore system timer granularity
+ SystemTimer_RestoreGranularity(&timer);
- // Notify: not running
- stream->running = FALSE;
+ // Notify: stream inactive
+ stream->isActive = FALSE;
- // Notify: thread exited
- SetEvent(stream->hThreadExit);
+ // Notify: thread exited
+ SetEvent(stream->hThreadExit);
- return 0;
+ // Notify: WASAPI-specific stream state
+ NotifyStateChanged(stream, paWasapiStreamStateThreadStop, hr);
+
+ return 0;
thread_error:
- // Prevent deadlocking in Pa_StreamStart
- SetEvent(stream->hThreadStart);
+ // Prevent deadlocking in Pa_StreamStart
+ SetEvent(stream->hThreadStart);
+
+ // Exit
+ goto thread_end;
+}
+
+// ------------------------------------------------------------------------------------------
+static UINT32 GetSleepTime(PaWasapiStream *stream, UINT32 sleepTimeIn, UINT32 sleepTimeOut, UINT32 userFramesOut)
+{
+ UINT32 sleepTime;
+
+ // According to the issue [https://github.com/PortAudio/portaudio/issues/303] glitches may occur when user frames
+ // equal to 1/2 of the host buffer frames, therefore the empirical workaround for this problem is to lower
+ // the sleep time by 2
+ if (userFramesOut != 0)
+ {
+ UINT32 chunks = stream->out.framesPerHostCallback / userFramesOut;
+ if (chunks <= 2)
+ {
+ sleepTimeOut /= 2;
+ PRINT(("WASAPI: underrun workaround, sleep [%d] ms - 1/2 of the user buffer[%d] | host buffer[%d]\n", sleepTimeOut, userFramesOut, stream->out.framesPerHostCallback));
+ }
+ }
+
+ // Choose the smallest
+ if ((sleepTimeIn != 0) && (sleepTimeOut != 0))
+ sleepTime = min(sleepTimeIn, sleepTimeOut);
+ else
+ sleepTime = (sleepTimeIn ? sleepTimeIn : sleepTimeOut);
- // Exit
- goto thread_end;
+ return sleepTime;
+}
+
+// ------------------------------------------------------------------------------------------
+static UINT32 ConfigureLoopSleepTimeAndScheduler(PaWasapiStream *stream, ThreadIdleScheduler *scheduler)
+{
+ UINT32 sleepTime, sleepTimeIn, sleepTimeOut;
+ UINT32 userFramesIn = stream->in.framesPerHostCallback / WASAPI_PACKETS_PER_INPUT_BUFFER;
+ UINT32 userFramesOut = stream->out.framesPerBuffer;
+
+ // Adjust polling time for non-paUtilFixedHostBufferSize, input stream is not adjustable as it is being
+ // polled according to its packet length
+ if (stream->bufferMode != paUtilFixedHostBufferSize)
+ {
+ userFramesOut = (stream->bufferProcessor.framesPerUserBuffer ? stream->bufferProcessor.framesPerUserBuffer :
+ stream->out.params.frames_per_buffer);
+ }
+
+ // Calculate timeout for the next polling attempt
+ sleepTimeIn = GetFramesSleepTime(userFramesIn, stream->in.wavexu.ext.Format.nSamplesPerSec);
+ sleepTimeOut = GetFramesSleepTime(userFramesOut, stream->out.wavexu.ext.Format.nSamplesPerSec);
+
+ // WASAPI input packets tend to expire very easily, let's limit sleep time to 2 milliseconds
+ // for all cases. Please propose better solution if any
+ if (sleepTimeIn > 2)
+ sleepTimeIn = 2;
+
+ sleepTime = GetSleepTime(stream, sleepTimeIn, sleepTimeOut, userFramesOut);
+
+ // Make sure not 0, othervise use ThreadIdleScheduler to bounce between [0, 1] ms to avoid too busy loop
+ if (sleepTime == 0)
+ {
+ sleepTimeIn = GetFramesSleepTimeMicroseconds(userFramesIn, stream->in.wavexu.ext.Format.nSamplesPerSec);
+ sleepTimeOut = GetFramesSleepTimeMicroseconds(userFramesOut, stream->out.wavexu.ext.Format.nSamplesPerSec);
+
+ sleepTime = GetSleepTime(stream, sleepTimeIn, sleepTimeOut, userFramesOut);
+
+ // Setup thread sleep scheduler
+ ThreadIdleScheduler_Setup(scheduler, 1, sleepTime/* microseconds here */);
+ sleepTime = 0;
+ }
+
+ return sleepTime;
+}
+
+// ------------------------------------------------------------------------------------------
+static inline INT32 GetNextSleepTime(SystemTimer *timer, ThreadIdleScheduler *scheduler, LONGLONG startTime,
+ UINT32 sleepTime)
+{
+ INT32 nextSleepTime;
+ INT32 procTime;
+
+ // Get next sleep time
+ if (sleepTime == 0)
+ nextSleepTime = ThreadIdleScheduler_NextSleep(scheduler);
+ else
+ nextSleepTime = sleepTime;
+
+ // Adjust next sleep time dynamically depending on how much time was spent in ProcessOutputBuffer/ProcessInputBuffer
+ // therefore periodicity will not jitter or be increased for the amount of time spent in processing;
+ // example when sleepTime is 10 ms where [] is polling time slot, {} processing time slot:
+ //
+ // [9],{2},[8],{1},[9],{1},[9],{3},[7],{2},[8],{3},[7],{2},[8],{2},[8],{3},[7],{2},[8],...
+ //
+ procTime = (INT32)(SystemTimer_GetTime(timer) - startTime);
+ nextSleepTime -= procTime;
+ if (nextSleepTime < timer->granularity)
+ nextSleepTime = 0;
+ else
+ if (timer->granularity > 1)
+ nextSleepTime = ALIGN_BWD(nextSleepTime, timer->granularity);
+
+#ifdef PA_WASAPI_LOG_TIME_SLOTS
+ printf("{%d},", procTime);
+#endif
+
+ return nextSleepTime;
}
// ------------------------------------------------------------------------------------------
PA_THREAD_FUNC ProcThreadPoll(void *param)
{
PaWasapiHostProcessor processor[S_COUNT];
- HRESULT hr;
+ HRESULT hr = S_OK;
PaWasapiStream *stream = (PaWasapiStream *)param;
- PaWasapiHostProcessor defaultProcessor;
- INT32 i;
- ThreadIdleScheduler scheduler;
-
- // Calculate the actual duration of the allocated buffer.
- DWORD sleep_ms = 0;
- DWORD sleep_ms_in;
- DWORD sleep_ms_out;
-
- BOOL bThreadComInitialized = FALSE;
-
- /*
- If COM is already initialized CoInitialize will either return
- FALSE, or RPC_E_CHANGED_MODE if it was initialized in a different
- threading mode. In either case we shouldn't consider it an error
- but we need to be careful to not call CoUninitialize() if
- RPC_E_CHANGED_MODE was returned.
- */
- hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
- if (FAILED(hr) && (hr != RPC_E_CHANGED_MODE))
- {
- PRINT(("WASAPI: failed ProcThreadPoll CoInitialize"));
- return (UINT32)paUnanticipatedHostError;
- }
- if (hr != RPC_E_CHANGED_MODE)
- bThreadComInitialized = TRUE;
-
- // Unmarshal stream pointers for safe COM operation
- hr = UnmarshalStreamComPointers(stream);
- if (hr != S_OK)
- {
- PRINT(("Error unmarshaling stream COM pointers. HRESULT: %i\n", hr));
- return 0;
- }
-
- // Calculate timeout for next polling attempt.
- sleep_ms_in = GetFramesSleepTime(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);
- sleep_ms_out = GetFramesSleepTime(stream->out.framesPerBuffer, stream->out.wavex.Format.nSamplesPerSec);
-
- // WASAPI Input packets tend to expire very easily, let's limit sleep time to 2 milliseconds
- // for all cases. Please propose better solution if any.
- if (sleep_ms_in > 2)
- sleep_ms_in = 2;
-
- // Adjust polling time for non-paUtilFixedHostBufferSize. Input stream is not adjustable as it is being
- // polled according its packet length.
- if (stream->bufferMode != paUtilFixedHostBufferSize)
- {
- //sleep_ms_in = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->in.wavex.Format.nSamplesPerSec);
- sleep_ms_out = GetFramesSleepTime(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);
- }
-
- // Choose smallest
- if ((sleep_ms_in != 0) && (sleep_ms_out != 0))
- sleep_ms = min(sleep_ms_in, sleep_ms_out);
- else
- {
- sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);
- }
- // Make sure not 0, othervise use ThreadIdleScheduler
- if (sleep_ms == 0)
- {
- sleep_ms_in = GetFramesSleepTimeMicroseconds(stream->in.framesPerHostCallback/WASAPI_PACKETS_PER_INPUT_BUFFER, stream->in.wavex.Format.nSamplesPerSec);
- sleep_ms_out = GetFramesSleepTimeMicroseconds(stream->bufferProcessor.framesPerUserBuffer, stream->out.wavex.Format.nSamplesPerSec);
-
- // Choose smallest
- if ((sleep_ms_in != 0) && (sleep_ms_out != 0))
- sleep_ms = min(sleep_ms_in, sleep_ms_out);
- else
- {
- sleep_ms = (sleep_ms_in ? sleep_ms_in : sleep_ms_out);
- }
-
- // Setup thread sleep scheduler
- ThreadIdleScheduler_Setup(&scheduler, 1, sleep_ms/* microseconds here */);
- sleep_ms = 0;
- }
+ PaWasapiHostProcessor defaultProcessor;
+ INT32 i;
+ ThreadIdleScheduler scheduler;
+ SystemTimer timer;
+ LONGLONG startTime;
+ UINT32 sleepTime;
+ INT32 nextSleepTime = 0; //! Do first loop without waiting as time could be spent when calling other APIs before ProcessXXXBuffer.
+ BOOL threadComInitialized = FALSE;
+#ifdef PA_WASAPI_LOG_TIME_SLOTS
+ LONGLONG startWaitTime;
+#endif
+
+ // Notify: WASAPI-specific stream state
+ NotifyStateChanged(stream, paWasapiStreamStateThreadPrepare, ERROR_SUCCESS);
+
+ // Prepare COM pointers
+ if (!PrepareComPointers(stream, &threadComInitialized))
+ return (UINT32)paUnanticipatedHostError;
+
+ // Request fine (1 ms) granularity of the system timer functions to guarantee correct logic around WaitForSingleObject
+ SystemTimer_SetGranularity(&timer, 1);
+
+ // Calculate sleep time of the processing loop (inside WaitForSingleObject)
+ sleepTime = ConfigureLoopSleepTimeAndScheduler(stream, &scheduler);
// Setup data processors
defaultProcessor.processor = WaspiHostProcessingLoop;
@@ -5265,478 +6129,374 @@ PA_THREAD_FUNC ProcThreadPoll(void *param)
processor[S_INPUT] = (stream->hostProcessOverrideInput.processor != NULL ? stream->hostProcessOverrideInput : defaultProcessor);
processor[S_OUTPUT] = (stream->hostProcessOverrideOutput.processor != NULL ? stream->hostProcessOverrideOutput : defaultProcessor);
- // Boost thread priority
- PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
-
- // Initialize event & start INPUT stream
- if (stream->in.clientProc)
- {
- if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
- }
-
- // Initialize event & start OUTPUT stream
- if (stream->out.clientProc)
- {
- // Preload buffer (obligatory, othervise ->Start() will fail), avoid processing
- // when in full-duplex mode as it requires input processing as well
- if (!PA_WASAPI__IS_FULLDUPLEX(stream))
- {
- UINT32 frames = 0;
- if ((hr = _PollGetOutputFramesAvailable(stream, &frames)) == S_OK)
+ // Boost thread priority
+ PaWasapi_ThreadPriorityBoost((void **)&stream->hAvTask, stream->nThreadPriority);
+
+ // Signal: stream active (reconfirm)
+ stream->isActive = TRUE;
+
+ // Notify: thread started
+ SetEvent(stream->hThreadStart);
+
+ // Initialize event & start INPUT stream
+ if (stream->in.clientProc)
+ {
+ if ((hr = IAudioClient_Start(stream->in.clientProc)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+ }
+
+ // Initialize event & start OUTPUT stream
+ if (stream->out.clientProc)
+ {
+ // Preload buffer (obligatory, othervise ->Start() will fail), avoid processing
+ // when in full-duplex mode as it requires input processing as well
+ if (!PA_WASAPI__IS_FULLDUPLEX(stream))
+ {
+ UINT32 frames = 0;
+ if ((hr = _PollGetOutputFramesAvailable(stream, &frames)) == S_OK)
{
- if (stream->bufferMode == paUtilFixedHostBufferSize)
- {
- if (frames >= stream->out.framesPerBuffer)
- {
- frames = stream->out.framesPerBuffer;
-
- if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
- {
- LogHostError(hr); // not fatal, just log
- }
- }
- }
- else
- {
- if (frames != 0)
- {
- if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
- {
- LogHostError(hr); // not fatal, just log
- }
- }
- }
+ if (stream->bufferMode == paUtilFixedHostBufferSize)
+ {
+ // It is important to preload whole host buffer to avoid underruns/glitches when stream is started,
+ // for more details see the discussion: https://github.com/PortAudio/portaudio/issues/303
+ while ((frames >= stream->out.framesPerBuffer) && !CheckForStop(stream))
+ {
+ if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
+ {
+ LogHostError(hr); // not fatal, just log
+ break;
+ }
+
+ frames -= stream->out.framesPerBuffer;
+ }
+ }
+ else
+ {
+ // Some devices may not start (will get stuck with 0 ready frames) if data not prefetched
+ if (frames == 0)
+ frames = stream->out.framesPerBuffer;
+
+ // USB DACs report large buffer in Exclusive mode and if it is filled fully will stuck in
+ // non playing state, e.g. IAudioClient_GetCurrentPadding() will start reporting max buffer size
+ // constantly, thus preload data size equal to the user buffer to allow process going
+ if ((stream->out.shareMode == AUDCLNT_SHAREMODE_EXCLUSIVE) && (frames >= (stream->out.framesPerBuffer * 2)))
+ frames -= stream->out.framesPerBuffer;
+
+ if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
+ {
+ LogHostError(hr); // not fatal, just log
+ }
+ }
}
else
- {
- LogHostError(hr); // not fatal, just log
- }
- }
-
- // Start
- if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
- }
-
- // Signal: stream running
- stream->running = TRUE;
-
- // Notify: thread started
- SetEvent(stream->hThreadStart);
-
- if (!PA_WASAPI__IS_FULLDUPLEX(stream))
- {
- // Processing Loop
- UINT32 next_sleep = sleep_ms;
- while (WaitForSingleObject(stream->hCloseRequest, next_sleep) == WAIT_TIMEOUT)
- {
- // Get next sleep time
- if (sleep_ms == 0)
- {
- next_sleep = ThreadIdleScheduler_NextSleep(&scheduler);
- }
-
- for (i = 0; i < S_COUNT; ++i)
- {
- // Process S_INPUT/S_OUTPUT
- switch (i)
- {
- // Input stream
- case S_INPUT: {
-
- if (stream->captureClient == NULL)
- break;
-
- if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
-
- break; }
-
- // Output stream
- case S_OUTPUT: {
-
- UINT32 frames;
- if (stream->renderClient == NULL)
- break;
-
- // get available frames
- if ((hr = _PollGetOutputFramesAvailable(stream, &frames)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
-
- // output
- if (stream->bufferMode == paUtilFixedHostBufferSize)
- {
- while (frames >= stream->out.framesPerBuffer)
- {
- if ((hr = ProcessOutputBuffer(stream, processor, stream->out.framesPerBuffer)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
-
- frames -= stream->out.framesPerBuffer;
- }
- }
- else
- {
- if (frames != 0)
- {
- if ((hr = ProcessOutputBuffer(stream, processor, frames)) != S_OK)
- {
- LogHostError(hr);
- goto thread_error;
- }
- }
- }
-
- break; }
- }
- }
- }
- }
- else
- {
-#if 0
- // Processing Loop
- while (WaitForSingleObject(stream->hCloseRequest, 1) == WAIT_TIMEOUT)
- {
- UINT32 i_frames = 0, i_processed = 0;
- BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;
- DWORD i_flags = 0;
- UINT32 o_frames = 0;
-
- // get host input buffer
- if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)
- {
- if (hr == AUDCLNT_S_BUFFER_EMPTY)
- continue; // no data in capture buffer
-
- LogHostError(hr);
- break;
- }
-
- // get available frames
- if ((hr = _PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)
- {
- // release input buffer
- IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
-
- LogHostError(hr);
- break;
- }
-
- // process equal ammount of frames
- if (o_frames >= i_frames)
- {
- // process input ammount of frames
- UINT32 o_processed = i_frames;
-
- // get host output buffer
- if ((hr = IAudioRenderClient_GetBuffer(stream->procRCClient, o_processed, &o_data)) == S_OK)
- {
- // processed amount of i_frames
- i_processed = i_frames;
- o_data_host = o_data;
-
- // convert output mono
- if (stream->out.monoMixer)
- {
- UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8);
- // expand buffer
- if (mono_frames_size > stream->out.monoBufferSize)
- {
- stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
- if (stream->out.monoBuffer == NULL)
- {
- // release input buffer
- IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
- // release output buffer
- IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
-
- LogPaError(paInsufficientMemory);
- break;
- }
- }
-
- // replace buffer pointer
- o_data = (BYTE *)stream->out.monoBuffer;
- }
-
- // convert input mono
- if (stream->in.monoMixer)
- {
- UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8);
- // expand buffer
- if (mono_frames_size > stream->in.monoBufferSize)
- {
- stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
- if (stream->in.monoBuffer == NULL)
- {
- // release input buffer
- IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
- // release output buffer
- IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
-
- LogPaError(paInsufficientMemory);
- break;
- }
- }
-
- // mix 2 to 1 input channels
- stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);
-
- // replace buffer pointer
- i_data = (BYTE *)stream->in.monoBuffer;
- }
-
- // process
- processor[S_FULLDUPLEX].processor(i_data, i_processed, o_data, o_processed, processor[S_FULLDUPLEX].userData);
-
- // mix 1 to 2 output channels
- if (stream->out.monoBuffer)
- stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);
-
- // release host output buffer
- if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, o_processed, 0)) != S_OK)
- LogHostError(hr);
- }
- else
- {
- if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)
- LogHostError(hr); // be silent in shared mode, try again next time
- }
- }
-
- // release host input buffer
- if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, i_processed)) != S_OK)
- {
- LogHostError(hr);
- break;
- }
- }
-#else
- // Processing Loop
- UINT32 next_sleep = sleep_ms;
- while (WaitForSingleObject(stream->hCloseRequest, next_sleep) == WAIT_TIMEOUT)
- {
- UINT32 i_frames = 0, i_processed = 0;
- BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;
- DWORD i_flags = 0;
- UINT32 o_frames = 0;
-
- // Get next sleep time
- if (sleep_ms == 0)
- {
- next_sleep = ThreadIdleScheduler_NextSleep(&scheduler);
- }
-
- // get available frames
- if ((hr = _PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)
- {
- LogHostError(hr);
- break;
- }
-
- while (o_frames != 0)
- {
- // get host input buffer
- if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)
- {
- if (hr == AUDCLNT_S_BUFFER_EMPTY)
- break; // no data in capture buffer
-
- LogHostError(hr);
- break;
- }
-
- // process equal ammount of frames
- if (o_frames >= i_frames)
- {
- // process input ammount of frames
- UINT32 o_processed = i_frames;
-
- // get host output buffer
- if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, o_processed, &o_data)) == S_OK)
- {
- // processed amount of i_frames
- i_processed = i_frames;
- o_data_host = o_data;
-
- // convert output mono
- if (stream->out.monoMixer)
- {
- UINT32 mono_frames_size = o_processed * (stream->out.wavex.Format.wBitsPerSample / 8);
- // expand buffer
- if (mono_frames_size > stream->out.monoBufferSize)
- {
- stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
- if (stream->out.monoBuffer == NULL)
- {
- // release input buffer
- IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
- // release output buffer
- IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
-
- LogPaError(paInsufficientMemory);
- goto thread_error;
- }
- }
-
- // replace buffer pointer
- o_data = (BYTE *)stream->out.monoBuffer;
- }
-
- // convert input mono
- if (stream->in.monoMixer)
- {
- UINT32 mono_frames_size = i_processed * (stream->in.wavex.Format.wBitsPerSample / 8);
- // expand buffer
- if (mono_frames_size > stream->in.monoBufferSize)
- {
- stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
- if (stream->in.monoBuffer == NULL)
- {
- // release input buffer
- IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
- // release output buffer
- IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
-
- LogPaError(paInsufficientMemory);
- goto thread_error;
- }
- }
-
- // mix 2 to 1 input channels
- stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);
-
- // replace buffer pointer
- i_data = (BYTE *)stream->in.monoBuffer;
- }
-
- // process
- processor[S_FULLDUPLEX].processor(i_data, i_processed, o_data, o_processed, processor[S_FULLDUPLEX].userData);
-
- // mix 1 to 2 output channels
- if (stream->out.monoBuffer)
- stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);
-
- // release host output buffer
- if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, o_processed, 0)) != S_OK)
- LogHostError(hr);
-
- o_frames -= o_processed;
- }
- else
- {
- if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)
- LogHostError(hr); // be silent in shared mode, try again next time
- }
- }
- else
- {
- i_processed = 0;
- goto fd_release_buffer_in;
- }
+ {
+ LogHostError(hr); // not fatal, just log
+ }
+ }
-fd_release_buffer_in:
+ // Start
+ if ((hr = IAudioClient_Start(stream->out.clientProc)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+ }
+
+ // Notify: WASAPI-specific stream state
+ NotifyStateChanged(stream, paWasapiStreamStateThreadStart, ERROR_SUCCESS);
- // release host input buffer
- if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, i_processed)) != S_OK)
- {
- LogHostError(hr);
- break;
- }
-
- // break processing, input hasn't been accumulated yet
- if (i_processed == 0)
- break;
- }
- }
+#ifdef PA_WASAPI_LOG_TIME_SLOTS
+ startWaitTime = SystemTimer_GetTime(&timer);
#endif
- }
+
+ if (!PA_WASAPI__IS_FULLDUPLEX(stream))
+ {
+ // Processing Loop
+ while (!CheckForStopOrWait(stream, nextSleepTime))
+ {
+ startTime = SystemTimer_GetTime(&timer);
+
+ #ifdef PA_WASAPI_LOG_TIME_SLOTS
+ printf("[%d|%d],", nextSleepTime, (INT32)(startTime - startWaitTime));
+ #endif
+
+ for (i = 0; i < S_COUNT; ++i)
+ {
+ // Process S_INPUT/S_OUTPUT
+ switch (i)
+ {
+ // Input stream
+ case S_INPUT: {
+
+ if (stream->captureClient == NULL)
+ break;
+
+ if ((hr = ProcessInputBuffer(stream, processor)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+
+ break; }
+
+ // Output stream
+ case S_OUTPUT: {
+
+ UINT32 framesAvail;
+
+ if (stream->renderClient == NULL)
+ break;
+
+ // Get available frames
+ if ((hr = _PollGetOutputFramesAvailable(stream, &framesAvail)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+
+ // Output data to the user callback
+ if (stream->bufferMode == paUtilFixedHostBufferSize)
+ {
+ UINT32 framesProc = stream->out.framesPerBuffer;
+
+ // If we got less frames avoid sleeping again as it might be the corner case and buffer
+ // has sufficient number of frames now, in case 'out.framesPerBuffer' is 1/2 of the host
+ // buffer sleeping again may cause underruns. Do short busy waiting (normally might take
+ // 1-2 iterations)
+ if (framesAvail < framesProc)
+ {
+ nextSleepTime = 0;
+ continue;
+ }
+
+ do
+ {
+ if ((hr = ProcessOutputBuffer(stream, processor, framesProc)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+
+ framesAvail -= framesProc;
+ }
+ while ((framesAvail >= framesProc) && !CheckForStop(stream));
+ }
+ else
+ if (framesAvail != 0)
+ {
+ if ((hr = ProcessOutputBuffer(stream, processor, framesAvail)) != S_OK)
+ {
+ LogHostError(hr);
+ goto thread_error;
+ }
+ }
+
+ break; }
+ }
+ }
+
+ // Get next sleep time
+ nextSleepTime = GetNextSleepTime(&timer, &scheduler, startTime, sleepTime);
+
+ #ifdef PA_WASAPI_LOG_TIME_SLOTS
+ startWaitTime = SystemTimer_GetTime(&timer);
+ #endif
+ }
+ }
+ else
+ {
+ // Processing Loop (full-duplex)
+ while (!CheckForStopOrWait(stream, nextSleepTime))
+ {
+ UINT32 i_frames = 0, i_processed = 0, o_frames = 0;
+ BYTE *i_data = NULL, *o_data = NULL, *o_data_host = NULL;
+ DWORD i_flags = 0;
+
+ startTime = SystemTimer_GetTime(&timer);
+
+ #ifdef PA_WASAPI_LOG_TIME_SLOTS
+ printf("[%d|%d],", nextSleepTime, (INT32)(startTime - startWaitTime));
+ #endif
+
+ // get available frames
+ if ((hr = _PollGetOutputFramesAvailable(stream, &o_frames)) != S_OK)
+ {
+ LogHostError(hr);
+ break;
+ }
+
+ while (o_frames != 0)
+ {
+ // get host input buffer
+ if ((hr = IAudioCaptureClient_GetBuffer(stream->captureClient, &i_data, &i_frames, &i_flags, NULL, NULL)) != S_OK)
+ {
+ if (hr == AUDCLNT_S_BUFFER_EMPTY)
+ break; // no data in capture buffer
+
+ LogHostError(hr);
+ break;
+ }
+
+ // process equal amount of frames
+ if (o_frames >= i_frames)
+ {
+ // process input amount of frames
+ UINT32 o_processed = i_frames;
+
+ // get host output buffer
+ if ((hr = IAudioRenderClient_GetBuffer(stream->renderClient, o_processed, &o_data)) == S_OK)
+ {
+ // processed amount of i_frames
+ i_processed = i_frames;
+ o_data_host = o_data;
+
+ // convert output mono
+ if (stream->out.monoMixer)
+ {
+ UINT32 mono_frames_size = o_processed * (stream->out.wavexu.ext.Format.wBitsPerSample / 8);
+ // expand buffer
+ if (mono_frames_size > stream->out.monoBufferSize)
+ {
+ stream->out.monoBuffer = PaWasapi_ReallocateMemory(stream->out.monoBuffer, (stream->out.monoBufferSize = mono_frames_size));
+ if (stream->out.monoBuffer == NULL)
+ {
+ // release input buffer
+ IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
+ // release output buffer
+ IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
+
+ LogPaError(paInsufficientMemory);
+ goto thread_error;
+ }
+ }
+
+ // replace buffer pointer
+ o_data = (BYTE *)stream->out.monoBuffer;
+ }
+
+ // convert input mono
+ if (stream->in.monoMixer)
+ {
+ UINT32 mono_frames_size = i_processed * (stream->in.wavexu.ext.Format.wBitsPerSample / 8);
+ // expand buffer
+ if (mono_frames_size > stream->in.monoBufferSize)
+ {
+ stream->in.monoBuffer = PaWasapi_ReallocateMemory(stream->in.monoBuffer, (stream->in.monoBufferSize = mono_frames_size));
+ if (stream->in.monoBuffer == NULL)
+ {
+ // release input buffer
+ IAudioCaptureClient_ReleaseBuffer(stream->captureClient, 0);
+ // release output buffer
+ IAudioRenderClient_ReleaseBuffer(stream->renderClient, 0, 0);
+
+ LogPaError(paInsufficientMemory);
+ goto thread_error;
+ }
+ }
+
+ // mix 2 to 1 input channels
+ stream->in.monoMixer(stream->in.monoBuffer, i_data, i_processed);
+
+ // replace buffer pointer
+ i_data = (BYTE *)stream->in.monoBuffer;
+ }
+
+ // process
+ processor[S_FULLDUPLEX].processor(i_data, i_processed, o_data, o_processed, processor[S_FULLDUPLEX].userData);
+
+ // mix 1 to 2 output channels
+ if (stream->out.monoBuffer)
+ stream->out.monoMixer(o_data_host, stream->out.monoBuffer, o_processed);
+
+ // release host output buffer
+ if ((hr = IAudioRenderClient_ReleaseBuffer(stream->renderClient, o_processed, 0)) != S_OK)
+ LogHostError(hr);
+
+ o_frames -= o_processed;
+ }
+ else
+ {
+ if (stream->out.shareMode != AUDCLNT_SHAREMODE_SHARED)
+ LogHostError(hr); // be silent in shared mode, try again next time
+ }
+ }
+ else
+ {
+ i_processed = 0;
+ goto fd_release_buffer_in;
+ }
+
+fd_release_buffer_in:
+
+ // release host input buffer
+ if ((hr = IAudioCaptureClient_ReleaseBuffer(stream->captureClient, i_processed)) != S_OK)
+ {
+ LogHostError(hr);
+ break;
+ }
+
+ // break processing, input hasn't been accumulated yet
+ if (i_processed == 0)
+ break;
+ }
+
+ // Get next sleep time
+ nextSleepTime = GetNextSleepTime(&timer, &scheduler, startTime, sleepTime);
+
+ #ifdef PA_WASAPI_LOG_TIME_SLOTS
+ startWaitTime = SystemTimer_GetTime(&timer);
+ #endif
+ }
+ }
thread_end:
- // Process stop
- _StreamOnStop(stream);
+ // Process stop
+ _StreamOnStop(stream);
- // Release unmarshaled COM pointers
- ReleaseUnmarshaledComPointers(stream);
+ // Release unmarshaled COM pointers
+ FinishComPointers(stream, threadComInitialized);
- // Cleanup COM for this thread
- if (bThreadComInitialized == TRUE)
- CoUninitialize();
+ // Restore system timer granularity
+ SystemTimer_RestoreGranularity(&timer);
- // Notify: not running
- stream->running = FALSE;
+ // Notify: state inactive
+ stream->isActive = FALSE;
- // Notify: thread exited
- SetEvent(stream->hThreadExit);
+ // Notify: thread exited
+ SetEvent(stream->hThreadExit);
- return 0;
+ // Notify: WASAPI-specific stream state
+ NotifyStateChanged(stream, paWasapiStreamStateThreadStop, hr);
+
+ return 0;
thread_error:
- // Prevent deadlocking in Pa_StreamStart
- SetEvent(stream->hThreadStart);
+ // Prevent deadlocking in Pa_StreamStart
+ SetEvent(stream->hThreadStart);
- // Exit
- goto thread_end;
+ // Exit
+ goto thread_end;
}
// ------------------------------------------------------------------------------------------
-void *PaWasapi_ReallocateMemory(void *ptr, size_t size)
+void *PaWasapi_ReallocateMemory(void *prev, size_t size)
{
- return realloc(ptr, size);
+ void *ret = realloc(prev, size);
+ if (ret == NULL)
+ {
+ PaWasapi_FreeMemory(prev);
+ return NULL;
+ }
+ return ret;
}
// ------------------------------------------------------------------------------------------
void PaWasapi_FreeMemory(void *ptr)
{
- free(ptr);
+ free(ptr);
}
-
-//#endif //VC 2005
-
-
-
-
-#if 0
- if(bFirst) {
- float masteur;
- hr = stream->outVol->GetMasterVolumeLevelScalar(&masteur);
- if (hr != S_OK)
- LogHostError(hr);
- float chan1, chan2;
- hr = stream->outVol->GetChannelVolumeLevelScalar(0, &chan1);
- if (hr != S_OK)
- LogHostError(hr);
- hr = stream->outVol->GetChannelVolumeLevelScalar(1, &chan2);
- if (hr != S_OK)
- LogHostError(hr);
-
- BOOL bMute;
- hr = stream->outVol->GetMute(&bMute);
- if (hr != S_OK)
- LogHostError(hr);
-
- stream->outVol->SetMasterVolumeLevelScalar(0.5, NULL);
- stream->outVol->SetChannelVolumeLevelScalar(0, 0.5, NULL);
- stream->outVol->SetChannelVolumeLevelScalar(1, 0.5, NULL);
- stream->outVol->SetMute(FALSE, NULL);
- bFirst = FALSE;
- }
-#endif