aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pd/portaudio/pa_win_wdmks/pa_win_wdmks.c3179
-rw-r--r--pd/portaudio/pa_win_wdmks/readme.txt71
2 files changed, 3250 insertions, 0 deletions
diff --git a/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c b/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c
new file mode 100644
index 00000000..97d1c1f6
--- /dev/null
+++ b/pd/portaudio/pa_win_wdmks/pa_win_wdmks.c
@@ -0,0 +1,3179 @@
+/*
+ * $Id: pa_win_wdmks.c,v 1.1 2005-12-31 01:46:24 millerpuckette Exp $
+ * PortAudio Windows WDM-KS interface
+ *
+ * Author: Andrew Baldwin
+ * 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.
+ *
+ * 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.
+ *
+ * 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.
+ */
+
+/** @file
+ @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)
+ 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>
+
+/* 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 "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 <windows.h>
+#include <winioctl.h>
+
+
+#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
+
+#ifdef _MSC_VER
+ #define DYNAMIC_GUID(data) {data}
+ #define _INC_MMREG
+ #define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
+ #undef DEFINE_GUID
+ #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)
+ #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
+#endif
+
+#include <ks.h>
+#include <ksmedia.h>
+#include <tchar.h>
+#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;
+
+/* Forward definition to break circular type reference between pin and filter */
+struct __PaWinWdmFilter;
+typedef struct __PaWinWdmFilter PaWinWdmFilter;
+
+/* The Pin structure
+ * A pin is an input or output node, e.g. for audio flow */
+typedef struct __PaWinWdmPin
+{
+ HANDLE handle;
+ PaWinWdmFilter* parentFilter;
+ unsigned long pinId;
+ 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 bestSampleRate;
+}
+PaWinWdmPin;
+
+/* The Filter structure
+ * A filter has a number of pins and a "friendly name" */
+struct __PaWinWdmFilter
+{
+ HANDLE handle;
+ int pinCount;
+ PaWinWdmPin** pins;
+ TCHAR filterName[MAX_PATH];
+ TCHAR friendlyName[MAX_PATH];
+ int maxInputChannels;
+ int maxOutputChannels;
+ unsigned long formats;
+ int usageCount;
+ int bestSampleRate;
+};
+
+/* PaWinWdmHostApiRepresentation - host api datastructure specific to this implementation */
+typedef struct __PaWinWdmHostApiRepresentation
+{
+ PaUtilHostApiRepresentation inheritedHostApiRep;
+ PaUtilStreamInterface callbackStreamInterface;
+ PaUtilStreamInterface blockingStreamInterface;
+
+ PaUtilAllocationGroup *allocations;
+ PaWinWdmFilter** filters;
+ int filterCount;
+}
+PaWinWdmHostApiRepresentation;
+
+typedef struct __PaWinWdmDeviceInfo
+{
+ PaDeviceInfo inheritedDeviceInfo;
+ PaWinWdmFilter* filter;
+}
+PaWinWdmDeviceInfo;
+
+typedef struct __DATAPACKET
+{
+ KSSTREAM_HEADER Header;
+ OVERLAPPED Signal;
+} DATAPACKET;
+
+/* PaWinWdmStream - a stream data structure specifically for this implementation */
+typedef struct __PaWinWdmStream
+{
+ PaUtilStreamRepresentation streamRepresentation;
+ PaUtilCpuLoadMeasurer cpuLoadMeasurer;
+ PaUtilBufferProcessor bufferProcessor;
+
+ PaWinWdmPin* recordingPin;
+ PaWinWdmPin* playbackPin;
+ char* hostBuffer;
+ unsigned long framesPerHostIBuffer;
+ unsigned long framesPerHostOBuffer;
+ int bytesPerInputFrame;
+ int bytesPerOutputFrame;
+ int streamStarted;
+ int streamActive;
+ int streamStop;
+ int streamAbort;
+ int oldProcessPriority;
+ HANDLE streamThread;
+ HANDLE events[5]; /* 2 play + 2 record packets + abort events */
+ DATAPACKET packets[4]; /* 2 play + 2 record */
+ PaStreamFlags streamFlags;
+ /* 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;
+ int inputSampleSize;
+ int outputSampleSize;
+}
+PaWinWdmStream;
+
+#include <setupapi.h>
+
+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,
+ void* instance,
+ unsigned long instanceCount);
+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);
+static PaError WdmGetPinPropertyMulti(HANDLE handle,
+ unsigned long pinId,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ KSMULTIPLE_ITEM** ksMultipleItem);
+
+/** 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);
+
+/* Filter management functions */
+static PaWinWdmFilter* FilterNew(
+ TCHAR* filterName,
+ TCHAR* friendlyName,
+ PaError* error);
+static void FilterFree(PaWinWdmFilter* filter);
+static PaWinWdmPin* FilterCreateRenderPin(
+ PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex,
+ PaError* error);
+static PaWinWdmPin* FilterFindViableRenderPin(
+ PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex,
+ PaError* error);
+static PaError FilterCanCreateRenderPin(
+ PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex);
+static PaWinWdmPin* FilterCreateCapturePin(
+ PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex,
+ PaError* error);
+static PaWinWdmPin* FilterFindViableCapturePin(
+ PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex,
+ PaError* error);
+static PaError FilterCanCreateCapturePin(
+ PaWinWdmFilter* filter,
+ const WAVEFORMATEX* pwfx);
+static PaError FilterUse(
+ PaWinWdmFilter* filter);
+static void FilterRelease(
+ PaWinWdmFilter* filter);
+
+/* Interface functions */
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
+static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
+ const PaStreamParameters *inputParameters,
+ const PaStreamParameters *outputParameters,
+ double sampleRate );
+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 PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi);
+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);
+static DWORD WINAPI ProcessingThread(LPVOID pParam);
+
+/* Function bodies */
+
+static unsigned long GetWfexSize(const WAVEFORMATEX* wfex)
+{
+ if ( wfex->wFormatTag == WAVE_FORMAT_PCM )
+ {
+ return sizeof( WAVEFORMATEX );
+ }
+ else
+ {
+ return (sizeof( WAVEFORMATEX ) + wfex->cbSize);
+ }
+}
+
+/*
+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;
+ OVERLAPPED overlapped;
+ int boolResult;
+ unsigned long dummyBytesReturned;
+ unsigned long error;
+
+ if (!bytesReturned)
+ {
+ /* User a dummy as the caller hasn't supplied one */
+ bytesReturned = &dummyBytesReturned;
+ }
+
+ FillMemory((void *)&overlapped,sizeof(overlapped),0);
+ overlapped.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
+ if ( !overlapped.hEvent )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+ overlapped.hEvent = (HANDLE)((DWORD_PTR)overlapped.hEvent | 0x1);
+
+ boolResult = DeviceIoControl(handle, ioctlNumber, inBuffer, inBufferCount,
+ outBuffer, outBufferCount, bytesReturned, &overlapped);
+ if ( !boolResult )
+ {
+ error = GetLastError();
+ if ( error == ERROR_IO_PENDING )
+ {
+ error = WaitForSingleObject(overlapped.hEvent,INFINITE);
+ if ( error != WAIT_OBJECT_0 )
+ {
+ result = paUnanticipatedHostError;
+ goto error;
+ }
+ }
+ else if ((( error == ERROR_INSUFFICIENT_BUFFER ) ||
+ ( error == ERROR_MORE_DATA )) &&
+ ( ioctlNumber == IOCTL_KS_PROPERTY ) &&
+ ( outBufferCount == 0 ))
+ {
+ boolResult = TRUE;
+ }
+ else
+ {
+ result = paUnanticipatedHostError;
+ }
+ }
+ if ( !boolResult )
+ *bytesReturned = 0;
+
+error:
+ if ( overlapped.hEvent )
+ {
+ CloseHandle( overlapped.hEvent );
+ }
+ return result;
+}
+
+static PaError WdmGetPropertySimple(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;
+
+ propertyCount = sizeof(KSPROPERTY) + instanceCount;
+ ksProperty = (KSPROPERTY*)PaUtil_AllocateMemory( propertyCount );
+ if( !ksProperty )
+ {
+ return paInsufficientMemory;
+ }
+
+ FillMemory((void*)ksProperty,sizeof(ksProperty),0);
+ ksProperty->Set = *guidPropertySet;
+ ksProperty->Id = property;
+ ksProperty->Flags = KSPROPERTY_TYPE_GET;
+
+ if ( instance )
+ {
+ memcpy( (void*)(((char*)ksProperty)+sizeof(KSPROPERTY)), instance, instanceCount );
+ }
+
+ result = WdmSyncIoctl(
+ handle,
+ IOCTL_KS_PROPERTY,
+ ksProperty,
+ propertyCount,
+ value,
+ valueCount,
+ NULL);
+
+ PaUtil_FreeMemory( ksProperty );
+ 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*)PaUtil_AllocateMemory( 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);
+
+ PaUtil_FreeMemory( ksProperty );
+ return result;
+}
+
+static PaError WdmGetPinPropertySimple(
+ HANDLE handle,
+ unsigned long pinId,
+ const GUID* const guidPropertySet,
+ unsigned long property,
+ void* value,
+ unsigned long valueCount)
+{
+ 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,
+ NULL);
+
+ 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;
+}
+
+
+/*
+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;
+
+ PA_LOGE_;
+ PA_DEBUG(("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 = KSINTERFACE_STANDARD_STREAMING;
+ 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));
+ if ( result != paNoError )
+ goto error;
+
+ if ( /*(pin->communication != KSPIN_COMMUNICATION_SOURCE) &&*/
+ (pin->communication != KSPIN_COMMUNICATION_SINK) &&
+ (pin->communication != KSPIN_COMMUNICATION_BOTH) )
+ {
+ PA_DEBUG(("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));
+
+ 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 ( !memcmp( (void*)&identifier[i].Set, (void*)&KSINTERFACESETID_Standard, sizeof( GUID ) ) &&
+ ( identifier[i].Id == KSINTERFACE_STANDARD_STREAMING ) )
+ {
+ result = paNoError;
+ break;
+ }
+ }
+
+ if ( result != paNoError )
+ {
+ PA_DEBUG(("No standard streaming\n"));
+ 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 ( !memcmp( (void*)&identifier[i].Set, (void*)&KSMEDIUMSETID_Standard, sizeof( GUID ) ) &&
+ ( 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->bestSampleRate = 0;
+ pin->formats = 0;
+ for ( i = 0; i <pin->dataRangesItem->Count; i++)
+ {
+ PA_DEBUG(("DR major format %x\n",*(unsigned long*)(&(dataRange->MajorFormat))));
+ /* Check that subformat is WAVEFORMATEX, PCM or WILDCARD */
+ if (
+ IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat) ||
+ !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_PCM, sizeof ( GUID ) ) ||
+ ( !memcmp((void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof ( GUID ) ) &&
+ ( !memcmp((void*)&dataRange->MajorFormat, (void*)&KSDATAFORMAT_TYPE_AUDIO, sizeof ( GUID ) ) ) ) )
+ {
+ result = paNoError;
+ /* Record the maximum possible channels with this pin */
+ PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));
+ if ((int)((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels > pin->maxChannels)
+ {
+ pin->maxChannels = ((KSDATARANGE_AUDIO*)dataRange)->MaximumChannels;
+ /*PA_DEBUG(("MaxChannel: %d\n",pin->maxChannels));*/
+ }
+ /* Record the formats (bit depths) that are supported */
+ if (((KSDATARANGE_AUDIO*)dataRange)->MinimumBitsPerSample <= 16)
+ {
+ pin->formats |= paInt16;
+ PA_DEBUG(("Format 16 bit supported\n"));
+ }
+ if (((KSDATARANGE_AUDIO*)dataRange)->MaximumBitsPerSample >= 24)
+ {
+ pin->formats |= paInt24;
+ PA_DEBUG(("Format 24 bit supported\n"));
+ }
+ if (
+ ( pin->bestSampleRate != 48000) &&
+ (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 48000) &&
+ (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 48000) )
+ {
+ pin->bestSampleRate = 48000;
+ PA_DEBUG(("48kHz supported\n"));
+ }
+ else if (
+ ( pin->bestSampleRate != 48000) && ( pin->bestSampleRate != 44100 ) &&
+ (((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency >= 44100) &&
+ (((KSDATARANGE_AUDIO*)dataRange)->MinimumSampleFrequency <= 44100) )
+ {
+ pin->bestSampleRate = 44100;
+ PA_DEBUG(("44.1kHz supported\n"));
+ }
+ else
+ {
+ pin->bestSampleRate = ((KSDATARANGE_AUDIO*)dataRange)->MaximumSampleFrequency;
+ }
+ }
+ dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
+ }
+
+ if ( result != paNoError )
+ goto error;
+
+ /* Get instance information */
+ result = WdmGetPinPropertySimple(
+ parentFilter->handle,
+ pinId,
+ &KSPROPSETID_Pin,
+ KSPROPERTY_PIN_CINSTANCES,
+ &pin->instances,
+ sizeof(KSPIN_CINSTANCES));
+
+ if ( result != paNoError )
+ goto error;
+
+ /* Success */
+ *error = paNoError;
+ PA_DEBUG(("Pin created successfully\n"));
+ PA_LOGL_;
+ return pin;
+
+error:
+ /*
+ Error cleanup
+ */
+ PaUtil_FreeMemory( item );
+ if ( pin )
+ {
+ PaUtil_FreeMemory( pin->pinConnect );
+ PaUtil_FreeMemory( pin->dataRangesItem );
+ PaUtil_FreeMemory( pin );
+ }
+ *error = result;
+ PA_LOGL_;
+ return NULL;
+}
+
+/*
+Safely free all resources associated with the pin
+*/
+static void PinFree(PaWinWdmPin* pin)
+{
+ PA_LOGE_;
+ if ( pin )
+ {
+ PinClose(pin);
+ if ( pin->pinConnect )
+ {
+ PaUtil_FreeMemory( pin->pinConnect );
+ }
+ if ( pin->dataRangesItem )
+ {
+ PaUtil_FreeMemory( pin->dataRangesItem );
+ }
+ 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;
+
+ PA_LOGE_;
+ if ( pin == NULL )
+ return paInternalError;
+ if ( pin->handle == NULL )
+ return paInternalError;
+
+ result = WdmSetPropertySimple(
+ pin->handle,
+ &KSPROPSETID_Connection,
+ KSPROPERTY_CONNECTION_STATE,
+ &state,
+ sizeof(state),
+ NULL,
+ 0);
+ 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 = %x\n",createResult));
+ if ( createResult != ERROR_SUCCESS )
+ {
+ FilterRelease(pin->parentFilter);
+ pin->handle = NULL;
+ return paInvalidDevice;
+ }
+
+ result = WdmGetPropertySimple(
+ pin->handle,
+ &KSPROPSETID_Connection,
+ KSPROPERTY_CONNECTION_ALLOCATORFRAMING,
+ &ksaf,
+ sizeof(ksaf),
+ NULL,
+ 0);
+
+ if ( result != paNoError )
+ {
+ result = WdmGetPropertySimple(
+ pin->handle,
+ &KSPROPSETID_Connection,
+ KSPROPERTY_CONNECTION_ALLOCATORFRAMING_EX,
+ &ksafex,
+ sizeof(ksafex),
+ NULL,
+ 0);
+ if ( result == paNoError )
+ {
+ pin->frameSize = ksafex.FramingItem[0].FramingRange.Range.MinFrameSize;
+ }
+ }
+ else
+ {
+ pin->frameSize = ksaf.FrameSize;
+ }
+
+ PA_LOGL_;
+
+ return paNoError;
+}
+
+/* NOT USED
+static PaError PinGetState(PaWinWdmPin* pin, KSSTATE* state)
+{
+ PaError result;
+
+ if ( state == NULL )
+ return paInternalError;
+ if ( pin == NULL )
+ return paInternalError;
+ if ( pin->handle == NULL )
+ return paInternalError;
+
+ result = WdmGetPropertySimple(
+ pin->handle,
+ KSPROPSETID_Connection,
+ KSPROPERTY_CONNECTION_STATE,
+ state,
+ sizeof(KSSTATE),
+ NULL,
+ 0);
+
+ return result;
+}
+*/
+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;
+
+ PA_LOGE_;
+
+ if ( format->wFormatTag == WAVE_FORMAT_EXTENSIBLE )
+ {
+ guid = ((WAVEFORMATEXTENSIBLE*)format)->SubFormat;
+ }
+ dataRange = (KSDATARANGE_AUDIO*)pin->dataRanges;
+ for (count = 0; count<pin->dataRangesItem->Count; count++)
+ {
+ if (( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_AUDIO,sizeof(GUID)) ) ||
+ ( !memcmp(&(dataRange->DataRange.MajorFormat),&KSDATAFORMAT_TYPE_WILDCARD,sizeof(GUID)) ))
+ {
+ /* This is an audio or wildcard datarange... */
+ if (( !memcmp(&(dataRange->DataRange.SubFormat),&KSDATAFORMAT_SUBTYPE_WILDCARD,sizeof(GUID)) ) ||
+ ( !memcmp(&(dataRange->DataRange.SubFormat),&guid,sizeof(GUID)) ))
+ {
+ if (( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WILDCARD,sizeof(GUID)) ) ||
+ ( !memcmp(&(dataRange->DataRange.Specifier),&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX,sizeof(GUID) )))
+ {
+
+ 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 < format->nChannels )
+ {
+ result = paInvalidChannelCount;
+ continue;
+ }
+ if ( dataRange->MinimumBitsPerSample > format->wBitsPerSample )
+ {
+ result = paSampleFormatNotSupported;
+ continue;
+ }
+ if ( dataRange->MaximumBitsPerSample < format->wBitsPerSample )
+ {
+ result = paSampleFormatNotSupported;
+ continue;
+ }
+ if ( dataRange->MinimumSampleFrequency > format->nSamplesPerSec )
+ {
+ result = paInvalidSampleRate;
+ continue;
+ }
+ if ( dataRange->MaximumSampleFrequency < format->nSamplesPerSec )
+ {
+ result = paInvalidSampleRate;
+ continue;
+ }
+ /* Success! */
+ PA_LOGL_;
+ return paNoError;
+ }
+ }
+ }
+ dataRange = (KSDATARANGE_AUDIO*)( ((char*)dataRange) + dataRange->DataRange.FormatSize);
+ }
+
+ PA_LOGL_;
+
+ return result;
+}
+
+/**
+ * Create a new filter object
+ */
+static PaWinWdmFilter* FilterNew(TCHAR* filterName, TCHAR* friendlyName, PaError* error)
+{
+ PaWinWdmFilter* filter;
+ PaError result;
+ int pinId;
+ int valid;
+
+
+ /* Allocate the new filter object */
+ filter = (PaWinWdmFilter*)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter) );
+ if( !filter )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* Zero the filter object - done by AllocateMemory */
+ /* memset( (void*)filter, 0, sizeof(PaWinWdmFilter) ); */
+
+ /* Copy the filter name */
+ _tcsncpy(filter->filterName, filterName, MAX_PATH);
+
+ /* Copy the friendly name */
+ _tcsncpy(filter->friendlyName, friendlyName, MAX_PATH);
+
+ /* 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)
+ );
+
+ if ( result != paNoError)
+ {
+ goto error;
+ }
+
+ /* 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 */
+ filter->maxInputChannels = 0;
+ filter->maxOutputChannels = 0;
+ filter->bestSampleRate = 0;
+
+ valid = 0;
+ 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;
+ valid = 1;
+
+ /* Get the max output channel count */
+ if (( newPin->dataFlow == KSPIN_DATAFLOW_IN ) &&
+ (( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
+ ( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
+ {
+ if (newPin->maxChannels > filter->maxOutputChannels)
+ filter->maxOutputChannels = newPin->maxChannels;
+ filter->formats |= newPin->formats;
+ }
+ /* Get the max input channel count */
+ if (( newPin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
+ (( newPin->communication == KSPIN_COMMUNICATION_SINK) ||
+ ( newPin->communication == KSPIN_COMMUNICATION_BOTH)))
+ {
+ if (newPin->maxChannels > filter->maxInputChannels)
+ filter->maxInputChannels = newPin->maxChannels;
+ filter->formats |= newPin->formats;
+ }
+
+ if (newPin->bestSampleRate > filter->bestSampleRate)
+ {
+ filter->bestSampleRate = newPin->bestSampleRate;
+ }
+ }
+ }
+
+ if (( filter->maxInputChannels == 0) && ( filter->maxOutputChannels == 0))
+ {
+ /* No input or output... not valid */
+ valid = 0;
+ }
+
+ if ( !valid )
+ {
+ /* No valid pin was found on this filter so we destroy it */
+ result = paDeviceUnavailable;
+ goto error;
+ }
+
+ /* Close the filter handle for now
+ * It will be opened later when needed */
+ FilterRelease(filter);
+
+ *error = paNoError;
+ return filter;
+
+error:
+ /*
+ Error cleanup
+ */
+ if ( filter )
+ {
+ for ( pinId = 0; pinId < filter->pinCount; pinId++ )
+ PinFree(filter->pins[pinId]);
+ PaUtil_FreeMemory( filter->pins );
+ if ( filter->handle )
+ CloseHandle( filter->handle );
+ PaUtil_FreeMemory( filter );
+ }
+ *error = result;
+ return NULL;
+}
+
+/**
+ * Free a previously created filter
+ */
+static void FilterFree(PaWinWdmFilter* filter)
+{
+ int pinId;
+ PA_LOGL_;
+ if ( filter )
+ {
+ for ( pinId = 0; pinId < filter->pinCount; pinId++ )
+ PinFree(filter->pins[pinId]);
+ PaUtil_FreeMemory( filter->pins );
+ 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 = CreateFile(
+ filter->filterName,
+ 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_;
+ filter->usageCount--;
+ if ( filter->usageCount == 0 )
+ {
+ if ( filter->handle != NULL )
+ {
+ CloseHandle( filter->handle );
+ filter->handle = NULL;
+ }
+ }
+ PA_LOGL_;
+}
+
+/**
+ * Create a render (playback) Pin using the supplied format
+ **/
+static PaWinWdmPin* FilterCreateRenderPin(PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex,
+ PaError* error)
+{
+ PaError result;
+ PaWinWdmPin* pin;
+
+ assert( filter );
+
+ pin = FilterFindViableRenderPin(filter,wfex,&result);
+ if (!pin)
+ {
+ goto error;
+ }
+ result = PinSetFormat(pin,wfex);
+ if ( result != paNoError )
+ {
+ goto error;
+ }
+ result = PinInstantiate(pin);
+ if ( result != paNoError )
+ {
+ goto error;
+ }
+
+ *error = paNoError;
+ return pin;
+
+error:
+ *error = result;
+ return NULL;
+}
+
+/**
+ * Find a pin that supports the given format
+ **/
+static PaWinWdmPin* FilterFindViableRenderPin(PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex,
+ PaError* error)
+{
+ int pinId;
+ PaWinWdmPin* pin;
+ PaError result = paDeviceUnavailable;
+ *error = paNoError;
+
+ assert( filter );
+
+ for ( pinId = 0; pinId<filter->pinCount; pinId++ )
+ {
+ pin = filter->pins[pinId];
+ if ( pin != NULL )
+ {
+ if (( pin->dataFlow == KSPIN_DATAFLOW_IN ) &&
+ (( pin->communication == KSPIN_COMMUNICATION_SINK) ||
+ ( pin->communication == KSPIN_COMMUNICATION_BOTH)))
+ {
+ result = PinIsFormatSupported( pin, wfex );
+ if ( result == paNoError )
+ {
+ return pin;
+ }
+ }
+ }
+ }
+
+ *error = result;
+ return NULL;
+}
+
+/**
+ * Check if there is a pin that should playback
+ * with the supplied format
+ **/
+static PaError FilterCanCreateRenderPin(PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex)
+{
+ PaWinWdmPin* pin;
+ PaError result;
+
+ assert ( filter );
+
+ pin = FilterFindViableRenderPin(filter,wfex,&result);
+ /* result will be paNoError if pin found
+ * or else an error code indicating what is wrong with the format
+ **/
+ return result;
+}
+
+/**
+ * Create a capture (record) Pin using the supplied format
+ **/
+static PaWinWdmPin* FilterCreateCapturePin(PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex,
+ PaError* error)
+{
+ PaError result;
+ PaWinWdmPin* pin;
+
+ assert( filter );
+
+ pin = FilterFindViableCapturePin(filter,wfex,&result);
+ if (!pin)
+ {
+ goto error;
+ }
+
+ result = PinSetFormat(pin,wfex);
+ if ( result != paNoError )
+ {
+ goto error;
+ }
+
+ result = PinInstantiate(pin);
+ if ( result != paNoError )
+ {
+ goto error;
+ }
+
+ *error = paNoError;
+ return pin;
+
+error:
+ *error = result;
+ return NULL;
+}
+
+/**
+ * Find a capture pin that supports the given format
+ **/
+static PaWinWdmPin* FilterFindViableCapturePin(PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex,
+ PaError* error)
+{
+ int pinId;
+ PaWinWdmPin* pin;
+ PaError result = paDeviceUnavailable;
+ *error = paNoError;
+
+ assert( filter );
+
+ for ( pinId = 0; pinId<filter->pinCount; pinId++ )
+ {
+ pin = filter->pins[pinId];
+ if ( pin != NULL )
+ {
+ if (( pin->dataFlow == KSPIN_DATAFLOW_OUT ) &&
+ (( pin->communication == KSPIN_COMMUNICATION_SINK) ||
+ ( pin->communication == KSPIN_COMMUNICATION_BOTH)))
+ {
+ result = PinIsFormatSupported( pin, wfex );
+ if ( result == paNoError )
+ {
+ return pin;
+ }
+ }
+ }
+ }
+
+ *error = result;
+ return NULL;
+}
+
+/**
+ * Check if there is a pin that should playback
+ * with the supplied format
+ **/
+static PaError FilterCanCreateCapturePin(PaWinWdmFilter* filter,
+ const WAVEFORMATEX* wfex)
+{
+ PaWinWdmPin* pin;
+ PaError result;
+
+ assert ( filter );
+
+ pin = FilterFindViableCapturePin(filter,wfex,&result);
+ /* result will be paNoError if pin found
+ * or else an error code indicating what is wrong with the format
+ **/
+ return result;
+}
+
+/**
+ * Build the list of available filters
+ */
+static PaError BuildFilterList(PaWinWdmHostApiRepresentation* wdmHostApi)
+{
+ PaError result = paNoError;
+ 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* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA*)interfaceDetailsArray;
+ TCHAR friendlyName[MAX_PATH];
+ HKEY hkey;
+ DWORD sizeFriendlyName;
+ DWORD type;
+ PaWinWdmFilter* newFilter;
+ GUID* category = (GUID*)&KSCATEGORY_AUDIO;
+ GUID* alias_render = (GUID*)&KSCATEGORY_RENDER;
+ GUID* alias_capture = (GUID*)&KSCATEGORY_CAPTURE;
+ DWORD hasAlias;
+
+ PA_LOGE_;
+
+ devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+
+ /* Open a handle to search for devices (filters) */
+ handle = SetupDiGetClassDevs(category,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if ( handle == NULL )
+ {
+ return paUnanticipatedHostError;
+ }
+ 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 */
+ hasAlias = 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));
+ hasAlias |= 1; /* 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));
+ hasAlias |= 2; /* Has capture alias */
+ }
+ else
+ {
+ PA_DEBUG(("Device %d has no capture alias\n",device));
+ }
+ }
+ if (!hasAlias)
+ invalidDevices++; /* This was not a valid capture or render audio device */
+
+ }
+ /* Remember how many there are */
+ wdmHostApi->filterCount = device-invalidDevices;
+
+ PA_DEBUG(("Interfaces found: %d\n",device-invalidDevices));
+
+ /* Now allocate the list of pointers to devices */
+ wdmHostApi->filters = (PaWinWdmFilter**)PaUtil_AllocateMemory( sizeof(PaWinWdmFilter*) * device );
+ if( !wdmHostApi->filters )
+ {
+ if (handle != NULL)
+ SetupDiDestroyDeviceInfoList(handle);
+ return paInsufficientMemory;
+ }
+
+ /* 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;
+
+ noError = SetupDiEnumDeviceInterfaces(handle,NULL,category,device,&interfaceData);
+ if ( !noError )
+ break; /* No more devices */
+
+ /* Check this one has the render or capture alias */
+ hasAlias = 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));
+ hasAlias |= 1; /* 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));
+ hasAlias |= 2; /* Has capture alias */
+ }
+ }
+ if (!hasAlias)
+ continue; /* This was not a valid capture or render audio device */
+
+ noError = SetupDiGetDeviceInterfaceDetail(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
+ if ( noError )
+ {
+ /* Try to get the "friendly name" for this interface */
+ sizeFriendlyName = sizeof(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=SetupDiOpenDeviceInterfaceRegKey(handle,&interfaceData,0,KEY_QUERY_VALUE);
+ if (hkey!=INVALID_HANDLE_VALUE)
+ {
+ noError = RegQueryValueEx(hkey,TEXT("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;
+ }
+ }
+ newFilter = FilterNew(devInterfaceDetails->DevicePath,friendlyName,&result);
+ if ( result == paNoError )
+ {
+ PA_DEBUG(("Filter created\n"));
+ wdmHostApi->filters[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 */
+ wdmHostApi->filterCount--;
+ }
+ }
+ }
+
+ /* Clean up */
+ if (handle != NULL)
+ SetupDiDestroyDeviceInfoList(handle);
+
+ return paNoError;
+}
+
+PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
+{
+ PaError result = paNoError;
+ int i, deviceCount;
+ PaWinWdmHostApiRepresentation *wdmHostApi;
+ PaWinWdmDeviceInfo *deviceInfoArray;
+ PaWinWdmFilter* pFilter;
+ PaWinWdmDeviceInfo *wdmDeviceInfo;
+ PaDeviceInfo *deviceInfo;
+
+ PA_LOGE_;
+
+ /*
+ 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;
+
+ wdmHostApi = (PaWinWdmHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinWdmHostApiRepresentation) );
+ if( !wdmHostApi )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ wdmHostApi->allocations = PaUtil_CreateAllocationGroup();
+ if( !wdmHostApi->allocations )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ result = BuildFilterList( wdmHostApi );
+ if ( result != paNoError )
+ {
+ goto error;
+ }
+ deviceCount = wdmHostApi->filterCount;
+
+ *hostApi = &wdmHostApi->inheritedHostApiRep;
+ (*hostApi)->info.structVersion = 1;
+ (*hostApi)->info.type = paWDMKS;
+ (*hostApi)->info.name = "Windows WDM-KS";
+
+ (*hostApi)->info.defaultOutputDevice = paNoDevice;
+
+ if( deviceCount > 0 )
+ {
+ (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
+ wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo*) * deviceCount );
+ if( !(*hostApi)->deviceInfos )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ /* allocate all device info structs in a contiguous block */
+ deviceInfoArray = (PaWinWdmDeviceInfo*)PaUtil_GroupAllocateMemory(
+ wdmHostApi->allocations, sizeof(PaWinWdmDeviceInfo) * deviceCount );
+ if( !deviceInfoArray )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ for( i=0; i < deviceCount; ++i )
+ {
+ wdmDeviceInfo = &deviceInfoArray[i];
+ deviceInfo = &wdmDeviceInfo->inheritedDeviceInfo;
+ pFilter = wdmHostApi->filters[i];
+ if ( pFilter == NULL )
+ continue;
+ wdmDeviceInfo->filter = pFilter;
+ deviceInfo->structVersion = 2;
+ deviceInfo->hostApi = hostApiIndex;
+ deviceInfo->name = (char*)pFilter->friendlyName;
+ PA_DEBUG(("Device found name: %s\n",(char*)pFilter->friendlyName));
+ deviceInfo->maxInputChannels = pFilter->maxInputChannels;
+ if (deviceInfo->maxInputChannels > 0)
+ {
+ /* Set the default input device to the first device we find with
+ * more than zero input channels
+ **/
+ if ((*hostApi)->info.defaultInputDevice == paNoDevice)
+ {
+ (*hostApi)->info.defaultInputDevice = i;
+ }
+ }
+
+ deviceInfo->maxOutputChannels = pFilter->maxOutputChannels;
+ if (deviceInfo->maxOutputChannels > 0)
+ {
+ /* Set the default output device to the first device we find with
+ * more than zero output channels
+ **/
+ if ((*hostApi)->info.defaultOutputDevice == paNoDevice)
+ {
+ (*hostApi)->info.defaultOutputDevice = i;
+ }
+ }
+
+ /* 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
+ **/
+ deviceInfo->defaultLowInputLatency = (512.0/48000.0);
+ deviceInfo->defaultLowOutputLatency = (512.0/48000.0);
+ deviceInfo->defaultHighInputLatency = (4096.0/48000.0);
+ deviceInfo->defaultHighOutputLatency = (4096.0/48000.0);
+ deviceInfo->defaultSampleRate = (double)(pFilter->bestSampleRate);
+
+ (*hostApi)->deviceInfos[i] = deviceInfo;
+ ++(*hostApi)->info.deviceCount;
+ }
+ }
+
+ (*hostApi)->info.deviceCount = deviceCount;
+
+ (*hostApi)->Terminate = Terminate;
+ (*hostApi)->OpenStream = OpenStream;
+ (*hostApi)->IsFormatSupported = IsFormatSupported;
+
+ 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:
+ if ( DllKsUser != NULL )
+ {
+ FreeLibrary( DllKsUser );
+ DllKsUser = NULL;
+ }
+
+ if( wdmHostApi )
+ {
+ PaUtil_FreeMemory( wdmHostApi->filters );
+ if( wdmHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( wdmHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
+ }
+ PaUtil_FreeMemory( wdmHostApi );
+ }
+ PA_LOGL_;
+ return result;
+}
+
+
+static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
+{
+ PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
+ int i;
+ PA_LOGE_;
+
+ if( wdmHostApi->filters )
+ {
+ for ( i=0; i<wdmHostApi->filterCount; i++)
+ {
+ if ( wdmHostApi->filters[i] != NULL )
+ {
+ FilterFree( wdmHostApi->filters[i] );
+ wdmHostApi->filters[i] = NULL;
+ }
+ }
+ }
+ PaUtil_FreeMemory( wdmHostApi->filters );
+ if( wdmHostApi->allocations )
+ {
+ PaUtil_FreeAllAllocations( wdmHostApi->allocations );
+ PaUtil_DestroyAllocationGroup( wdmHostApi->allocations );
+ }
+ PaUtil_FreeMemory( wdmHostApi );
+ PA_LOGL_;
+}
+
+static void FillWFEXT( WAVEFORMATEXTENSIBLE* pwfext, PaSampleFormat sampleFormat, double sampleRate, int channelCount)
+ {
+ PA_LOGE_;
+ PA_DEBUG(( "sampleFormat = %lx\n" , sampleFormat ));
+ PA_DEBUG(( "sampleRate = %f\n" , sampleRate ));
+ PA_DEBUG(( "chanelCount = %d\n", channelCount ));
+
+ pwfext->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ pwfext->Format.nChannels = channelCount;
+ pwfext->Format.nSamplesPerSec = (int)sampleRate;
+ if (channelCount == 1)
+ pwfext->dwChannelMask = KSAUDIO_SPEAKER_DIRECTOUT;
+ else
+ pwfext->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
+ if (sampleFormat == paFloat32)
+ {
+ pwfext->Format.nBlockAlign = channelCount * 4;
+ pwfext->Format.wBitsPerSample = 32;
+ pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+ pwfext->Samples.wValidBitsPerSample = 32;
+ pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+ }
+ else if (sampleFormat == paInt32)
+ {
+ pwfext->Format.nBlockAlign = channelCount * 4;
+ pwfext->Format.wBitsPerSample = 32;
+ pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+ pwfext->Samples.wValidBitsPerSample = 32;
+ pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ }
+ else if (sampleFormat == paInt24)
+ {
+ pwfext->Format.nBlockAlign = channelCount * 3;
+ pwfext->Format.wBitsPerSample = 24;
+ pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+ pwfext->Samples.wValidBitsPerSample = 24;
+ pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ }
+ else if (sampleFormat == paInt16)
+ {
+ pwfext->Format.nBlockAlign = channelCount * 2;
+ pwfext->Format.wBitsPerSample = 16;
+ pwfext->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+ pwfext->Samples.wValidBitsPerSample = 16;
+ pwfext->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+ }
+ pwfext->Format.nAvgBytesPerSec = pwfext->Format.nSamplesPerSec * pwfext->Format.nBlockAlign;
+
+ 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;
+
+ PA_LOGE_;
+
+ if( inputParameters )
+ {
+ 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 )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support inputChannelCount */
+ if( inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ /* Check that the input format is supported */
+ FillWFEXT(&wfx,paInt16,sampleRate,inputChannelCount);
+
+ pFilter = wdmHostApi->filters[inputParameters->device];
+ result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
+ if ( result != paNoError )
+ {
+ /* Try a WAVE_FORMAT_PCM instead */
+ wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.Format.cbSize = 0;
+ wfx.Samples.wValidBitsPerSample = 0;
+ wfx.dwChannelMask = 0;
+ wfx.SubFormat = GUID_NULL;
+ result = FilterCanCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx);
+ if ( result != paNoError )
+ return result;
+ }
+ }
+ else
+ {
+ inputChannelCount = 0;
+ }
+
+ if( outputParameters )
+ {
+ 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 )
+ return paSampleFormatNotSupported;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that output device can support outputChannelCount */
+ if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ /* Check that the output format is supported */
+ FillWFEXT(&wfx,paInt16,sampleRate,outputChannelCount);
+
+ pFilter = wdmHostApi->filters[outputParameters->device];
+ result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
+ if ( result != paNoError )
+ {
+ /* Try a WAVE_FORMAT_PCM instead */
+ wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.Format.cbSize = 0;
+ wfx.Samples.wValidBitsPerSample = 0;
+ wfx.dwChannelMask = 0;
+ wfx.SubFormat = GUID_NULL;
+ result = FilterCanCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx);
+ if ( result != paNoError )
+ 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))
+ result = paSampleFormatNotSupported; /* Not right error */
+
+ PA_LOGL_;
+ return result;
+}
+
+/* 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 framesPerBuffer,
+ 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;
+ int size;
+ PaWinWdmFilter* pFilter;
+ WAVEFORMATEXTENSIBLE wfx;
+
+ PA_LOGE_;
+ PA_DEBUG(("sampleRate = %f;",sampleRate));
+ PA_DEBUG(("framesPerBuffer = %lu;",framesPerBuffer));
+
+ if( inputParameters )
+ {
+ userInputChannels = inputParameters->channelCount;
+ inputSampleFormat = inputParameters->sampleFormat;
+
+ /* unless alternate device specification is supported, reject the use of
+ paUseHostApiSpecificDeviceSpecification */
+
+ if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
+ return paInvalidDevice;
+
+ /* check that input device can support stream->userInputChannels */
+ if( userInputChannels > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels )
+ return paInvalidChannelCount;
+
+ /* validate inputStreamInfo */
+ if( inputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ }
+ else
+ {
+ userInputChannels = 0;
+ inputSampleFormat = hostInputSampleFormat = paInt16; /* Surpress '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 )
+ return paInvalidDevice;
+
+ /* check that output device can support stream->userInputChannels */
+ if( userOutputChannels > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels )
+ return paInvalidChannelCount;
+
+ /* validate outputStreamInfo */
+ if( outputParameters->hostApiSpecificStreamInfo )
+ return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
+
+ }
+ else
+ {
+ userOutputChannels = 0;
+ outputSampleFormat = hostOutputSampleFormat = paInt16; /* Surpress 'uninitialized var' warnings. */
+ }
+
+ /* validate platform specific flags */
+ if( (streamFlags & paPlatformSpecificFlags) != 0 )
+ return paInvalidFlag; /* unexpected platform specific flag */
+
+ stream = (PaWinWdmStream*)PaUtil_AllocateMemory( sizeof(PaWinWdmStream) );
+ if( !stream )
+ {
+ 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 );
+ }
+
+ PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
+
+ /* Instantiate the input pin if necessary */
+ if (userInputChannels > 0)
+ {
+ stream->userInputChannels = userInputChannels;
+ pFilter = wdmHostApi->filters[inputParameters->device];
+ hostInputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( pFilter->formats, inputSampleFormat );
+ if ( hostInputSampleFormat == paInt16 )
+ stream->inputSampleSize = 2;
+ else
+ stream->inputSampleSize = 3;
+ FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,stream->userInputChannels);
+ stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
+ stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+ stream->deviceInputChannels = stream->userInputChannels;
+ if (result != paNoError)
+ {
+ /* Try a WAVE_FORMAT_PCM instead */
+ wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.Format.cbSize = 0;
+ wfx.Samples.wValidBitsPerSample = 0;
+ wfx.dwChannelMask = 0;
+ wfx.SubFormat = GUID_NULL;
+ stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+ if ( result != paNoError )
+ {
+ /* 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
+ **/
+ if ( stream->userInputChannels < pFilter->maxInputChannels )
+ {
+ FillWFEXT(&wfx,hostInputSampleFormat,sampleRate,pFilter->maxInputChannels);
+ stream->bytesPerInputFrame = wfx.Format.nBlockAlign;
+ stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+ stream->deviceInputChannels = pFilter->maxInputChannels;
+ if ( result != paNoError )
+ {
+ /* Try a WAVE_FORMAT_PCM instead */
+ wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.Format.cbSize = 0;
+ wfx.Samples.wValidBitsPerSample = 0;
+ wfx.dwChannelMask = 0;
+ wfx.SubFormat = GUID_NULL;
+ stream->recordingPin = FilterCreateCapturePin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+ }
+ }
+ }
+ }
+ if (stream->recordingPin == NULL)
+ {
+ goto error;
+ }
+ stream->recordingPin->frameSize /= stream->bytesPerInputFrame;
+ PA_DEBUG(("Pin output frames: %d\n",stream->recordingPin->frameSize));
+ }
+ else
+ {
+ stream->recordingPin = NULL;
+ stream->bytesPerInputFrame = 0;
+ }
+
+ /* Instantiate the output pin if necessary */
+ if (userOutputChannels > 0)
+ {
+ stream->userOutputChannels = userOutputChannels;
+ pFilter = wdmHostApi->filters[outputParameters->device];
+ hostOutputSampleFormat =
+ PaUtil_SelectClosestAvailableFormat( pFilter->formats/*paInt16*/, outputSampleFormat );
+ if ( hostOutputSampleFormat == paInt16 )
+ stream->outputSampleSize = 2;
+ else
+ stream->outputSampleSize = 3;
+ FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,stream->userOutputChannels);
+ stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
+ stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
+ stream->deviceOutputChannels = stream->userOutputChannels;
+ if (result != paNoError)
+ {
+ /* Try a WAVE_FORMAT_PCM instead */
+ wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.Format.cbSize = 0;
+ wfx.Samples.wValidBitsPerSample = 0;
+ wfx.dwChannelMask = 0;
+ wfx.SubFormat = GUID_NULL;
+ stream->playbackPin = FilterCreateRenderPin(pFilter,(WAVEFORMATEX*)&wfx,&result);
+ if ( result != paNoError )
+ {
+ /* 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
+ **/
+ if ( stream->userOutputChannels < pFilter->maxOutputChannels )
+ {
+ FillWFEXT(&wfx,hostOutputSampleFormat,sampleRate,pFilter->maxOutputChannels);
+ stream->bytesPerOutputFrame = wfx.Format.nBlockAlign;
+ stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+ stream->deviceOutputChannels = pFilter->maxOutputChannels;
+ if ( result != paNoError )
+ {
+ /* Try a WAVE_FORMAT_PCM instead */
+ wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.Format.cbSize = 0;
+ wfx.Samples.wValidBitsPerSample = 0;
+ wfx.dwChannelMask = 0;
+ wfx.SubFormat = GUID_NULL;
+ stream->playbackPin = FilterCreateRenderPin(pFilter,(const WAVEFORMATEX*)&wfx,&result);
+ }
+ }
+ }
+ }
+ if (stream->playbackPin == NULL)
+ {
+ goto error;
+ }
+ stream->playbackPin->frameSize /= stream->bytesPerOutputFrame;
+ PA_DEBUG(("Pin output frames: %d\n",stream->playbackPin->frameSize));
+ }
+ else
+ {
+ stream->playbackPin = NULL;
+ stream->bytesPerOutputFrame = 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->framesPerHostIBuffer = (unsigned long)((inputParameters->suggestedLatency*sampleRate)+0.0001);
+ if (stream->framesPerHostIBuffer > (unsigned long)sampleRate)
+ { /* Upper limit is 1 second */
+ stream->framesPerHostIBuffer = (unsigned long)sampleRate;
+ }
+ /* Uncomment the following code to make the device-reported
+ * frame size the lower limit*/
+ /*
+ else if (stream->framesPerHostIBuffer < stream->recordingPin->frameSize)
+ {
+ stream->framesPerHostIBuffer = stream->recordingPin->frameSize;
+ }
+ */
+ PA_DEBUG(("Input frames chosen:%ld\n",stream->framesPerHostIBuffer));
+ }
+
+ if (outputParameters)
+ {
+ /* Calculate the frames from the user's value - add a bit to round up */
+ stream->framesPerHostOBuffer = (unsigned long)((outputParameters->suggestedLatency*sampleRate)+0.0001);
+ if (stream->framesPerHostOBuffer > (unsigned long)sampleRate)
+ { /* Upper limit is 1 second */
+ stream->framesPerHostOBuffer = (unsigned long)sampleRate;
+ }
+ /* Uncomment the following code to make the device-reported
+ * frame size the lower limit*/
+ /*
+ else if (stream->framesPerHostOBuffer < stream->playbackPin->frameSize)
+ {
+ stream->framesPerHostOBuffer = stream->playbackPin->frameSize;
+ }
+ */
+ PA_DEBUG(("Output frames chosen:%ld\n",stream->framesPerHostOBuffer));
+ }
+
+ /* Host buffer size is bounded 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, framesPerBuffer,
+ max(stream->framesPerHostOBuffer,stream->framesPerHostIBuffer),
+ paUtilBoundedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError )
+ goto error;
+
+ stream->streamRepresentation.streamInfo.inputLatency =
+ ((double)stream->framesPerHostIBuffer) / sampleRate;
+ stream->streamRepresentation.streamInfo.outputLatency =
+ ((double)stream->framesPerHostOBuffer) / sampleRate;
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+
+ PA_DEBUG(("BytesPerInputFrame = %d\n",stream->bytesPerInputFrame));
+ PA_DEBUG(("BytesPerOutputFrame = %d\n",stream->bytesPerOutputFrame));
+
+ /* Allocate all the buffers for host I/O */
+ size = 2 * (stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame);
+ PA_DEBUG(("Buffer size = %d\n",size));
+ stream->hostBuffer = (char*)PaUtil_AllocateMemory(size);
+ PA_DEBUG(("Buffer allocated\n"));
+ if( !stream->hostBuffer )
+ {
+ PA_DEBUG(("Cannot allocate host buffer!\n"));
+ result = paInsufficientMemory;
+ goto error;
+ }
+ PA_DEBUG(("Buffer start = %p\n",stream->hostBuffer));
+ /* memset(stream->hostBuffer,0,size); */
+
+ /* Set up the packets */
+ stream->events[0] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ ResetEvent(stream->events[0]); /* Record buffer 1 */
+ stream->events[1] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ ResetEvent(stream->events[1]); /* Record buffer 2 */
+ stream->events[2] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ ResetEvent(stream->events[2]); /* Play buffer 1 */
+ stream->events[3] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ ResetEvent(stream->events[3]); /* Play buffer 2 */
+ stream->events[4] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ ResetEvent(stream->events[4]); /* Abort event */
+ if (stream->userInputChannels > 0)
+ {
+ DATAPACKET *p = &(stream->packets[0]);
+ p->Signal.hEvent = stream->events[0];
+ p->Header.Data = stream->hostBuffer;
+ p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
+ p->Header.DataUsed = 0;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+ p = &(stream->packets[1]);
+ p->Signal.hEvent = stream->events[1];
+ p->Header.Data = stream->hostBuffer + stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
+ p->Header.FrameExtent = stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
+ p->Header.DataUsed = 0;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+ }
+ if (stream->userOutputChannels > 0)
+ {
+ DATAPACKET *p = &(stream->packets[2]);
+ p->Signal.hEvent = stream->events[2];
+ p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame;
+ p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+ p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+ p = &(stream->packets[3]);
+ p->Signal.hEvent = stream->events[3];
+ p->Header.Data = stream->hostBuffer + 2*stream->framesPerHostIBuffer*stream->bytesPerInputFrame + stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+ p->Header.FrameExtent = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+ p->Header.DataUsed = stream->framesPerHostOBuffer*stream->bytesPerOutputFrame;
+ p->Header.Size = sizeof(p->Header);
+ p->Header.PresentationTime.Numerator = 1;
+ p->Header.PresentationTime.Denominator = 1;
+ }
+
+ stream->streamStarted = 0;
+ stream->streamActive = 0;
+ stream->streamStop = 0;
+ stream->streamAbort = 0;
+ stream->streamFlags = streamFlags;
+ stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+
+ *s = (PaStream*)stream;
+
+ PA_LOGL_;
+ return result;
+
+error:
+ size = 5;
+ while (size--)
+ {
+ if (stream->events[size] != NULL)
+ {
+ CloseHandle(stream->events[size]);
+ stream->events[size] = NULL;
+ }
+ }
+ if (stream->hostBuffer)
+ PaUtil_FreeMemory( stream->hostBuffer );
+
+ if (stream->playbackPin)
+ PinClose(stream->playbackPin);
+ if (stream->recordingPin)
+ PinClose(stream->recordingPin);
+
+ if( stream )
+ 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;
+ int size;
+
+ PA_LOGE_;
+
+ assert(!stream->streamStarted);
+ assert(!stream->streamActive);
+
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+ PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
+ size = 5;
+ while (size--)
+ {
+ if (stream->events[size] != NULL)
+ {
+ CloseHandle(stream->events[size]);
+ stream->events[size] = NULL;
+ }
+ }
+ if (stream->hostBuffer)
+ PaUtil_FreeMemory( stream->hostBuffer );
+
+ if (stream->playbackPin)
+ PinClose(stream->playbackPin);
+ if (stream->recordingPin)
+ PinClose(stream->recordingPin);
+
+ PaUtil_FreeMemory( stream );
+
+ PA_LOGL_;
+ return result;
+}
+
+/*
+Write the supplied packet to the pin
+Asynchronous
+Should return false on success
+*/
+static BOOL PinWrite(HANDLE h, DATAPACKET* p)
+{
+ unsigned long cbReturned = 0;
+ return DeviceIoControl(h,IOCTL_KS_WRITE_STREAM,NULL,0,
+ &p->Header,p->Header.Size,&cbReturned,&p->Signal);
+}
+
+/*
+Read to the supplied packet from the pin
+Asynchronous
+Should return false on success
+*/
+static BOOL PinRead(HANDLE h, DATAPACKET* p)
+{
+ unsigned long cbReturned = 0;
+ return DeviceIoControl(h,IOCTL_KS_READ_STREAM,NULL,0,
+ &p->Header,p->Header.Size,&cbReturned,&p->Signal);
+}
+
+/*
+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;
+ }
+ }
+}
+
+static DWORD WINAPI ProcessingThread(LPVOID pParam)
+{
+ PaWinWdmStream *stream = (PaWinWdmStream*)pParam;
+ PaStreamCallbackTimeInfo ti;
+ int cbResult = paContinue;
+ int inbuf = 0;
+ int outbuf = 0;
+ int pending = 0;
+ PaError result;
+ unsigned long wait;
+ unsigned long eventSignaled;
+ int fillPlaybuf = 0;
+ int emptyRecordbuf = 0;
+ int framesProcessed;
+ unsigned long timeout;
+ int i;
+ int doChannelCopy;
+ int priming = 0;
+ PaStreamCallbackFlags underover = 0;
+
+ PA_LOGE_;
+
+ ti.inputBufferAdcTime = 0.0;
+ ti.currentTime = 0.0;
+ ti.outputBufferDacTime = 0.0;
+
+ /* Get double buffering going */
+
+ /* Submit buffers */
+ if (stream->playbackPin)
+ {
+ result = PinSetState(stream->playbackPin, KSSTATE_RUN);
+
+ PA_DEBUG(("play state run = %d;",(int)result));
+ SetEvent(stream->events[outbuf+2]);
+ outbuf = (outbuf+1)&1;
+ SetEvent(stream->events[outbuf+2]);
+ outbuf = (outbuf+1)&1;
+ pending += 2;
+ priming += 4;
+ }
+ if (stream->recordingPin)
+ {
+ result = PinSetState(stream->recordingPin, KSSTATE_RUN);
+
+ PA_DEBUG(("recording state run = %d;",(int)result));
+ PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
+ inbuf = (inbuf+1)&1; // Increment and wrap
+ PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
+ inbuf = (inbuf+1)&1; // Increment and wrap
+ /* FIXME - do error checking */
+ pending += 2;
+ }
+ PA_DEBUG(("Out buffer len:%f\n",(2000*stream->framesPerHostOBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
+ PA_DEBUG(("In buffer len:%f\n",(2000*stream->framesPerHostIBuffer) / stream->streamRepresentation.streamInfo.sampleRate));
+ timeout = max(
+ ((2000*(DWORD)stream->framesPerHostOBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate),
+ ((2000*(DWORD)stream->framesPerHostIBuffer) / (DWORD)stream->streamRepresentation.streamInfo.sampleRate)
+ );
+ timeout = max(timeout,1);
+ PA_DEBUG(("Timeout = %ld ",timeout));
+
+ while(!stream->streamAbort)
+ {
+ fillPlaybuf = 0;
+ emptyRecordbuf = 0;
+
+ /* Wait for next input or output buffer to be finished with*/
+ assert(pending>0);
+
+ if (stream->streamStop)
+ {
+ PA_DEBUG(("ss1:pending=%d ",pending));
+ }
+ wait = WaitForMultipleObjects(5, stream->events, FALSE, 0);
+ if ( wait == WAIT_TIMEOUT )
+ {
+ /* No (under|over)flow has ocurred */
+ wait = WaitForMultipleObjects(5, stream->events, FALSE, timeout);
+ eventSignaled = wait - WAIT_OBJECT_0;
+ }
+ else
+ {
+ eventSignaled = wait - WAIT_OBJECT_0;
+ if ( eventSignaled < 2 )
+ {
+ underover |= paInputOverflow;
+ PA_DEBUG(("Input overflow\n"));
+ }
+ else if (( eventSignaled < 4 )&&(!priming))
+ {
+ underover |= paOutputUnderflow;
+ PA_DEBUG(("Output underflow\n"));
+ }
+ }
+
+ if (stream->streamStop)
+ {
+ PA_DEBUG(("ss2:wait=%ld",wait));
+ }
+ if (wait == WAIT_FAILED)
+ {
+ PA_DEBUG(("Wait fail = %ld! ",wait));
+ break;
+ }
+ if (wait == WAIT_TIMEOUT)
+ {
+ continue;
+ }
+
+ if (eventSignaled < 2)
+ { /* Recording input buffer has been filled */
+ PA_DEBUG(("R"));
+ if (stream->playbackPin)
+ {
+ /* First check if also the next playback buffer has been signaled */
+ wait = WaitForSingleObject(stream->events[outbuf+2],0);
+ if (wait == WAIT_OBJECT_0)
+ {
+ /* Yes, so do both buffers at same time */
+ fillPlaybuf = 1;
+ pending--;
+ /* Was this an underflow situation? */
+ if ( underover )
+ underover |= paOutputUnderflow; /* Yes! */
+ }
+ }
+ emptyRecordbuf = 1;
+ pending--;
+ }
+ else if (eventSignaled < 4)
+ { /* Playback output buffer has been emptied */
+ if (stream->recordingPin)
+ {
+ /* First check if also the next recording buffer has been signaled */
+ wait = WaitForSingleObject(stream->events[inbuf],0);
+ if (wait == WAIT_OBJECT_0)
+ { /* Yes, so do both buffers at same time */
+ emptyRecordbuf = 1;
+ pending--;
+ /* Was this an overflow situation? */
+ if ( underover )
+ underover |= paInputOverflow; /* Yes! */
+ }
+ }
+ fillPlaybuf = 1;
+ pending--;
+ }
+ else
+ {
+ /* Abort event! */
+ assert(stream->streamAbort); /* Should have been set */
+ PA_DEBUG(("ABORTING "));
+ break;
+ }
+ ResetEvent(stream->events[eventSignaled]);
+
+ if (stream->streamStop)
+ {
+ PA_DEBUG(("Stream stop! pending=%d",pending));
+ cbResult = paComplete; /* Stop, but play remaining buffers */
+ }
+
+ /* Do necessary buffer processing (which will invoke user callback if necessary */
+ doChannelCopy = 0;
+ if (cbResult==paContinue)
+ {
+ PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
+ PaUtil_BeginBufferProcessing(&stream->bufferProcessor,&ti,underover);
+ underover = 0; /* Reset the (under|over)flow status */
+ if (fillPlaybuf)
+ {
+ PaUtil_SetOutputFrameCount(&stream->bufferProcessor,0);
+ if ( stream->userOutputChannels == 1 )
+ {
+ /* Write the single user channel to the first interleaved block */
+ PaUtil_SetOutputChannel(&stream->bufferProcessor,0,stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels);
+ /* We will do a copy to the other channels after the data has been written */
+ doChannelCopy = 1;
+ }
+ else
+ {
+ for (i=0;i<stream->userOutputChannels;i++)
+ {
+ /* Only write the user output channels. Leave the rest blank */
+ PaUtil_SetOutputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[outbuf+2].Header.Data))+(i*stream->outputSampleSize),stream->deviceOutputChannels);
+ }
+ }
+ }
+ if (emptyRecordbuf)
+ {
+ PaUtil_SetInputFrameCount(&stream->bufferProcessor,stream->packets[inbuf].Header.DataUsed/stream->bytesPerInputFrame);
+ for (i=0;i<stream->userInputChannels;i++)
+ {
+ /* Only read as many channels as the user wants */
+ PaUtil_SetInputChannel(&stream->bufferProcessor,i,((unsigned char*)(stream->packets[inbuf].Header.Data))+(i*stream->inputSampleSize),stream->deviceInputChannels);
+ }
+ }
+ framesProcessed = PaUtil_EndBufferProcessing(&stream->bufferProcessor,&cbResult);
+ if ( doChannelCopy )
+ {
+ /* Copy the first output channel to the other channels */
+ if ( stream->outputSampleSize == 2 )
+ {
+ DuplicateFirstChannelInt16(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
+ }
+ else if ( stream->outputSampleSize == 3 )
+ {
+ DuplicateFirstChannelInt24(stream->packets[outbuf+2].Header.Data,stream->deviceOutputChannels,stream->framesPerHostOBuffer);
+ }
+ else
+ assert(0); /* Unsupported format! */
+ }
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
+ }
+ else
+ {
+ fillPlaybuf = 0;
+ emptyRecordbuf = 0;
+ }
+ /*
+ if (cbResult != paContinue)
+ {
+ PA_DEBUG(("cbResult=%d, pending=%d:",cbResult,pending));
+ }
+ */
+ /* Submit buffers */
+ if ((fillPlaybuf)&&(cbResult!=paAbort))
+ {
+ if (!PinWrite(stream->playbackPin->handle,&stream->packets[outbuf+2]))
+ outbuf = (outbuf+1)&1; /* Increment and wrap */
+ pending++;
+ if ( priming )
+ priming--; /* Have to prime twice */
+ }
+ if ((emptyRecordbuf)&&(cbResult==paContinue))
+ {
+ stream->packets[inbuf].Header.DataUsed = 0; /* Reset for reuse */
+ PinRead(stream->recordingPin->handle,&stream->packets[inbuf]);
+ inbuf = (inbuf+1)&1; /* Increment and wrap */
+ pending++;
+ }
+ if (pending==0)
+ {
+ PA_DEBUG(("pending==0 finished...;"));
+ break;
+ }
+ if ((!stream->playbackPin)&&(cbResult!=paContinue))
+ {
+ PA_DEBUG(("record only cbResult=%d...;",cbResult));
+ break;
+ }
+ }
+
+ PA_DEBUG(("Finished thread"));
+
+ /* Finished, either normally or aborted */
+ if (stream->playbackPin)
+ {
+ result = PinSetState(stream->playbackPin, KSSTATE_PAUSE);
+ result = PinSetState(stream->playbackPin, KSSTATE_STOP);
+ }
+ if (stream->recordingPin)
+ {
+ result = PinSetState(stream->recordingPin, KSSTATE_PAUSE);
+ result = PinSetState(stream->recordingPin, KSSTATE_STOP);
+ }
+
+ stream->streamActive = 0;
+
+ if ((!stream->streamStop)&&(!stream->streamAbort))
+ {
+ /* Invoke the user stream finished callback */
+ /* Only do it from here if not being stopped/aborted by user */
+ if( stream->streamRepresentation.streamFinishedCallback != 0 )
+ stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
+ }
+ stream->streamStop = 0;
+ stream->streamAbort = 0;
+
+ /* Reset process priority if necessary */
+ if (stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
+ {
+ SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
+ stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+ }
+
+ PA_LOGL_;
+ ExitThread(0);
+ return 0;
+}
+
+static PaError StartStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+ DWORD dwID;
+ BOOL ret;
+ int size;
+
+ PA_LOGE_;
+
+ stream->streamStop = 0;
+ stream->streamAbort = 0;
+ size = 5;
+ while (size--)
+ {
+ if (stream->events[size] != NULL)
+ {
+ ResetEvent(stream->events[size]);
+ }
+ }
+
+ 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->streamStarted = 1;
+ stream->streamThread = CreateThread(NULL, 0, ProcessingThread, stream, 0, &dwID);
+ if (stream->streamThread == NULL)
+ {
+ stream->streamStarted = 0;
+ result = paInsufficientMemory;
+ goto end;
+ }
+ ret = SetThreadPriority(stream->streamThread,THREAD_PRIORITY_TIME_CRITICAL);
+ PA_DEBUG(("Priority ret = %d;",ret));
+ /* Make the stream active */
+ stream->streamActive = 1;
+
+end:
+ PA_LOGL_;
+ return result;
+}
+
+
+static PaError StopStream( PaStream *s )
+{
+ PaError result = paNoError;
+ PaWinWdmStream *stream = (PaWinWdmStream*)s;
+ int doCb = 0;
+
+ PA_LOGE_;
+
+ if (stream->streamActive)
+ {
+ doCb = 1;
+ stream->streamStop = 1;
+ while (stream->streamActive)
+ {
+ PA_DEBUG(("W."));
+ Sleep(10); /* Let thread sleep for 10 msec */
+ }
+ }
+
+ PA_DEBUG(("Terminating thread"));
+ if (stream->streamStarted && stream->streamThread)
+ {
+ TerminateThread(stream->streamThread,0);
+ stream->streamThread = NULL;
+ }
+
+ stream->streamStarted = 0;
+
+ if (stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
+ {
+ SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
+ stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+ }
+
+ 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->events[4]); /* Signal immediately */
+ while (stream->streamActive)
+ {
+ Sleep(10);
+ }
+ }
+
+ if (stream->streamStarted && stream->streamThread)
+ {
+ TerminateThread(stream->streamThread,0);
+ stream->streamThread = NULL;
+ }
+
+ stream->streamStarted = 0;
+
+ if (stream->oldProcessPriority != REALTIME_PRIORITY_CLASS)
+ {
+ SetPriorityClass(GetCurrentProcess(),stream->oldProcessPriority);
+ stream->oldProcessPriority = REALTIME_PRIORITY_CLASS;
+ }
+
+ 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 paNoError;
+}
+
+
+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 paNoError;
+}
+
+
+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;
+}
+
+
+
+
diff --git a/pd/portaudio/pa_win_wdmks/readme.txt b/pd/portaudio/pa_win_wdmks/readme.txt
new file mode 100644
index 00000000..2fc6c75c
--- /dev/null
+++ b/pd/portaudio/pa_win_wdmks/readme.txt
@@ -0,0 +1,71 @@
+Notes about WDM-KS host API
+---------------------------
+
+Status history
+--------------
+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 penatly 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 undefine PA_NO_WDMKS from 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). \ No newline at end of file