309 lines
11 KiB
C
309 lines
11 KiB
C
/*
|
|
* PortAudio Portable Real-Time Audio Library
|
|
* Windows WDM KS utilities
|
|
*
|
|
* Copyright (c) 1999 - 2007 Andrew Baldwin, Ross Bencina
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files
|
|
* (the "Software"), to deal in the Software without restriction,
|
|
* including without limitation the rights to use, copy, modify, merge,
|
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
* and to permit persons to whom the Software is furnished to do so,
|
|
* subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
|
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* The text above constitutes the entire PortAudio license; however,
|
|
* the PortAudio community also makes the following non-binding requests:
|
|
*
|
|
* Any person wishing to distribute modifications to the Software is
|
|
* requested to send the modifications to the original developer so that
|
|
* they can be incorporated into the canonical version. It is also
|
|
* requested that these non-binding requests be included along with the
|
|
* license above.
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <mmreg.h>
|
|
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
|
#define WAVE_FORMAT_IEEE_FLOAT 0x0003 // MinGW32 does not define this
|
|
#endif
|
|
#ifndef _WAVEFORMATEXTENSIBLE_
|
|
#define _WAVEFORMATEXTENSIBLE_ // MinGW32 does not define this
|
|
#endif
|
|
#ifndef _INC_MMREG
|
|
#define _INC_MMREG // for STATIC_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
|
|
#endif
|
|
#include <winioctl.h> // MinGW32 does not define this automatically
|
|
|
|
#if defined(__GNUC__)
|
|
|
|
#include "../../hostapi/wasapi/mingw-include/ks.h"
|
|
#include "../../hostapi/wasapi/mingw-include/ksmedia.h"
|
|
|
|
#else
|
|
|
|
#include <ks.h>
|
|
#include <ksmedia.h>
|
|
|
|
#endif
|
|
|
|
#include <stdio.h> // just for some development printfs
|
|
|
|
#include "portaudio.h"
|
|
#include "pa_util.h"
|
|
#include "pa_win_wdmks_utils.h"
|
|
|
|
|
|
/* PortAudio-local instances of GUIDs previously sourced from ksguid.lib */
|
|
|
|
/* GUID KSDATAFORMAT_TYPE_AUDIO */
|
|
static const GUID pa_KSDATAFORMAT_TYPE_AUDIO = { STATIC_KSDATAFORMAT_TYPE_AUDIO };
|
|
|
|
/* GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT */
|
|
static const GUID pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { STATIC_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT };
|
|
|
|
/* GUID KSDATAFORMAT_SUBTYPE_PCM */
|
|
static const GUID pa_KSDATAFORMAT_SUBTYPE_PCM = { STATIC_KSDATAFORMAT_SUBTYPE_PCM };
|
|
|
|
/* GUID KSDATAFORMAT_SUBTYPE_WAVEFORMATEX */
|
|
static const GUID pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX = { STATIC_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX };
|
|
|
|
/* GUID KSMEDIUMSETID_Standard */
|
|
static const GUID pa_KSMEDIUMSETID_Standard = { STATIC_KSMEDIUMSETID_Standard };
|
|
|
|
/* GUID KSINTERFACESETID_Standard */
|
|
static const GUID pa_KSINTERFACESETID_Standard = { STATIC_KSINTERFACESETID_Standard };
|
|
|
|
/* GUID KSPROPSETID_Pin */
|
|
static const GUID pa_KSPROPSETID_Pin = { STATIC_KSPROPSETID_Pin };
|
|
|
|
#define pa_IS_VALID_WAVEFORMATEX_GUID(Guid)\
|
|
(!memcmp(((PUSHORT)&pa_KSDATAFORMAT_SUBTYPE_WAVEFORMATEX) + 1, ((PUSHORT)(Guid)) + 1, sizeof(GUID) - sizeof(USHORT)))
|
|
|
|
|
|
static PaError WdmGetPinPropertySimple(
|
|
HANDLE handle,
|
|
unsigned long pinId,
|
|
unsigned long property,
|
|
void* value,
|
|
unsigned long valueSize )
|
|
{
|
|
DWORD bytesReturned;
|
|
KSP_PIN ksPProp;
|
|
ksPProp.Property.Set = pa_KSPROPSETID_Pin;
|
|
ksPProp.Property.Id = property;
|
|
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
|
|
ksPProp.PinId = pinId;
|
|
ksPProp.Reserved = 0;
|
|
|
|
if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN),
|
|
value, valueSize, &bytesReturned, NULL ) == 0 || bytesReturned != valueSize )
|
|
{
|
|
return paUnanticipatedHostError;
|
|
}
|
|
else
|
|
{
|
|
return paNoError;
|
|
}
|
|
}
|
|
|
|
|
|
static PaError WdmGetPinPropertyMulti(
|
|
HANDLE handle,
|
|
unsigned long pinId,
|
|
unsigned long property,
|
|
KSMULTIPLE_ITEM** ksMultipleItem)
|
|
{
|
|
unsigned long multipleItemSize = 0;
|
|
KSP_PIN ksPProp;
|
|
DWORD bytesReturned;
|
|
|
|
*ksMultipleItem = 0;
|
|
|
|
ksPProp.Property.Set = pa_KSPROPSETID_Pin;
|
|
ksPProp.Property.Id = property;
|
|
ksPProp.Property.Flags = KSPROPERTY_TYPE_GET;
|
|
ksPProp.PinId = pinId;
|
|
ksPProp.Reserved = 0;
|
|
|
|
if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp.Property,
|
|
sizeof(KSP_PIN), NULL, 0, &multipleItemSize, NULL ) == 0 && GetLastError() != ERROR_MORE_DATA )
|
|
{
|
|
return paUnanticipatedHostError;
|
|
}
|
|
|
|
*ksMultipleItem = (KSMULTIPLE_ITEM*)PaUtil_AllocateMemory( multipleItemSize );
|
|
if( !*ksMultipleItem )
|
|
{
|
|
return paInsufficientMemory;
|
|
}
|
|
|
|
if( DeviceIoControl( handle, IOCTL_KS_PROPERTY, &ksPProp, sizeof(KSP_PIN),
|
|
(void*)*ksMultipleItem, multipleItemSize, &bytesReturned, NULL ) == 0 || bytesReturned != multipleItemSize )
|
|
{
|
|
PaUtil_FreeMemory( ksMultipleItem );
|
|
return paUnanticipatedHostError;
|
|
}
|
|
|
|
return paNoError;
|
|
}
|
|
|
|
|
|
static int GetKSFilterPinCount( HANDLE deviceHandle )
|
|
{
|
|
DWORD result;
|
|
|
|
if( WdmGetPinPropertySimple( deviceHandle, 0, KSPROPERTY_PIN_CTYPES, &result, sizeof(result) ) == paNoError ){
|
|
return result;
|
|
}else{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
static KSPIN_COMMUNICATION GetKSFilterPinPropertyCommunication( HANDLE deviceHandle, int pinId )
|
|
{
|
|
KSPIN_COMMUNICATION result;
|
|
|
|
if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_COMMUNICATION, &result, sizeof(result) ) == paNoError ){
|
|
return result;
|
|
}else{
|
|
return KSPIN_COMMUNICATION_NONE;
|
|
}
|
|
}
|
|
|
|
|
|
static KSPIN_DATAFLOW GetKSFilterPinPropertyDataflow( HANDLE deviceHandle, int pinId )
|
|
{
|
|
KSPIN_DATAFLOW result;
|
|
|
|
if( WdmGetPinPropertySimple( deviceHandle, pinId, KSPROPERTY_PIN_DATAFLOW, &result, sizeof(result) ) == paNoError ){
|
|
return result;
|
|
}else{
|
|
return (KSPIN_DATAFLOW)0;
|
|
}
|
|
}
|
|
|
|
|
|
static int KSFilterPinPropertyIdentifiersInclude(
|
|
HANDLE deviceHandle, int pinId, unsigned long property, const GUID *identifierSet, unsigned long identifierId )
|
|
{
|
|
KSMULTIPLE_ITEM* item = NULL;
|
|
KSIDENTIFIER* identifier;
|
|
int i;
|
|
int result = 0;
|
|
|
|
if( WdmGetPinPropertyMulti( deviceHandle, pinId, property, &item) != paNoError )
|
|
return 0;
|
|
|
|
identifier = (KSIDENTIFIER*)(item+1);
|
|
|
|
for( i = 0; i < (int)item->Count; i++ )
|
|
{
|
|
if( !memcmp( (void*)&identifier[i].Set, (void*)identifierSet, sizeof( GUID ) ) &&
|
|
( identifier[i].Id == identifierId ) )
|
|
{
|
|
result = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
PaUtil_FreeMemory( item );
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* return the maximum channel count supported by any pin on the device.
|
|
if isInput is non-zero we query input pins, otherwise output pins.
|
|
*/
|
|
int PaWin_WDMKS_QueryFilterMaximumChannelCount( void *wcharDevicePath, int isInput )
|
|
{
|
|
HANDLE deviceHandle;
|
|
ULONG i;
|
|
int pinCount, pinId;
|
|
int result = 0;
|
|
KSPIN_DATAFLOW requiredDataflowDirection = (isInput ? KSPIN_DATAFLOW_OUT : KSPIN_DATAFLOW_IN );
|
|
|
|
if( !wcharDevicePath )
|
|
return 0;
|
|
|
|
deviceHandle = CreateFileW( (LPCWSTR)wcharDevicePath, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL );
|
|
if( deviceHandle == INVALID_HANDLE_VALUE )
|
|
return 0;
|
|
|
|
pinCount = GetKSFilterPinCount( deviceHandle );
|
|
for( pinId = 0; pinId < pinCount; ++pinId )
|
|
{
|
|
KSPIN_COMMUNICATION communication = GetKSFilterPinPropertyCommunication( deviceHandle, pinId );
|
|
KSPIN_DATAFLOW dataflow = GetKSFilterPinPropertyDataflow( deviceHandle, pinId );
|
|
if( ( dataflow == requiredDataflowDirection ) &&
|
|
(( communication == KSPIN_COMMUNICATION_SINK) ||
|
|
( communication == KSPIN_COMMUNICATION_BOTH))
|
|
&& ( KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
|
|
KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_STREAMING )
|
|
|| KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
|
|
KSPROPERTY_PIN_INTERFACES, &pa_KSINTERFACESETID_Standard, KSINTERFACE_STANDARD_LOOPED_STREAMING ) )
|
|
&& KSFilterPinPropertyIdentifiersInclude( deviceHandle, pinId,
|
|
KSPROPERTY_PIN_MEDIUMS, &pa_KSMEDIUMSETID_Standard, KSMEDIUM_STANDARD_DEVIO ) )
|
|
{
|
|
KSMULTIPLE_ITEM* item = NULL;
|
|
if( WdmGetPinPropertyMulti( deviceHandle, pinId, KSPROPERTY_PIN_DATARANGES, &item ) == paNoError )
|
|
{
|
|
KSDATARANGE *dataRange = (KSDATARANGE*)(item+1);
|
|
|
|
for( i=0; i < item->Count; ++i ){
|
|
|
|
if( pa_IS_VALID_WAVEFORMATEX_GUID(&dataRange->SubFormat)
|
|
|| memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_PCM, sizeof(GUID) ) == 0
|
|
|| memcmp( (void*)&dataRange->SubFormat, (void*)&pa_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, sizeof(GUID) ) == 0
|
|
|| ( ( memcmp( (void*)&dataRange->MajorFormat, (void*)&pa_KSDATAFORMAT_TYPE_AUDIO, sizeof(GUID) ) == 0 )
|
|
&& ( memcmp( (void*)&dataRange->SubFormat, (void*)&KSDATAFORMAT_SUBTYPE_WILDCARD, sizeof(GUID) ) == 0 ) ) )
|
|
{
|
|
KSDATARANGE_AUDIO *dataRangeAudio = (KSDATARANGE_AUDIO*)dataRange;
|
|
|
|
/*
|
|
printf( ">>> %d %d %d %d %S\n", isInput, dataflow, communication, dataRangeAudio->MaximumChannels, devicePath );
|
|
|
|
if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WAVEFORMATEX, sizeof(GUID) ) == 0 )
|
|
printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WAVEFORMATEX\n" );
|
|
else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_DSOUND, sizeof(GUID) ) == 0 )
|
|
printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_DSOUND\n" );
|
|
else if( memcmp((void*)&dataRange->Specifier, (void*)&KSDATAFORMAT_SPECIFIER_WILDCARD, sizeof(GUID) ) == 0 )
|
|
printf( "\tspecifier: KSDATAFORMAT_SPECIFIER_WILDCARD\n" );
|
|
else
|
|
printf( "\tspecifier: ?\n" );
|
|
*/
|
|
|
|
/*
|
|
We assume that very high values for MaximumChannels are not useful and indicate
|
|
that the driver isn't prepared to tell us the real number of channels which it supports.
|
|
*/
|
|
if( dataRangeAudio->MaximumChannels < 0xFFFFUL && (int)dataRangeAudio->MaximumChannels > result )
|
|
result = (int)dataRangeAudio->MaximumChannels;
|
|
}
|
|
|
|
dataRange = (KSDATARANGE*)( ((char*)dataRange) + dataRange->FormatSize);
|
|
}
|
|
|
|
PaUtil_FreeMemory( item );
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle( deviceHandle );
|
|
return result;
|
|
}
|