summaryrefslogtreecommitdiff
path: root/3rdparty/portaudio/src/hostapi/wdmks
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-08-27 23:52:56 -0500
committersanine <sanine.not@pm.me>2022-08-27 23:52:56 -0500
commita4dd0ad63c00f4dee3b86dfd3075d1d61b2b3180 (patch)
tree13bd5bfa15e6fea2a12f176bae79adf9c6fd0933 /3rdparty/portaudio/src/hostapi/wdmks
parentbde3e4f1bb7b8f8abca0884a7d994ee1c17a66b1 (diff)
add plibsys
Diffstat (limited to '3rdparty/portaudio/src/hostapi/wdmks')
-rw-r--r--3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c6807
-rw-r--r--3rdparty/portaudio/src/hostapi/wdmks/readme.txt85
2 files changed, 6892 insertions, 0 deletions
diff --git a/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c b/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c
new file mode 100644
index 0000000..161c264
--- /dev/null
+++ b/3rdparty/portaudio/src/hostapi/wdmks/pa_win_wdmks.c
@@ -0,0 +1,6807 @@
+/*
+* $Id$
+* PortAudio Windows WDM-KS interface
+*
+* Author: Andrew Baldwin, Robert Bielik (WaveRT)
+* Based on the Open Source API proposed by Ross Bencina
+* Copyright (c) 1999-2004 Andrew Baldwin, Ross Bencina, Phil Burk
+*
+* Permission is hereby granted, free of charge, to any person obtaining
+* a copy of this software and associated documentation files
+* (the "Software"), to deal in the Software without restriction,
+* including without limitation the rights to use, copy, modify, merge,
+* publish, distribute, sublicense, and/or sell copies of the Software,
+* and to permit persons to whom the Software is furnished to do so,
+* subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be
+* included in all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/*
+* The text above constitutes the entire PortAudio license; however,
+* the PortAudio community also makes the following non-binding requests:
+*
+* Any person wishing to distribute modifications to the Software is
+* requested to send the modifications to the original developer so that
+* they can be incorporated into the canonical version. It is also
+* requested that these non-binding requests be included along with the
+* license above.
+*/
+
+/** @file
+@ingroup hostapi_src
+@brief Portaudio WDM-KS host API.
+
+@note This is the implementation of the Portaudio host API using the
+Windows WDM/Kernel Streaming API in order to enable very low latency
+playback and recording on all modern Windows platforms (e.g. 2K, XP, Vista, Win7)
+Note: This API accesses the device drivers below the usual KMIXER
+component which is normally used to enable multi-client mixing and
+format conversion. That means that it will lock out all other users
+of a device for the duration of active stream using those devices
+*/
+
+#include <stdio.h>
+
+#if (defined(_WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
+#pragma comment( lib, "setupapi.lib" )
+#endif
+
+/* Debugging/tracing support */
+
+#define PA_LOGE_
+#define PA_LOGL_
+
+#ifdef __GNUC__
+#include <initguid.h>
+#define _WIN32_WINNT 0x0501
+#define WINVER 0x0501
+#endif
+
+#include <string.h> /* strlen() */
+#include <assert.h>
+#include <wchar.h> /* iswspace() */
+
+#include "pa_util.h"
+#include "pa_allocation.h"
+#include "pa_hostapi.h"
+#include "pa_stream.h"
+#include "pa_cpuload.h"
+#include "pa_process.h"
+#include "portaudio.h"
+#include "pa_debugprint.h"
+#include "pa_memorybarrier.h"
+#include "pa_ringbuffer.h"
+#include "pa_trace.h"
+#include "pa_win_waveformat.h"
+
+#include "pa_win_wdmks.h"
+
+#ifndef DRV_QUERYDEVICEINTERFACE
+#define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12)
+#endif
+#ifndef DRV_QUERYDEVICEINTERFACESIZE
+#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
+#endif
+
+#include <windows.h>
+#ifndef __GNUC__ /* Fix for ticket #257: MinGW-w64: Inclusion of <winioctl.h> triggers multiple redefinition errors. */
+#include <winioctl.h>
+#endif
+#include <process.h>
+
+#include <math.h>
+
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
+/* The PA_HP_TRACE macro is used in RT parts, so it can be switched off without affecting
+the rest of the debug tracing */
+#if 1
+#define PA_HP_TRACE(x) PaUtil_AddHighSpeedLogMessage x ;
+#else
+#define PA_HP_TRACE(x)
+#endif
+
+/* A define that selects whether the resulting pin names are chosen from pin category
+instead of the available pin names, who sometimes can be quite cheesy, like "Volume control".
+Default is to use the pin category.
+*/
+#ifndef PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES
+#define PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES 1
+#endif
+
+#ifdef __GNUC__
+#undef PA_LOGE_
+#define PA_LOGE_ PA_DEBUG(("%s {\n",__FUNCTION__))
+#undef PA_LOGL_
+#define PA_LOGL_ PA_DEBUG(("} %s\n",__FUNCTION__))
+/* These defines are set in order to allow the WIndows DirectX
+* headers to compile with a GCC compiler such as MinGW
+* NOTE: The headers may generate a few warning in GCC, but
+* they should compile */
+#define _INC_MMSYSTEM
+#define _INC_MMREG
+#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
+#define DEFINE_GUID_THUNK(name,guid) DEFINE_GUID(name,guid)
+#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK( n, STATIC_##n )
+#if !defined( DEFINE_WAVEFORMATEX_GUID )
+#define DEFINE_WAVEFORMATEX_GUID(x) (USHORT)(x), 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+#endif
+#define WAVE_FORMAT_ADPCM 0x0002
+#define WAVE_FORMAT_IEEE_FLOAT 0x0003
+#define WAVE_FORMAT_ALAW 0x0006
+#define WAVE_FORMAT_MULAW 0x0007
+#define WAVE_FORMAT_MPEG 0x0050
+#define WAVE_FORMAT_DRM 0x0009
+#define DYNAMIC_GUID_THUNK(l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
+#define DYNAMIC_GUID(data) DYNAMIC_GUID_THUNK(data)
+#endif
+
+/* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */
+#if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
+#define CREATE_THREAD_FUNCTION (HANDLE)_beginthreadex
+#define PA_THREAD_FUNC static unsigned WINAPI
+#else
+#define CREATE_THREAD_FUNCTION CreateThread
+#define PA_THREAD_FUNC static DWORD WINAPI
+#endif
+
+#ifdef _MSC_VER
+#define NOMMIDS
+#define DYNAMIC_GUID(data) {data}
+#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
+#undef DEFINE_GUID
+#if defined(__clang__) || (defined(_MSVC_TRADITIONAL) && !_MSVC_TRADITIONAL) /* clang-cl and new msvc preprocessor: avoid too many arguments error */
+ #define DEFINE_GUID(n, ...) EXTERN_C const GUID n = {__VA_ARGS__}
+ #define DEFINE_GUID_THUNK(n, ...) DEFINE_GUID(n, __VA_ARGS__)
+ #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
+#else
+ #define DEFINE_GUID(n, data) EXTERN_C const GUID n = {data}
+ #define DEFINE_GUID_THUNK(n, data) DEFINE_GUID(n, data)
+ #define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
+#endif /* __clang__, !_MSVC_TRADITIONAL */
+#endif
+
+#include <setupapi.h>
+
+#ifndef EXTERN_C
+#define EXTERN_C extern
+#endif
+
+#if defined(__GNUC__)
+
+/* For MinGW we reference mingw-include files supplied with WASAPI */
+#define WINBOOL BOOL
+
+#include "../wasapi/mingw-include/ks.h"
+#include "../wasapi/mingw-include/ksmedia.h"
+
+#else
+
+#include <mmreg.h>
+#include <ks.h>
+
+/* Note that Windows SDK V6.0A or later is needed for WaveRT specific structs to be present in
+ ksmedia.h. Also make sure that the SDK include path is before other include paths (that may contain
+ an "old" ksmedia.h), so the proper ksmedia.h is used */
+#include <ksmedia.h>
+
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+
+/* These next definitions allow the use of the KSUSER DLL */
+typedef /*KSDDKAPI*/ DWORD WINAPI KSCREATEPIN(HANDLE, PKSPIN_CONNECT, ACCESS_MASK, PHANDLE);
+extern HMODULE DllKsUser;
+extern KSCREATEPIN* FunctionKsCreatePin;
+
+/* These definitions allows the use of AVRT.DLL on Vista and later OSs */
+typedef enum _PA_AVRT_PRIORITY
+{
+ PA_AVRT_PRIORITY_LOW = -1,
+ PA_AVRT_PRIORITY_NORMAL,
+ PA_AVRT_PRIORITY_HIGH,
+ PA_AVRT_PRIORITY_CRITICAL
+} PA_AVRT_PRIORITY, *PPA_AVRT_PRIORITY;
+
+typedef struct
+{
+ HINSTANCE hInstance;
+
+ HANDLE (WINAPI *AvSetMmThreadCharacteristics) (LPCSTR, LPDWORD);
+ BOOL (WINAPI *AvRevertMmThreadCharacteristics) (HANDLE);
+ BOOL (WINAPI *AvSetMmThreadPriority) (HANDLE, PA_AVRT_PRIORITY);
+} PaWinWDMKSAvRtEntryPoints;
+
+static PaWinWDMKSAvRtEntryPoints paWinWDMKSAvRtEntryPoints = {0};
+
+/* An unspecified channel count (-1) is not treated correctly, so we replace it with
+* an arbitrarily large number */
+#define MAXIMUM_NUMBER_OF_CHANNELS 256
+
+/* Forward definition to break circular type reference between pin and filter */
+struct __PaWinWdmFilter;
+typedef struct __PaWinWdmFilter PaWinWdmFilter;
+
+struct __PaWinWdmPin;
+typedef struct __PaWinWdmPin PaWinWdmPin;
+
+struct __PaWinWdmStream;
+typedef struct __PaWinWdmStream PaWinWdmStream;
+
+/* Function prototype for getting audio position */
+typedef PaError (*FunctionGetPinAudioPosition)(PaWinWdmPin*, unsigned long*);
+
+/* Function prototype for memory barrier */
+typedef void (*FunctionMemoryBarrier)(void);
+
+struct __PaProcessThreadInfo;
+typedef struct __PaProcessThreadInfo PaProcessThreadInfo;
+
+typedef PaError (*FunctionPinHandler)(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+
+typedef enum __PaStreamStartEnum
+{
+ StreamStart_kOk,
+ StreamStart_kFailed,
+ StreamStart_kCnt
+} PaStreamStartEnum;
+
+/* Multiplexed input structure.
+* Very often several physical inputs are multiplexed through a MUX node (represented in the topology filter) */
+typedef struct __PaWinWdmMuxedInput
+{
+ wchar_t friendlyName[MAX_PATH];
+ ULONG muxPinId;
+ ULONG muxNodeId;
+ ULONG endpointPinId;
+} PaWinWdmMuxedInput;
+
+/* The Pin structure
+* A pin is an input or output node, e.g. for audio flow */
+struct __PaWinWdmPin
+{
+ HANDLE handle;
+ PaWinWdmMuxedInput** inputs;
+ unsigned inputCount;
+ wchar_t friendlyName[MAX_PATH];
+
+ PaWinWdmFilter* parentFilter;
+ PaWDMKSSubType pinKsSubType;
+ unsigned long pinId;
+ unsigned long endpointPinId; /* For output pins */
+ KSPIN_CONNECT* pinConnect;
+ unsigned long pinConnectSize;
+ KSDATAFORMAT_WAVEFORMATEX* ksDataFormatWfx;
+ KSPIN_COMMUNICATION communication;
+ KSDATARANGE* dataRanges;
+ KSMULTIPLE_ITEM* dataRangesItem;
+ KSPIN_DATAFLOW dataFlow;
+ KSPIN_CINSTANCES instances;
+ unsigned long frameSize;
+ int maxChannels;
+ unsigned long formats;
+ int defaultSampleRate;
+ ULONG *positionRegister; /* WaveRT */
+ ULONG hwLatency; /* WaveRT */
+ FunctionMemoryBarrier fnMemBarrier; /* WaveRT */
+ FunctionGetPinAudioPosition fnAudioPosition; /* WaveRT */
+ FunctionPinHandler fnEventHandler;
+ FunctionPinHandler fnSubmitHandler;
+};
+
+/* The Filter structure
+* A filter has a number of pins and a "friendly name" */
+struct __PaWinWdmFilter
+{
+ HANDLE handle;
+ PaWinWDMKSDeviceInfo devInfo; /* This will hold information that is exposed in PaDeviceInfo */
+
+ DWORD deviceNode;
+ int pinCount;
+ PaWinWdmPin** pins;
+ PaWinWdmFilter* topologyFilter;
+ wchar_t friendlyName[MAX_PATH];
+ int validPinCount;
+ int usageCount;
+ KSMULTIPLE_ITEM* connections;
+ KSMULTIPLE_ITEM* nodes;
+ int filterRefCount;
+};
+
+
+typedef struct __PaWinWdmDeviceInfo
+{
+ PaDeviceInfo inheritedDeviceInfo;
+ char compositeName[MAX_PATH]; /* Composite name consists of pin name + device name in utf8 */
+ PaWinWdmFilter* filter;
+ unsigned long pin;
+ int muxPosition; /* Used only for input devices */
+ int endpointPinId;
+}
+PaWinWdmDeviceInfo;
+
+/* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */
+typedef struct __PaWinWdmHostApiRepresentation
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup* allocations;
+ int deviceCount;
+}
+PaWinWdmHostApiRepresentation;
+
+typedef struct __DATAPACKET
+{
+ KSSTREAM_HEADER Header;
+ OVERLAPPED Signal;
+} DATAPACKET;
+
+typedef struct __PaIOPacket
+{
+ DATAPACKET* packet;
+ unsigned startByte;
+ unsigned lengthBytes;
+} PaIOPacket;
+
+typedef struct __PaWinWdmIOInfo
+{
+ PaWinWdmPin* pPin;
+ char* hostBuffer;
+ unsigned hostBufferSize;
+ unsigned framesPerBuffer;
+ unsigned bytesPerFrame;
+ unsigned bytesPerSample;
+ unsigned noOfPackets; /* Only used in WaveCyclic */
+ HANDLE *events; /* noOfPackets handles (WaveCyclic) 1 (WaveRT) */
+ DATAPACKET *packets; /* noOfPackets packets (WaveCyclic) 2 (WaveRT) */
+ /* WaveRT polled mode */
+ unsigned lastPosition;
+ unsigned pollCntr;
+} PaWinWdmIOInfo;
+
+/* PaWinWdmStream - a stream data structure specifically for this implementation */
+struct __PaWinWdmStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaWDMKSSpecificStreamInfo hostApiStreamInfo; /* This holds info that is exposed through PaStreamInfo */
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+#if PA_TRACE_REALTIME_EVENTS
+ LogHandle hLog;
+#endif
+
+ PaUtilAllocationGroup* allocGroup;
+ PaWinWdmIOInfo capture;
+ PaWinWdmIOInfo render;
+ int streamStarted;
+ int streamActive;
+ int streamStop;
+ int streamAbort;
+ int oldProcessPriority;
+ HANDLE streamThread;
+ HANDLE eventAbort;
+ HANDLE eventStreamStart[StreamStart_kCnt]; /* 0 = OK, 1 = Failed */
+ PaError threadResult;
+ PaStreamFlags streamFlags;
+
+ /* Capture ring buffer */
+ PaUtilRingBuffer ringBuffer;
+ char* ringBufferData;
+
+ /* These values handle the case where the user wants to use fewer
+ * channels than the device has */
+ int userInputChannels;
+ int deviceInputChannels;
+ int userOutputChannels;
+ int deviceOutputChannels;
+};
+
+/* Gather all processing variables in a struct */
+struct __PaProcessThreadInfo
+{
+ PaWinWdmStream *stream;
+ PaStreamCallbackTimeInfo ti;
+ PaStreamCallbackFlags underover;
+ int cbResult;
+ volatile int pending;
+ volatile int priming;
+ volatile int pinsStarted;
+ unsigned long timeout;
+ unsigned captureHead;
+ unsigned captureTail;
+ unsigned renderHead;
+ unsigned renderTail;
+ PaIOPacket capturePackets[4];
+ PaIOPacket renderPackets[4];
+};
+
+/* Used for transferring device infos during scanning / rescanning */
+typedef struct __PaWinWDMScanDeviceInfosResults
+{
+ PaDeviceInfo **deviceInfos;
+ PaDeviceIndex defaultInputDevice;
+ PaDeviceIndex defaultOutputDevice;
+} PaWinWDMScanDeviceInfosResults;
+
+static const unsigned cPacketsArrayMask = 3;
+
+HMODULE DllKsUser = NULL;
+KSCREATEPIN* FunctionKsCreatePin = NULL;
+
+/* prototypes for functions declared in this file */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif /* __cplusplus */
+
+ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+/* Low level I/O functions */
+static PaError WdmSyncIoctl(HANDLE handle,
+ unsigned long ioctlNumber,
+ void* inBuffer,
+ unsigned long inBufferCount,
+ void* outBuffer,
+ unsigned long outBufferCount,
+ unsigned long* bytesReturned);
+
+static PaError WdmGetPropertySimple(HANDLE handle,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ void* value,
+ unsigned long valueCount);
+
+static PaError WdmSetPropertySimple(HANDLE handle,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ void* value,
+ unsigned long valueCount,
+ void* instance,
+ unsigned long instanceCount);
+
+static PaError WdmGetPinPropertySimple(HANDLE handle,
+ unsigned long pinId,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ void* value,
+ unsigned long valueCount,
+ unsigned long* byteCount);
+
+static PaError WdmGetPinPropertyMulti(HANDLE handle,
+ unsigned long pinId,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ KSMULTIPLE_ITEM** ksMultipleItem);
+
+static PaError WdmGetPropertyMulti(HANDLE handle,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ KSMULTIPLE_ITEM** ksMultipleItem);
+
+static PaError WdmSetMuxNodeProperty(HANDLE handle,
+ ULONG nodeId,
+ ULONG pinId);
+
+
+/** Pin management functions */
+static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error);
+static void PinFree(PaWinWdmPin* pin);
+static void PinClose(PaWinWdmPin* pin);
+static PaError PinInstantiate(PaWinWdmPin* pin);
+/*static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state); NOT USED */
+static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state);
+static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format);
+static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format);
+/* WaveRT support */
+static PaError PinQueryNotificationSupport(PaWinWdmPin* pPin, BOOL* pbResult);
+static PaError PinGetBuffer(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier);
+static PaError PinRegisterPositionRegister(PaWinWdmPin* pPin);
+static PaError PinRegisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle);
+static PaError PinUnregisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle);
+static PaError PinGetHwLatency(PaWinWdmPin* pPin, ULONG* pFifoSize, ULONG* pChipsetDelay, ULONG* pCodecDelay);
+static PaError PinGetAudioPositionMemoryMapped(PaWinWdmPin* pPin, ULONG* pPosition);
+static PaError PinGetAudioPositionViaIOCTLRead(PaWinWdmPin* pPin, ULONG* pPosition);
+static PaError PinGetAudioPositionViaIOCTLWrite(PaWinWdmPin* pPin, ULONG* pPosition);
+
+/* Filter management functions */
+static PaWinWdmFilter* FilterNew(PaWDMKSType type, DWORD devNode, const wchar_t* filterName, const wchar_t* friendlyName, PaError* error);
+static PaError FilterInitializePins(PaWinWdmFilter* filter);
+static void FilterFree(PaWinWdmFilter* filter);
+static void FilterAddRef(PaWinWdmFilter* filter);
+static PaWinWdmPin* FilterCreatePin(
+ PaWinWdmFilter* filter,
+ int pinId,
+ const WAVEFORMATEX* wfex,
+ PaError* error);
+static PaError FilterUse(PaWinWdmFilter* filter);
+static void FilterRelease(PaWinWdmFilter* filter);
+
+/* Hot plug functions */
+static BOOL IsDeviceTheSame(const PaWinWdmDeviceInfo* pDev1,
+ const PaWinWdmDeviceInfo* pDev2);
+
+/* Interface functions */
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported(
+struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+
+static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void **newDeviceInfos, int *newDeviceCount );
+static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *deviceInfos, int deviceCount );
+static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *deviceInfos, int deviceCount );
+
+static PaError OpenStream(
+struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData );
+static PaError CloseStream( PaStream* stream );
+static PaError StartStream( PaStream *stream );
+static PaError StopStream( PaStream *stream );
+static PaError AbortStream( PaStream *stream );
+static PaError IsStreamStopped( PaStream *s );
+static PaError IsStreamActive( PaStream *stream );
+static PaTime GetStreamTime( PaStream *stream );
+static double GetStreamCpuLoad( PaStream* stream );
+static PaError ReadStream(
+ PaStream* stream,
+ void *buffer,
+ unsigned long frames );
+static PaError WriteStream(
+ PaStream* stream,
+ const void *buffer,
+ unsigned long frames );
+static signed long GetStreamReadAvailable( PaStream* stream );
+static signed long GetStreamWriteAvailable( PaStream* stream );
+
+/* Utility functions */
+static unsigned long GetWfexSize(const WAVEFORMATEX* wfex);
+static PaWinWdmFilter** BuildFilterList(int* filterCount, int* noOfPaDevices, PaError* result);
+static BOOL PinWrite(HANDLE h, DATAPACKET* p);
+static BOOL PinRead(HANDLE h, DATAPACKET* p);
+static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples);
+static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples);
+PA_THREAD_FUNC ProcessingThread(void*);
+
+/* Pin handler functions */
+static PaError PaPinCaptureEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+static PaError PaPinCaptureSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+
+static PaError PaPinRenderEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+static PaError PaPinRenderSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+
+static PaError PaPinCaptureEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+static PaError PaPinCaptureEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+static PaError PaPinCaptureSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+static PaError PaPinCaptureSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+
+static PaError PaPinRenderEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+static PaError PaPinRenderEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+static PaError PaPinRenderSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+static PaError PaPinRenderSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex);
+
+/* Function bodies */
+
+#if defined(_DEBUG) && defined(PA_ENABLE_DEBUG_OUTPUT)
+#define PA_WDMKS_SET_TREF
+static PaTime tRef = 0;
+
+static void PaWinWdmDebugPrintf(const char* fmt, ...)
+{
+ va_list list;
+ char buffer[1024];
+ PaTime t = PaUtil_GetTime() - tRef;
+ va_start(list, fmt);
+ _vsnprintf(buffer, 1023, fmt, list);
+ va_end(list);
+ PaUtil_DebugPrint("%6.3lf: %s", t, buffer);
+}
+
+#ifdef PA_DEBUG
+#undef PA_DEBUG
+#define PA_DEBUG(x) PaWinWdmDebugPrintf x ;
+#endif
+#endif
+
+static BOOL IsDeviceTheSame(const PaWinWdmDeviceInfo* pDev1,
+ const PaWinWdmDeviceInfo* pDev2)
+{
+ if (pDev1 == NULL || pDev2 == NULL)
+ return FALSE;
+
+ if (pDev1 == pDev2)
+ return TRUE;
+
+ if (strcmp(pDev1->compositeName, pDev2->compositeName) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL IsEarlierThanVista()
+{
+/*
+NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
+versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
+Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
+is faster, for now we just disable the deprecation warning.
+See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
+See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
+*/
+#pragma warning (disable : 4996) /* use of GetVersionEx */
+
+ OSVERSIONINFO osvi;
+ osvi.dwOSVersionInfoSize = sizeof(osvi);
+ if (GetVersionEx(&osvi) && osvi.dwMajorVersion<6)
+ {
+ return TRUE;
+ }
+ return FALSE;
+
+#pragma warning (default : 4996)
+}
+
+
+
+static void MemoryBarrierDummy(void)
+{
+ /* Do nothing */
+}
+
+static void MemoryBarrierRead(void)
+{
+ PaUtil_ReadMemoryBarrier();
+}
+
+static void MemoryBarrierWrite(void)
+{
+ PaUtil_WriteMemoryBarrier();
+}
+
+static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
+{
+ if( wfex->wFormatTag == WAVE_FORMAT_PCM )
+ {
+ return sizeof( WAVEFORMATEX );
+ }
+ else
+ {
+ return (sizeof( WAVEFORMATEX ) + wfex->cbSize);
+ }
+}
+
+static void PaWinWDM_SetLastErrorInfo(long errCode, const char* fmt, ...)
+{
+ va_list list;
+ char buffer[1024];
+ va_start(list, fmt);
+ _vsnprintf(buffer, 1023, fmt, list);
+ va_end(list);
+ PaUtil_SetLastHostErrorInfo(paWDMKS, errCode, buffer);
+}
+
+/*
+Low level pin/filter access functions
+*/
+static PaError WdmSyncIoctl(
+ HANDLE handle,
+ unsigned long ioctlNumber,
+ void* inBuffer,
+ unsigned long inBufferCount,
+ void* outBuffer,
+ unsigned long outBufferCount,
+ unsigned long* bytesReturned)
+{
+ PaError result = paNoError;
+ unsigned long dummyBytesReturned = 0;
+ BOOL bRes;
+
+ if( !bytesReturned )
+ {
+ /* Use a dummy as the caller hasn't supplied one */
+ bytesReturned = &dummyBytesReturned;
+ }
+
+ bRes = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount, outBuffer, outBufferCount, bytesReturned, NULL);
+ if (!bRes)
+ {
+ unsigned long error = GetLastError();
+ if ( !(((error == ERROR_INSUFFICIENT_BUFFER ) || ( error == ERROR_MORE_DATA )) &&
+ ( ioctlNumber == IOCTL_KS_PROPERTY ) &&
+ ( outBufferCount == 0 ) ) )
+ {
+ KSPROPERTY* ksProperty = (KSPROPERTY*)inBuffer;
+
+ PaWinWDM_SetLastErrorInfo(result, "WdmSyncIoctl: DeviceIoControl GLE = 0x%08X (prop_set = {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}, prop_id = %u)",
+ error,
+ ksProperty->Set.Data1, ksProperty->Set.Data2, ksProperty->Set.Data3,
+ ksProperty->Set.Data4[0], ksProperty->Set.Data4[1],
+ ksProperty->Set.Data4[2], ksProperty->Set.Data4[3],
+ ksProperty->Set.Data4[4], ksProperty->Set.Data4[5],
+ ksProperty->Set.Data4[6], ksProperty->Set.Data4[7],
+ ksProperty->Id
+ );
+ result = paUnanticipatedHostError;
+ }
+ }
+ return result;
+}
+
+static PaError WdmGetPropertySimple(HANDLE handle,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ void* value,
+ unsigned long valueCount)
+{
+ PaError result;
+ KSPROPERTY ksProperty;
+
+ ksProperty.Set = *guidPropertySet;
+ ksProperty.Id = property;
+ ksProperty.Flags = KSPROPERTY_TYPE_GET;
+
+ result = WdmSyncIoctl(
+ handle,
+ IOCTL_KS_PROPERTY,
+ &ksProperty,
+ sizeof(KSPROPERTY),
+ value,
+ valueCount,
+ NULL);
+
+ return result;
+}
+
+static PaError WdmSetPropertySimple(
+ HANDLE handle,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ void* value,
+ unsigned long valueCount,
+ void* instance,
+ unsigned long instanceCount)
+{
+ PaError result;
+ KSPROPERTY* ksProperty;
+ unsigned long propertyCount = 0;
+
+ propertyCount = sizeof(KSPROPERTY) + instanceCount;
+ ksProperty = (KSPROPERTY*)_alloca( propertyCount );
+ if( !ksProperty )
+ {
+ return paInsufficientMemory;
+ }
+
+ ksProperty->Set = *guidPropertySet;
+ ksProperty->Id = property;
+ ksProperty->Flags = KSPROPERTY_TYPE_SET;
+
+ if( instance )
+ {
+ memcpy((void*)((char*)ksProperty + sizeof(KSPROPERTY)), instance, instanceCount);
+ }
+
+ result = WdmSyncIoctl(
+ handle,
+ IOCTL_KS_PROPERTY,
+ ksProperty,
+ propertyCount,
+ value,
+ valueCount,
+ NULL);
+
+ return result;
+}
+
+static PaError WdmGetPinPropertySimple(
+ HANDLE handle,
+ unsigned long pinId,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ void* value,
+ unsigned long valueCount,
+ unsigned long *byteCount)
+{
+ PaError result;
+
+ KSP_PIN ksPProp;
+ ksPProp.Property.Set = *guidPropertySet;
+ ksPProp.Property.Id = property;
+ ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
+ ksPProp.PinId = pinId;
+ ksPProp.Reserved = 0;
+
+ result = WdmSyncIoctl(
+ handle,
+ IOCTL_KS_PROPERTY,
+ &ksPProp,
+ sizeof(KSP_PIN),
+ value,
+ valueCount,
+ byteCount);
+
+ return result;
+}
+
+static PaError WdmGetPinPropertyMulti(
+ HANDLE handle,
+ unsigned long pinId,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ KSMULTIPLE_ITEM** ksMultipleItem)
+{
+ PaError result;
+ unsigned long multipleItemSize = 0;
+ KSP_PIN ksPProp;
+
+ ksPProp.Property.Set = *guidPropertySet;
+ ksPProp.Property.Id = property;
+ ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
+ ksPProp.PinId = pinId;
+ ksPProp.Reserved = 0;
+
+ result = WdmSyncIoctl(
+ handle,
+ IOCTL_KS_PROPERTY,
+ &ksPProp.Property,
+ sizeof(KSP_PIN),
+ NULL,
+ 0,
+ &multipleItemSize);
+ if( result != paNoError )
+ {
+ return result;
+ }
+
+ *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
+ if( !*ksMultipleItem )
+ {
+ return paInsufficientMemory;
+ }
+
+ result = WdmSyncIoctl(
+ handle,
+ IOCTL_KS_PROPERTY,
+ &ksPProp,
+ sizeof(KSP_PIN),
+ (void*)*ksMultipleItem,
+ multipleItemSize,
+ NULL);
+
+ if( result != paNoError )
+ {
+ PaUtil_FreeMemory( ksMultipleItem );
+ }
+
+ return result;
+}
+
+static PaError WdmGetPropertyMulti(HANDLE handle,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ KSMULTIPLE_ITEM** ksMultipleItem)
+{
+ PaError result;
+ unsigned long multipleItemSize = 0;
+ KSPROPERTY ksProp;
+
+ ksProp.Set = *guidPropertySet;
+ ksProp.Id = property;
+ ksProp.Flags = KSPROPERTY_TYPE_GET;
+
+ result = WdmSyncIoctl(
+ handle,
+ IOCTL_KS_PROPERTY,
+ &ksProp,
+ sizeof(KSPROPERTY),
+ NULL,
+ 0,
+ &multipleItemSize);
+ if( result != paNoError )
+ {
+ return result;
+ }
+
+ *ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
+ if( !*ksMultipleItem )
+ {
+ return paInsufficientMemory;
+ }
+
+ result = WdmSyncIoctl(
+ handle,
+ IOCTL_KS_PROPERTY,
+ &ksProp,
+ sizeof(KSPROPERTY),
+ (void*)*ksMultipleItem,
+ multipleItemSize,
+ NULL);
+
+ if( result != paNoError )
+ {
+ PaUtil_FreeMemory( ksMultipleItem );
+ }
+
+ return result;
+}
+
+static PaError WdmSetMuxNodeProperty(HANDLE handle,
+ ULONG nodeId,
+ ULONG pinId)
+{
+ PaError result = paNoError;
+ KSNODEPROPERTY prop;
+ prop.Property.Set = KSPROPSETID_Audio;
+ prop.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
+ prop.Property.Flags = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY;
+ prop.NodeId = nodeId;
+ prop.Reserved = 0;
+
+ result = WdmSyncIoctl(handle, IOCTL_KS_PROPERTY, &prop, sizeof(KSNODEPROPERTY), &pinId, sizeof(ULONG), NULL);
+
+ return result;
+}
+
+/* Used when traversing topology for outputs */
+static const KSTOPOLOGY_CONNECTION* GetConnectionTo(const KSTOPOLOGY_CONNECTION* pFrom, PaWinWdmFilter* filter, int muxIdx)
+{
+ unsigned i;
+ const KSTOPOLOGY_CONNECTION* retval = NULL;
+ const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
+ (void)muxIdx;
+ PA_DEBUG(("GetConnectionTo: Checking %u connections... (pFrom = %p)", filter->connections->Count, pFrom));
+ for (i = 0; i < filter->connections->Count; ++i)
+ {
+ const KSTOPOLOGY_CONNECTION* pConn = connections + i;
+ if (pConn == pFrom)
+ continue;
+
+ if (pConn->FromNode == pFrom->ToNode)
+ {
+ retval = pConn;
+ break;
+ }
+ }
+ PA_DEBUG(("GetConnectionTo: Returning %p\n", retval));
+ return retval;
+}
+
+/* Used when traversing topology for inputs */
+static const KSTOPOLOGY_CONNECTION* GetConnectionFrom(const KSTOPOLOGY_CONNECTION* pTo, PaWinWdmFilter* filter, int muxIdx)
+{
+ unsigned i;
+ const KSTOPOLOGY_CONNECTION* retval = NULL;
+ const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
+ int muxCntr = 0;
+ PA_DEBUG(("GetConnectionFrom: Checking %u connections... (pTo = %p)\n", filter->connections->Count, pTo));
+ for (i = 0; i < filter->connections->Count; ++i)
+ {
+ const KSTOPOLOGY_CONNECTION* pConn = connections + i;
+ if (pConn == pTo)
+ continue;
+
+ if (pConn->ToNode == pTo->FromNode)
+ {
+ if (muxIdx >= 0)
+ {
+ if (muxCntr < muxIdx)
+ {
+ ++muxCntr;
+ continue;
+ }
+ }
+ retval = pConn;
+ break;
+ }
+ }
+ PA_DEBUG(("GetConnectionFrom: Returning %p\n", retval));
+ return retval;
+}
+
+static ULONG GetNumberOfConnectionsTo(const KSTOPOLOGY_CONNECTION* pTo, PaWinWdmFilter* filter)
+{
+ ULONG retval = 0;
+ unsigned i;
+ const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
+ PA_DEBUG(("GetNumberOfConnectionsTo: Checking %u connections...\n", filter->connections->Count));
+ for (i = 0; i < filter->connections->Count; ++i)
+ {
+ const KSTOPOLOGY_CONNECTION* pConn = connections + i;
+ if (pConn->ToNode == pTo->FromNode &&
+ (pTo->FromNode != KSFILTER_NODE || pConn->ToNodePin == pTo->FromNodePin))
+ {
+ ++retval;
+ }
+ }
+ PA_DEBUG(("GetNumberOfConnectionsTo: Returning %d\n", retval));
+ return retval;
+}
+
+typedef const KSTOPOLOGY_CONNECTION *(*TFnGetConnection)(const KSTOPOLOGY_CONNECTION*, PaWinWdmFilter*, int);
+
+static const KSTOPOLOGY_CONNECTION* FindStartConnectionFrom(ULONG startPin, PaWinWdmFilter* filter)
+{
+ unsigned i;
+ const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
+ PA_DEBUG(("FindStartConnectionFrom: Startpin %u, Checking %u connections...\n", startPin, filter->connections->Count));
+ for (i = 0; i < filter->connections->Count; ++i)
+ {
+ const KSTOPOLOGY_CONNECTION* pConn = connections + i;
+ if (pConn->ToNode == KSFILTER_NODE && pConn->ToNodePin == startPin)
+ {
+ PA_DEBUG(("FindStartConnectionFrom: returning %p\n", pConn));
+ return pConn;
+ }
+ }
+
+ /* Some devices may report topologies that leave pins unconnected. This may be by design or driver installation
+ issues. Pass the error condition back to caller. */
+ PA_DEBUG(("FindStartConnectionFrom: returning NULL\n"));
+ return 0;
+}
+
+static const KSTOPOLOGY_CONNECTION* FindStartConnectionTo(ULONG startPin, PaWinWdmFilter* filter)
+{
+ unsigned i;
+ const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
+ PA_DEBUG(("FindStartConnectionTo: Startpin %u, Checking %u connections...\n", startPin, filter->connections->Count));
+ for (i = 0; i < filter->connections->Count; ++i)
+ {
+ const KSTOPOLOGY_CONNECTION* pConn = connections + i;
+ if (pConn->FromNode == KSFILTER_NODE && pConn->FromNodePin == startPin)
+ {
+ PA_DEBUG(("FindStartConnectionTo: returning %p\n", pConn));
+ return pConn;
+ }
+ }
+
+ /* Unconnected pin. Inform caller. */
+ PA_DEBUG(("FindStartConnectionTo: returning NULL\n"));
+ return 0;
+}
+
+static ULONG GetConnectedPin(ULONG startPin, BOOL forward, PaWinWdmFilter* filter, int muxPosition, ULONG *muxInputPinId, ULONG *muxNodeId)
+{
+ int limit=1000;
+ const KSTOPOLOGY_CONNECTION *conn = NULL;
+ TFnGetConnection fnGetConnection = forward ? GetConnectionTo : GetConnectionFrom ;
+ PA_LOGE_;
+ while (1)
+ {
+ limit--;
+ if (limit == 0) {
+ PA_DEBUG(("GetConnectedPin: LOOP LIMIT REACHED\n"));
+ break;
+ }
+
+ if (conn == NULL)
+ {
+ conn = forward ? FindStartConnectionTo(startPin, filter) : FindStartConnectionFrom(startPin, filter);
+ }
+ else
+ {
+ conn = fnGetConnection(conn, filter, -1);
+ }
+
+ /* Handling case of erroneous connection list */
+ if (conn == NULL)
+ {
+ break;
+ }
+
+ if (forward ? conn->ToNode == KSFILTER_NODE : conn->FromNode == KSFILTER_NODE)
+ {
+ return forward ? conn->ToNodePin : conn->FromNodePin;
+ }
+ else
+ {
+ PA_DEBUG(("GetConnectedPin: count=%d, forward=%d, muxPosition=%d\n", filter->nodes->Count, forward, muxPosition));
+ if (filter->nodes->Count > 0 && !forward && muxPosition >= 0)
+ {
+ const GUID* nodes = (const GUID*)(filter->nodes + 1);
+ if (IsEqualGUID(&nodes[conn->FromNode], &KSNODETYPE_MUX))
+ {
+ ULONG nConn = GetNumberOfConnectionsTo(conn, filter);
+ conn = fnGetConnection(conn, filter, muxPosition);
+ if (conn == NULL)
+ {
+ break;
+ }
+ if (muxInputPinId != 0)
+ {
+ *muxInputPinId = conn->ToNodePin;
+ }
+ if (muxNodeId != 0)
+ {
+ *muxNodeId = conn->ToNode;
+ }
+ }
+ }
+ }
+ }
+ PA_LOGL_;
+ return KSFILTER_NODE;
+}
+
+static void DumpConnectionsAndNodes(PaWinWdmFilter* filter)
+{
+ unsigned i;
+ const KSTOPOLOGY_CONNECTION* connections = (const KSTOPOLOGY_CONNECTION*)(filter->connections + 1);
+ const GUID* nodes = (const GUID*)(filter->nodes + 1);
+
+ PA_LOGE_;
+ PA_DEBUG(("DumpConnectionsAndNodes: connections=%d, nodes=%d\n", filter->connections->Count, filter->nodes->Count));
+
+ for (i=0; i < filter->connections->Count; ++i)
+ {
+ const KSTOPOLOGY_CONNECTION* pConn = connections + i;
+ PA_DEBUG((" Connection: %u - FromNode=%u,FromPin=%u -> ToNode=%u,ToPin=%u\n",
+ i,
+ pConn->FromNode, pConn->FromNodePin,
+ pConn->ToNode, pConn->ToNodePin
+ ));
+ }
+
+ for (i=0; i < filter->nodes->Count; ++i)
+ {
+ const GUID* pConn = nodes + i;
+ PA_DEBUG((" Node: %d - {%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
+ i,
+ pConn->Data1, pConn->Data2, pConn->Data3,
+ pConn->Data4[0], pConn->Data4[1],
+ pConn->Data4[2], pConn->Data4[3],
+ pConn->Data4[4], pConn->Data4[5],
+ pConn->Data4[6], pConn->Data4[7]
+ ));
+ }
+ PA_LOGL_;
+
+}
+
+typedef struct __PaUsbTerminalGUIDToName
+{
+ USHORT usbGUID;
+ wchar_t name[64];
+} PaUsbTerminalGUIDToName;
+
+static const PaUsbTerminalGUIDToName kNames[] =
+{
+ /* Types copied from: http://msdn.microsoft.com/en-us/library/ff537742(v=vs.85).aspx */
+ /* Input terminal types */
+ { 0x0201, L"Microphone" },
+ { 0x0202, L"Desktop Microphone" },
+ { 0x0203, L"Personal Microphone" },
+ { 0x0204, L"Omni Directional Microphone" },
+ { 0x0205, L"Microphone Array" },
+ { 0x0206, L"Processing Microphone Array" },
+ /* Output terminal types */
+ { 0x0301, L"Speakers" },
+ { 0x0302, L"Headphones" },
+ { 0x0303, L"Head Mounted Display Audio" },
+ { 0x0304, L"Desktop Speaker" },
+ { 0x0305, L"Room Speaker" },
+ { 0x0306, L"Communication Speaker" },
+ { 0x0307, L"LFE Speakers" },
+ /* External terminal types */
+ { 0x0601, L"Analog" },
+ { 0x0602, L"Digital" },
+ { 0x0603, L"Line" },
+ { 0x0604, L"Audio" },
+ { 0x0605, L"SPDIF" },
+};
+
+static const unsigned kNamesCnt = sizeof(kNames)/sizeof(PaUsbTerminalGUIDToName);
+
+static int PaUsbTerminalGUIDToNameCmp(const void* lhs, const void* rhs)
+{
+ const PaUsbTerminalGUIDToName* pL = (const PaUsbTerminalGUIDToName*)lhs;
+ const PaUsbTerminalGUIDToName* pR = (const PaUsbTerminalGUIDToName*)rhs;
+ return ((int)(pL->usbGUID) - (int)(pR->usbGUID));
+}
+
+static PaError GetNameFromCategory(const GUID* pGUID, BOOL input, wchar_t* name, unsigned length)
+{
+ PaError result = paUnanticipatedHostError;
+ USHORT usbTerminalGUID = (USHORT)(pGUID->Data1 - 0xDFF219E0);
+
+ PA_LOGE_;
+ if (input && usbTerminalGUID >= 0x301 && usbTerminalGUID < 0x400)
+ {
+ /* Output terminal name for an input !? Set it to Line! */
+ usbTerminalGUID = 0x603;
+ }
+ if (!input && usbTerminalGUID >= 0x201 && usbTerminalGUID < 0x300)
+ {
+ /* Input terminal name for an output !? Set it to Line! */
+ usbTerminalGUID = 0x603;
+ }
+ if (usbTerminalGUID >= 0x201 && usbTerminalGUID < 0x713)
+ {
+ PaUsbTerminalGUIDToName s = { usbTerminalGUID };
+ const PaUsbTerminalGUIDToName* ptr = bsearch(
+ &s,
+ kNames,
+ kNamesCnt,
+ sizeof(PaUsbTerminalGUIDToName),
+ PaUsbTerminalGUIDToNameCmp
+ );
+ if (ptr != 0)
+ {
+ PA_DEBUG(("GetNameFromCategory: USB GUID %04X -> '%S'\n", usbTerminalGUID, ptr->name));
+
+ if (name != NULL && length > 0)
+ {
+ int n = _snwprintf(name, length, L"%s", ptr->name);
+ if (usbTerminalGUID >= 0x601 && usbTerminalGUID < 0x700)
+ {
+ _snwprintf(name + n, length - n, L" %s", (input ? L"In":L"Out"));
+ }
+ }
+ result = paNoError;
+ }
+ }
+ else
+ {
+ PaWinWDM_SetLastErrorInfo(result, "GetNameFromCategory: usbTerminalGUID = %04X ", usbTerminalGUID);
+ }
+ PA_LOGL_;
+ return result;
+}
+
+static BOOL IsFrequencyWithinRange(const KSDATARANGE_AUDIO* range, int frequency)
+{
+ if (frequency < (int)range->MinimumSampleFrequency)
+ return FALSE;
+ if (frequency > (int)range->MaximumSampleFrequency)
+ return FALSE;
+ return TRUE;
+}
+
+static BOOL IsBitsWithinRange(const KSDATARANGE_AUDIO* range, int noOfBits)
+{
+ if (noOfBits < (int)range->MinimumBitsPerSample)
+ return FALSE;
+ if (noOfBits > (int)range->MaximumBitsPerSample)
+ return FALSE;
+ return TRUE;
+}
+
+/* Note: Somewhat different order compared to WMME implementation, as we want to focus on fidelity first */
+static const int defaultSampleRateSearchOrder[] =
+{ 44100, 48000, 88200, 96000, 192000, 32000, 24000, 22050, 16000, 12000, 11025, 9600, 8000 };
+static const int defaultSampleRateSearchOrderCount = sizeof(defaultSampleRateSearchOrder)/sizeof(defaultSampleRateSearchOrder[0]);
+
+static int DefaultSampleFrequencyIndex(const KSDATARANGE_AUDIO* range)
+{
+ int i;
+
+ for(i=0; i < defaultSampleRateSearchOrderCount; ++i)
+ {
+ int currentFrequency = defaultSampleRateSearchOrder[i];
+
+ if (IsFrequencyWithinRange(range, currentFrequency))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*
+Create a new pin object belonging to a filter
+The pin object holds all the configuration information about the pin
+before it is opened, and then the handle of the pin after is opened
+*/
+static PaWinWdmPin* PinNew(PaWinWdmFilter* parentFilter, unsigned long pinId, PaError* error)
+{
+ PaWinWdmPin* pin;
+ PaError result;
+ unsigned long i;
+ KSMULTIPLE_ITEM* item = NULL;
+ KSIDENTIFIER* identifier;
+ KSDATARANGE* dataRange;
+ const ULONG streamingId = (parentFilter->devInfo.streamingType == Type_kWaveRT) ? KSINTERFACE_STANDARD_LOOPED_STREAMING : KSINTERFACE_STANDARD_STREAMING;
+ int defaultSampleRateIndex = defaultSampleRateSearchOrderCount;
+
+ PA_LOGE_;
+ PA_DEBUG(("PinNew: Creating pin %d:\n",pinId));
+
+ /* Allocate the new PIN object */
+ pin = (PaWinWdmPin*)PaUtil_AllocateMemory( sizeof(PaWinWdmPin) );
+ if( !pin )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* Zero the pin object */
+ /* memset( (void*)pin, 0, sizeof(PaWinWdmPin) ); */
+
+ pin->parentFilter = parentFilter;
+ pin->pinId = pinId;
+
+ /* Allocate a connect structure */
+ pin->pinConnectSize = sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX);
+ pin->pinConnect = (KSPIN_CONNECT*)PaUtil_AllocateMemory( pin->pinConnectSize );
+ if( !pin->pinConnect )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* Configure the connect structure with default values */
+ pin->pinConnect->Interface.Set = KSINTERFACESETID_Standard;
+ pin->pinConnect->Interface.Id = streamingId;
+ pin->pinConnect->Interface.Flags = 0;
+ pin->pinConnect->Medium.Set = KSMEDIUMSETID_Standard;
+ pin->pinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
+ pin->pinConnect->Medium.Flags = 0;
+ pin->pinConnect->PinId = pinId;
+ pin->pinConnect->PinToHandle = NULL;
+ pin->pinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
+ pin->pinConnect->Priority.PrioritySubClass = 1;
+ pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)(pin->pinConnect + 1);
+ pin->ksDataFormatWfx->DataFormat.FormatSize = sizeof(KSDATAFORMAT_WAVEFORMATEX);
+ pin->ksDataFormatWfx->DataFormat.Flags = 0;
+ pin->ksDataFormatWfx->DataFormat.Reserved = 0;
+ pin->ksDataFormatWfx->DataFormat.MajorFormat = KSDATAFORMAT_TYPE_AUDIO;
+ pin->ksDataFormatWfx->DataFormat.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ pin->ksDataFormatWfx->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX;
+
+ pin->frameSize = 0; /* Unknown until we instantiate pin */
+
+ /* Get the COMMUNICATION property */
+ result = WdmGetPinPropertySimple(
+ parentFilter->handle,
+ pinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_COMMUNICATION,
+ &pin->communication,
+ sizeof(KSPIN_COMMUNICATION),
+ NULL);
+ if( result != paNoError )
+ goto error;
+
+ if( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
+ (pin->communication != KSPIN_COMMUNICATION_SINK) &&
+ (pin->communication != KSPIN_COMMUNICATION_BOTH) )
+ {
+ PA_DEBUG(("PinNew: Not source/sink\n"));
+ result = paInvalidDevice;
+ goto error;
+ }
+
+ /* Get dataflow information */
+ result = WdmGetPinPropertySimple(
+ parentFilter->handle,
+ pinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_DATAFLOW,
+ &pin->dataFlow,
+ sizeof(KSPIN_DATAFLOW),
+ NULL);
+
+ if( result != paNoError )
+ goto error;
+
+ /* Get the INTERFACE property list */
+ result = WdmGetPinPropertyMulti(
+ parentFilter->handle,
+ pinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_INTERFACES,
+ &item);
+
+ if( result != paNoError )
+ goto error;
+
+ identifier = (KSIDENTIFIER*)(item+1);
+
+ /* Check that at least one interface is STANDARD_STREAMING */
+ result = paUnanticipatedHostError;
+ for( i = 0; i < item->Count; i++ )
+ {
+ if( IsEqualGUID(&identifier[i].Set, &KSINTERFACESETID_Standard) && ( identifier[i].Id == streamingId ) )
+ {
+ result = paNoError;
+ break;
+ }
+ }
+
+ if( result != paNoError )
+ {
+ PA_DEBUG(("PinNew: No %s streaming\n", streamingId==KSINTERFACE_STANDARD_LOOPED_STREAMING?"looped":"standard"));
+ goto error;
+ }
+
+ /* Don't need interfaces any more */
+ PaUtil_FreeMemory( item );
+ item = NULL;
+
+ /* Get the MEDIUM properties list */
+ result = WdmGetPinPropertyMulti(
+ parentFilter->handle,
+ pinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_MEDIUMS,
+ &item);
+
+ if( result != paNoError )
+ goto error;
+
+ identifier = (KSIDENTIFIER*)(item+1); /* Not actually necessary... */
+
+ /* Check that at least one medium is STANDARD_DEVIO */
+ result = paUnanticipatedHostError;
+ for( i = 0; i < item->Count; i++ )
+ {
+ if( IsEqualGUID(&identifier[i].Set, &KSMEDIUMSETID_Standard) && ( identifier[i].Id == KSMEDIUM_STANDARD_DEVIO ) )
+ {
+ result = paNoError;
+ break;
+ }
+ }
+
+ if( result != paNoError )
+ {
+ PA_DEBUG(("No standard devio\n"));
+ goto error;
+ }
+ /* Don't need mediums any more */
+ PaUtil_FreeMemory( item );
+ item = NULL;
+
+ /* Get DATARANGES */
+ result = WdmGetPinPropertyMulti(
+ parentFilter->handle,
+ pinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_DATARANGES,
+ &pin->dataRangesItem);
+
+ if( result != paNoError )
+ goto error;
+
+ pin->dataRanges = (KSDATARANGE*)(pin->dataRangesItem +1);
+
+ /* Check that at least one datarange supports audio */
+ result = paUnanticipatedHostError;
+ dataRange = pin->dataRanges;
+ pin->maxChannels = 0;
+ pin->defaultSampleRate = 0;
+ pin->formats = 0;
+ PA_DEBUG(("PinNew: Checking %u no of dataranges...\n", pin->dataRangesItem->Count));
+ for( i = 0; i < pin->dataRangesItem->Count; i++)
+ {
+ PA_DEBUG(("PinNew: DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat))));
+ /* Check that subformat is WAVEFORMATEX, PCM or WILDCARD */
+ if( IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) ||
+ IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM) ||
+ IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) ||
+ IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_WILDCARD) ||
+ IsEqualGUID(&dataRange->MajorFormat, &KSDATAFORMAT_TYPE_AUDIO) )
+ {
+ int defaultIndex;
+ result = paNoError;
+ /* Record the maximum possible channels with this pin */
+ if( ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels == (ULONG) -1 )
+ {
+ pin->maxChannels = MAXIMUM_NUMBER_OF_CHANNELS;
+ }
+ else if( (int) ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels )
+ {
+ pin->maxChannels = (int) ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels;
+ }
+ PA_DEBUG(("PinNew: MaxChannel: %d\n",pin->maxChannels));
+
+ /* Record the formats (bit depths) that are supported */
+ if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 8) )
+ {
+ pin->formats |= paInt8;
+ PA_DEBUG(("PinNew: Format PCM 8 bit supported\n"));
+ }
+ if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 16) )
+ {
+ pin->formats |= paInt16;
+ PA_DEBUG(("PinNew: Format PCM 16 bit supported\n"));
+ }
+ if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 24) )
+ {
+ pin->formats |= paInt24;
+ PA_DEBUG(("PinNew: Format PCM 24 bit supported\n"));
+ }
+ if( IsBitsWithinRange((KSDATARANGE_AUDIO*)dataRange, 32) )
+ {
+ if (IsEqualGUID(&dataRange->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+ {
+ pin->formats |= paFloat32;
+ PA_DEBUG(("PinNew: Format IEEE float 32 bit supported\n"));
+ }
+ else
+ {
+ pin->formats |= paInt32;
+ PA_DEBUG(("PinNew: Format PCM 32 bit supported\n"));
+ }
+ }
+
+ defaultIndex = DefaultSampleFrequencyIndex((KSDATARANGE_AUDIO*)dataRange);
+ if (defaultIndex >= 0 && defaultIndex < defaultSampleRateIndex)
+ {
+ defaultSampleRateIndex = defaultIndex;
+ }
+ }
+ dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
+ }
+
+ if( result != paNoError )
+ goto error;
+
+ /* If none of the frequencies searched for are present, there's something seriously wrong */
+ if (defaultSampleRateIndex == defaultSampleRateSearchOrderCount)
+ {
+ PA_DEBUG(("PinNew: No default sample rate found, skipping pin!\n"));
+ PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "PinNew: No default sample rate found");
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+
+ /* Set the default sample rate */
+ pin->defaultSampleRate = defaultSampleRateSearchOrder[defaultSampleRateIndex];
+ PA_DEBUG(("PinNew: Default sample rate = %d Hz\n", pin->defaultSampleRate));
+
+ /* Get instance information */
+ result = WdmGetPinPropertySimple(
+ parentFilter->handle,
+ pinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_CINSTANCES,
+ &pin->instances,
+ sizeof(KSPIN_CINSTANCES),
+ NULL);
+
+ if( result != paNoError )
+ goto error;
+
+ /* If WaveRT, check if pin supports notification mode */
+ if (parentFilter->devInfo.streamingType == Type_kWaveRT)
+ {
+ BOOL bSupportsNotification = FALSE;
+ if (PinQueryNotificationSupport(pin, &bSupportsNotification) == paNoError)
+ {
+ pin->pinKsSubType = bSupportsNotification ? SubType_kNotification : SubType_kPolled;
+ }
+ }
+
+ /* Query pin name (which means we need to traverse to non IRP pin, via physical connection to topology filter pin, through
+ its nodes to the endpoint pin, and get that ones name... phew...) */
+ PA_DEBUG(("PinNew: Finding topology pin...\n"));
+
+ {
+ ULONG topoPinId = GetConnectedPin(pinId, (pin->dataFlow == KSPIN_DATAFLOW_IN), parentFilter, -1, NULL, NULL);
+ const wchar_t kInputName[] = L"Input";
+ const wchar_t kOutputName[] = L"Output";
+
+ if (topoPinId != KSFILTER_NODE)
+ {
+ /* Get physical connection for topo pin */
+ unsigned long cbBytes = 0;
+ PA_DEBUG(("PinNew: Getting physical connection...\n"));
+ result = WdmGetPinPropertySimple(parentFilter->handle,
+ topoPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_PHYSICALCONNECTION,
+ 0,
+ 0,
+ &cbBytes
+ );
+
+ if (result != paNoError)
+ {
+ /* No physical connection -> there is no topology filter! So we get the name of the pin! */
+ PA_DEBUG(("PinNew: No physical connection! Getting the pin name\n"));
+ result = WdmGetPinPropertySimple(parentFilter->handle,
+ topoPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_NAME,
+ pin->friendlyName,
+ MAX_PATH,
+ NULL);
+ if (result != paNoError)
+ {
+ GUID category = {0};
+
+ /* Get pin category information */
+ result = WdmGetPinPropertySimple(parentFilter->handle,
+ topoPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_CATEGORY,
+ &category,
+ sizeof(GUID),
+ NULL);
+
+ if (result == paNoError)
+ {
+ result = GetNameFromCategory(&category, (pin->dataFlow == KSPIN_DATAFLOW_OUT), pin->friendlyName, MAX_PATH);
+ }
+ }
+
+ /* Make sure pin gets a name here... */
+ if (wcslen(pin->friendlyName) == 0)
+ {
+ wcscpy(pin->friendlyName, (pin->dataFlow == KSPIN_DATAFLOW_IN) ? kOutputName : kInputName);
+#ifdef UNICODE
+ PA_DEBUG(("PinNew: Setting pin friendly name to '%s'\n", pin->friendlyName));
+#else
+ PA_DEBUG(("PinNew: Setting pin friendly name to '%S'\n", pin->friendlyName));
+#endif
+ }
+
+ /* This is then == the endpoint pin */
+ pin->endpointPinId = (pin->dataFlow == KSPIN_DATAFLOW_IN) ? pinId : topoPinId;
+ }
+ else
+ {
+ KSPIN_PHYSICALCONNECTION* pc = (KSPIN_PHYSICALCONNECTION*)PaUtil_AllocateMemory(cbBytes + 2);
+ ULONG pcPin;
+ wchar_t symbLinkName[MAX_PATH];
+ PA_DEBUG(("PinNew: Physical connection found!\n"));
+ if (pc == NULL)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ result = WdmGetPinPropertySimple(parentFilter->handle,
+ topoPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_PHYSICALCONNECTION,
+ pc,
+ cbBytes,
+ NULL
+ );
+
+ pcPin = pc->Pin;
+ wcsncpy(symbLinkName, pc->SymbolicLinkName, MAX_PATH);
+ PaUtil_FreeMemory( pc );
+
+ if (result != paNoError)
+ {
+ /* Shouldn't happen, but fail if it does */
+ PA_DEBUG(("PinNew: failed to retrieve physical connection!\n"));
+ goto error;
+ }
+
+ if (symbLinkName[1] == TEXT('?'))
+ {
+ symbLinkName[1] = TEXT('\\');
+ }
+
+ if (pin->parentFilter->topologyFilter == NULL)
+ {
+ PA_DEBUG(("PinNew: Creating topology filter '%S'\n", symbLinkName));
+
+ pin->parentFilter->topologyFilter = FilterNew(Type_kNotUsed, 0, symbLinkName, L"", &result);
+ if (pin->parentFilter->topologyFilter == NULL)
+ {
+ PA_DEBUG(("PinNew: Failed creating topology filter\n"));
+ result = paUnanticipatedHostError;
+ PaWinWDM_SetLastErrorInfo(result, "Failed to create topology filter '%S'", symbLinkName);
+ goto error;
+ }
+
+ /* Copy info so we have it in device info */
+ wcsncpy(pin->parentFilter->devInfo.topologyPath, symbLinkName, MAX_PATH);
+ }
+ else
+ {
+ /* Must be the same */
+ assert(wcscmp(symbLinkName, pin->parentFilter->topologyFilter->devInfo.filterPath) == 0);
+ }
+
+ PA_DEBUG(("PinNew: Opening topology filter..."));
+
+ result = FilterUse(pin->parentFilter->topologyFilter);
+ if (result == paNoError)
+ {
+ unsigned long endpointPinId;
+
+ if (pin->dataFlow == KSPIN_DATAFLOW_IN)
+ {
+ /* The "endpointPinId" is what WASAPI looks at for pin names */
+ GUID category = {0};
+
+ PA_DEBUG(("PinNew: Checking for output endpoint pin id...\n"));
+
+ endpointPinId = GetConnectedPin(pcPin, TRUE, pin->parentFilter->topologyFilter, -1, NULL, NULL);
+
+ if (endpointPinId == KSFILTER_NODE)
+ {
+ result = paUnanticipatedHostError;
+ PaWinWDM_SetLastErrorInfo(result, "Failed to get endpoint pin ID on topology filter!");
+ goto error;
+ }
+
+ PA_DEBUG(("PinNew: Found endpoint pin id %u\n", endpointPinId));
+
+ /* Get pin category information */
+ result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
+ endpointPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_CATEGORY,
+ &category,
+ sizeof(GUID),
+ NULL);
+
+ if (result == paNoError)
+ {
+#if !PA_WDMKS_USE_CATEGORY_FOR_PIN_NAMES
+ wchar_t pinName[MAX_PATH];
+
+ PA_DEBUG(("PinNew: Getting pin name property..."));
+
+ /* Ok, try pin name also, and favor that if available */
+ result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
+ endpointPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_NAME,
+ pinName,
+ MAX_PATH,
+ NULL);
+
+ if (result == paNoError && wcslen(pinName)>0)
+ {
+ wcsncpy(pin->friendlyName, pinName, MAX_PATH);
+ }
+ else
+#endif
+ {
+ result = GetNameFromCategory(&category, (pin->dataFlow == KSPIN_DATAFLOW_OUT), pin->friendlyName, MAX_PATH);
+ }
+ }
+
+ /* Make sure we get a name for the pin */
+ if (wcslen(pin->friendlyName) == 0)
+ {
+ wcscpy(pin->friendlyName, kOutputName);
+ }
+#ifdef UNICODE
+ PA_DEBUG(("PinNew: Pin name '%s'\n", pin->friendlyName));
+#else
+ PA_DEBUG(("PinNew: Pin name '%S'\n", pin->friendlyName));
+#endif
+
+ /* Set endpoint pin ID (this is the topology INPUT pin, since portmixer will always traverse the
+ filter in audio streaming direction, see http://msdn.microsoft.com/en-us/library/windows/hardware/ff536331(v=vs.85).aspx
+ for more information)
+ */
+ pin->endpointPinId = pcPin;
+ }
+ else
+ {
+ unsigned muxCount = 0;
+ int muxPos = 0;
+ /* Max 64 multiplexer inputs... sanity check :) */
+ for (i = 0; i < 64; ++i)
+ {
+ ULONG muxNodeIdTest = (unsigned)-1;
+ PA_DEBUG(("PinNew: Checking for input endpoint pin id (%d)...\n", i));
+
+ endpointPinId = GetConnectedPin(pcPin,
+ FALSE,
+ pin->parentFilter->topologyFilter,
+ (int)i,
+ NULL,
+ &muxNodeIdTest);
+
+ if (endpointPinId == KSFILTER_NODE)
+ {
+ /* We're done */
+ PA_DEBUG(("PinNew: Done with inputs.\n", endpointPinId));
+ break;
+ }
+ else
+ {
+ /* The "endpointPinId" is what WASAPI looks at for pin names */
+ GUID category = {0};
+
+ PA_DEBUG(("PinNew: Found endpoint pin id %u\n", endpointPinId));
+
+ /* Get pin category information */
+ result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
+ endpointPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_CATEGORY,
+ &category,
+ sizeof(GUID),
+ NULL);
+
+ if (result == paNoError)
+ {
+ if (muxNodeIdTest == (unsigned)-1)
+ {
+ /* Ok, try pin name, and favor that if available */
+ result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
+ endpointPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_NAME,
+ pin->friendlyName,
+ MAX_PATH,
+ NULL);
+
+ if (result != paNoError)
+ {
+ result = GetNameFromCategory(&category, TRUE, pin->friendlyName, MAX_PATH);
+ }
+ break;
+ }
+ else
+ {
+ result = GetNameFromCategory(&category, TRUE, NULL, 0);
+
+ if (result == paNoError)
+ {
+ ++muxCount;
+ }
+ }
+ }
+ else
+ {
+ PA_DEBUG(("PinNew: Failed to get pin category"));
+ }
+ }
+ }
+
+ if (muxCount == 0)
+ {
+ pin->endpointPinId = endpointPinId;
+ /* Make sure we get a name for the pin */
+ if (wcslen(pin->friendlyName) == 0)
+ {
+ wcscpy(pin->friendlyName, kInputName);
+ }
+#ifdef UNICODE
+ PA_DEBUG(("PinNew: Input friendly name '%s'\n", pin->friendlyName));
+#else
+ PA_DEBUG(("PinNew: Input friendly name '%S'\n", pin->friendlyName));
+#endif
+ }
+ else // muxCount > 0
+ {
+ PA_DEBUG(("PinNew: Setting up %u inputs\n", muxCount));
+
+ /* Now we redo the operation once known how many multiplexer positions there are */
+ pin->inputs = (PaWinWdmMuxedInput**)PaUtil_AllocateMemory(muxCount * sizeof(PaWinWdmMuxedInput*));
+ if (pin->inputs == NULL)
+ {
+ FilterRelease(pin->parentFilter->topologyFilter);
+ result = paInsufficientMemory;
+ goto error;
+ }
+ pin->inputCount = muxCount;
+
+ for (i = 0; i < muxCount; ++muxPos)
+ {
+ PA_DEBUG(("PinNew: Setting up input %u...\n", i));
+
+ if (pin->inputs[i] == NULL)
+ {
+ pin->inputs[i] = (PaWinWdmMuxedInput*)PaUtil_AllocateMemory(sizeof(PaWinWdmMuxedInput));
+ if (pin->inputs[i] == NULL)
+ {
+ FilterRelease(pin->parentFilter->topologyFilter);
+ result = paInsufficientMemory;
+ goto error;
+ }
+ }
+
+ endpointPinId = GetConnectedPin(pcPin,
+ FALSE,
+ pin->parentFilter->topologyFilter,
+ muxPos,
+ &pin->inputs[i]->muxPinId,
+ &pin->inputs[i]->muxNodeId);
+
+ if (endpointPinId != KSFILTER_NODE)
+ {
+ /* The "endpointPinId" is what WASAPI looks at for pin names */
+ GUID category = {0};
+
+ /* Set input endpoint ID */
+ pin->inputs[i]->endpointPinId = endpointPinId;
+
+ /* Get pin category information */
+ result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
+ endpointPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_CATEGORY,
+ &category,
+ sizeof(GUID),
+ NULL);
+
+ if (result == paNoError)
+ {
+ /* Try pin name first, and if that is not defined, use category instead */
+ result = WdmGetPinPropertySimple(pin->parentFilter->topologyFilter->handle,
+ endpointPinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_NAME,
+ pin->inputs[i]->friendlyName,
+ MAX_PATH,
+ NULL);
+
+ if (result != paNoError)
+ {
+ result = GetNameFromCategory(&category, TRUE, pin->inputs[i]->friendlyName, MAX_PATH);
+ if (result != paNoError)
+ {
+ /* Only specify name, let name hash in ScanDeviceInfos fix postfix enumerators */
+ wcscpy(pin->inputs[i]->friendlyName, kInputName);
+ }
+ }
+#ifdef UNICODE
+ PA_DEBUG(("PinNew: Input (%u) friendly name '%s'\n", i, pin->inputs[i]->friendlyName));
+#else
+ PA_DEBUG(("PinNew: Input (%u) friendly name '%S'\n", i, pin->inputs[i]->friendlyName));
+#endif
+ ++i;
+ }
+ }
+ else
+ {
+ /* Unconnected pin */
+ goto error;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ PA_DEBUG(("PinNew: No topology pin id found. Bad...\n"));
+ /* No TOPO pin id ??? This is bad. Ok, so we just say it is an input or output... */
+ wcscpy(pin->friendlyName, (pin->dataFlow == KSPIN_DATAFLOW_IN) ? kOutputName : kInputName);
+ }
+ }
+
+ /* Release topology filter if it has been used */
+ if (pin->parentFilter->topologyFilter && pin->parentFilter->topologyFilter->handle != NULL)
+ {
+ PA_DEBUG(("PinNew: Releasing topology filter...\n"));
+ FilterRelease(pin->parentFilter->topologyFilter);
+ }
+
+ /* Success */
+ *error = paNoError;
+ PA_DEBUG(("Pin created successfully\n"));
+ PA_LOGL_;
+ return pin;
+
+error:
+ PA_DEBUG(("PinNew: Error %d\n", result));
+ /*
+ Error cleanup
+ */
+
+ if (pin->parentFilter->topologyFilter && pin->parentFilter->topologyFilter->handle != NULL)
+ {
+ FilterRelease(pin->parentFilter->topologyFilter);
+ }
+
+ PaUtil_FreeMemory( item );
+ PinFree(pin);
+
+ *error = result;
+ PA_LOGL_;
+ return NULL;
+}
+
+/*
+Safely free all resources associated with the pin
+*/
+static void PinFree(PaWinWdmPin* pin)
+{
+ unsigned i;
+ PA_LOGE_;
+ if( pin )
+ {
+ PinClose(pin);
+ if( pin->pinConnect )
+ {
+ PaUtil_FreeMemory( pin->pinConnect );
+ }
+ if( pin->dataRangesItem )
+ {
+ PaUtil_FreeMemory( pin->dataRangesItem );
+ }
+ if( pin->inputs )
+ {
+ for (i = 0; i < pin->inputCount; ++i)
+ {
+ PaUtil_FreeMemory( pin->inputs[i] );
+ }
+ PaUtil_FreeMemory( pin->inputs );
+ }
+ PaUtil_FreeMemory( pin );
+ }
+ PA_LOGL_;
+}
+
+/*
+If the pin handle is open, close it
+*/
+static void PinClose(PaWinWdmPin* pin)
+{
+ PA_LOGE_;
+ if( pin == NULL )
+ {
+ PA_DEBUG(("Closing NULL pin!"));
+ PA_LOGL_;
+ return;
+ }
+ if( pin->handle != NULL )
+ {
+ PinSetState( pin, KSSTATE_PAUSE );
+ PinSetState( pin, KSSTATE_STOP );
+ CloseHandle( pin->handle );
+ pin->handle = NULL;
+ FilterRelease(pin->parentFilter);
+ }
+ PA_LOGL_;
+}
+
+/*
+Set the state of this (instantiated) pin
+*/
+static PaError PinSetState(PaWinWdmPin* pin, KSSTATE state)
+{
+ PaError result = paNoError;
+ KSPROPERTY prop;
+
+ PA_LOGE_;
+ prop.Set = KSPROPSETID_Connection;
+ prop.Id = KSPROPERTY_CONNECTION_STATE;
+ prop.Flags = KSPROPERTY_TYPE_SET;
+
+ if( pin == NULL )
+ return paInternalError;
+ if( pin->handle == NULL )
+ return paInternalError;
+
+ result = WdmSyncIoctl(pin->handle, IOCTL_KS_PROPERTY, &prop, sizeof(KSPROPERTY), &state, sizeof(KSSTATE), NULL);
+
+ PA_LOGL_;
+ return result;
+}
+
+static PaError PinInstantiate(PaWinWdmPin* pin)
+{
+ PaError result;
+ unsigned long createResult;
+ KSALLOCATOR_FRAMING ksaf;
+ KSALLOCATOR_FRAMING_EX ksafex;
+
+ PA_LOGE_;
+
+ if( pin == NULL )
+ return paInternalError;
+ if(!pin->pinConnect)
+ return paInternalError;
+
+ FilterUse(pin->parentFilter);
+
+ createResult = FunctionKsCreatePin(
+ pin->parentFilter->handle,
+ pin->pinConnect,
+ GENERIC_WRITE | GENERIC_READ,
+ &pin->handle
+ );
+
+ PA_DEBUG(("Pin create result = 0x%08x\n",createResult));
+ if( createResult != ERROR_SUCCESS )
+ {
+ FilterRelease(pin->parentFilter);
+ pin->handle = NULL;
+ switch (createResult)
+ {
+ case ERROR_INVALID_PARAMETER:
+ /* First case when pin actually don't support the format */
+ return paSampleFormatNotSupported;
+ case ERROR_BAD_COMMAND:
+ /* Case when pin is occupied (by another application) */
+ return paDeviceUnavailable;
+ default:
+ /* All other cases */
+ return paInvalidDevice;
+ }
+ }
+
+ if (pin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
+ {
+ /* Framing size query only valid for WaveCyclic devices */
+ result = WdmGetPropertySimple(
+ pin->handle,
+ &KSPROPSETID_Connection,
+ KSPROPERTY_CONNECTION_ALLOCATORFRAMING,
+ &ksaf,
+ sizeof(ksaf));
+
+ if( result != paNoError )
+ {
+ result = WdmGetPropertySimple(
+ pin->handle,
+ &KSPROPSETID_Connection,
+ KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX,
+ &ksafex,
+ sizeof(ksafex));
+ if( result == paNoError )
+ {
+ pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize;
+ }
+ }
+ else
+ {
+ pin->frameSize = ksaf.FrameSize;
+ }
+ }
+
+ PA_LOGL_;
+
+ return paNoError;
+}
+
+static PaError PinSetFormat(PaWinWdmPin* pin, const WAVEFORMATEX* format)
+{
+ unsigned long size;
+ void* newConnect;
+
+ PA_LOGE_;
+
+ if( pin == NULL )
+ return paInternalError;
+ if( format == NULL )
+ return paInternalError;
+
+ size = GetWfexSize(format) + sizeof(KSPIN_CONNECT) + sizeof(KSDATAFORMAT_WAVEFORMATEX) - sizeof(WAVEFORMATEX);
+
+ if( pin->pinConnectSize != size )
+ {
+ newConnect = PaUtil_AllocateMemory( size );
+ if( newConnect == NULL )
+ return paInsufficientMemory;
+ memcpy( newConnect, (void*)pin->pinConnect, min(pin->pinConnectSize,size) );
+ PaUtil_FreeMemory( pin->pinConnect );
+ pin->pinConnect = (KSPIN_CONNECT*)newConnect;
+ pin->pinConnectSize = size;
+ pin->ksDataFormatWfx = (KSDATAFORMAT_WAVEFORMATEX*)((KSPIN_CONNECT*)newConnect + 1);
+ pin->ksDataFormatWfx->DataFormat.FormatSize = size - sizeof(KSPIN_CONNECT);
+ }
+
+ memcpy( (void*)&(pin->ksDataFormatWfx->WaveFormatEx), format, GetWfexSize(format) );
+ pin->ksDataFormatWfx->DataFormat.SampleSize = (unsigned short)(format->nChannels * (format->wBitsPerSample / 8));
+
+ PA_LOGL_;
+
+ return paNoError;
+}
+
+static PaError PinIsFormatSupported(PaWinWdmPin* pin, const WAVEFORMATEX* format)
+{
+ KSDATARANGE_AUDIO* dataRange;
+ unsigned long count;
+ GUID guid = DYNAMIC_GUID( DEFINE_WAVEFORMATEX_GUID(format->wFormatTag) );
+ PaError result = paInvalidDevice;
+ const WAVEFORMATEXTENSIBLE* pFormatExt = (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) ? (const WAVEFORMATEXTENSIBLE*)format : 0;
+
+ PA_LOGE_;
+
+ if( pFormatExt != 0 )
+ {
+ guid = pFormatExt->SubFormat;
+ }
+ dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges;
+ for(count = 0;
+ count<pin->dataRangesItem->Count;
+ count++,
+ dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize)) /* Need to update dataRange here, due to 'continue' !! */
+ {
+ /* Check major format*/
+ if (!(IsEqualGUID(&(dataRange->DataRange.MajorFormat), &KSDATAFORMAT_TYPE_AUDIO) ||
+ IsEqualGUID(&(dataRange->DataRange.MajorFormat), &KSDATAFORMAT_TYPE_WILDCARD)))
+ {
+ continue;
+ }
+
+ /* This is an audio or wildcard datarange... */
+ if (! (IsEqualGUID(&(dataRange->DataRange.SubFormat), &KSDATAFORMAT_SUBTYPE_WILDCARD) ||
+ IsEqualGUID(&(dataRange->DataRange.SubFormat), &KSDATAFORMAT_SUBTYPE_PCM) ||
+ IsEqualGUID(&(dataRange->DataRange.SubFormat), &guid) ))
+ {
+ continue;
+ }
+
+ /* Check specifier... */
+ if (! (IsEqualGUID(&(dataRange->DataRange.Specifier), &KSDATAFORMAT_SPECIFIER_WILDCARD) ||
+ IsEqualGUID(&(dataRange->DataRange.Specifier), &KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) )
+ {
+ continue;
+ }
+
+ PA_DEBUG(("Pin:%x, DataRange:%d\n",(void*)pin,count));
+ PA_DEBUG(("\tFormatSize:%d, SampleSize:%d\n",dataRange->DataRange.FormatSize,dataRange->DataRange.SampleSize));
+ PA_DEBUG(("\tMaxChannels:%d\n",dataRange->MaximumChannels));
+ PA_DEBUG(("\tBits:%d-%d\n",dataRange->MinimumBitsPerSample,dataRange->MaximumBitsPerSample));
+ PA_DEBUG(("\tSampleRate:%d-%d\n",dataRange->MinimumSampleFrequency,dataRange->MaximumSampleFrequency));
+
+ if( dataRange->MaximumChannels != (ULONG)-1 &&
+ dataRange->MaximumChannels < format->nChannels )
+ {
+ result = paInvalidChannelCount;
+ continue;
+ }
+
+ if (pFormatExt != 0)
+ {
+ if (!IsBitsWithinRange(dataRange, pFormatExt->Samples.wValidBitsPerSample))
+ {
+ result = paSampleFormatNotSupported;
+ continue;
+ }
+ }
+ else
+ {
+ if (!IsBitsWithinRange(dataRange, format->wBitsPerSample))
+ {
+ result = paSampleFormatNotSupported;
+ continue;
+ }
+ }
+
+ if (!IsFrequencyWithinRange(dataRange, format->nSamplesPerSec))
+ {
+ result = paInvalidSampleRate;
+ continue;
+ }
+
+ /* Success! */
+ result = paNoError;
+ break;
+ }
+
+ PA_LOGL_;
+ return result;
+}
+
+static PaError PinQueryNotificationSupport(PaWinWdmPin* pPin, BOOL* pbResult)
+{
+ PaError result = paNoError;
+ KSPROPERTY propIn;
+
+ PA_LOGE_;
+
+ propIn.Set = KSPROPSETID_RtAudio;
+ propIn.Id = 8; /* = KSPROPERTY_RTAUDIO_QUERY_NOTIFICATION_SUPPORT */
+ propIn.Flags = KSPROPERTY_TYPE_GET;
+
+ result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
+ &propIn,
+ sizeof(KSPROPERTY),
+ pbResult,
+ sizeof(BOOL),
+ NULL);
+
+ if (result != paNoError)
+ {
+ PA_DEBUG(("Failed PinQueryNotificationSupport\n"));
+ }
+
+ PA_LOGL_;
+ return result;
+}
+
+static PaError PinGetBufferWithNotification(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier)
+{
+ PaError result = paNoError;
+ KSRTAUDIO_BUFFER_PROPERTY_WITH_NOTIFICATION propIn;
+ KSRTAUDIO_BUFFER propOut;
+
+ PA_LOGE_;
+
+ propIn.BaseAddress = 0;
+ propIn.NotificationCount = 2;
+ propIn.RequestedBufferSize = *pRequestedBufSize;
+ propIn.Property.Set = KSPROPSETID_RtAudio;
+ propIn.Property.Id = KSPROPERTY_RTAUDIO_BUFFER_WITH_NOTIFICATION;
+ propIn.Property.Flags = KSPROPERTY_TYPE_GET;
+
+ result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
+ &propIn,
+ sizeof(KSRTAUDIO_BUFFER_PROPERTY_WITH_NOTIFICATION),
+ &propOut,
+ sizeof(KSRTAUDIO_BUFFER),
+ NULL);
+
+ if (result == paNoError)
+ {
+ *pBuffer = propOut.BufferAddress;
+ *pRequestedBufSize = propOut.ActualBufferSize;
+ *pbCallMemBarrier = propOut.CallMemoryBarrier;
+ }
+ else
+ {
+ PA_DEBUG(("Failed to get buffer with notification\n"));
+ }
+
+ PA_LOGL_;
+ return result;
+}
+
+static PaError PinGetBufferWithoutNotification(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier)
+{
+ PaError result = paNoError;
+ KSRTAUDIO_BUFFER_PROPERTY propIn;
+ KSRTAUDIO_BUFFER propOut;
+
+ PA_LOGE_;
+
+ propIn.BaseAddress = NULL;
+ propIn.RequestedBufferSize = *pRequestedBufSize;
+ propIn.Property.Set = KSPROPSETID_RtAudio;
+ propIn.Property.Id = KSPROPERTY_RTAUDIO_BUFFER;
+ propIn.Property.Flags = KSPROPERTY_TYPE_GET;
+
+ result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
+ &propIn,
+ sizeof(KSRTAUDIO_BUFFER_PROPERTY),
+ &propOut,
+ sizeof(KSRTAUDIO_BUFFER),
+ NULL);
+
+ if (result == paNoError)
+ {
+ *pBuffer = propOut.BufferAddress;
+ *pRequestedBufSize = propOut.ActualBufferSize;
+ *pbCallMemBarrier = propOut.CallMemoryBarrier;
+ }
+ else
+ {
+ PA_DEBUG(("Failed to get buffer without notification\n"));
+ }
+
+ PA_LOGL_;
+ return result;
+}
+
+/* greatest common divisor - PGCD in French */
+static unsigned long PaWinWDMGCD( unsigned long a, unsigned long b )
+{
+ return (b==0) ? a : PaWinWDMGCD( b, a%b);
+}
+
+
+/* This function will handle getting the cyclic buffer from a WaveRT driver. Certain WaveRT drivers needs to have
+requested buffer size on multiples of 128 bytes:
+
+*/
+static PaError PinGetBuffer(PaWinWdmPin* pPin, void** pBuffer, DWORD* pRequestedBufSize, BOOL* pbCallMemBarrier)
+{
+ PaError result = paNoError;
+ int limit = 1000;
+ PA_LOGE_;
+
+ while (1)
+ {
+ limit--;
+ if (limit == 0) {
+ PA_DEBUG(("PinGetBuffer: LOOP LIMIT REACHED\n"));
+ break;
+ }
+
+ if (pPin->pinKsSubType != SubType_kPolled)
+ {
+ /* In case of unknown (or notification), we try both modes */
+ result = PinGetBufferWithNotification(pPin, pBuffer, pRequestedBufSize, pbCallMemBarrier);
+ if (result == paNoError)
+ {
+ PA_DEBUG(("PinGetBuffer: SubType_kNotification\n"));
+ pPin->pinKsSubType = SubType_kNotification;
+ break;
+ }
+ }
+
+ result = PinGetBufferWithoutNotification(pPin, pBuffer, pRequestedBufSize, pbCallMemBarrier);
+ if (result == paNoError)
+ {
+ PA_DEBUG(("PinGetBuffer: SubType_kPolled\n"));
+ pPin->pinKsSubType = SubType_kPolled;
+ break;
+ }
+
+ /* Check if requested size is on a 128 byte boundary */
+ if (((*pRequestedBufSize) % 128UL) == 0)
+ {
+ PA_DEBUG(("Buffer size on 128 byte boundary, still fails :(\n"));
+ /* Ok, can't do much more */
+ break;
+ }
+ else
+ {
+ /* Compute LCM so we know which sizes are on a 128 byte boundary */
+ const unsigned gcd = PaWinWDMGCD(128UL, pPin->ksDataFormatWfx->WaveFormatEx.nBlockAlign);
+ const unsigned lcm = (128UL * pPin->ksDataFormatWfx->WaveFormatEx.nBlockAlign) / gcd;
+ DWORD dwOldSize = *pRequestedBufSize;
+
+ /* Align size to (next larger) LCM byte boundary, and then we try again. Note that LCM is not necessarily a
+ power of 2. */
+ *pRequestedBufSize = ((*pRequestedBufSize + lcm - 1) / lcm) * lcm;
+
+ PA_DEBUG(("Adjusting buffer size from %u to %u bytes (128 byte boundary, LCM=%u)\n", dwOldSize, *pRequestedBufSize, lcm));
+ }
+ }
+
+ PA_LOGL_;
+
+ return result;
+}
+
+static PaError PinRegisterPositionRegister(PaWinWdmPin* pPin)
+{
+ PaError result = paNoError;
+ KSRTAUDIO_HWREGISTER_PROPERTY propIn;
+ KSRTAUDIO_HWREGISTER propOut;
+
+ PA_LOGE_;
+
+ propIn.BaseAddress = NULL;
+ propIn.Property.Set = KSPROPSETID_RtAudio;
+ propIn.Property.Id = KSPROPERTY_RTAUDIO_POSITIONREGISTER;
+ propIn.Property.Flags = KSPROPERTY_TYPE_SET;
+
+ result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
+ &propIn,
+ sizeof(KSRTAUDIO_HWREGISTER_PROPERTY),
+ &propOut,
+ sizeof(KSRTAUDIO_HWREGISTER),
+ NULL);
+
+ if (result == paNoError)
+ {
+ pPin->positionRegister = (ULONG*)propOut.Register;
+ }
+ else
+ {
+ PA_DEBUG(("Failed to register position register\n"));
+ }
+
+ PA_LOGL_;
+
+ return result;
+}
+
+static PaError PinRegisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle)
+{
+ PaError result = paNoError;
+ KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY prop;
+
+ PA_LOGE_;
+
+ prop.NotificationEvent = handle;
+ prop.Property.Set = KSPROPSETID_RtAudio;
+ prop.Property.Id = KSPROPERTY_RTAUDIO_REGISTER_NOTIFICATION_EVENT;
+ prop.Property.Flags = KSPROPERTY_TYPE_SET;
+
+ result = WdmSyncIoctl(pPin->handle,
+ IOCTL_KS_PROPERTY,
+ &prop,
+ sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
+ &prop,
+ sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
+ NULL);
+
+ if (result != paNoError) {
+ PA_DEBUG(("Failed to register notification handle 0x%08X\n", handle));
+ }
+
+ PA_LOGL_;
+
+ return result;
+}
+
+static PaError PinUnregisterNotificationHandle(PaWinWdmPin* pPin, HANDLE handle)
+{
+ PaError result = paNoError;
+ KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY prop;
+
+ PA_LOGE_;
+
+ if (handle != NULL)
+ {
+ prop.NotificationEvent = handle;
+ prop.Property.Set = KSPROPSETID_RtAudio;
+ prop.Property.Id = KSPROPERTY_RTAUDIO_UNREGISTER_NOTIFICATION_EVENT;
+ prop.Property.Flags = KSPROPERTY_TYPE_SET;
+
+ result = WdmSyncIoctl(pPin->handle,
+ IOCTL_KS_PROPERTY,
+ &prop,
+ sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
+ &prop,
+ sizeof(KSRTAUDIO_NOTIFICATION_EVENT_PROPERTY),
+ NULL);
+
+ if (result != paNoError) {
+ PA_DEBUG(("Failed to unregister notification handle 0x%08X\n", handle));
+ }
+ }
+ PA_LOGL_;
+
+ return result;
+}
+
+static PaError PinGetHwLatency(PaWinWdmPin* pPin, ULONG* pFifoSize, ULONG* pChipsetDelay, ULONG* pCodecDelay)
+{
+ PaError result = paNoError;
+ KSPROPERTY propIn;
+ KSRTAUDIO_HWLATENCY propOut;
+
+ PA_LOGE_;
+
+ propIn.Set = KSPROPSETID_RtAudio;
+ propIn.Id = KSPROPERTY_RTAUDIO_HWLATENCY;
+ propIn.Flags = KSPROPERTY_TYPE_GET;
+
+ result = WdmSyncIoctl(pPin->handle, IOCTL_KS_PROPERTY,
+ &propIn,
+ sizeof(KSPROPERTY),
+ &propOut,
+ sizeof(KSRTAUDIO_HWLATENCY),
+ NULL);
+
+ if (result == paNoError)
+ {
+ *pFifoSize = propOut.FifoSize;
+ *pChipsetDelay = propOut.ChipsetDelay;
+ *pCodecDelay = propOut.CodecDelay;
+ }
+ else
+ {
+ PA_DEBUG(("Failed to retrieve hardware FIFO size!\n"));
+ }
+
+ PA_LOGL_;
+
+ return result;
+}
+
+/* This one is used for WaveRT */
+static PaError PinGetAudioPositionMemoryMapped(PaWinWdmPin* pPin, ULONG* pPosition)
+{
+ *pPosition = (*pPin->positionRegister);
+ return paNoError;
+}
+
+/* This one also, but in case the driver hasn't implemented memory mapped access to the position register */
+static PaError PinGetAudioPositionViaIOCTLRead(PaWinWdmPin* pPin, ULONG* pPosition)
+{
+ PaError result = paNoError;
+ KSPROPERTY propIn;
+ KSAUDIO_POSITION propOut;
+
+ PA_LOGE_;
+
+ propIn.Set = KSPROPSETID_Audio;
+ propIn.Id = KSPROPERTY_AUDIO_POSITION;
+ propIn.Flags = KSPROPERTY_TYPE_GET;
+
+ result = WdmSyncIoctl(pPin->handle,
+ IOCTL_KS_PROPERTY,
+ &propIn, sizeof(KSPROPERTY),
+ &propOut, sizeof(KSAUDIO_POSITION),
+ NULL);
+
+ if (result == paNoError)
+ {
+ *pPosition = (ULONG)(propOut.PlayOffset);
+ }
+ else
+ {
+ PA_DEBUG(("Failed to get audio play position!\n"));
+ }
+
+ PA_LOGL_;
+
+ return result;
+
+}
+
+/* This one also, but in case the driver hasn't implemented memory mapped access to the position register */
+static PaError PinGetAudioPositionViaIOCTLWrite(PaWinWdmPin* pPin, ULONG* pPosition)
+{
+ PaError result = paNoError;
+ KSPROPERTY propIn;
+ KSAUDIO_POSITION propOut;
+
+ PA_LOGE_;
+
+ propIn.Set = KSPROPSETID_Audio;
+ propIn.Id = KSPROPERTY_AUDIO_POSITION;
+ propIn.Flags = KSPROPERTY_TYPE_GET;
+
+ result = WdmSyncIoctl(pPin->handle,
+ IOCTL_KS_PROPERTY,
+ &propIn, sizeof(KSPROPERTY),
+ &propOut, sizeof(KSAUDIO_POSITION),
+ NULL);
+
+ if (result == paNoError)
+ {
+ *pPosition = (ULONG)(propOut.WriteOffset);
+ }
+ else
+ {
+ PA_DEBUG(("Failed to get audio write position!\n"));
+ }
+
+ PA_LOGL_;
+
+ return result;
+
+}
+
+/***********************************************************************************************/
+
+/**
+* Create a new filter object.
+*/
+static PaWinWdmFilter* FilterNew( PaWDMKSType type, DWORD devNode, const wchar_t* filterName, const wchar_t* friendlyName, PaError* error )
+{
+ PaWinWdmFilter* filter = 0;
+ PaError result;
+
+ /* Allocate the new filter object */
+ filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) );
+ if( !filter )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ PA_DEBUG(("FilterNew: Creating filter '%S'\n", friendlyName));
+
+ /* Set type flag */
+ filter->devInfo.streamingType = type;
+
+ /* Store device node */
+ filter->deviceNode = devNode;
+
+ /* Zero the filter object - done by AllocateMemory */
+ /* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */
+
+ /* Copy the filter name */
+ wcsncpy(filter->devInfo.filterPath, filterName, MAX_PATH);
+
+ /* Copy the friendly name */
+ wcsncpy(filter->friendlyName, friendlyName, MAX_PATH);
+
+ PA_DEBUG(("FilterNew: Opening filter...\n", friendlyName));
+
+ /* Open the filter handle */
+ result = FilterUse(filter);
+ if( result != paNoError )
+ {
+ goto error;
+ }
+
+ /* Get pin count */
+ result = WdmGetPinPropertySimple
+ (
+ filter->handle,
+ 0,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_CTYPES,
+ &filter->pinCount,
+ sizeof(filter->pinCount),
+ NULL);
+
+ if( result != paNoError)
+ {
+ goto error;
+ }
+
+ /* Get connections & nodes for filter */
+ result = WdmGetPropertyMulti(
+ filter->handle,
+ &KSPROPSETID_Topology,
+ KSPROPERTY_TOPOLOGY_CONNECTIONS,
+ &filter->connections);
+
+ if( result != paNoError)
+ {
+ goto error;
+ }
+
+ result = WdmGetPropertyMulti(
+ filter->handle,
+ &KSPROPSETID_Topology,
+ KSPROPERTY_TOPOLOGY_NODES,
+ &filter->nodes);
+
+ if( result != paNoError)
+ {
+ goto error;
+ }
+
+ /* For debugging purposes */
+ DumpConnectionsAndNodes(filter);
+
+ /* Get product GUID (it might not be supported) */
+ {
+ KSCOMPONENTID compId;
+ if (WdmGetPropertySimple(filter->handle, &KSPROPSETID_General, KSPROPERTY_GENERAL_COMPONENTID, &compId, sizeof(KSCOMPONENTID)) == paNoError)
+ {
+ filter->devInfo.deviceProductGuid = compId.Product;
+ }
+ }
+
+ /* This section is not executed for topology filters */
+ if (type != Type_kNotUsed)
+ {
+ /* Initialize the pins */
+ result = FilterInitializePins(filter);
+
+ if( result != paNoError)
+ {
+ goto error;
+ }
+ }
+
+ /* Close the filter handle for now
+ * It will be opened later when needed */
+ FilterRelease(filter);
+
+ *error = paNoError;
+ return filter;
+
+error:
+ PA_DEBUG(("FilterNew: Error %d\n", result));
+ /*
+ Error cleanup
+ */
+ FilterFree(filter);
+
+ *error = result;
+ return NULL;
+}
+
+/**
+* Add reference to filter
+*/
+static void FilterAddRef( PaWinWdmFilter* filter )
+{
+ if (filter != 0)
+ {
+ filter->filterRefCount++;
+ }
+}
+
+
+/**
+* Initialize the pins of the filter. This is separated from FilterNew because this might fail if there is another
+* process using the pin(s).
+*/
+PaError FilterInitializePins( PaWinWdmFilter* filter )
+{
+ PaError result = paNoError;
+ int pinId;
+
+ if (filter->devInfo.streamingType == Type_kNotUsed)
+ return paNoError;
+
+ if (filter->pins != NULL)
+ return paNoError;
+
+ /* Allocate pointer array to hold the pins */
+ filter->pins = (PaWinWdmPin**)PaUtil_AllocateMemory( sizeof(PaWinWdmPin*) * filter->pinCount );
+ if( !filter->pins )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* Create all the pins we can */
+ for(pinId = 0; pinId < filter->pinCount; pinId++)
+ {
+ /* Create the pin with this Id */
+ PaWinWdmPin* newPin;
+ newPin = PinNew(filter, pinId, &result);
+ if( result == paInsufficientMemory )
+ goto error;
+ if( newPin != NULL )
+ {
+ filter->pins[pinId] = newPin;
+ ++filter->validPinCount;
+ }
+ else
+ {
+ filter->pins[pinId] = 0;
+ }
+ }
+
+ if (filter->validPinCount == 0)
+ {
+ result = paDeviceUnavailable;
+ goto error;
+ }
+
+ return paNoError;
+
+error:
+
+ if (filter->pins)
+ {
+ for (pinId = 0; pinId < filter->pinCount; ++pinId)
+ {
+ if (filter->pins[pinId])
+ {
+ PinFree(filter->pins[pinId]);
+ filter->pins[pinId] = 0;
+ }
+ }
+ PaUtil_FreeMemory( filter->pins );
+ filter->pins = 0;
+ }
+
+ return result;
+}
+
+
+/**
+* Free a previously created filter
+*/
+static void FilterFree(PaWinWdmFilter* filter)
+{
+ PA_LOGL_;
+ if( filter )
+ {
+ if (--filter->filterRefCount > 0)
+ {
+ /* Ok, a stream has a ref count to this filter */
+ return;
+ }
+
+ if ( filter->topologyFilter )
+ {
+ FilterFree(filter->topologyFilter);
+ filter->topologyFilter = 0;
+ }
+ if ( filter->pins )
+ {
+ int pinId;
+ for( pinId = 0; pinId < filter->pinCount; pinId++ )
+ PinFree(filter->pins[pinId]);
+ PaUtil_FreeMemory( filter->pins );
+ filter->pins = 0;
+ }
+ if( filter->connections )
+ {
+ PaUtil_FreeMemory(filter->connections);
+ filter->connections = 0;
+ }
+ if( filter->nodes )
+ {
+ PaUtil_FreeMemory(filter->nodes);
+ filter->nodes = 0;
+ }
+ if( filter->handle )
+ CloseHandle( filter->handle );
+ PaUtil_FreeMemory( filter );
+ }
+ PA_LOGE_;
+}
+
+/**
+* Reopen the filter handle if necessary so it can be used
+**/
+static PaError FilterUse(PaWinWdmFilter* filter)
+{
+ assert( filter );
+
+ PA_LOGE_;
+ if( filter->handle == NULL )
+ {
+ /* Open the filter */
+ filter->handle = CreateFileW(
+ filter->devInfo.filterPath,
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+ NULL);
+
+ if( filter->handle == NULL )
+ {
+ return paDeviceUnavailable;
+ }
+ }
+ filter->usageCount++;
+ PA_LOGL_;
+ return paNoError;
+}
+
+/**
+* Release the filter handle if nobody is using it
+**/
+static void FilterRelease(PaWinWdmFilter* filter)
+{
+ assert( filter );
+ assert( filter->usageCount > 0 );
+
+ PA_LOGE_;
+ /* Check first topology filter, if used */
+ if (filter->topologyFilter != NULL && filter->topologyFilter->handle != NULL)
+ {
+ FilterRelease(filter->topologyFilter);
+ }
+
+ filter->usageCount--;
+ if( filter->usageCount == 0 )
+ {
+ if( filter->handle != NULL )
+ {
+ CloseHandle( filter->handle );
+ filter->handle = NULL;
+ }
+ }
+ PA_LOGL_;
+}
+
+/**
+* Create a render or playback pin using the supplied format
+**/
+static PaWinWdmPin* FilterCreatePin(PaWinWdmFilter* filter,
+ int pinId,
+ const WAVEFORMATEX* wfex,
+ PaError* error)
+{
+ PaError result = paNoError;
+ PaWinWdmPin* pin = NULL;
+ assert( filter );
+ assert( pinId < filter->pinCount );
+ pin = filter->pins[pinId];
+ assert( pin );
+ result = PinSetFormat(pin,wfex);
+ if( result == paNoError )
+ {
+ result = PinInstantiate(pin);
+ }
+ *error = result;
+ return result == paNoError ? pin : 0;
+}
+
+static const wchar_t kUsbPrefix[] = L"\\\\?\\USB";
+
+static BOOL IsUSBDevice(const wchar_t* devicePath)
+{
+ /* Alex Lessard pointed out that different devices might present the device path with
+ lower case letters. */
+ return (_wcsnicmp(devicePath, kUsbPrefix, sizeof(kUsbPrefix)/sizeof(kUsbPrefix[0]) ) == 0);
+}
+
+/* This should make it more language tolerant, I hope... */
+static const wchar_t kUsbNamePrefix[] = L"USB Audio";
+
+static BOOL IsNameUSBAudioDevice(const wchar_t* friendlyName)
+{
+ return (_wcsnicmp(friendlyName, kUsbNamePrefix, sizeof(kUsbNamePrefix)/sizeof(kUsbNamePrefix[0])) == 0);
+}
+
+typedef enum _tag_EAlias
+{
+ Alias_kRender = (1<<0),
+ Alias_kCapture = (1<<1),
+ Alias_kRealtime = (1<<2),
+} EAlias;
+
+/* Trim whitespace from string */
+static void TrimString(wchar_t* str, size_t length)
+{
+ wchar_t* s = str;
+ wchar_t* e = 0;
+
+ /* Find start of string */
+ while (iswspace(*s)) ++s;
+ e=s+min(length,wcslen(s))-1;
+
+ /* Find end of string */
+ while(e>s && iswspace(*e)) --e;
+ ++e;
+
+ length = e - s;
+ memmove(str, s, length * sizeof(wchar_t));
+ str[length] = 0;
+}
+
+/**
+* Build the list of available filters
+* Use the SetupDi API to enumerate all devices in the KSCATEGORY_AUDIO which
+* have a KSCATEGORY_RENDER or KSCATEGORY_CAPTURE alias. For each of these
+* devices initialise a PaWinWdmFilter structure by calling our NewFilter()
+* function. We enumerate devices twice, once to count how many there are,
+* and once to initialize the PaWinWdmFilter structures.
+*
+* Vista and later: Also check KSCATEGORY_REALTIME for WaveRT devices.
+*/
+//PaError BuildFilterList( PaWinWdmHostApiRepresentation* wdmHostApi, int* noOfPaDevices )
+PaWinWdmFilter** BuildFilterList( int* pFilterCount, int* pNoOfPaDevices, PaError* pResult )
+{
+ PaWinWdmFilter** ppFilters = NULL;
+ HDEVINFO handle = NULL;
+ int device;
+ int invalidDevices;
+ int slot;
+ SP_DEVICE_INTERFACE_DATA interfaceData;
+ SP_DEVICE_INTERFACE_DATA aliasData;
+ SP_DEVINFO_DATA devInfoData;
+ int noError;
+ const int sizeInterface = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR));
+ unsigned char interfaceDetailsArray[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + (MAX_PATH * sizeof(WCHAR))];
+ SP_DEVICE_INTERFACE_DETAIL_DATA_W* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)interfaceDetailsArray;
+ const GUID* category = (const GUID*)&KSCATEGORY_AUDIO;
+ const GUID* alias_render = (const GUID*)&KSCATEGORY_RENDER;
+ const GUID* alias_capture = (const GUID*)&KSCATEGORY_CAPTURE;
+ const GUID* category_realtime = (const GUID*)&KSCATEGORY_REALTIME;
+ DWORD aliasFlags;
+ PaWDMKSType streamingType;
+ int filterCount = 0;
+ int noOfPaDevices = 0;
+
+ PA_LOGE_;
+
+ assert(pFilterCount != NULL);
+ assert(pNoOfPaDevices != NULL);
+ assert(pResult != NULL);
+
+ devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
+ *pFilterCount = 0;
+ *pNoOfPaDevices = 0;
+
+ /* Open a handle to search for devices (filters) */
+ handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if( handle == INVALID_HANDLE_VALUE )
+ {
+ *pResult = paUnanticipatedHostError;
+ return NULL;
+ }
+ PA_DEBUG(("Setup called\n"));
+
+ /* First let's count the number of devices so we can allocate a list */
+ invalidDevices = 0;
+ for( device = 0;;device++ )
+ {
+ interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ interfaceData.Reserved = 0;
+ aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ aliasData.Reserved = 0;
+ noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
+ PA_DEBUG(("Enum called\n"));
+ if( !noError )
+ break; /* No more devices */
+
+ /* Check this one has the render or capture alias */
+ aliasFlags = 0;
+ noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
+ PA_DEBUG(("noError = %d\n",noError));
+ if(noError)
+ {
+ if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+ {
+ PA_DEBUG(("Device %d has render alias\n",device));
+ aliasFlags |= Alias_kRender; /* Has render alias */
+ }
+ else
+ {
+ PA_DEBUG(("Device %d has no render alias\n",device));
+ }
+ }
+ noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
+ if(noError)
+ {
+ if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+ {
+ PA_DEBUG(("Device %d has capture alias\n",device));
+ aliasFlags |= Alias_kCapture; /* Has capture alias */
+ }
+ else
+ {
+ PA_DEBUG(("Device %d has no capture alias\n",device));
+ }
+ }
+ if(!aliasFlags)
+ invalidDevices++; /* This was not a valid capture or render audio device */
+ }
+ /* Remember how many there are */
+ filterCount = device-invalidDevices;
+
+ PA_DEBUG(("Interfaces found: %d\n",device-invalidDevices));
+
+ /* Now allocate the list of pointers to devices */
+ ppFilters = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * filterCount);
+ if( ppFilters == 0 )
+ {
+ if(handle != NULL)
+ SetupDiDestroyDeviceInfoList(handle);
+ *pResult = paInsufficientMemory;
+ return NULL;
+ }
+
+ /* Now create filter objects for each interface found */
+ slot = 0;
+ for( device = 0;;device++ )
+ {
+ interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ interfaceData.Reserved = 0;
+ aliasData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ aliasData.Reserved = 0;
+ devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
+ devInfoData.Reserved = 0;
+ streamingType = Type_kWaveCyclic;
+
+ noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
+ if( !noError )
+ break; /* No more devices */
+
+ /* Check this one has the render or capture alias */
+ aliasFlags = 0;
+ noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_render,&aliasData);
+ if(noError)
+ {
+ if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+ {
+ PA_DEBUG(("Device %d has render alias\n",device));
+ aliasFlags |= Alias_kRender; /* Has render alias */
+ }
+ }
+ noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,alias_capture,&aliasData);
+ if(noError)
+ {
+ if(aliasData.Flags && (!(aliasData.Flags & SPINT_REMOVED)))
+ {
+ PA_DEBUG(("Device %d has capture alias\n",device));
+ aliasFlags |= Alias_kCapture; /* Has capture alias */
+ }
+ }
+ if(!aliasFlags)
+ {
+ continue; /* This was not a valid capture or render audio device */
+ }
+ else
+ {
+ /* Check if filter is WaveRT, if not it is a WaveCyclic */
+ noError = SetupDiGetDeviceInterfaceAlias(handle,&interfaceData,category_realtime,&aliasData);
+ if (noError)
+ {
+ PA_DEBUG(("Device %d has realtime alias\n",device));
+ aliasFlags |= Alias_kRealtime;
+ streamingType = Type_kWaveRT;
+ }
+ }
+
+ noError = SetupDiGetDeviceInterfaceDetailW(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
+ if( noError )
+ {
+ DWORD type;
+ WCHAR friendlyName[MAX_PATH] = {0};
+ DWORD sizeFriendlyName;
+ PaWinWdmFilter* newFilter = 0;
+
+ PaError result = paNoError;
+ /* Try to get the "friendly name" for this interface */
+ sizeFriendlyName = sizeof(friendlyName);
+
+ if (IsEarlierThanVista() && IsUSBDevice(devInterfaceDetails->DevicePath))
+ {
+ /* XP and USB audio device needs to look elsewhere, otherwise it'll only be a "USB Audio Device". Not
+ very literate. */
+ if (!SetupDiGetDeviceRegistryPropertyW(handle,
+ &devInfoData,
+ SPDRP_LOCATION_INFORMATION,
+ &type,
+ (BYTE*)friendlyName,
+ sizeof(friendlyName),
+ NULL))
+ {
+ friendlyName[0] = 0;
+ }
+ }
+
+ if (friendlyName[0] == 0 || IsNameUSBAudioDevice(friendlyName))
+ {
+ /* Fix contributed by Ben Allison
+ * Removed KEY_SET_VALUE from flags on following call
+ * as its causes failure when running without admin rights
+ * and it was not required */
+ HKEY hkey=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE);
+ if(hkey!=INVALID_HANDLE_VALUE)
+ {
+ noError = RegQueryValueExW(hkey,L"FriendlyName",0,&type,(BYTE*)friendlyName,&sizeFriendlyName);
+ if( noError == ERROR_SUCCESS )
+ {
+ PA_DEBUG(("Interface %d, Name: %s\n",device,friendlyName));
+ RegCloseKey(hkey);
+ }
+ else
+ {
+ friendlyName[0] = 0;
+ }
+ }
+ }
+
+ TrimString(friendlyName, sizeFriendlyName);
+
+ newFilter = FilterNew(streamingType,
+ devInfoData.DevInst,
+ devInterfaceDetails->DevicePath,
+ friendlyName,
+ &result);
+
+ if( result == paNoError )
+ {
+ int pin;
+ unsigned filterIOs = 0;
+
+ /* Increment number of "devices" */
+ for (pin = 0; pin < newFilter->pinCount; ++pin)
+ {
+ PaWinWdmPin* pPin = newFilter->pins[pin];
+ if (pPin == NULL)
+ continue;
+
+ filterIOs += max(1, pPin->inputCount);
+ }
+
+ noOfPaDevices += filterIOs;
+
+ PA_DEBUG(("Filter (%s) created with %d valid pins (total I/Os: %u)\n", ((newFilter->devInfo.streamingType==Type_kWaveRT)?"WaveRT":"WaveCyclic"), newFilter->validPinCount, filterIOs));
+
+ assert(slot < filterCount);
+
+ ppFilters[slot] = newFilter;
+
+ slot++;
+ }
+ else
+ {
+ PA_DEBUG(("Filter NOT created\n"));
+ /* As there are now less filters than we initially thought
+ * we must reduce the count by one */
+ filterCount--;
+ }
+ }
+ }
+
+ /* Clean up */
+ if(handle != NULL)
+ SetupDiDestroyDeviceInfoList(handle);
+
+ *pFilterCount = filterCount;
+ *pNoOfPaDevices = noOfPaDevices;
+
+ return ppFilters;
+}
+
+typedef struct PaNameHashIndex
+{
+ unsigned index;
+ unsigned count;
+ ULONG hash;
+ struct PaNameHashIndex *next;
+} PaNameHashIndex;
+
+typedef struct PaNameHashObject
+{
+ PaNameHashIndex* list;
+ PaUtilAllocationGroup* allocGroup;
+} PaNameHashObject;
+
+static ULONG GetNameHash(const wchar_t* str, const BOOL input)
+{
+ /* This is to make sure that a name that exists as both input & output won't get the same hash value */
+ const ULONG fnv_prime = (input ? 0x811C9DD7 : 0x811FEB0B);
+ ULONG hash = 0;
+ for(; *str != 0; str++)
+ {
+ hash *= fnv_prime;
+ hash ^= (*str);
+ }
+ assert(hash != 0);
+ return hash;
+}
+
+static PaError CreateHashEntry(PaNameHashObject* obj, const wchar_t* name, const BOOL input)
+{
+ ULONG hash = GetNameHash(name, input);
+ PaNameHashIndex * pLast = NULL;
+ PaNameHashIndex * p = obj->list;
+ while (p != 0)
+ {
+ if (p->hash == hash)
+ {
+ break;
+ }
+ pLast = p;
+ p = p->next;
+ }
+ if (p == NULL)
+ {
+ p = (PaNameHashIndex*)PaUtil_GroupAllocateMemory(obj->allocGroup, sizeof(PaNameHashIndex));
+ if (p == NULL)
+ {
+ return paInsufficientMemory;
+ }
+ p->hash = hash;
+ p->count = 1;
+ if (pLast != 0)
+ {
+ assert(pLast->next == 0);
+ pLast->next = p;
+ }
+ if (obj->list == 0)
+ {
+ obj->list = p;
+ }
+ }
+ else
+ {
+ ++p->count;
+ }
+ return paNoError;
+}
+
+static PaError InitNameHashObject(PaNameHashObject* obj, PaWinWdmFilter* pFilter)
+{
+ int i;
+
+ obj->allocGroup = PaUtil_CreateAllocationGroup();
+ if (obj->allocGroup == NULL)
+ {
+ return paInsufficientMemory;
+ }
+
+ for (i = 0; i < pFilter->pinCount; ++i)
+ {
+ unsigned m;
+ PaWinWdmPin* pin = pFilter->pins[i];
+
+ if (pin == NULL)
+ continue;
+
+ for (m = 0; m < max(1, pin->inputCount); ++m)
+ {
+ const BOOL isInput = (pin->dataFlow == KSPIN_DATAFLOW_OUT);
+ const wchar_t* name = (pin->inputs == NULL) ? pin->friendlyName : pin->inputs[m]->friendlyName;
+
+ PaError result = CreateHashEntry(obj, name, isInput);
+
+ if (result != paNoError)
+ {
+ return result;
+ }
+ }
+ }
+ return paNoError;
+}
+
+static void DeinitNameHashObject(PaNameHashObject* obj)
+{
+ assert(obj != 0);
+ PaUtil_FreeAllAllocations(obj->allocGroup);
+ PaUtil_DestroyAllocationGroup(obj->allocGroup);
+ memset(obj, 0, sizeof(PaNameHashObject));
+}
+
+static unsigned GetNameIndex(PaNameHashObject* obj, const wchar_t* name, const BOOL input)
+{
+ ULONG hash = GetNameHash(name, input);
+ PaNameHashIndex* p = obj->list;
+ while (p != NULL)
+ {
+ if (p->hash == hash)
+ {
+ if (p->count > 1)
+ {
+ return (++p->index);
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ p = p->next;
+ }
+ // Should never get here!!
+ assert(FALSE);
+ return 0;
+}
+
+static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, void **scanResults, int *newDeviceCount )
+{
+ PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+ PaError result = paNoError;
+ PaWinWdmFilter** ppFilters = 0;
+ PaWinWDMScanDeviceInfosResults *outArgument = 0;
+ int filterCount = 0;
+ int totalDeviceCount = 0;
+ int idxDevice = 0;
+ DWORD defaultInDevPathSize = 0;
+ DWORD defaultOutDevPathSize = 0;
+ wchar_t* defaultInDevPath = 0;
+ wchar_t* defaultOutDevPath = 0;
+
+ ppFilters = BuildFilterList( &filterCount, &totalDeviceCount, &result );
+ if( result != paNoError )
+ {
+ goto error;
+ }
+
+ // Get hold of default device paths for capture & playback
+ if( waveInMessage(0, DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&defaultInDevPathSize, 0 ) == MMSYSERR_NOERROR )
+ {
+ defaultInDevPath = (wchar_t *)PaUtil_AllocateMemory((defaultInDevPathSize + 1) * sizeof(wchar_t));
+ waveInMessage(0, DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)defaultInDevPath, defaultInDevPathSize);
+ }
+ if( waveOutMessage(0, DRV_QUERYDEVICEINTERFACESIZE, (DWORD_PTR)&defaultOutDevPathSize, 0 ) == MMSYSERR_NOERROR )
+ {
+ defaultOutDevPath = (wchar_t *)PaUtil_AllocateMemory((defaultOutDevPathSize + 1) * sizeof(wchar_t));
+ waveOutMessage(0, DRV_QUERYDEVICEINTERFACE, (DWORD_PTR)defaultOutDevPath, defaultOutDevPathSize);
+ }
+
+ if( totalDeviceCount > 0 )
+ {
+ PaWinWdmDeviceInfo *deviceInfoArray = 0;
+ int idxFilter;
+ int i;
+ unsigned devIsDefaultIn = 0, devIsDefaultOut = 0;
+
+ /* Allocate the out param for all the info we need */
+ outArgument = (PaWinWDMScanDeviceInfosResults *) PaUtil_GroupAllocateMemory(
+ wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults) );
+ if( !outArgument )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ outArgument->defaultInputDevice = paNoDevice;
+ outArgument->defaultOutputDevice = paNoDevice;
+
+ outArgument->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ wdmHostApi->allocations, sizeof(PaDeviceInfo*) * totalDeviceCount );
+ if( !outArgument->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory(
+ wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * totalDeviceCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* Make sure all items in array */
+ for( i = 0 ; i < totalDeviceCount; ++i )
+ {
+ PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->name = 0;
+ outArgument->deviceInfos[ i ] = deviceInfo;
+ }
+
+ idxDevice = 0;
+ for (idxFilter = 0; idxFilter < filterCount; ++idxFilter)
+ {
+ PaNameHashObject nameHash = {0};
+ PaWinWdmFilter* pFilter = ppFilters[idxFilter];
+ if( pFilter == NULL )
+ continue;
+
+ if (InitNameHashObject(&nameHash, pFilter) != paNoError)
+ {
+ DeinitNameHashObject(&nameHash);
+ continue;
+ }
+
+ devIsDefaultIn = (defaultInDevPath && (_wcsicmp(pFilter->devInfo.filterPath, defaultInDevPath) == 0));
+ devIsDefaultOut = (defaultOutDevPath && (_wcsicmp(pFilter->devInfo.filterPath, defaultOutDevPath) == 0));
+
+ for (i = 0; i < pFilter->pinCount; ++i)
+ {
+ unsigned m;
+ ULONG nameIndex = 0;
+ ULONG nameIndexHash = 0;
+ PaWinWdmPin* pin = pFilter->pins[i];
+
+ if (pin == NULL)
+ continue;
+
+ for (m = 0; m < max(1, pin->inputCount); ++m)
+ {
+ PaWinWdmDeviceInfo *wdmDeviceInfo = (PaWinWdmDeviceInfo *)outArgument->deviceInfos[idxDevice];
+ PaDeviceInfo *deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo;
+ wchar_t localCompositeName[MAX_PATH];
+ unsigned nameIndex = 0;
+ const BOOL isInput = (pin->dataFlow == KSPIN_DATAFLOW_OUT);
+
+ wdmDeviceInfo->filter = pFilter;
+
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->name = wdmDeviceInfo->compositeName;
+ /* deviceInfo->hostApiSpecificDeviceInfo = &pFilter->devInfo; */
+
+ wdmDeviceInfo->pin = pin->pinId;
+
+ /* Get the name of the "device" */
+ if (pin->inputs == NULL)
+ {
+ wcsncpy(localCompositeName, pin->friendlyName, MAX_PATH);
+ wdmDeviceInfo->muxPosition = -1;
+ wdmDeviceInfo->endpointPinId = pin->endpointPinId;
+ }
+ else
+ {
+ PaWinWdmMuxedInput* input = pin->inputs[m];
+ wcsncpy(localCompositeName, input->friendlyName, MAX_PATH);
+ wdmDeviceInfo->muxPosition = (int)m;
+ wdmDeviceInfo->endpointPinId = input->endpointPinId;
+ }
+
+ {
+ /* Get base length */
+ size_t n = wcslen(localCompositeName);
+
+ /* Check if there are more entries with same name (which might very well be the case), if there
+ are, the name will be postfixed with an index. */
+ nameIndex = GetNameIndex(&nameHash, localCompositeName, isInput);
+ if (nameIndex > 0)
+ {
+ /* This name has multiple instances, so we post fix with a number */
+ n += _snwprintf(localCompositeName + n, MAX_PATH - n, L" %u", nameIndex);
+ }
+ /* Postfix with filter name */
+ _snwprintf(localCompositeName + n, MAX_PATH - n, L" (%s)", pFilter->friendlyName);
+ }
+
+ /* Convert wide char string to utf-8 */
+ WideCharToMultiByte(CP_UTF8, 0, localCompositeName, -1, wdmDeviceInfo->compositeName, MAX_PATH, NULL, NULL);
+
+ /* NB! WDM/KS has no concept of a full-duplex device, each pin is either an input or an output */
+ if (isInput)
+ {
+ /* INPUT ! */
+ deviceInfo->maxInputChannels = pin->maxChannels;
+ deviceInfo->maxOutputChannels = 0;
+
+ /* RoBi NB: Due to the fact that input audio endpoints in Vista (& later OSs) can be the same device, but with
+ different input mux settings, there might be a discrepancy between the default input device chosen, and
+ that which will be used by Portaudio. Not much to do about that unfortunately.
+ */
+ if ((defaultInDevPath == 0 || devIsDefaultIn) &&
+ outArgument->defaultInputDevice == paNoDevice)
+ {
+ outArgument->defaultInputDevice = idxDevice;
+ }
+ }
+ else
+ {
+ /* OUTPUT ! */
+ deviceInfo->maxInputChannels = 0;
+ deviceInfo->maxOutputChannels = pin->maxChannels;
+
+ if ((defaultOutDevPath == 0 || devIsDefaultOut) &&
+ outArgument->defaultOutputDevice == paNoDevice)
+ {
+ outArgument->defaultOutputDevice = idxDevice;
+ }
+ }
+
+ /* These low values are not very useful because
+ * a) The lowest latency we end up with can depend on many factors such
+ * as the device buffer sizes/granularities, sample rate, channels and format
+ * b) We cannot know the device buffer sizes until we try to open/use it at
+ * a particular setting
+ * So: we give 512x48000Hz frames as the default low input latency
+ **/
+ switch (pFilter->devInfo.streamingType)
+ {
+ case Type_kWaveCyclic:
+ if (IsEarlierThanVista())
+ {
+ /* XP doesn't tolerate low latency, unless the Process Priority Class is set to REALTIME_PRIORITY_CLASS
+ through SetPriorityClass, then 10 ms is quite feasible. However, one should then bear in mind that ALL of
+ the process is running in REALTIME_PRIORITY_CLASS, which might not be appropriate for an application with
+ a GUI . In this case it is advisable to separate the audio engine in another process and use IPC to communicate
+ with it. */
+ deviceInfo->defaultLowInputLatency = 0.02;
+ deviceInfo->defaultLowOutputLatency = 0.02;
+ }
+ else
+ {
+ /* This is a conservative estimate. Most WaveCyclic drivers will limit the available latency, but f.i. my Edirol
+ PCR-A30 can reach 3 ms latency easily... */
+ deviceInfo->defaultLowInputLatency = 0.01;
+ deviceInfo->defaultLowOutputLatency = 0.01;
+ }
+ deviceInfo->defaultHighInputLatency = (4096.0/48000.0);
+ deviceInfo->defaultHighOutputLatency = (4096.0/48000.0);
+ deviceInfo->defaultSampleRate = (double)(pin->defaultSampleRate);
+ break;
+ case Type_kWaveRT:
+ /* This is also a conservative estimate, based on WaveRT polled mode. In polled mode, the latency will be dictated
+ by the buffer size given by the driver. */
+ deviceInfo->defaultLowInputLatency = 0.01;
+ deviceInfo->defaultLowOutputLatency = 0.01;
+ deviceInfo->defaultHighInputLatency = 0.04;
+ deviceInfo->defaultHighOutputLatency = 0.04;
+ deviceInfo->defaultSampleRate = (double)(pin->defaultSampleRate);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ /* Add reference to filter */
+ FilterAddRef(wdmDeviceInfo->filter);
+
+ assert(idxDevice < totalDeviceCount);
+ ++idxDevice;
+ }
+ }
+
+ /* If no one has add ref'd the filter, drop it */
+ if (pFilter->filterRefCount == 0)
+ {
+ FilterFree(pFilter);
+ }
+
+ /* Deinitialize name hash object */
+ DeinitNameHashObject(&nameHash);
+ }
+ }
+
+ *scanResults = outArgument;
+ *newDeviceCount = idxDevice;
+ return result;
+
+error:
+ result = DisposeDeviceInfos(hostApi, outArgument, totalDeviceCount);
+
+ return result;
+}
+
+static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *scanResults, int deviceCount )
+{
+ PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+
+ hostApi->info.deviceCount = 0;
+ hostApi->info.defaultInputDevice = paNoDevice;
+ hostApi->info.defaultOutputDevice = paNoDevice;
+
+ /* Free any old memory which might be in the device info */
+ if( hostApi->deviceInfos )
+ {
+ PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory(
+ wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults));
+ localScanResults->deviceInfos = hostApi->deviceInfos;
+
+ DisposeDeviceInfos(hostApi, &localScanResults, hostApi->info.deviceCount);
+
+ hostApi->deviceInfos = NULL;
+ }
+
+ if( scanResults != NULL )
+ {
+ PaWinWDMScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinWDMScanDeviceInfosResults * ) scanResults;
+
+ if( deviceCount > 0 )
+ {
+ /* use the array allocated in ScanDeviceInfos() as our deviceInfos */
+ hostApi->deviceInfos = scanDeviceInfosResults->deviceInfos;
+
+ hostApi->info.defaultInputDevice = scanDeviceInfosResults->defaultInputDevice;
+ hostApi->info.defaultOutputDevice = scanDeviceInfosResults->defaultOutputDevice;
+
+ hostApi->info.deviceCount = deviceCount;
+ }
+
+ PaUtil_GroupFreeMemory( wdmHostApi->allocations, scanDeviceInfosResults );
+ }
+
+ return paNoError;
+
+}
+
+static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount )
+{
+ PaWinWdmHostApiRepresentation *winDsHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+
+ if( scanResults != NULL )
+ {
+ PaWinWDMScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinWDMScanDeviceInfosResults * ) scanResults;
+
+ if( scanDeviceInfosResults->deviceInfos )
+ {
+ int i;
+ for (i = 0; i < deviceCount; ++i)
+ {
+ PaWinWdmDeviceInfo* pDevice = (PaWinWdmDeviceInfo*)scanDeviceInfosResults->deviceInfos[i];
+ if (pDevice->filter != 0)
+ {
+ FilterFree(pDevice->filter);
+ }
+ }
+
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); /* all device info structs are allocated in a block so we can destroy them here */
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos );
+ }
+
+ PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults );
+ }
+
+ return paNoError;
+
+}
+
+PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int deviceCount = 0;
+ void *scanResults = 0;
+ PaWinWdmHostApiRepresentation *wdmHostApi = NULL;
+
+ PA_LOGE_;
+
+#ifdef PA_WDMKS_SET_TREF
+ tRef = PaUtil_GetTime();
+#endif
+
+ /*
+ Attempt to load the KSUSER.DLL without which we cannot create pins
+ We will unload this on termination
+ */
+ if(DllKsUser == NULL)
+ {
+ DllKsUser = LoadLibrary(TEXT("ksuser.dll"));
+ if(DllKsUser == NULL)
+ goto error;
+ }
+ FunctionKsCreatePin = (KSCREATEPIN*)GetProcAddress(DllKsUser, "KsCreatePin");
+ if(FunctionKsCreatePin == NULL)
+ goto error;
+
+ /* Attempt to load AVRT.DLL, if we can't, then we'll just use time critical prio instead... */
+ if(paWinWDMKSAvRtEntryPoints.hInstance == NULL)
+ {
+ paWinWDMKSAvRtEntryPoints.hInstance = LoadLibrary(TEXT("avrt.dll"));
+ if (paWinWDMKSAvRtEntryPoints.hInstance != NULL)
+ {
+ paWinWDMKSAvRtEntryPoints.AvSetMmThreadCharacteristics =
+ (HANDLE(WINAPI*)(LPCSTR,LPDWORD))GetProcAddress(paWinWDMKSAvRtEntryPoints.hInstance,"AvSetMmThreadCharacteristicsA");
+ paWinWDMKSAvRtEntryPoints.AvRevertMmThreadCharacteristics =
+ (BOOL(WINAPI*)(HANDLE))GetProcAddress(paWinWDMKSAvRtEntryPoints.hInstance, "AvRevertMmThreadCharacteristics");
+ paWinWDMKSAvRtEntryPoints.AvSetMmThreadPriority =
+ (BOOL(WINAPI*)(HANDLE,PA_AVRT_PRIORITY))GetProcAddress(paWinWDMKSAvRtEntryPoints.hInstance, "AvSetMmThreadPriority");
+ }
+ }
+
+ wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) );
+ if( !wdmHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ wdmHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !wdmHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ *hostApi = &wdmHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paWDMKS;
+ (*hostApi)->info.name = "Windows WDM-KS";
+
+ /* these are all updated by CommitDeviceInfos() */
+ (*hostApi)->info.deviceCount = 0;
+ (*hostApi)->info.defaultInputDevice = paNoDevice;
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+ (*hostApi)->deviceInfos = 0;
+
+ result = ScanDeviceInfos(&wdmHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, &deviceCount);
+ if (result != paNoError)
+ {
+ goto error;
+ }
+
+ CommitDeviceInfos(&wdmHostApi->inheritedHostApiRep, hostApiIndex, scanResults, deviceCount);
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+ /* In preparation for hotplug
+ (*hostApi)->ScanDeviceInfos = ScanDeviceInfos;
+ (*hostApi)->CommitDeviceInfos = CommitDeviceInfos;
+ (*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos;
+ */
+ PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, GetStreamCpuLoad,
+ PaUtil_DummyRead, PaUtil_DummyWrite,
+ PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
+
+ PaUtil_InitializeStreamInterface( &wdmHostApi->blockingStreamInterface, CloseStream, StartStream,
+ StopStream, AbortStream, IsStreamStopped, IsStreamActive,
+ GetStreamTime, PaUtil_DummyGetCpuLoad,
+ ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
+
+ PA_LOGL_;
+ return result;
+
+error:
+ Terminate( (PaUtilHostApiRepresentation*)wdmHostApi );
+
+ PA_LOGL_;
+ return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+ PA_LOGE_;
+
+ /* Do not unload the libraries */
+ if( DllKsUser != NULL )
+ {
+ FreeLibrary( DllKsUser );
+ DllKsUser = NULL;
+ }
+
+ if( paWinWDMKSAvRtEntryPoints.hInstance != NULL )
+ {
+ FreeLibrary( paWinWDMKSAvRtEntryPoints.hInstance );
+ paWinWDMKSAvRtEntryPoints.hInstance = NULL;
+ }
+
+ if( wdmHostApi)
+ {
+ PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory(
+ wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults));
+ localScanResults->deviceInfos = hostApi->deviceInfos;
+ DisposeDeviceInfos(hostApi, localScanResults, hostApi->info.deviceCount);
+
+ if( wdmHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( wdmHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
+ }
+ PaUtil_FreeMemory( wdmHostApi );
+ }
+ PA_LOGL_;
+}
+
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate )
+{
+ int inputChannelCount, outputChannelCount;
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+ PaWinWdmFilter* pFilter;
+ int result = paFormatIsSupported;
+ WAVEFORMATEXTENSIBLE wfx;
+ PaWinWaveFormatChannelMask channelMask;
+
+ PA_LOGE_;
+
+ if( inputParameters )
+ {
+ PaWinWdmDeviceInfo* pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
+ PaWinWdmPin* pin;
+ unsigned fmt;
+ unsigned long testFormat = 0;
+ unsigned validBits = 0;
+
+ inputChannelCount = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( inputSampleFormat & paCustomFormat )
+ {
+ PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "IsFormatSupported: Custom input format not supported");
+ return paSampleFormatNotSupported;
+ }
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ PaWinWDM_SetLastErrorInfo(paInvalidDevice, "IsFormatSupported: paUseHostApiSpecificDeviceSpecification not supported");
+ return paInvalidDevice;
+ }
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ {
+ PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "IsFormatSupported: Invalid input channel count");
+ return paInvalidChannelCount;
+ }
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ {
+ PaWinWDM_SetLastErrorInfo(paIncompatibleHostApiSpecificStreamInfo, "Host API stream info not supported");
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+
+ pFilter = pDeviceInfo->filter;
+ pin = pFilter->pins[pDeviceInfo->pin];
+
+ /* Find out the testing format */
+ for (fmt = paFloat32; fmt <= paUInt8; fmt <<= 1)
+ {
+ if ((fmt & pin->formats) != 0)
+ {
+ /* Found a matching format! */
+ testFormat = fmt;
+ break;
+ }
+ }
+ if (testFormat == 0)
+ {
+ PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(capture) failed: no testformat found!");
+ return paUnanticipatedHostError;
+ }
+
+ /* Due to special considerations, WaveRT devices with paInt24 should be tested with paInt32 and
+ valid bits = 24 (instead of 24 bit samples) */
+ if (pFilter->devInfo.streamingType == Type_kWaveRT && testFormat == paInt24)
+ {
+ PA_DEBUG(("IsFormatSupported (capture): WaveRT overriding testFormat paInt24 with paInt32 (24 valid bits)"));
+ testFormat = paInt32;
+ validBits = 24;
+ }
+
+ /* Check that the input format is supported */
+ channelMask = PaWin_DefaultChannelMask(inputChannelCount);
+ PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
+ inputChannelCount,
+ testFormat,
+ PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
+ sampleRate,
+ channelMask );
+ if (validBits != 0)
+ {
+ wfx.Samples.wValidBitsPerSample = validBits;
+ }
+
+ result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
+ if( result != paNoError )
+ {
+ /* Try a WAVE_FORMAT_PCM instead */
+ PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
+ inputChannelCount,
+ testFormat,
+ PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
+ sampleRate);
+
+ if (validBits != 0)
+ {
+ wfx.Samples.wValidBitsPerSample = validBits;
+ }
+
+ result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
+ if( result != paNoError )
+ {
+ PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(capture) failed: sr=%u,ch=%u,bits=%u", wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample);
+ return result;
+ }
+ }
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ PaWinWdmDeviceInfo* pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
+ PaWinWdmPin* pin;
+ unsigned fmt;
+ unsigned long testFormat = 0;
+ unsigned validBits = 0;
+
+ outputChannelCount = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* all standard sample formats are supported by the buffer adapter,
+ this implementation doesn't support any custom sample formats */
+ if( outputSampleFormat & paCustomFormat )
+ {
+ PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "IsFormatSupported: Custom output format not supported");
+ return paSampleFormatNotSupported;
+ }
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ PaWinWDM_SetLastErrorInfo(paInvalidDevice, "IsFormatSupported: paUseHostApiSpecificDeviceSpecification not supported");
+ return paInvalidDevice;
+ }
+
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ {
+ PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid output channel count");
+ return paInvalidChannelCount;
+ }
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ {
+ PaWinWDM_SetLastErrorInfo(paIncompatibleHostApiSpecificStreamInfo, "Host API stream info not supported");
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+ }
+
+ pFilter = pDeviceInfo->filter;
+ pin = pFilter->pins[pDeviceInfo->pin];
+
+ /* Find out the testing format */
+ for (fmt = paFloat32; fmt <= paUInt8; fmt <<= 1)
+ {
+ if ((fmt & pin->formats) != 0)
+ {
+ /* Found a matching format! */
+ testFormat = fmt;
+ break;
+ }
+ }
+ if (testFormat == 0)
+ {
+ PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(render) failed: no testformat found!");
+ return paUnanticipatedHostError;
+ }
+
+ /* Due to special considerations, WaveRT devices with paInt24 should be tested with paInt32 and
+ valid bits = 24 (instead of 24 bit samples) */
+ if (pFilter->devInfo.streamingType == Type_kWaveRT && testFormat == paInt24)
+ {
+ PA_DEBUG(("IsFormatSupported (render): WaveRT overriding testFormat paInt24 with paInt32 (24 valid bits)"));
+ testFormat = paInt32;
+ validBits = 24;
+ }
+
+ /* Check that the output format is supported */
+ channelMask = PaWin_DefaultChannelMask(outputChannelCount);
+ PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
+ outputChannelCount,
+ testFormat,
+ PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
+ sampleRate,
+ channelMask );
+
+ if (validBits != 0)
+ {
+ wfx.Samples.wValidBitsPerSample = validBits;
+ }
+
+ result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
+ if( result != paNoError )
+ {
+ /* Try a WAVE_FORMAT_PCM instead */
+ PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
+ outputChannelCount,
+ testFormat,
+ PaWin_SampleFormatToLinearWaveFormatTag(testFormat),
+ sampleRate);
+
+ if (validBits != 0)
+ {
+ wfx.Samples.wValidBitsPerSample = validBits;
+ }
+
+ result = PinIsFormatSupported(pin, (const WAVEFORMATEX*)&wfx);
+ if( result != paNoError )
+ {
+ PaWinWDM_SetLastErrorInfo(result, "IsFormatSupported(render) failed: %u,%u,%u", wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample);
+ return result;
+ }
+ }
+
+ }
+ else
+ {
+ outputChannelCount = 0;
+ }
+
+ /*
+ IMPLEMENT ME:
+
+ - if a full duplex stream is requested, check that the combination
+ of input and output parameters is supported if necessary
+
+ - check that the device supports sampleRate
+
+ Because the buffer adapter handles conversion between all standard
+ sample formats, the following checks are only required if paCustomFormat
+ is implemented, or under some other unusual conditions.
+
+ - check that input device can support inputSampleFormat, or that
+ we have the capability to convert from inputSampleFormat to
+ a native format
+
+ - check that output device can support outputSampleFormat, or that
+ we have the capability to convert from outputSampleFormat to
+ a native format
+ */
+ if((inputChannelCount == 0)&&(outputChannelCount == 0))
+ {
+ PaWinWDM_SetLastErrorInfo(paSampleFormatNotSupported, "No input or output channels defined");
+ result = paSampleFormatNotSupported; /* Not right error */
+ }
+
+ PA_LOGL_;
+ return result;
+}
+
+static void ResetStreamEvents(PaWinWdmStream* stream)
+{
+ unsigned i;
+ ResetEvent(stream->eventAbort);
+ ResetEvent(stream->eventStreamStart[StreamStart_kOk]);
+ ResetEvent(stream->eventStreamStart[StreamStart_kFailed]);
+
+ for (i=0; i<stream->capture.noOfPackets; ++i)
+ {
+ if (stream->capture.events && stream->capture.events[i])
+ {
+ ResetEvent(stream->capture.events[i]);
+ }
+ }
+
+ for (i=0; i<stream->render.noOfPackets; ++i)
+ {
+ if (stream->render.events && stream->render.events[i])
+ {
+ ResetEvent(stream->render.events[i]);
+ }
+ }
+}
+
+static void CloseStreamEvents(PaWinWdmStream* stream)
+{
+ unsigned i;
+ PaWinWdmIOInfo* ios[2] = { &stream->capture, &stream->render };
+
+ if (stream->eventAbort)
+ {
+ CloseHandle(stream->eventAbort);
+ stream->eventAbort = 0;
+ }
+ if (stream->eventStreamStart[StreamStart_kOk])
+ {
+ CloseHandle(stream->eventStreamStart[StreamStart_kOk]);
+ }
+ if (stream->eventStreamStart[StreamStart_kFailed])
+ {
+ CloseHandle(stream->eventStreamStart[StreamStart_kFailed]);
+ }
+
+ for (i = 0; i < 2; ++i)
+ {
+ unsigned j;
+ /* Unregister notification handles for WaveRT */
+ if (ios[i]->pPin && ios[i]->pPin->parentFilter->devInfo.streamingType == Type_kWaveRT &&
+ ios[i]->pPin->pinKsSubType == SubType_kNotification &&
+ ios[i]->events != 0)
+ {
+ PinUnregisterNotificationHandle(ios[i]->pPin, ios[i]->events[0]);
+ }
+
+ for (j=0; j < ios[i]->noOfPackets; ++j)
+ {
+ if (ios[i]->events && ios[i]->events[j])
+ {
+ CloseHandle(ios[i]->events[j]);
+ ios[i]->events[j] = 0;
+ }
+ }
+ }
+}
+
+static unsigned NextPowerOf2(unsigned val)
+{
+ val--;
+ val = (val >> 1) | val;
+ val = (val >> 2) | val;
+ val = (val >> 4) | val;
+ val = (val >> 8) | val;
+ val = (val >> 16) | val;
+ return ++val;
+}
+
+static PaError ValidateSpecificStreamParameters(
+ const PaStreamParameters *streamParameters,
+ const PaWinWDMKSInfo *streamInfo,
+ unsigned isInput)
+{
+ if( streamInfo )
+ {
+ if( streamInfo->size != sizeof( PaWinWDMKSInfo )
+ || streamInfo->version != 1 )
+ {
+ PA_DEBUG(("Stream parameters: size or version not correct"));
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+
+ if (!!(streamInfo->flags & ~(paWinWDMKSOverrideFramesize | paWinWDMKSUseGivenChannelMask)))
+ {
+ PA_DEBUG(("Stream parameters: non supported flags set"));
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+
+ if (streamInfo->noOfPackets != 0 &&
+ (streamInfo->noOfPackets < 2 || streamInfo->noOfPackets > 8))
+ {
+ PA_DEBUG(("Stream parameters: noOfPackets %u out of range [2,8]", streamInfo->noOfPackets));
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+
+ if (streamInfo->flags & paWinWDMKSUseGivenChannelMask)
+ {
+ if (isInput)
+ {
+ PA_DEBUG(("Stream parameters: Channels mask setting not supported for input stream"));
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+
+ if (streamInfo->channelMask & PAWIN_SPEAKER_RESERVED)
+ {
+ PA_DEBUG(("Stream parameters: Given channels mask 0x%08X not supported", streamInfo->channelMask));
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
+ }
+
+ }
+
+ return paNoError;
+}
+
+
+
+
+/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
+
+static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
+ PaStream** s,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate,
+ unsigned long framesPerUserBuffer,
+ PaStreamFlags streamFlags,
+ PaStreamCallback *streamCallback,
+ void *userData )
+{
+ PaError result = paNoError;
+ PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+ PaWinWdmStream *stream = 0;
+ /* unsigned long framesPerHostBuffer; these may not be equivalent for all implementations */
+ PaSampleFormat inputSampleFormat, outputSampleFormat;
+ PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
+ int userInputChannels,userOutputChannels;
+ WAVEFORMATEXTENSIBLE wfx;
+
+ PA_LOGE_;
+ PA_DEBUG(("OpenStream:sampleRate = %f\n",sampleRate));
+ PA_DEBUG(("OpenStream:framesPerBuffer = %lu\n",framesPerUserBuffer));
+
+ if( inputParameters )
+ {
+ userInputChannels = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ PaWinWDM_SetLastErrorInfo(paInvalidDevice, "paUseHostApiSpecificDeviceSpecification(in) not supported");
+ return paInvalidDevice;
+ }
+
+ /* check that input device can support stream->userInputChannels */
+ if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ {
+ PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid input channel count");
+ return paInvalidChannelCount;
+ }
+
+ /* validate inputStreamInfo */
+ result = ValidateSpecificStreamParameters(inputParameters, inputParameters->hostApiSpecificStreamInfo, 1 );
+ if(result != paNoError)
+ {
+ PaWinWDM_SetLastErrorInfo(result, "Host API stream info not supported (in)");
+ return result; /* this implementation doesn't use custom stream info */
+ }
+ }
+ else
+ {
+ userInputChannels = 0;
+ inputSampleFormat = paInt16; /* Suppress 'uninitialised var' warnings. */
+ }
+
+ if( outputParameters )
+ {
+ userOutputChannels = outputParameters->channelCount;
+ outputSampleFormat = outputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ {
+ PaWinWDM_SetLastErrorInfo(paInvalidDevice, "paUseHostApiSpecificDeviceSpecification(out) not supported");
+ return paInvalidDevice;
+ }
+
+ /* check that output device can support stream->userInputChannels */
+ if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ {
+ PaWinWDM_SetLastErrorInfo(paInvalidChannelCount, "Invalid output channel count");
+ return paInvalidChannelCount;
+ }
+
+ /* validate outputStreamInfo */
+ result = ValidateSpecificStreamParameters( outputParameters, outputParameters->hostApiSpecificStreamInfo, 0 );
+ if (result != paNoError)
+ {
+ PaWinWDM_SetLastErrorInfo(result, "Host API stream info not supported (out)");
+ return result; /* this implementation doesn't use custom stream info */
+ }
+ }
+ else
+ {
+ userOutputChannels = 0;
+ outputSampleFormat = paInt16; /* Suppress 'uninitialized var' warnings. */
+ }
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ {
+ PaWinWDM_SetLastErrorInfo(paInvalidFlag, "Invalid flag supplied");
+ return paInvalidFlag; /* unexpected platform specific flag */
+ }
+
+ stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) );
+ if( !stream )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* Create allocation group */
+ stream->allocGroup = PaUtil_CreateAllocationGroup();
+ if( !stream->allocGroup )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* Zero the stream object */
+ /* memset((void*)stream,0,sizeof(PaWinWdmStream)); */
+
+ if( streamCallback )
+ {
+ PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &wdmHostApi->callbackStreamInterface, streamCallback, userData );
+ }
+ else
+ {
+ /* PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
+ &wdmHostApi->blockingStreamInterface, streamCallback, userData ); */
+
+ /* We don't support the blocking API yet */
+ PA_DEBUG(("Blocking API not supported yet!\n"));
+ PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Blocking API not supported yet");
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+ /* Instantiate the input pin if necessary */
+ if(userInputChannels > 0)
+ {
+ PaWinWdmFilter* pFilter;
+ PaWinWdmDeviceInfo* pDeviceInfo;
+ PaWinWdmPin* pPin;
+ unsigned validBitsPerSample = 0;
+ PaWinWaveFormatChannelMask channelMask = PaWin_DefaultChannelMask( userInputChannels );
+
+ result = paSampleFormatNotSupported;
+ pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
+ pFilter = pDeviceInfo->filter;
+ pPin = pFilter->pins[pDeviceInfo->pin];
+
+ stream->userInputChannels = userInputChannels;
+
+ hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( pPin->formats, inputSampleFormat );
+ if (hostInputSampleFormat == paSampleFormatNotSupported)
+ {
+ result = paUnanticipatedHostError;
+ PaWinWDM_SetLastErrorInfo(result, "PU_SCAF(%X,%X) failed (input)", pPin->formats, inputSampleFormat);
+ goto error;
+ }
+ else if (pFilter->devInfo.streamingType == Type_kWaveRT && hostInputSampleFormat == paInt24)
+ {
+ /* For WaveRT, we choose 32 bit format instead of paInt24, since we MIGHT need to align buffer on a
+ 128 byte boundary (see PinGetBuffer) */
+ hostInputSampleFormat = paInt32;
+ /* But we'll tell the driver that it's 24 bit in 32 bit container */
+ validBitsPerSample = 24;
+ }
+
+ while (hostInputSampleFormat <= paUInt8)
+ {
+ unsigned channelsToProbe = stream->userInputChannels;
+ /* Some or all KS devices can only handle the exact number of channels
+ * they specify. But PortAudio clients expect to be able to
+ * at least specify mono I/O on a multi-channel device
+ * If this is the case, then we will do the channel mapping internally
+ * The following loop tests this case
+ **/
+ while (1)
+ {
+ PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
+ channelsToProbe,
+ hostInputSampleFormat,
+ PaWin_SampleFormatToLinearWaveFormatTag(hostInputSampleFormat),
+ sampleRate,
+ channelMask );
+ stream->capture.bytesPerFrame = wfx.Format.nBlockAlign;
+ if (validBitsPerSample != 0)
+ {
+ wfx.Samples.wValidBitsPerSample = validBitsPerSample;
+ }
+ stream->capture.pPin = FilterCreatePin(pFilter, pPin->pinId, (WAVEFORMATEX*)&wfx, &result);
+ stream->deviceInputChannels = channelsToProbe;
+
+ if( result != paNoError && result != paDeviceUnavailable )
+ {
+ /* Try a WAVE_FORMAT_PCM instead */
+ PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
+ channelsToProbe,
+ hostInputSampleFormat,
+ PaWin_SampleFormatToLinearWaveFormatTag(hostInputSampleFormat),
+ sampleRate);
+ if (validBitsPerSample != 0)
+ {
+ wfx.Samples.wValidBitsPerSample = validBitsPerSample;
+ }
+ stream->capture.pPin = FilterCreatePin(pFilter, pPin->pinId, (const WAVEFORMATEX*)&wfx, &result);
+ }
+
+ if (result == paDeviceUnavailable) goto occupied;
+
+ if (result == paNoError)
+ {
+ /* We're done */
+ break;
+ }
+
+ if (channelsToProbe < (unsigned)pPin->maxChannels)
+ {
+ /* Go to next multiple of 2 */
+ channelsToProbe = min((((channelsToProbe>>1)+1)<<1), (unsigned)pPin->maxChannels);
+ continue;
+ }
+
+ break;
+ }
+
+ if (result == paNoError)
+ {
+ /* We're done */
+ break;
+ }
+
+ /* Go to next format in line with lower resolution */
+ hostInputSampleFormat <<= 1;
+ }
+
+ if(stream->capture.pPin == NULL)
+ {
+ PaWinWDM_SetLastErrorInfo(result, "Failed to create capture pin: sr=%u,ch=%u,bits=%u,align=%u",
+ wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample, wfx.Format.nBlockAlign);
+ goto error;
+ }
+
+ /* Select correct mux input on MUX node of topology filter */
+ if (pDeviceInfo->muxPosition >= 0)
+ {
+ assert(pPin->parentFilter->topologyFilter != NULL);
+
+ result = FilterUse(pPin->parentFilter->topologyFilter);
+ if (result != paNoError)
+ {
+ PaWinWDM_SetLastErrorInfo(result, "Failed to open topology filter");
+ goto error;
+ }
+
+ result = WdmSetMuxNodeProperty(pPin->parentFilter->topologyFilter->handle,
+ pPin->inputs[pDeviceInfo->muxPosition]->muxNodeId,
+ pPin->inputs[pDeviceInfo->muxPosition]->muxPinId);
+
+ FilterRelease(pPin->parentFilter->topologyFilter);
+
+ if(result != paNoError)
+ {
+ PaWinWDM_SetLastErrorInfo(result, "Failed to set topology mux node");
+ goto error;
+ }
+ }
+
+ stream->capture.bytesPerSample = stream->capture.bytesPerFrame / stream->deviceInputChannels;
+ stream->capture.pPin->frameSize /= stream->capture.bytesPerFrame;
+ PA_DEBUG(("Capture pin frames: %d\n",stream->capture.pPin->frameSize));
+ }
+ else
+ {
+ hostInputSampleFormat = (PaSampleFormat)0; /* Avoid uninitialized variable warning */
+
+ stream->capture.pPin = NULL;
+ stream->capture.bytesPerFrame = 0;
+ }
+
+ /* Instantiate the output pin if necessary */
+ if(userOutputChannels > 0)
+ {
+ PaWinWdmFilter* pFilter;
+ PaWinWdmDeviceInfo* pDeviceInfo;
+ PaWinWdmPin* pPin;
+ PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)(outputParameters->hostApiSpecificStreamInfo);
+ unsigned validBitsPerSample = 0;
+ PaWinWaveFormatChannelMask channelMask = PaWin_DefaultChannelMask( userOutputChannels );
+ if (pInfo && (pInfo->flags & paWinWDMKSUseGivenChannelMask))
+ {
+ PA_DEBUG(("Using channelMask 0x%08X instead of default 0x%08X\n",
+ pInfo->channelMask,
+ channelMask));
+ channelMask = pInfo->channelMask;
+ }
+
+ result = paSampleFormatNotSupported;
+ pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[outputParameters->device];
+ pFilter = pDeviceInfo->filter;
+ pPin = pFilter->pins[pDeviceInfo->pin];
+
+ stream->userOutputChannels = userOutputChannels;
+
+ hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( pPin->formats, outputSampleFormat );
+ if (hostOutputSampleFormat == paSampleFormatNotSupported)
+ {
+ result = paUnanticipatedHostError;
+ PaWinWDM_SetLastErrorInfo(result, "PU_SCAF(%X,%X) failed (output)", pPin->formats, hostOutputSampleFormat);
+ goto error;
+ }
+ else if (pFilter->devInfo.streamingType == Type_kWaveRT && hostOutputSampleFormat == paInt24)
+ {
+ /* For WaveRT, we choose 32 bit format instead of paInt24, since we MIGHT need to align buffer on a
+ 128 byte boundary (see PinGetBuffer) */
+ hostOutputSampleFormat = paInt32;
+ /* But we'll tell the driver that it's 24 bit in 32 bit container */
+ validBitsPerSample = 24;
+ }
+
+ while (hostOutputSampleFormat <= paUInt8)
+ {
+ unsigned channelsToProbe = stream->userOutputChannels;
+ /* Some or all KS devices can only handle the exact number of channels
+ * they specify. But PortAudio clients expect to be able to
+ * at least specify mono I/O on a multi-channel device
+ * If this is the case, then we will do the channel mapping internally
+ * The following loop tests this case
+ **/
+ while (1)
+ {
+ PaWin_InitializeWaveFormatExtensible((PaWinWaveFormat*)&wfx,
+ channelsToProbe,
+ hostOutputSampleFormat,
+ PaWin_SampleFormatToLinearWaveFormatTag(hostOutputSampleFormat),
+ sampleRate,
+ channelMask );
+ stream->render.bytesPerFrame = wfx.Format.nBlockAlign;
+ if (validBitsPerSample != 0)
+ {
+ wfx.Samples.wValidBitsPerSample = validBitsPerSample;
+ }
+ stream->render.pPin = FilterCreatePin(pFilter, pPin->pinId, (WAVEFORMATEX*)&wfx, &result);
+ stream->deviceOutputChannels = channelsToProbe;
+
+ if( result != paNoError && result != paDeviceUnavailable )
+ {
+ PaWin_InitializeWaveFormatEx((PaWinWaveFormat*)&wfx,
+ channelsToProbe,
+ hostOutputSampleFormat,
+ PaWin_SampleFormatToLinearWaveFormatTag(hostOutputSampleFormat),
+ sampleRate);
+ if (validBitsPerSample != 0)
+ {
+ wfx.Samples.wValidBitsPerSample = validBitsPerSample;
+ }
+ stream->render.pPin = FilterCreatePin(pFilter, pPin->pinId, (const WAVEFORMATEX*)&wfx, &result);
+ }
+
+ if (result == paDeviceUnavailable) goto occupied;
+
+ if (result == paNoError)
+ {
+ /* We're done */
+ break;
+ }
+
+ if (channelsToProbe < (unsigned)pPin->maxChannels)
+ {
+ /* Go to next multiple of 2 */
+ channelsToProbe = min((((channelsToProbe>>1)+1)<<1), (unsigned)pPin->maxChannels);
+ continue;
+ }
+
+ break;
+ };
+
+ if (result == paNoError)
+ {
+ /* We're done */
+ break;
+ }
+
+ /* Go to next format in line with lower resolution */
+ hostOutputSampleFormat <<= 1;
+ }
+
+ if(stream->render.pPin == NULL)
+ {
+ PaWinWDM_SetLastErrorInfo(result, "Failed to create render pin: sr=%u,ch=%u,bits=%u,align=%u",
+ wfx.Format.nSamplesPerSec, wfx.Format.nChannels, wfx.Format.wBitsPerSample, wfx.Format.nBlockAlign);
+ goto error;
+ }
+
+ stream->render.bytesPerSample = stream->render.bytesPerFrame / stream->deviceOutputChannels;
+ stream->render.pPin->frameSize /= stream->render.bytesPerFrame;
+ PA_DEBUG(("Render pin frames: %d\n",stream->render.pPin->frameSize));
+ }
+ else
+ {
+ hostOutputSampleFormat = (PaSampleFormat)0; /* Avoid uninitialized variable warning */
+
+ stream->render.pPin = NULL;
+ stream->render.bytesPerFrame = 0;
+ }
+
+ /* Calculate the framesPerHostXxxxBuffer size based upon the suggested latency values */
+ /* Record the buffer length */
+ if(inputParameters)
+ {
+ /* Calculate the frames from the user's value - add a bit to round up */
+ stream->capture.framesPerBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001);
+ if(stream->capture.framesPerBuffer > (unsigned long)sampleRate)
+ { /* Upper limit is 1 second */
+ stream->capture.framesPerBuffer = (unsigned long)sampleRate;
+ }
+ else if(stream->capture.framesPerBuffer < stream->capture.pPin->frameSize)
+ {
+ stream->capture.framesPerBuffer = stream->capture.pPin->frameSize;
+ }
+ PA_DEBUG(("Input frames chosen:%ld\n",stream->capture.framesPerBuffer));
+
+ /* Setup number of packets to use */
+ stream->capture.noOfPackets = 2;
+
+ if (inputParameters->hostApiSpecificStreamInfo)
+ {
+ PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)inputParameters->hostApiSpecificStreamInfo;
+
+ if (stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic &&
+ pInfo->noOfPackets != 0)
+ {
+ stream->capture.noOfPackets = pInfo->noOfPackets;
+ }
+ }
+ }
+
+ if(outputParameters)
+ {
+ /* Calculate the frames from the user's value - add a bit to round up */
+ stream->render.framesPerBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001);
+ if(stream->render.framesPerBuffer > (unsigned long)sampleRate)
+ { /* Upper limit is 1 second */
+ stream->render.framesPerBuffer = (unsigned long)sampleRate;
+ }
+ else if(stream->render.framesPerBuffer < stream->render.pPin->frameSize)
+ {
+ stream->render.framesPerBuffer = stream->render.pPin->frameSize;
+ }
+ PA_DEBUG(("Output frames chosen:%ld\n",stream->render.framesPerBuffer));
+
+ /* Setup number of packets to use */
+ stream->render.noOfPackets = 2;
+
+ if (outputParameters->hostApiSpecificStreamInfo)
+ {
+ PaWinWDMKSInfo* pInfo = (PaWinWDMKSInfo*)outputParameters->hostApiSpecificStreamInfo;
+
+ if (stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic &&
+ pInfo->noOfPackets != 0)
+ {
+ stream->render.noOfPackets = pInfo->noOfPackets;
+ }
+ }
+ }
+
+ /* Host buffer size is bound to the largest of the input and output frame sizes */
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
+ stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerUserBuffer,
+ max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer),
+ paUtilBoundedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError )
+ {
+ PaWinWDM_SetLastErrorInfo(result, "PaUtil_InitializeBufferProcessor failed: ich=%u, isf=%u, hisf=%u, och=%u, osf=%u, hosf=%u, sr=%lf, flags=0x%X, fpub=%u, fphb=%u",
+ stream->userInputChannels, inputSampleFormat, hostInputSampleFormat,
+ stream->userOutputChannels, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerUserBuffer,
+ max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer));
+ goto error;
+ }
+
+ /* Allocate/get all the buffers for host I/O */
+ if (stream->userInputChannels > 0)
+ {
+ stream->streamRepresentation.streamInfo.inputLatency = stream->capture.framesPerBuffer / sampleRate;
+
+ switch (stream->capture.pPin->parentFilter->devInfo.streamingType)
+ {
+ case Type_kWaveCyclic:
+ {
+ unsigned size = stream->capture.noOfPackets * stream->capture.framesPerBuffer * stream->capture.bytesPerFrame;
+ /* Allocate input host buffer */
+ stream->capture.hostBuffer = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, size);
+ PA_DEBUG(("Input buffer allocated (size = %u)\n", size));
+ if( !stream->capture.hostBuffer )
+ {
+ PA_DEBUG(("Cannot allocate host input buffer!\n"));
+ PaWinWDM_SetLastErrorInfo(paInsufficientMemory, "Failed to allocate input buffer");
+ result = paInsufficientMemory;
+ goto error;
+ }
+ stream->capture.hostBufferSize = size;
+ PA_DEBUG(("Input buffer start = %p (size=%u)\n",stream->capture.hostBuffer, stream->capture.hostBufferSize));
+ stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveCyclic;
+ stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveCyclic;
+ }
+ break;
+ case Type_kWaveRT:
+ {
+ const DWORD dwTotalSize = 2 * stream->capture.framesPerBuffer * stream->capture.bytesPerFrame;
+ DWORD dwRequestedSize = dwTotalSize;
+ BOOL bCallMemoryBarrier = FALSE;
+ ULONG hwFifoLatency = 0;
+ ULONG dummy;
+ result = PinGetBuffer(stream->capture.pPin, (void**)&stream->capture.hostBuffer, &dwRequestedSize, &bCallMemoryBarrier);
+ if (!result)
+ {
+ PA_DEBUG(("Input buffer start = %p, size = %u\n", stream->capture.hostBuffer, dwRequestedSize));
+ if (dwRequestedSize != dwTotalSize)
+ {
+ PA_DEBUG(("Buffer length changed by driver from %u to %u !\n", dwTotalSize, dwRequestedSize));
+ /* Recalculate to what the driver has given us */
+ stream->capture.framesPerBuffer = dwRequestedSize / (2 * stream->capture.bytesPerFrame);
+ }
+ stream->capture.hostBufferSize = dwRequestedSize;
+
+ if (stream->capture.pPin->pinKsSubType == SubType_kPolled)
+ {
+ stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveRTPolled;
+ stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveRTPolled;
+ }
+ else
+ {
+ stream->capture.pPin->fnEventHandler = PaPinCaptureEventHandler_WaveRTEvent;
+ stream->capture.pPin->fnSubmitHandler = PaPinCaptureSubmitHandler_WaveRTEvent;
+ }
+
+ stream->capture.pPin->fnMemBarrier = bCallMemoryBarrier ? MemoryBarrierRead : MemoryBarrierDummy;
+ }
+ else
+ {
+ PA_DEBUG(("Failed to get input buffer (WaveRT)\n"));
+ PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to get input buffer (WaveRT)");
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+
+ /* Get latency */
+ result = PinGetHwLatency(stream->capture.pPin, &hwFifoLatency, &dummy, &dummy);
+ if (result == paNoError)
+ {
+ stream->capture.pPin->hwLatency = hwFifoLatency;
+
+ /* Add HW latency into total input latency */
+ stream->streamRepresentation.streamInfo.inputLatency += ((hwFifoLatency / stream->capture.bytesPerFrame) / sampleRate);
+ }
+ else
+ {
+ PA_DEBUG(("Failed to get size of FIFO hardware buffer (is set to zero)\n"));
+ stream->capture.pPin->hwLatency = 0;
+ }
+ }
+ break;
+ default:
+ /* Undefined wave type!! */
+ assert(0);
+ result = paInternalError;
+ PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
+ goto error;
+ }
+ }
+ else
+ {
+ stream->capture.hostBuffer = 0;
+ }
+
+ if (stream->userOutputChannels > 0)
+ {
+ stream->streamRepresentation.streamInfo.outputLatency = stream->render.framesPerBuffer / sampleRate;
+
+ switch (stream->render.pPin->parentFilter->devInfo.streamingType)
+ {
+ case Type_kWaveCyclic:
+ {
+ unsigned size = stream->render.noOfPackets * stream->render.framesPerBuffer * stream->render.bytesPerFrame;
+ /* Allocate output device buffer */
+ stream->render.hostBuffer = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, size);
+ PA_DEBUG(("Output buffer allocated (size = %u)\n", size));
+ if( !stream->render.hostBuffer )
+ {
+ PA_DEBUG(("Cannot allocate host output buffer!\n"));
+ PaWinWDM_SetLastErrorInfo(paInsufficientMemory, "Failed to allocate output buffer");
+ result = paInsufficientMemory;
+ goto error;
+ }
+ stream->render.hostBufferSize = size;
+ PA_DEBUG(("Output buffer start = %p (size=%u)\n",stream->render.hostBuffer, stream->render.hostBufferSize));
+
+ stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveCyclic;
+ stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveCyclic;
+ }
+ break;
+ case Type_kWaveRT:
+ {
+ const DWORD dwTotalSize = 2 * stream->render.framesPerBuffer * stream->render.bytesPerFrame;
+ DWORD dwRequestedSize = dwTotalSize;
+ BOOL bCallMemoryBarrier = FALSE;
+ ULONG hwFifoLatency = 0;
+ ULONG dummy;
+ result = PinGetBuffer(stream->render.pPin, (void**)&stream->render.hostBuffer, &dwRequestedSize, &bCallMemoryBarrier);
+ if (!result)
+ {
+ PA_DEBUG(("Output buffer start = %p, size = %u, membarrier = %u\n", stream->render.hostBuffer, dwRequestedSize, bCallMemoryBarrier));
+ if (dwRequestedSize != dwTotalSize)
+ {
+ PA_DEBUG(("Buffer length changed by driver from %u to %u !\n", dwTotalSize, dwRequestedSize));
+ /* Recalculate to what the driver has given us */
+ stream->render.framesPerBuffer = dwRequestedSize / (2 * stream->render.bytesPerFrame);
+ }
+ stream->render.hostBufferSize = dwRequestedSize;
+
+ if (stream->render.pPin->pinKsSubType == SubType_kPolled)
+ {
+ stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveRTPolled;
+ stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveRTPolled;
+ }
+ else
+ {
+ stream->render.pPin->fnEventHandler = PaPinRenderEventHandler_WaveRTEvent;
+ stream->render.pPin->fnSubmitHandler = PaPinRenderSubmitHandler_WaveRTEvent;
+ }
+
+ stream->render.pPin->fnMemBarrier = bCallMemoryBarrier ? MemoryBarrierWrite : MemoryBarrierDummy;
+ }
+ else
+ {
+ PA_DEBUG(("Failed to get output buffer (with notification)\n"));
+ PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to get output buffer (with notification)");
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+
+ /* Get latency */
+ result = PinGetHwLatency(stream->render.pPin, &hwFifoLatency, &dummy, &dummy);
+ if (result == paNoError)
+ {
+ stream->render.pPin->hwLatency = hwFifoLatency;
+
+ /* Add HW latency into total output latency */
+ stream->streamRepresentation.streamInfo.outputLatency += ((hwFifoLatency / stream->render.bytesPerFrame) / sampleRate);
+ }
+ else
+ {
+ PA_DEBUG(("Failed to get size of FIFO hardware buffer (is set to zero)\n"));
+ stream->render.pPin->hwLatency = 0;
+ }
+ }
+ break;
+ default:
+ /* Undefined wave type!! */
+ assert(0);
+ result = paInternalError;
+ PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
+ goto error;
+ }
+ }
+ else
+ {
+ stream->render.hostBuffer = 0;
+ }
+
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+ PA_DEBUG(("BytesPerInputFrame = %d\n",stream->capture.bytesPerFrame));
+ PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->render.bytesPerFrame));
+
+ /* memset(stream->hostBuffer,0,size); */
+
+ /* Abort */
+ stream->eventAbort = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (stream->eventAbort == 0)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ stream->eventStreamStart[0] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (stream->eventStreamStart[0] == 0)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ stream->eventStreamStart[1] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (stream->eventStreamStart[1] == 0)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ if(stream->userInputChannels > 0)
+ {
+ const unsigned bufferSizeInBytes = stream->capture.framesPerBuffer * stream->capture.bytesPerFrame;
+ const unsigned ringBufferFrameSize = NextPowerOf2( 1024 + 2 * max(stream->capture.framesPerBuffer, stream->render.framesPerBuffer) );
+
+ stream->capture.events = (HANDLE*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->capture.noOfPackets * sizeof(HANDLE));
+ if (stream->capture.events == NULL)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ stream->capture.packets = (DATAPACKET*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->capture.noOfPackets * sizeof(DATAPACKET));
+ if (stream->capture.packets == NULL)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ switch(stream->capture.pPin->parentFilter->devInfo.streamingType)
+ {
+ case Type_kWaveCyclic:
+ {
+ /* WaveCyclic case */
+ unsigned i;
+ for (i = 0; i < stream->capture.noOfPackets; ++i)
+ {
+ /* Set up the packets */
+ DATAPACKET *p = stream->capture.packets + i;
+
+ /* Record event */
+ stream->capture.events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ p->Signal.hEvent = stream->capture.events[i];
+ p->Header.Data = stream->capture.hostBuffer + (i*bufferSizeInBytes);
+ p->Header.FrameExtent = bufferSizeInBytes;
+ p->Header.DataUsed = 0;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+ }
+ }
+ break;
+ case Type_kWaveRT:
+ {
+ /* Set up the "packets" */
+ DATAPACKET *p = stream->capture.packets + 0;
+
+ /* Record event: WaveRT has a single event for 2 notification per buffer */
+ stream->capture.events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ p->Header.Data = stream->capture.hostBuffer;
+ p->Header.FrameExtent = bufferSizeInBytes;
+ p->Header.DataUsed = 0;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+
+ ++p;
+ p->Header.Data = stream->capture.hostBuffer + bufferSizeInBytes;
+ p->Header.FrameExtent = bufferSizeInBytes;
+ p->Header.DataUsed = 0;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+
+ if (stream->capture.pPin->pinKsSubType == SubType_kNotification)
+ {
+ result = PinRegisterNotificationHandle(stream->capture.pPin, stream->capture.events[0]);
+
+ if (result != paNoError)
+ {
+ PA_DEBUG(("Failed to register capture notification handle\n"));
+ PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to register capture notification handle");
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+ }
+
+ result = PinRegisterPositionRegister(stream->capture.pPin);
+
+ if (result != paNoError)
+ {
+ unsigned long pos = 0xdeadc0de;
+ PA_DEBUG(("Failed to register capture position register, using PinGetAudioPositionViaIOCTLWrite\n"));
+ stream->capture.pPin->fnAudioPosition = PinGetAudioPositionViaIOCTLWrite;
+ /* Test position function */
+ result = (stream->capture.pPin->fnAudioPosition)(stream->capture.pPin, &pos);
+ if (result != paNoError || pos != 0x0)
+ {
+ PA_DEBUG(("Failed to read capture position register (IOCTL)\n"));
+ PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to read capture position register (IOCTL)");
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+ }
+ else
+ {
+ stream->capture.pPin->fnAudioPosition = PinGetAudioPositionMemoryMapped;
+ }
+ }
+ break;
+ default:
+ /* Undefined wave type!! */
+ assert(0);
+ result = paInternalError;
+ PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
+ goto error;
+ }
+
+ /* Setup the input ring buffer here */
+ stream->ringBufferData = (char*)PaUtil_GroupAllocateMemory(stream->allocGroup, ringBufferFrameSize * stream->capture.bytesPerFrame);
+ if (stream->ringBufferData == NULL)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ PaUtil_InitializeRingBuffer(&stream->ringBuffer, stream->capture.bytesPerFrame, ringBufferFrameSize, stream->ringBufferData);
+ }
+ if(stream->userOutputChannels > 0)
+ {
+ const unsigned bufferSizeInBytes = stream->render.framesPerBuffer * stream->render.bytesPerFrame;
+
+ stream->render.events = (HANDLE*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->render.noOfPackets * sizeof(HANDLE));
+ if (stream->render.events == NULL)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ stream->render.packets = (DATAPACKET*)PaUtil_GroupAllocateMemory(stream->allocGroup, stream->render.noOfPackets * sizeof(DATAPACKET));
+ if (stream->render.packets == NULL)
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ switch(stream->render.pPin->parentFilter->devInfo.streamingType)
+ {
+ case Type_kWaveCyclic:
+ {
+ /* WaveCyclic case */
+ unsigned i;
+ for (i = 0; i < stream->render.noOfPackets; ++i)
+ {
+ /* Set up the packets */
+ DATAPACKET *p = stream->render.packets + i;
+
+ /* Playback event */
+ stream->render.events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ /* In this case, we just use the packets as ptr to the device buffer */
+ p->Signal.hEvent = stream->render.events[i];
+ p->Header.Data = stream->render.hostBuffer + (i*bufferSizeInBytes);
+ p->Header.FrameExtent = bufferSizeInBytes;
+ p->Header.DataUsed = bufferSizeInBytes;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+ }
+ }
+ break;
+ case Type_kWaveRT:
+ {
+ /* WaveRT case */
+
+ /* Set up the "packets" */
+ DATAPACKET *p = stream->render.packets;
+
+ /* The only playback event */
+ stream->render.events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ /* In this case, we just use the packets as ptr to the device buffer */
+ p->Header.Data = stream->render.hostBuffer;
+ p->Header.FrameExtent = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
+ p->Header.DataUsed = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+
+ ++p;
+ p->Header.Data = stream->render.hostBuffer + stream->render.framesPerBuffer*stream->render.bytesPerFrame;
+ p->Header.FrameExtent = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
+ p->Header.DataUsed = stream->render.framesPerBuffer*stream->render.bytesPerFrame;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+
+ if (stream->render.pPin->pinKsSubType == SubType_kNotification)
+ {
+ result = PinRegisterNotificationHandle(stream->render.pPin, stream->render.events[0]);
+
+ if (result != paNoError)
+ {
+ PA_DEBUG(("Failed to register rendering notification handle\n"));
+ PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to register rendering notification handle");
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+ }
+
+ result = PinRegisterPositionRegister(stream->render.pPin);
+
+ if (result != paNoError)
+ {
+ unsigned long pos = 0xdeadc0de;
+ PA_DEBUG(("Failed to register rendering position register, using PinGetAudioPositionViaIOCTLRead\n"));
+ stream->render.pPin->fnAudioPosition = PinGetAudioPositionViaIOCTLRead;
+ /* Test position function */
+ result = (stream->render.pPin->fnAudioPosition)(stream->render.pPin, &pos);
+ if (result != paNoError || pos != 0x0)
+ {
+ PA_DEBUG(("Failed to read render position register (IOCTL)\n"));
+ PaWinWDM_SetLastErrorInfo(paUnanticipatedHostError, "Failed to read render position register (IOCTL)");
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+ }
+ else
+ {
+ stream->render.pPin->fnAudioPosition = PinGetAudioPositionMemoryMapped;
+ }
+ }
+ break;
+ default:
+ /* Undefined wave type!! */
+ assert(0);
+ result = paInternalError;
+ PaWinWDM_SetLastErrorInfo(result, "Wave type %u ??", stream->capture.pPin->parentFilter->devInfo.streamingType);
+ goto error;
+ }
+ }
+
+ stream->streamStarted = 0;
+ stream->streamActive = 0;
+ stream->streamStop = 0;
+ stream->streamAbort = 0;
+ stream->streamFlags = streamFlags;
+ stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+
+ /* Increase ref count on filters in use, so that a CommitDeviceInfos won't delete them */
+ if (stream->capture.pPin != 0)
+ {
+ FilterAddRef(stream->capture.pPin->parentFilter);
+ }
+ if (stream->render.pPin != 0)
+ {
+ FilterAddRef(stream->render.pPin->parentFilter);
+ }
+
+ /* Ok, now update our host API specific stream info */
+ if (stream->userInputChannels)
+ {
+ PaWinWdmDeviceInfo *pDeviceInfo = (PaWinWdmDeviceInfo*)wdmHostApi->inheritedHostApiRep.deviceInfos[inputParameters->device];
+
+ stream->hostApiStreamInfo.input.device = Pa_HostApiDeviceIndexToDeviceIndex(Pa_HostApiTypeIdToHostApiIndex(paWDMKS), inputParameters->device);
+ stream->hostApiStreamInfo.input.channels = stream->deviceInputChannels;
+ stream->hostApiStreamInfo.input.muxNodeId = -1;
+ if (stream->capture.pPin->inputs)
+ {
+ stream->hostApiStreamInfo.input.muxNodeId = stream->capture.pPin->inputs[pDeviceInfo->muxPosition]->muxNodeId;
+ }
+ stream->hostApiStreamInfo.input.endpointPinId = pDeviceInfo->endpointPinId;
+ stream->hostApiStreamInfo.input.framesPerHostBuffer = stream->capture.framesPerBuffer;
+ stream->hostApiStreamInfo.input.streamingSubType = stream->capture.pPin->pinKsSubType;
+ }
+ else
+ {
+ stream->hostApiStreamInfo.input.device = paNoDevice;
+ }
+ if (stream->userOutputChannels)
+ {
+ stream->hostApiStreamInfo.output.device = Pa_HostApiDeviceIndexToDeviceIndex(Pa_HostApiTypeIdToHostApiIndex(paWDMKS), outputParameters->device);
+ stream->hostApiStreamInfo.output.channels = stream->deviceOutputChannels;
+ stream->hostApiStreamInfo.output.framesPerHostBuffer = stream->render.framesPerBuffer;
+ stream->hostApiStreamInfo.output.endpointPinId = stream->render.pPin->endpointPinId;
+ stream->hostApiStreamInfo.output.streamingSubType = stream->render.pPin->pinKsSubType;
+ }
+ else
+ {
+ stream->hostApiStreamInfo.output.device = paNoDevice;
+ }
+ /*stream->streamRepresentation.streamInfo.hostApiTypeId = paWDMKS;
+ stream->streamRepresentation.streamInfo.hostApiSpecificStreamInfo = &stream->hostApiStreamInfo;*/
+ stream->streamRepresentation.streamInfo.structVersion = 2;
+
+ *s = (PaStream*)stream;
+
+ PA_LOGL_;
+ return result;
+
+occupied:
+ /* Ok, someone else is hogging the pin, bail out */
+ assert (result == paDeviceUnavailable);
+ PaWinWDM_SetLastErrorInfo(result, "Device is occupied");
+
+error:
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+
+ CloseStreamEvents(stream);
+
+ if (stream->allocGroup)
+ {
+ PaUtil_FreeAllAllocations(stream->allocGroup);
+ PaUtil_DestroyAllocationGroup(stream->allocGroup);
+ stream->allocGroup = 0;
+ }
+
+ if(stream->render.pPin)
+ PinClose(stream->render.pPin);
+ if(stream->capture.pPin)
+ PinClose(stream->capture.pPin);
+
+ PaUtil_FreeMemory( stream );
+
+ PA_LOGL_;
+ return result;
+}
+
+/*
+When CloseStream() is called, the multi-api layer ensures that
+the stream has already been stopped or aborted.
+*/
+static PaError CloseStream( PaStream* s )
+{
+ PaError result = paNoError;
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+ PA_LOGE_;
+
+ assert(!stream->streamStarted);
+ assert(!stream->streamActive);
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+
+ CloseStreamEvents(stream);
+
+ if (stream->allocGroup)
+ {
+ PaUtil_FreeAllAllocations(stream->allocGroup);
+ PaUtil_DestroyAllocationGroup(stream->allocGroup);
+ stream->allocGroup = 0;
+ }
+
+ if(stream->render.pPin)
+ {
+ PinClose(stream->render.pPin);
+ }
+ if(stream->capture.pPin)
+ {
+ PinClose(stream->capture.pPin);
+ }
+
+ if (stream->render.pPin)
+ {
+ FilterFree(stream->render.pPin->parentFilter);
+ }
+ if (stream->capture.pPin)
+ {
+ FilterFree(stream->capture.pPin->parentFilter);
+ }
+
+ PaUtil_FreeMemory( stream );
+
+ PA_LOGL_;
+ return result;
+}
+
+/*
+Write the supplied packet to the pin
+Asynchronous
+Should return paNoError on success
+*/
+static PaError PinWrite(HANDLE h, DATAPACKET* p)
+{
+ PaError result = paNoError;
+ unsigned long cbReturned = 0;
+ BOOL fRes = DeviceIoControl(h,
+ IOCTL_KS_WRITE_STREAM,
+ NULL,
+ 0,
+ &p->Header,
+ p->Header.Size,
+ &cbReturned,
+ &p->Signal);
+ if (!fRes)
+ {
+ unsigned long error = GetLastError();
+ if (error != ERROR_IO_PENDING)
+ {
+ result = paInternalError;
+ }
+ }
+ return result;
+}
+
+/*
+Read to the supplied packet from the pin
+Asynchronous
+Should return paNoError on success
+*/
+static PaError PinRead(HANDLE h, DATAPACKET* p)
+{
+ PaError result = paNoError;
+ unsigned long cbReturned = 0;
+ BOOL fRes = DeviceIoControl(h,
+ IOCTL_KS_READ_STREAM,
+ NULL,
+ 0,
+ &p->Header,
+ p->Header.Size,
+ &cbReturned,
+ &p->Signal);
+ if (!fRes)
+ {
+ unsigned long error = GetLastError();
+ if (error != ERROR_IO_PENDING)
+ {
+ result = paInternalError;
+ }
+ }
+ return result;
+}
+
+/*
+Copy the first interleaved channel of 16 bit data to the other channels
+*/
+static void DuplicateFirstChannelInt16(void* buffer, int channels, int samples)
+{
+ unsigned short* data = (unsigned short*)buffer;
+ int channel;
+ unsigned short sourceSample;
+ while( samples-- )
+ {
+ sourceSample = *data++;
+ channel = channels-1;
+ while( channel-- )
+ {
+ *data++ = sourceSample;
+ }
+ }
+}
+
+/*
+Copy the first interleaved channel of 24 bit data to the other channels
+*/
+static void DuplicateFirstChannelInt24(void* buffer, int channels, int samples)
+{
+ unsigned char* data = (unsigned char*)buffer;
+ int channel;
+ unsigned char sourceSample[3];
+ while( samples-- )
+ {
+ sourceSample[0] = data[0];
+ sourceSample[1] = data[1];
+ sourceSample[2] = data[2];
+ data += 3;
+ channel = channels-1;
+ while( channel-- )
+ {
+ data[0] = sourceSample[0];
+ data[1] = sourceSample[1];
+ data[2] = sourceSample[2];
+ data += 3;
+ }
+ }
+}
+
+/*
+Copy the first interleaved channel of 32 bit data to the other channels
+*/
+static void DuplicateFirstChannelInt32(void* buffer, int channels, int samples)
+{
+ unsigned long* data = (unsigned long*)buffer;
+ int channel;
+ unsigned long sourceSample;
+ while( samples-- )
+ {
+ sourceSample = *data++;
+ channel = channels-1;
+ while( channel-- )
+ {
+ *data++ = sourceSample;
+ }
+ }
+}
+
+/*
+Increase the priority of the calling thread to RT
+*/
+static HANDLE BumpThreadPriority()
+{
+ HANDLE hThread = GetCurrentThread();
+ DWORD dwTask = 0;
+ HANDLE hAVRT = NULL;
+
+ /* If we have access to AVRT.DLL (Vista and later), use it */
+ if (paWinWDMKSAvRtEntryPoints.AvSetMmThreadCharacteristics != NULL)
+ {
+ hAVRT = paWinWDMKSAvRtEntryPoints.AvSetMmThreadCharacteristics("Pro Audio", &dwTask);
+ if (hAVRT != NULL && hAVRT != INVALID_HANDLE_VALUE)
+ {
+ BOOL bret = paWinWDMKSAvRtEntryPoints.AvSetMmThreadPriority(hAVRT, PA_AVRT_PRIORITY_CRITICAL);
+ if (!bret)
+ {
+ PA_DEBUG(("Set mm thread prio to critical failed!\n"));
+ }
+ else
+ {
+ return hAVRT;
+ }
+ }
+ else
+ {
+ PA_DEBUG(("Set mm thread characteristic to 'Pro Audio' failed, reverting to SetThreadPriority\n"));
+ }
+ }
+
+ /* For XP and earlier, or if AvSetMmThreadCharacteristics fails (MMCSS disabled ?) */
+ if (timeBeginPeriod(1) != TIMERR_NOERROR) {
+ PA_DEBUG(("timeBeginPeriod(1) failed!\n"));
+ }
+
+ if (!SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL)) {
+ PA_DEBUG(("SetThreadPriority failed!\n"));
+ }
+
+ return hAVRT;
+}
+
+/*
+Decrease the priority of the calling thread to normal
+*/
+static void DropThreadPriority(HANDLE hAVRT)
+{
+ HANDLE hThread = GetCurrentThread();
+
+ if (hAVRT != NULL)
+ {
+ paWinWDMKSAvRtEntryPoints.AvSetMmThreadPriority(hAVRT, PA_AVRT_PRIORITY_NORMAL);
+ paWinWDMKSAvRtEntryPoints.AvRevertMmThreadCharacteristics(hAVRT);
+ return;
+ }
+
+ SetThreadPriority(hThread, THREAD_PRIORITY_NORMAL);
+ timeEndPeriod(1);
+}
+
+static PaError PreparePinForStart(PaWinWdmPin* pin)
+{
+ PaError result;
+ result = PinSetState(pin, KSSTATE_ACQUIRE);
+ if (result != paNoError)
+ {
+ goto error;
+ }
+ result = PinSetState(pin, KSSTATE_PAUSE);
+ if (result != paNoError)
+ {
+ goto error;
+ }
+ return result;
+
+error:
+ PinSetState(pin, KSSTATE_STOP);
+ return result;
+}
+
+static PaError PreparePinsForStart(PaProcessThreadInfo* pInfo)
+{
+ PaError result = paNoError;
+ /* Submit buffers */
+ if (pInfo->stream->capture.pPin)
+ {
+ if ((result = PreparePinForStart(pInfo->stream->capture.pPin)) != paNoError)
+ {
+ goto error;
+ }
+
+ if (pInfo->stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
+ {
+ unsigned i;
+ for(i=0; i < pInfo->stream->capture.noOfPackets; ++i)
+ {
+ if ((result = PinRead(pInfo->stream->capture.pPin->handle, pInfo->stream->capture.packets + i)) != paNoError)
+ {
+ goto error;
+ }
+ ++pInfo->pending;
+ }
+ }
+ else
+ {
+ pInfo->pending = 2;
+ }
+ }
+
+ if(pInfo->stream->render.pPin)
+ {
+ if ((result = PreparePinForStart(pInfo->stream->render.pPin)) != paNoError)
+ {
+ goto error;
+ }
+
+ pInfo->priming += pInfo->stream->render.noOfPackets;
+ ++pInfo->pending;
+ SetEvent(pInfo->stream->render.events[0]);
+ if (pInfo->stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
+ {
+ unsigned i;
+ for(i=1; i < pInfo->stream->render.noOfPackets; ++i)
+ {
+ SetEvent(pInfo->stream->render.events[i]);
+ ++pInfo->pending;
+ }
+ }
+ }
+
+error:
+ PA_DEBUG(("PreparePinsForStart = %d\n", result));
+ return result;
+}
+
+static PaError StartPin(PaWinWdmPin* pin)
+{
+ return PinSetState(pin, KSSTATE_RUN);
+}
+
+static PaError StartPins(PaProcessThreadInfo* pInfo)
+{
+ PaError result = paNoError;
+ /* Start the pins as synced as possible */
+ if (pInfo->stream->capture.pPin)
+ {
+ result = StartPin(pInfo->stream->capture.pPin);
+ }
+ if(pInfo->stream->render.pPin)
+ {
+ result = StartPin(pInfo->stream->render.pPin);
+ }
+ PA_DEBUG(("StartPins = %d\n", result));
+ return result;
+}
+
+
+static PaError StopPin(PaWinWdmPin* pin)
+{
+ PinSetState(pin, KSSTATE_PAUSE);
+ PinSetState(pin, KSSTATE_STOP);
+ return paNoError;
+}
+
+
+static PaError StopPins(PaProcessThreadInfo* pInfo)
+{
+ PaError result = paNoError;
+ if(pInfo->stream->render.pPin)
+ {
+ StopPin(pInfo->stream->render.pPin);
+ }
+ if(pInfo->stream->capture.pPin)
+ {
+ StopPin(pInfo->stream->capture.pPin);
+ }
+ return result;
+}
+
+typedef void (*TSetInputFrameCount)(PaUtilBufferProcessor*, unsigned long);
+typedef void (*TSetInputChannel)(PaUtilBufferProcessor*, unsigned int, void *, unsigned int);
+static const TSetInputFrameCount fnSetInputFrameCount[2] = { PaUtil_SetInputFrameCount, PaUtil_Set2ndInputFrameCount };
+static const TSetInputChannel fnSetInputChannel[2] = { PaUtil_SetInputChannel, PaUtil_Set2ndInputChannel };
+
+static PaError PaDoProcessing(PaProcessThreadInfo* pInfo)
+{
+ PaError result = paNoError;
+ int i, framesProcessed = 0, doChannelCopy = 0;
+ ring_buffer_size_t inputFramesAvailable = PaUtil_GetRingBufferReadAvailable(&pInfo->stream->ringBuffer);
+
+ /* Do necessary buffer processing (which will invoke user callback if necessary) */
+ if (pInfo->cbResult == paContinue &&
+ (pInfo->renderHead != pInfo->renderTail || inputFramesAvailable))
+ {
+ unsigned processFullDuplex = pInfo->stream->capture.pPin && pInfo->stream->render.pPin && (!pInfo->priming);
+
+ PA_HP_TRACE((pInfo->stream->hLog, "DoProcessing: InputFrames=%u", inputFramesAvailable));
+
+ PaUtil_BeginCpuLoadMeasurement( &pInfo->stream->cpuLoadMeasurer );
+
+ pInfo->ti.currentTime = PaUtil_GetTime();
+
+ PaUtil_BeginBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->ti, pInfo->underover);
+ pInfo->underover = 0; /* Reset the (under|over)flow status */
+
+ if (pInfo->renderTail != pInfo->renderHead)
+ {
+ DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet;
+
+ assert(packet != 0);
+ assert(packet->Header.Data != 0);
+
+ PaUtil_SetOutputFrameCount(&pInfo->stream->bufferProcessor, pInfo->stream->render.framesPerBuffer);
+
+ for(i=0;i<pInfo->stream->userOutputChannels;i++)
+ {
+ /* Only write the user output channels. Leave the rest blank */
+ PaUtil_SetOutputChannel(&pInfo->stream->bufferProcessor,
+ i,
+ ((unsigned char*)(packet->Header.Data))+(i*pInfo->stream->render.bytesPerSample),
+ pInfo->stream->deviceOutputChannels);
+ }
+
+ /* We will do a copy to the other channels after the data has been written */
+ doChannelCopy = ( pInfo->stream->userOutputChannels == 1 );
+ }
+
+ if (inputFramesAvailable && (!pInfo->stream->userOutputChannels || inputFramesAvailable >= (int)pInfo->stream->render.framesPerBuffer))
+ {
+ unsigned wrapCntr = 0;
+ void* data[2] = {0};
+ ring_buffer_size_t size[2] = {0};
+
+ /* If full-duplex, we just extract output buffer number of frames */
+ if (pInfo->stream->userOutputChannels)
+ {
+ inputFramesAvailable = min(inputFramesAvailable, (int)pInfo->stream->render.framesPerBuffer);
+ }
+
+ inputFramesAvailable = PaUtil_GetRingBufferReadRegions(&pInfo->stream->ringBuffer,
+ inputFramesAvailable,
+ &data[0],
+ &size[0],
+ &data[1],
+ &size[1]);
+
+ for (wrapCntr = 0; wrapCntr < 2; ++wrapCntr)
+ {
+ if (size[wrapCntr] == 0)
+ break;
+
+ fnSetInputFrameCount[wrapCntr](&pInfo->stream->bufferProcessor, size[wrapCntr]);
+ for(i=0;i<pInfo->stream->userInputChannels;i++)
+ {
+ /* Only read as many channels as the user wants */
+ fnSetInputChannel[wrapCntr](&pInfo->stream->bufferProcessor,
+ i,
+ ((unsigned char*)(data[wrapCntr]))+(i*pInfo->stream->capture.bytesPerSample),
+ pInfo->stream->deviceInputChannels);
+ }
+ }
+ }
+ else
+ {
+ /* We haven't consumed anything from the ring buffer... */
+ inputFramesAvailable = 0;
+ /* If we have full-duplex, this is at startup, so mark no-input! */
+ if (pInfo->stream->userOutputChannels>0 && pInfo->stream->userInputChannels>0)
+ {
+ PA_HP_TRACE((pInfo->stream->hLog, "Input startup, marking no input."));
+ PaUtil_SetNoInput(&pInfo->stream->bufferProcessor);
+ }
+ }
+
+ if (processFullDuplex) /* full duplex */
+ {
+ /* Only call the EndBufferProcessing function when the total input frames == total output frames */
+ const unsigned long totalInputFrameCount = pInfo->stream->bufferProcessor.hostInputFrameCount[0] + pInfo->stream->bufferProcessor.hostInputFrameCount[1];
+ const unsigned long totalOutputFrameCount = pInfo->stream->bufferProcessor.hostOutputFrameCount[0] + pInfo->stream->bufferProcessor.hostOutputFrameCount[1];
+
+ if(totalInputFrameCount == totalOutputFrameCount && totalOutputFrameCount != 0)
+ {
+ framesProcessed = PaUtil_EndBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->cbResult);
+ }
+ else
+ {
+ framesProcessed = 0;
+ }
+ }
+ else
+ {
+ framesProcessed = PaUtil_EndBufferProcessing(&pInfo->stream->bufferProcessor, &pInfo->cbResult);
+ }
+
+ PA_HP_TRACE((pInfo->stream->hLog, "Frames processed: %u %s", framesProcessed, (pInfo->priming ? "(priming)":"")));
+
+ if( doChannelCopy )
+ {
+ DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet;
+ /* Copy the first output channel to the other channels */
+ switch (pInfo->stream->render.bytesPerSample)
+ {
+ case 2:
+ DuplicateFirstChannelInt16(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer);
+ break;
+ case 3:
+ DuplicateFirstChannelInt24(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer);
+ break;
+ case 4:
+ DuplicateFirstChannelInt32(packet->Header.Data, pInfo->stream->deviceOutputChannels, pInfo->stream->render.framesPerBuffer);
+ break;
+ default:
+ assert(0); /* Unsupported format! */
+ break;
+ }
+ }
+ PaUtil_EndCpuLoadMeasurement( &pInfo->stream->cpuLoadMeasurer, framesProcessed );
+
+ if (inputFramesAvailable)
+ {
+ PaUtil_AdvanceRingBufferReadIndex(&pInfo->stream->ringBuffer, inputFramesAvailable);
+ }
+
+ if (pInfo->renderTail != pInfo->renderHead)
+ {
+ if (!pInfo->stream->streamStop)
+ {
+ result = pInfo->stream->render.pPin->fnSubmitHandler(pInfo, pInfo->renderTail);
+ if (result != paNoError)
+ {
+ PA_HP_TRACE((pInfo->stream->hLog, "Capture submit handler failed with result %d", result));
+ return result;
+ }
+ }
+ pInfo->renderTail++;
+ if (!pInfo->pinsStarted && pInfo->priming == 0)
+ {
+ /* We start the pins here to allow "prime time" */
+ if ((result = StartPins(pInfo)) == paNoError)
+ {
+ PA_HP_TRACE((pInfo->stream->hLog, "Starting pins!"));
+ pInfo->pinsStarted = 1;
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+static VOID CALLBACK TimerAPCWaveRTPolledMode(
+ LPVOID lpArgToCompletionRoutine,
+ DWORD dwTimerLowValue,
+ DWORD dwTimerHighValue)
+{
+ HANDLE* pHandles = (HANDLE*)lpArgToCompletionRoutine;
+ if (pHandles[0]) SetEvent(pHandles[0]);
+ if (pHandles[1]) SetEvent(pHandles[1]);
+}
+
+static DWORD GetCurrentTimeInMillisecs()
+{
+ return timeGetTime();
+}
+
+PA_THREAD_FUNC ProcessingThread(void* pParam)
+{
+ PaError result = paNoError;
+ HANDLE hAVRT = NULL;
+ HANDLE hTimer = NULL;
+ HANDLE *handleArray = NULL;
+ HANDLE timerEventHandles[2] = {0};
+ unsigned noOfHandles = 0;
+ unsigned captureEvents = 0;
+ unsigned renderEvents = 0;
+ unsigned timerPeriod = 0;
+ DWORD timeStamp[2] = {0};
+
+ PaProcessThreadInfo info;
+ memset(&info, 0, sizeof(PaProcessThreadInfo));
+ info.stream = (PaWinWdmStream*)pParam;
+
+ info.stream->threadResult = paNoError;
+
+ PA_LOGE_;
+
+ info.ti.inputBufferAdcTime = 0.0;
+ info.ti.currentTime = 0.0;
+ info.ti.outputBufferDacTime = 0.0;
+
+ PA_DEBUG(("In buffer len: %.3f ms\n",(2000*info.stream->capture.framesPerBuffer) / info.stream->streamRepresentation.streamInfo.sampleRate));
+ PA_DEBUG(("Out buffer len: %.3f ms\n",(2000*info.stream->render.framesPerBuffer) / info.stream->streamRepresentation.streamInfo.sampleRate));
+ info.timeout = (DWORD)max(
+ (2000*info.stream->render.framesPerBuffer/info.stream->streamRepresentation.streamInfo.sampleRate + 0.5),
+ (2000*info.stream->capture.framesPerBuffer/info.stream->streamRepresentation.streamInfo.sampleRate + 0.5));
+ info.timeout = max(info.timeout*8, 100);
+ timerPeriod = info.timeout;
+ PA_DEBUG(("Timeout = %ld ms\n",info.timeout));
+
+ /* Allocate handle array */
+ handleArray = (HANDLE*)PaUtil_AllocateMemory((info.stream->capture.noOfPackets + info.stream->render.noOfPackets + 1) * sizeof(HANDLE));
+
+ /* Setup handle array for WFMO */
+ if (info.stream->capture.pPin != 0)
+ {
+ handleArray[noOfHandles++] = info.stream->capture.events[0];
+ if (info.stream->capture.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
+ {
+ unsigned i;
+ for(i=1; i < info.stream->capture.noOfPackets; ++i)
+ {
+ handleArray[noOfHandles++] = info.stream->capture.events[i];
+ }
+ }
+ captureEvents = noOfHandles;
+ renderEvents = noOfHandles;
+ }
+
+ if (info.stream->render.pPin != 0)
+ {
+ handleArray[noOfHandles++] = info.stream->render.events[0];
+ if (info.stream->render.pPin->parentFilter->devInfo.streamingType == Type_kWaveCyclic)
+ {
+ unsigned i;
+ for(i=1; i < info.stream->render.noOfPackets; ++i)
+ {
+ handleArray[noOfHandles++] = info.stream->render.events[i];
+ }
+ }
+ renderEvents = noOfHandles;
+ }
+ handleArray[noOfHandles++] = info.stream->eventAbort;
+ assert(noOfHandles <= (info.stream->capture.noOfPackets + info.stream->render.noOfPackets + 1));
+
+ /* Prepare render and capture pins */
+ if ((result = PreparePinsForStart(&info)) != paNoError)
+ {
+ PA_DEBUG(("Failed to prepare device(s)!\n"));
+ goto error;
+ }
+
+ /* Init high speed logger */
+ if (PaUtil_InitializeHighSpeedLog(&info.stream->hLog, 1000000) != paNoError)
+ {
+ PA_DEBUG(("Failed to init high speed logger!\n"));
+ goto error;
+ }
+
+ /* Heighten priority here */
+ hAVRT = BumpThreadPriority();
+
+ /* If input only, we start the pins immediately */
+ if (info.stream->render.pPin == 0)
+ {
+ if ((result = StartPins(&info)) != paNoError)
+ {
+ PA_DEBUG(("Failed to start device(s)!\n"));
+ goto error;
+ }
+ info.pinsStarted = 1;
+ }
+
+ /* Handle WaveRT polled mode */
+ {
+ const unsigned fs = (unsigned)info.stream->streamRepresentation.streamInfo.sampleRate;
+ if (info.stream->capture.pPin != 0 && info.stream->capture.pPin->pinKsSubType == SubType_kPolled)
+ {
+ timerEventHandles[0] = info.stream->capture.events[0];
+ timerPeriod = min(timerPeriod, (1000*info.stream->capture.framesPerBuffer)/fs);
+ }
+
+ if (info.stream->render.pPin != 0 && info.stream->render.pPin->pinKsSubType == SubType_kPolled)
+ {
+ timerEventHandles[1] = info.stream->render.events[0];
+ timerPeriod = min(timerPeriod, (1000*info.stream->render.framesPerBuffer)/fs);
+ }
+
+ if (timerEventHandles[0] || timerEventHandles[1])
+ {
+ LARGE_INTEGER dueTime = {0};
+
+ timerPeriod=max(timerPeriod/5,1);
+ PA_DEBUG(("Timer event handles=0x%04X,0x%04X period=%u ms", timerEventHandles[0], timerEventHandles[1], timerPeriod));
+ hTimer = CreateWaitableTimer(0, FALSE, NULL);
+ if (hTimer == NULL)
+ {
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+ /* invoke first timeout immediately */
+ if (!SetWaitableTimer(hTimer, &dueTime, timerPeriod, TimerAPCWaveRTPolledMode, timerEventHandles, FALSE))
+ {
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+ PA_DEBUG(("Waitable timer started, period = %u ms\n", timerPeriod));
+ }
+ }
+
+ /* Mark stream as active */
+ info.stream->streamActive = 1;
+ info.stream->threadResult = paNoError;
+
+ /* Up and running... */
+ SetEvent(info.stream->eventStreamStart[StreamStart_kOk]);
+
+ /* Take timestamp here */
+ timeStamp[0] = timeStamp[1] = GetCurrentTimeInMillisecs();
+
+ while(!info.stream->streamAbort)
+ {
+ unsigned doProcessing = 1;
+ unsigned wait = WaitForMultipleObjects(noOfHandles, handleArray, FALSE, 0);
+ unsigned eventSignalled = wait - WAIT_OBJECT_0;
+ DWORD dwCurrentTime = 0;
+
+ if (wait == WAIT_FAILED)
+ {
+ PA_DEBUG(("Wait failed = %ld! \n",wait));
+ break;
+ }
+ if (wait == WAIT_TIMEOUT)
+ {
+ wait = WaitForMultipleObjectsEx(noOfHandles, handleArray, FALSE, 50, TRUE);
+ eventSignalled = wait - WAIT_OBJECT_0;
+ }
+ else
+ {
+ if (eventSignalled < captureEvents)
+ {
+ if (PaUtil_GetRingBufferWriteAvailable(&info.stream->ringBuffer) == 0)
+ {
+ PA_HP_TRACE((info.stream->hLog, "!!!!! Input overflow !!!!!"));
+ info.underover |= paInputOverflow;
+ }
+ }
+ else if (eventSignalled < renderEvents)
+ {
+ if (!info.priming && info.renderHead - info.renderTail > 1)
+ {
+ PA_HP_TRACE((info.stream->hLog, "!!!!! Output underflow !!!!!"));
+ info.underover |= paOutputUnderflow;
+ }
+ }
+ }
+
+ /* Get event time */
+ dwCurrentTime = GetCurrentTimeInMillisecs();
+
+ /* Since we can mix capture/render devices between WaveCyclic, WaveRT polled and WaveRT notification (3x3 combinations),
+ we can't rely on the timeout of WFMO to check for device timeouts, we need to keep tally. */
+ if (info.stream->capture.pPin && (dwCurrentTime - timeStamp[0]) >= info.timeout)
+ {
+ PA_DEBUG(("Timeout for capture device (%u ms)!", info.timeout, (dwCurrentTime - timeStamp[0])));
+ result = paTimedOut;
+ break;
+ }
+ if (info.stream->render.pPin && (dwCurrentTime - timeStamp[1]) >= info.timeout)
+ {
+ PA_DEBUG(("Timeout for render device (%u ms)!", info.timeout, (dwCurrentTime - timeStamp[1])));
+ result = paTimedOut;
+ break;
+ }
+
+ if (wait == WAIT_IO_COMPLETION)
+ {
+ /* Waitable timer has fired! */
+ PA_HP_TRACE((info.stream->hLog, "WAIT_IO_COMPLETION"));
+ continue;
+ }
+
+ if (wait == WAIT_TIMEOUT)
+ {
+ continue;
+ }
+ else
+ {
+ if (eventSignalled < captureEvents)
+ {
+ if (info.stream->capture.pPin->fnEventHandler(&info, eventSignalled) == paNoError)
+ {
+ timeStamp[0] = dwCurrentTime;
+
+ /* Since we use the ring buffer, we can submit the buffers directly */
+ if (!info.stream->streamStop)
+ {
+ result = info.stream->capture.pPin->fnSubmitHandler(&info, info.captureTail);
+ if (result != paNoError)
+ {
+ PA_HP_TRACE((info.stream->hLog, "Capture submit handler failed with result %d", result));
+ break;
+ }
+ }
+ ++info.captureTail;
+ /* If full-duplex, let _only_ render event trigger processing. We still need the stream stop
+ handling working, so let that be processed anyways... */
+ if (info.stream->userOutputChannels > 0)
+ {
+ doProcessing = 0;
+ }
+ }
+ }
+ else if (eventSignalled < renderEvents)
+ {
+ timeStamp[1] = dwCurrentTime;
+ eventSignalled -= captureEvents;
+ info.stream->render.pPin->fnEventHandler(&info, eventSignalled);
+ }
+ else
+ {
+ assert(info.stream->streamAbort);
+ PA_HP_TRACE((info.stream->hLog, "Stream abort!"));
+ continue;
+ }
+ }
+
+ /* Handle processing */
+ if (doProcessing)
+ {
+ result = PaDoProcessing(&info);
+ if (result != paNoError)
+ {
+ PA_HP_TRACE((info.stream->hLog, "PaDoProcessing failed!"));
+ break;
+ }
+ }
+
+ if(info.stream->streamStop && info.cbResult != paComplete)
+ {
+ PA_HP_TRACE((info.stream->hLog, "Stream stop! pending=%d",info.pending));
+ info.cbResult = paComplete; /* Stop, but play remaining buffers */
+ }
+
+ if(info.pending<=0)
+ {
+ PA_HP_TRACE((info.stream->hLog, "pending==0 finished..."));
+ break;
+ }
+ if((!info.stream->render.pPin)&&(info.cbResult!=paContinue))
+ {
+ PA_HP_TRACE((info.stream->hLog, "record only cbResult=%d...",info.cbResult));
+ break;
+ }
+ }
+
+ PA_DEBUG(("Finished processing loop\n"));
+
+ info.stream->threadResult = result;
+ goto bailout;
+
+error:
+ PA_DEBUG(("Error starting processing thread\n"));
+ /* Set the "error" event together with result */
+ info.stream->threadResult = result;
+ SetEvent(info.stream->eventStreamStart[StreamStart_kFailed]);
+
+bailout:
+ if (hTimer)
+ {
+ PA_DEBUG(("Waitable timer stopped\n", timerPeriod));
+ CancelWaitableTimer(hTimer);
+ CloseHandle(hTimer);
+ hTimer = 0;
+ }
+
+ if (info.pinsStarted)
+ {
+ StopPins(&info);
+ }
+
+ /* Lower prio here */
+ DropThreadPriority(hAVRT);
+
+ if (handleArray != NULL)
+ {
+ PaUtil_FreeMemory(handleArray);
+ }
+
+#if PA_TRACE_REALTIME_EVENTS
+ if (info.stream->hLog)
+ {
+ PA_DEBUG(("Dumping highspeed trace...\n"));
+ PaUtil_DumpHighSpeedLog(info.stream->hLog, "hp_trace.log");
+ PaUtil_DiscardHighSpeedLog(info.stream->hLog);
+ info.stream->hLog = 0;
+ }
+#endif
+ info.stream->streamActive = 0;
+
+ if((!info.stream->streamStop)&&(!info.stream->streamAbort))
+ {
+ /* Invoke the user stream finished callback */
+ /* Only do it from here if not being stopped/aborted by user */
+ if( info.stream->streamRepresentation.streamFinishedCallback != 0 )
+ info.stream->streamRepresentation.streamFinishedCallback( info.stream->streamRepresentation.userData );
+ }
+ info.stream->streamStop = 0;
+ info.stream->streamAbort = 0;
+
+ PA_LOGL_;
+ return 0;
+}
+
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+ PA_LOGE_;
+
+ if (stream->streamThread != NULL)
+ {
+ return paStreamIsNotStopped;
+ }
+
+ stream->streamStop = 0;
+ stream->streamAbort = 0;
+
+ ResetStreamEvents(stream);
+
+ PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
+
+ stream->oldProcessPriority = GetPriorityClass(GetCurrentProcess());
+ /* Uncomment the following line to enable dynamic boosting of the process
+ * priority to real time for best low latency support
+ * Disabled by default because RT processes can easily block the OS */
+ /*ret = SetPriorityClass(GetCurrentProcess(),REALTIME_PRIORITY_CLASS);
+ PA_DEBUG(("Class ret = %d;",ret));*/
+
+ stream->streamThread = CREATE_THREAD_FUNCTION (NULL, 0, ProcessingThread, stream, CREATE_SUSPENDED, NULL);
+ if(stream->streamThread == NULL)
+ {
+ result = paInsufficientMemory;
+ goto end;
+ }
+ ResumeThread(stream->streamThread);
+
+ switch (WaitForMultipleObjects(2, stream->eventStreamStart, FALSE, 5000))
+ {
+ case WAIT_OBJECT_0 + StreamStart_kOk:
+ PA_DEBUG(("Processing thread started!\n"));
+ result = paNoError;
+ /* streamActive is set in processing thread */
+ stream->streamStarted = 1;
+ break;
+ case WAIT_OBJECT_0 + StreamStart_kFailed:
+ PA_DEBUG(("Processing thread start failed! (result=%d)\n", stream->threadResult));
+ result = stream->threadResult;
+ /* Wait for the stream to really exit */
+ WaitForSingleObject(stream->streamThread, 200);
+ CloseHandle(stream->streamThread);
+ stream->streamThread = 0;
+ break;
+ case WAIT_TIMEOUT:
+ default:
+ result = paTimedOut;
+ PaWinWDM_SetLastErrorInfo(result, "Failed to start processing thread (timeout)!");
+ break;
+ }
+
+end:
+ PA_LOGL_;
+ return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+ BOOL doCb = FALSE;
+
+ PA_LOGE_;
+
+ if(stream->streamActive)
+ {
+ DWORD dwExitCode;
+ doCb = TRUE;
+ stream->streamStop = 1;
+ if (GetExitCodeThread(stream->streamThread, &dwExitCode) && dwExitCode == STILL_ACTIVE)
+ {
+ if (WaitForSingleObject(stream->streamThread, INFINITE) != WAIT_OBJECT_0)
+ {
+ PA_DEBUG(("StopStream: stream thread terminated\n"));
+ TerminateThread(stream->streamThread, -1);
+ result = paTimedOut;
+ }
+ }
+ else
+ {
+ PA_DEBUG(("StopStream: GECT says not active, but streamActive is not false ??"));
+ result = paUnanticipatedHostError;
+ PaWinWDM_SetLastErrorInfo(result, "StopStream: GECT says not active, but streamActive = %d", stream->streamActive);
+ }
+ }
+ else
+ {
+ if (stream->threadResult != paNoError)
+ {
+ PA_DEBUG(("StopStream: Stream not active (%d)\n", stream->threadResult));
+ result = stream->threadResult;
+ stream->threadResult = paNoError;
+ }
+ }
+
+ if (stream->streamThread != NULL)
+ {
+ CloseHandle(stream->streamThread);
+ stream->streamThread = 0;
+ }
+ stream->streamStarted = 0;
+ stream->streamActive = 0;
+
+ if(doCb)
+ {
+ /* Do user callback now after all state has been reset */
+ /* This means it should be safe for the called function */
+ /* to invoke e.g. StartStream */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+
+ PA_LOGL_;
+ return result;
+}
+
+static PaError AbortStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+ int doCb = 0;
+
+ PA_LOGE_;
+
+ if(stream->streamActive)
+ {
+ doCb = 1;
+ stream->streamAbort = 1;
+ SetEvent(stream->eventAbort); /* Signal immediately */
+ if (WaitForSingleObject(stream->streamThread, 10000) != WAIT_OBJECT_0)
+ {
+ TerminateThread(stream->streamThread, -1);
+ result = paTimedOut;
+
+ PA_DEBUG(("AbortStream: stream thread terminated\n"));
+ }
+ assert(!stream->streamActive);
+ }
+ CloseHandle(stream->streamThread);
+ stream->streamThread = NULL;
+ stream->streamStarted = 0;
+
+ if(doCb)
+ {
+ /* Do user callback now after all state has been reset */
+ /* This means it should be safe for the called function */
+ /* to invoke e.g. StartStream */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+
+ stream->streamActive = 0;
+ stream->streamStarted = 0;
+
+ PA_LOGL_;
+ return result;
+}
+
+
+static PaError IsStreamStopped( PaStream *s )
+{
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+ int result = 0;
+
+ PA_LOGE_;
+
+ if(!stream->streamStarted)
+ result = 1;
+
+ PA_LOGL_;
+ return result;
+}
+
+
+static PaError IsStreamActive( PaStream *s )
+{
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+ int result = 0;
+
+ PA_LOGE_;
+
+ if(stream->streamActive)
+ result = 1;
+
+ PA_LOGL_;
+ return result;
+}
+
+
+static PaTime GetStreamTime( PaStream* s )
+{
+ PA_LOGE_;
+ PA_LOGL_;
+ (void)s;
+ return PaUtil_GetTime();
+}
+
+
+static double GetStreamCpuLoad( PaStream* s )
+{
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+ double result;
+ PA_LOGE_;
+ result = PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
+ PA_LOGL_;
+ return result;
+}
+
+
+/*
+As separate stream interfaces are used for blocking and callback
+streams, the following functions can be guaranteed to only be called
+for blocking streams.
+*/
+
+static PaError ReadStream( PaStream* s,
+ void *buffer,
+ unsigned long frames )
+{
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+ PA_LOGE_;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+ PA_LOGL_;
+ return paInternalError;
+}
+
+
+static PaError WriteStream( PaStream* s,
+ const void *buffer,
+ unsigned long frames )
+{
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+ PA_LOGE_;
+
+ /* suppress unused variable warnings */
+ (void) buffer;
+ (void) frames;
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+ PA_LOGL_;
+ return paInternalError;
+}
+
+
+static signed long GetStreamReadAvailable( PaStream* s )
+{
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+ PA_LOGE_;
+
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+ PA_LOGL_;
+ return 0;
+}
+
+
+static signed long GetStreamWriteAvailable( PaStream* s )
+{
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+
+ PA_LOGE_;
+ /* suppress unused variable warnings */
+ (void) stream;
+
+ /* IMPLEMENT ME, see portaudio.h for required behavior*/
+ PA_LOGL_;
+ return 0;
+}
+
+/***************************************************************************************/
+/* Event and submit handlers for WaveCyclic */
+/***************************************************************************************/
+
+static PaError PaPinCaptureEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ PaError result = paNoError;
+ ring_buffer_size_t frameCount;
+ DATAPACKET* packet = pInfo->stream->capture.packets + eventIndex;
+
+ assert( eventIndex < pInfo->stream->capture.noOfPackets );
+
+ if (packet->Header.DataUsed == 0)
+ {
+ PA_HP_TRACE((pInfo->stream->hLog, ">>> Capture bogus event (no data): idx=%u", eventIndex));
+
+ /* Bogus event, reset! This is to handle the behavior of this USB mic: http://shop.xtz.se/measurement-system/microphone-to-dirac-live-room-correction-suite
+ on startup of streaming, where it erroneously sets the event without the corresponding buffer being filled (DataUsed == 0) */
+ ResetEvent(packet->Signal.hEvent);
+
+ result = -1; /* Only need this to be NOT paNoError */
+ }
+ else
+ {
+ pInfo->capturePackets[pInfo->captureHead & cPacketsArrayMask].packet = packet;
+
+ frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer, packet->Header.Data, pInfo->stream->capture.framesPerBuffer);
+
+ PA_HP_TRACE((pInfo->stream->hLog, ">>> Capture event: idx=%u (frames=%u)", eventIndex, frameCount));
+ ++pInfo->captureHead;
+ }
+
+ --pInfo->pending; /* This needs to be done in either case */
+ return result;
+}
+
+static PaError PaPinCaptureSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ PaError result = paNoError;
+ DATAPACKET* packet = pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet;
+ pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0;
+ assert(packet != 0);
+ PA_HP_TRACE((pInfo->stream->hLog, "Capture submit: %u", eventIndex));
+ packet->Header.DataUsed = 0; /* Reset for reuse */
+ packet->Header.OptionsFlags = 0; /* Reset for reuse. Required for e.g. Focusrite Scarlett 2i4 (1st Gen) see #310 */
+ ResetEvent(packet->Signal.hEvent);
+ result = PinRead(pInfo->stream->capture.pPin->handle, packet);
+ ++pInfo->pending;
+ return result;
+}
+
+static PaError PaPinRenderEventHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ assert( eventIndex < pInfo->stream->render.noOfPackets );
+
+ pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask].packet = pInfo->stream->render.packets + eventIndex;
+ PA_HP_TRACE((pInfo->stream->hLog, "<<< Render event : idx=%u head=%u", eventIndex, pInfo->renderHead));
+ ++pInfo->renderHead;
+ --pInfo->pending;
+ return paNoError;
+}
+
+static PaError PaPinRenderSubmitHandler_WaveCyclic(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ PaError result = paNoError;
+ DATAPACKET* packet = pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet;
+ pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0;
+ assert(packet != 0);
+
+ PA_HP_TRACE((pInfo->stream->hLog, "Render submit : %u idx=%u", pInfo->renderTail, (unsigned)(packet - pInfo->stream->render.packets)));
+ ResetEvent(packet->Signal.hEvent);
+ result = PinWrite(pInfo->stream->render.pPin->handle, packet);
+ /* Reset event, just in case we have an analogous situation to capture (see PaPinCaptureSubmitHandler_WaveCyclic) */
+ ++pInfo->pending;
+ if (pInfo->priming)
+ {
+ --pInfo->priming;
+ }
+ return result;
+}
+
+/***************************************************************************************/
+/* Event and submit handlers for WaveRT */
+/***************************************************************************************/
+
+static PaError PaPinCaptureEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ unsigned long pos;
+ unsigned realInBuf;
+ unsigned frameCount;
+ PaWinWdmIOInfo* pCapture = &pInfo->stream->capture;
+ const unsigned halfInputBuffer = pCapture->hostBufferSize >> 1;
+ PaWinWdmPin* pin = pCapture->pPin;
+ DATAPACKET* packet = 0;
+
+ /* Get hold of current ADC position */
+ pin->fnAudioPosition(pin, &pos);
+ /* Wrap it (robi: why not use hw latency compensation here ?? because pos then gets _way_ off from
+ where it should be, i.e. at beginning or half buffer position. Why? No idea.) */
+
+ pos %= pCapture->hostBufferSize;
+ /* Then realInBuf will point to "other" half of double buffer */
+ realInBuf = pos < halfInputBuffer ? 1U : 0U;
+
+ packet = pInfo->stream->capture.packets + realInBuf;
+
+ /* Call barrier (or dummy) */
+ pin->fnMemBarrier();
+
+ /* Put it in queue */
+ frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer, packet->Header.Data, pCapture->framesPerBuffer);
+
+ pInfo->capturePackets[pInfo->captureHead & cPacketsArrayMask].packet = packet;
+
+ PA_HP_TRACE((pInfo->stream->hLog, "Capture event (WaveRT): idx=%u head=%u (pos = %4.1lf%%, frames=%u)", realInBuf, pInfo->captureHead, (pos * 100.0 / pCapture->hostBufferSize), frameCount));
+
+ ++pInfo->captureHead;
+ --pInfo->pending;
+
+ return paNoError;
+}
+
+static PaError PaPinCaptureEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ unsigned long pos;
+ unsigned bytesToRead;
+ PaWinWdmIOInfo* pCapture = &pInfo->stream->capture;
+ const unsigned halfInputBuffer = pCapture->hostBufferSize>>1;
+ PaWinWdmPin* pin = pInfo->stream->capture.pPin;
+
+ /* Get hold of current ADC position */
+ pin->fnAudioPosition(pin, &pos);
+ /* Wrap it (robi: why not use hw latency compensation here ?? because pos then gets _way_ off from
+ where it should be, i.e. at beginning or half buffer position. Why? No idea.) */
+ /* Compensate for HW FIFO to get to last read buffer position */
+ pos += pin->hwLatency;
+ pos %= pCapture->hostBufferSize;
+ /* Need to align position on frame boundary */
+ pos &= ~(pCapture->bytesPerFrame - 1);
+
+ /* Call barrier (or dummy) */
+ pin->fnMemBarrier();
+
+ /* Put it in "queue" */
+ bytesToRead = (pCapture->hostBufferSize + pos - pCapture->lastPosition) % pCapture->hostBufferSize;
+ if (bytesToRead > 0)
+ {
+ unsigned frameCount = PaUtil_WriteRingBuffer(&pInfo->stream->ringBuffer,
+ pCapture->hostBuffer + pCapture->lastPosition,
+ bytesToRead / pCapture->bytesPerFrame);
+
+ pCapture->lastPosition = (pCapture->lastPosition + frameCount * pCapture->bytesPerFrame) % pCapture->hostBufferSize;
+
+ PA_HP_TRACE((pInfo->stream->hLog, "Capture event (WaveRTPolled): pos = %4.1lf%%, framesRead=%u", (pos * 100.0 / pCapture->hostBufferSize), frameCount));
+ ++pInfo->captureHead;
+ --pInfo->pending;
+ }
+ return paNoError;
+}
+
+static PaError PaPinCaptureSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0;
+ ++pInfo->pending;
+ return paNoError;
+}
+
+static PaError PaPinCaptureSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ pInfo->capturePackets[pInfo->captureTail & cPacketsArrayMask].packet = 0;
+ ++pInfo->pending;
+ return paNoError;
+}
+
+static PaError PaPinRenderEventHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ unsigned long pos;
+ unsigned realOutBuf;
+ PaWinWdmIOInfo* pRender = &pInfo->stream->render;
+ const unsigned halfOutputBuffer = pRender->hostBufferSize >> 1;
+ PaWinWdmPin* pin = pInfo->stream->render.pPin;
+ PaIOPacket* ioPacket = &pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask];
+
+ /* Get hold of current DAC position */
+ pin->fnAudioPosition(pin, &pos);
+ /* Compensate for HW FIFO to get to last read buffer position */
+ pos += pin->hwLatency;
+ /* Wrap it */
+ pos %= pRender->hostBufferSize;
+ /* And align it, not sure its really needed though */
+ pos &= ~(pRender->bytesPerFrame - 1);
+ /* Then realOutBuf will point to "other" half of double buffer */
+ realOutBuf = pos < halfOutputBuffer ? 1U : 0U;
+
+ if (pInfo->priming)
+ {
+ realOutBuf = pInfo->renderHead & 0x1;
+ }
+ ioPacket->packet = pInfo->stream->render.packets + realOutBuf;
+ ioPacket->startByte = realOutBuf * halfOutputBuffer;
+ ioPacket->lengthBytes = halfOutputBuffer;
+
+ PA_HP_TRACE((pInfo->stream->hLog, "Render event (WaveRT) : idx=%u head=%u (pos = %4.1lf%%)", realOutBuf, pInfo->renderHead, (pos * 100.0 / pRender->hostBufferSize) ));
+
+ ++pInfo->renderHead;
+ --pInfo->pending;
+ return paNoError;
+}
+
+static PaError PaPinRenderEventHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ unsigned long pos;
+ unsigned realOutBuf;
+ unsigned bytesToWrite;
+
+ PaWinWdmIOInfo* pRender = &pInfo->stream->render;
+ const unsigned halfOutputBuffer = pRender->hostBufferSize >> 1;
+ PaWinWdmPin* pin = pInfo->stream->render.pPin;
+ PaIOPacket* ioPacket = &pInfo->renderPackets[pInfo->renderHead & cPacketsArrayMask];
+
+ /* Get hold of current DAC position */
+ pin->fnAudioPosition(pin, &pos);
+ /* Compensate for HW FIFO to get to last read buffer position */
+ pos += pin->hwLatency;
+ /* Wrap it */
+ pos %= pRender->hostBufferSize;
+ /* And align it, not sure its really needed though */
+ pos &= ~(pRender->bytesPerFrame - 1);
+
+ if (pInfo->priming)
+ {
+ realOutBuf = pInfo->renderHead & 0x1;
+ ioPacket->packet = pInfo->stream->render.packets + realOutBuf;
+ ioPacket->startByte = realOutBuf * halfOutputBuffer;
+ ioPacket->lengthBytes = halfOutputBuffer;
+ ++pInfo->renderHead;
+ --pInfo->pending;
+ }
+ else
+ {
+ bytesToWrite = (pRender->hostBufferSize + pos - pRender->lastPosition) % pRender->hostBufferSize;
+ ++pRender->pollCntr;
+ if (bytesToWrite >= halfOutputBuffer)
+ {
+ realOutBuf = (pos < halfOutputBuffer) ? 1U : 0U;
+ ioPacket->packet = pInfo->stream->render.packets + realOutBuf;
+ pRender->lastPosition = realOutBuf ? 0U : halfOutputBuffer;
+ ioPacket->startByte = realOutBuf * halfOutputBuffer;
+ ioPacket->lengthBytes = halfOutputBuffer;
+ ++pInfo->renderHead;
+ --pInfo->pending;
+ PA_HP_TRACE((pInfo->stream->hLog, "Render event (WaveRTPolled) : idx=%u head=%u (pos = %4.1lf%%, cnt=%u)", realOutBuf, pInfo->renderHead, (pos * 100.0 / pRender->hostBufferSize), pRender->pollCntr));
+ pRender->pollCntr = 0;
+ }
+ }
+ return paNoError;
+}
+
+static PaError PaPinRenderSubmitHandler_WaveRTEvent(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ PaWinWdmPin* pin = pInfo->stream->render.pPin;
+ pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0;
+ /* Call barrier (if needed) */
+ pin->fnMemBarrier();
+ PA_HP_TRACE((pInfo->stream->hLog, "Render submit (WaveRT) : submit=%u", pInfo->renderTail));
+ ++pInfo->pending;
+ if (pInfo->priming)
+ {
+ --pInfo->priming;
+ if (pInfo->priming)
+ {
+ PA_HP_TRACE((pInfo->stream->hLog, "Setting WaveRT event for priming (2)"));
+ SetEvent(pInfo->stream->render.events[0]);
+ }
+ }
+ return paNoError;
+}
+
+static PaError PaPinRenderSubmitHandler_WaveRTPolled(PaProcessThreadInfo* pInfo, unsigned eventIndex)
+{
+ PaWinWdmPin* pin = pInfo->stream->render.pPin;
+ pInfo->renderPackets[pInfo->renderTail & cPacketsArrayMask].packet = 0;
+ /* Call barrier (if needed) */
+ pin->fnMemBarrier();
+ PA_HP_TRACE((pInfo->stream->hLog, "Render submit (WaveRTPolled) : submit=%u", pInfo->renderTail));
+ ++pInfo->pending;
+ if (pInfo->priming)
+ {
+ --pInfo->priming;
+ if (pInfo->priming)
+ {
+ PA_HP_TRACE((pInfo->stream->hLog, "Setting WaveRT event for priming (2)"));
+ SetEvent(pInfo->stream->render.events[0]);
+ }
+ }
+ return paNoError;
+}
diff --git a/3rdparty/portaudio/src/hostapi/wdmks/readme.txt b/3rdparty/portaudio/src/hostapi/wdmks/readme.txt
new file mode 100644
index 0000000..611acc7
--- /dev/null
+++ b/3rdparty/portaudio/src/hostapi/wdmks/readme.txt
@@ -0,0 +1,85 @@
+Notes about WDM-KS host API
+---------------------------
+
+Status history
+--------------
+16th January 2011:
+Added support for WaveRT device API (Vista and later) for even lesser
+latency support.
+
+10th November 2005:
+Made following changes:
+ * OpenStream: Try all PaSampleFormats internally if the the chosen
+ format is not supported natively. This fixed several problems
+ with soundcards that did not take kindly to using 24-bit 3-byte formats.
+ * OpenStream: Make the minimum framesPerHostIBuffer (and framesPerHostOBuffer)
+ the default frameSize for the playback/recording pin.
+ * ProcessingThread: Added a switch to only call PaUtil_EndBufferProcessing
+ if the total input frames equals the total output frames
+
+5th September 2004:
+This is the first public version of the code. It should be considered
+an alpha release with zero guarantee not to crash on any particular
+system. So far it has only been tested in the author's development
+environment, which means a Win2k/SP2 PIII laptop with integrated
+SoundMAX driver and USB Tascam US-428 compiled with both MinGW
+(GCC 3.3) and MSVC++6 using the MS DirectX 9 SDK.
+It has been most widely tested with the MinGW build, with most of the
+test programs (particularly paqa_devs and paqa_errs) passing.
+There are some notable failures: patest_out_underflow and both of the
+blocking I/O tests (as blocking I/O is not implemented).
+At this point the code needs to be tested with a much wider variety
+of configurations and feedback provided from testers regarding
+both working and failing cases.
+
+What is the WDM-KS host API?
+----------------------------
+PortAudio for Windows currently has 3 functional host implementations.
+MME uses the oldest Windows audio API which does not offer good
+play/record latency.
+DirectX improves this, but still imposes a penalty
+of 10s of milliseconds due to the system mixing of streams from
+multiple applications.
+ASIO offers very good latency, but requires special drivers which are
+not always available for cheaper audio hardware. Also, when ASIO
+drivers are available, they are not always so robust because they
+bypass all of the standardised Windows device driver architecture
+and hit the hardware their own way.
+Alternatively there are a couple of free (but closed source) ASIO
+implementations which connect to the lower level Windows
+"Kernel Streaming" API, but again these require special installation
+by the user, and can be limited in functionality or difficult to use.
+
+This is where the PortAudio "WDM-KS" host implementation comes in.
+It directly connects PortAudio to the same Kernel Streaming API which
+those ASIO bridges use. This avoids the mixing penalty of DirectX,
+giving at least as good latency as any ASIO driver, but it has the
+advantage of working with ANY Windows audio hardware which is available
+through the normal MME/DirectX routes without the user requiring
+any additional device drivers to be installed, and allowing all
+device selection to be done through the normal PortAudio API.
+
+Note that in general you should only be using this host API if your
+application has a real requirement for very low latency audio (<20ms),
+either because you are generating sounds in real-time based upon
+user input, or you a processing recorded audio in real time.
+
+The only thing to be aware of is that using the KS interface will
+block that device from being used by the rest of system through
+the higher level APIs, or conversely, if the system is using
+a device, the KS API will not be able to use it. MS recommend that
+you should keep the device open only when your application has focus.
+In PortAudio terms, this means having a stream Open on a WDMKS device.
+
+Usage
+-----
+To add the WDMKS backend to your program which is already using
+PortAudio, you must define PA_USE_WDMKS=1 in your build file,
+and include the pa_win_wdmks\pa_win_wdmks.c into your build.
+The file should compile in both C and C++.
+You will need a DirectX SDK installed on your system for the
+ks.h and ksmedia.h header files.
+You will need to link to the system "setupapi" library.
+Note that if you use MinGW, you will get more warnings from
+the DX header files when using GCC(C), and still a few warnings
+with G++(CPP).