From 21c068f1916330e90f814bed461fe0821d1665ec Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Sun, 9 Oct 2011 16:36:37 +0000 Subject: checked in pd-0.43-0.src.tar.gz svn path=/trunk/; revision=15557 --- pd/portaudio/src/hostapi/wmme/pa_win_wmme.c | 565 ++++++++++++++++++++++------ 1 file changed, 444 insertions(+), 121 deletions(-) (limited to 'pd/portaudio/src/hostapi/wmme') diff --git a/pd/portaudio/src/hostapi/wmme/pa_win_wmme.c b/pd/portaudio/src/hostapi/wmme/pa_win_wmme.c index 3264ebf8..d2c13f99 100644 --- a/pd/portaudio/src/hostapi/wmme/pa_win_wmme.c +++ b/pd/portaudio/src/hostapi/wmme/pa_win_wmme.c @@ -1,5 +1,5 @@ /* - * $Id: pa_win_wmme.c 1229 2007-06-15 16:11:11Z rossb $ + * $Id: pa_win_wmme.c 1405 2009-03-08 08:10:55Z rossb $ * pa_win_wmme.c * Implementation of PortAudio for Windows MultiMedia Extensions (WMME) * @@ -62,7 +62,9 @@ */ /** @file - @ingroup hostaip_src + @ingroup hostapi_src + + @brief Win32 host API implementation for the Windows MultiMedia Extensions (WMME) audio API. @todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now, needs to be reviewed and tested.) @@ -88,6 +90,10 @@ Non-critical stuff for the future: @todo define UNICODE and _UNICODE in the project settings and see what breaks + @todo refactor conversion of MMSYSTEM errors into PA arrors into a single function. + + @todo cleanup WAVEFORMATEXTENSIBLE retry in InitializeWaveHandles to not use a for loop + */ /* @@ -133,6 +139,24 @@ Non-critical stuff for the future: #include "pa_debugprint.h" #include "pa_win_wmme.h" +#include "pa_win_waveformat.h" + +#ifdef PAWIN_USE_WDMKS_DEVICE_INFO +#include "pa_win_wdmks_utils.h" +#ifndef DRV_QUERYDEVICEINTERFACE +#define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12) +#endif +#ifndef DRV_QUERYDEVICEINTERFACESIZE +#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13) +#endif +#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ + +/* use CreateThread for CYGWIN, _beginthreadex for all others */ +#ifndef __CYGWIN__ +#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ) +#else +#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ) +#endif #if (defined(UNDER_CE)) #pragma comment(lib, "Coredll.lib") @@ -374,6 +398,8 @@ typedef struct { PaDeviceInfo inheritedDeviceInfo; DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */ + char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/ + char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/ } PaWinMmeDeviceInfo; @@ -445,6 +471,21 @@ static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hos } +static int SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( PaSampleFormat sampleFormat, unsigned long winMmeSpecificFlags ) +{ + int waveFormatTag = 0; + + if( winMmeSpecificFlags & paWinMmeWaveFormatDolbyAc3Spdif ) + waveFormatTag = PAWIN_WAVE_FORMAT_DOLBY_AC3_SPDIF; + else if( winMmeSpecificFlags & paWinMmeWaveFormatWmaSpdif ) + waveFormatTag = PAWIN_WAVE_FORMAT_WMA_SPDIF; + else + waveFormatTag = PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ); + + return waveFormatTag; +} + + static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx ) { MMRESULT mmresult; @@ -499,41 +540,57 @@ static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo, PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), - int winMmeDeviceId, int channels, double sampleRate ) + int winMmeDeviceId, int channels, double sampleRate, unsigned long winMmeSpecificFlags ) { PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo; - WAVEFORMATEX waveFormatEx; + PaWinWaveFormat waveFormat; + PaSampleFormat sampleFormat; + int waveFormatTag; - if( sampleRate == 11025.0 - && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16)) - || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){ + /* @todo at the moment we only query with 16 bit sample format and directout speaker config*/ - return paNoError; - } + sampleFormat = paInt16; + waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags ); - if( sampleRate == 22050.0 - && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16)) - || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){ + if( waveFormatTag == PaWin_SampleFormatToLinearWaveFormatTag( paInt16 ) ){ + + /* attempt bypass querying the device for linear formats */ - return paNoError; + if( sampleRate == 11025.0 + && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16)) + || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){ + + return paNoError; + } + + if( sampleRate == 22050.0 + && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16)) + || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){ + + return paNoError; + } + + if( sampleRate == 44100.0 + && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16)) + || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){ + + return paNoError; + } } - if( sampleRate == 44100.0 - && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16)) - || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){ + /* first, attempt to query the device using WAVEFORMATEXTENSIBLE, + if this fails we fall back to WAVEFORMATEX */ + + PaWin_InitializeWaveFormatExtensible( &waveFormat, channels, sampleFormat, waveFormatTag, + sampleRate, PAWIN_SPEAKER_DIRECTOUT ); + + if( waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat ) == paNoError ) return paNoError; - } - waveFormatEx.wFormatTag = WAVE_FORMAT_PCM; - waveFormatEx.nChannels = (WORD)channels; - waveFormatEx.nSamplesPerSec = (DWORD)sampleRate; - waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short); - waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short)); - waveFormatEx.wBitsPerSample = 16; - waveFormatEx.cbSize = 0; + PaWin_InitializeWaveFormatEx( &waveFormat, channels, sampleFormat, waveFormatTag, sampleRate ); - return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx ); + return waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat ); } @@ -553,7 +610,7 @@ static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int w for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i ) { double sampleRate = defaultSampleRateSearchOrder_[ i ]; - PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate ); + PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate, 0 ); if( paerror == paNoError ) { deviceInfo->defaultSampleRate = sampleRate; @@ -563,6 +620,40 @@ static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int w } +#ifdef PAWIN_USE_WDMKS_DEVICE_INFO +static int QueryWaveInKSFilterMaxChannels( int waveInDeviceId, int *maxChannels ) +{ + void *devicePath; + DWORD devicePathSize; + int result = 0; + + if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE, + (DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR ) + return 0; + + devicePath = PaUtil_AllocateMemory( devicePathSize ); + if( !devicePath ) + return 0; + + /* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */ + if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACE, + (DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR ) + { + int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 1 ); + if( count > 0 ) + { + *maxChannels = count; + result = 1; + } + } + + PaUtil_FreeMemory( devicePath ); + + return result; +} +#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ + + static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success ) { @@ -616,17 +707,30 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH } deviceInfo->name = deviceName; - deviceInfo->maxInputChannels = wic.wChannels; - /* Sometimes a device can return a rediculously large number of channels. - * This happened with an SBLive card on a Windows ME box. - * If that happens, then force it to 2 channels. PLB20010413 + if( wic.wChannels == 0xFFFF || wic.wChannels < 1 || wic.wChannels > 255 ){ + /* For Windows versions using WDM (possibly Windows 98 ME and later) + * the kernel mixer sits between the application and the driver. As a result, + * wave*GetDevCaps often kernel mixer channel counts, which are unlimited. + * When this happens we assume the device is stereo and set a flag + * so that other channel counts can be tried with OpenStream -- i.e. when + * device*ChannelCountIsKnown is false, OpenStream will try whatever + * channel count you supply. + * see also InitializeOutputDeviceInfo() below. */ - if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) ) - { - PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels )); + + PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", wic.wChannels )); deviceInfo->maxInputChannels = 2; + winMmeDeviceInfo->deviceInputChannelCountIsKnown = 0; + }else{ + deviceInfo->maxInputChannels = wic.wChannels; + winMmeDeviceInfo->deviceInputChannelCountIsKnown = 1; } +#ifdef PAWIN_USE_WDMKS_DEVICE_INFO + winMmeDeviceInfo->deviceInputChannelCountIsKnown = + QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels ); +#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ + winMmeDeviceInfo->dwFormats = wic.dwFormats; DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId, @@ -639,6 +743,40 @@ error: } +#ifdef PAWIN_USE_WDMKS_DEVICE_INFO +static int QueryWaveOutKSFilterMaxChannels( int waveOutDeviceId, int *maxChannels ) +{ + void *devicePath; + DWORD devicePathSize; + int result = 0; + + if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE, + (DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR ) + return 0; + + devicePath = PaUtil_AllocateMemory( devicePathSize ); + if( !devicePath ) + return 0; + + /* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */ + if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACE, + (DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR ) + { + int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 0 ); + if( count > 0 ) + { + *maxChannels = count; + result = 1; + } + } + + PaUtil_FreeMemory( devicePath ); + + return result; +} +#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ + + static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success ) { @@ -647,7 +785,7 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme MMRESULT mmresult; WAVEOUTCAPS woc; PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo; - + *success = 0; mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) ); @@ -692,17 +830,30 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme } deviceInfo->name = deviceName; - deviceInfo->maxOutputChannels = woc.wChannels; - /* Sometimes a device can return a rediculously large number of channels. - * This happened with an SBLive card on a Windows ME box. - * It also happens on Win XP! + if( woc.wChannels == 0xFFFF || woc.wChannels < 1 || woc.wChannels > 255 ){ + /* For Windows versions using WDM (possibly Windows 98 ME and later) + * the kernel mixer sits between the application and the driver. As a result, + * wave*GetDevCaps often kernel mixer channel counts, which are unlimited. + * When this happens we assume the device is stereo and set a flag + * so that other channel counts can be tried with OpenStream -- i.e. when + * device*ChannelCountIsKnown is false, OpenStream will try whatever + * channel count you supply. + * see also InitializeInputDeviceInfo() above. */ - if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) ) - { - PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels )); + + PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", woc.wChannels )); deviceInfo->maxOutputChannels = 2; + winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 0; + }else{ + deviceInfo->maxOutputChannels = woc.wChannels; + winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1; } +#ifdef PAWIN_USE_WDMKS_DEVICE_INFO + winMmeDeviceInfo->deviceOutputChannelCountIsKnown = + QueryWaveOutKSFilterMaxChannels( winMmeOutputDeviceId, &deviceInfo->maxOutputChannels ); +#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */ + winMmeDeviceInfo->dwFormats = woc.dwFormats; DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId, @@ -748,6 +899,8 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd PaWinMmeDeviceInfo *deviceInfoArray; int deviceInfoInitializationSucceeded; PaTime defaultLowLatency, defaultHighLatency; + DWORD waveInPreferredDevice, waveOutPreferredDevice; + DWORD preferredDeviceStatusFlags; winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) ); if( !winMmeHostApi ) @@ -779,6 +932,19 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd winMmeHostApi->inputDeviceCount = 0; winMmeHostApi->outputDeviceCount = 0; +#if !defined(DRVM_MAPPER_PREFERRED_GET) +/* DRVM_MAPPER_PREFERRED_GET is defined in mmddk.h but we avoid a dependency on the DDK by defining it here */ +#define DRVM_MAPPER_PREFERRED_GET (0x2000+21) +#endif + + /* the following calls assume that if wave*Message fails the preferred device parameter won't be modified */ + preferredDeviceStatusFlags = 0; + waveInPreferredDevice = -1; + waveInMessage( (HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD)&waveInPreferredDevice, (DWORD)&preferredDeviceStatusFlags ); + + preferredDeviceStatusFlags = 0; + waveOutPreferredDevice = -1; + waveOutMessage( (HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD)&waveOutPreferredDevice, (DWORD)&preferredDeviceStatusFlags ); maximumPossibleDeviceCount = 0; @@ -830,7 +996,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd deviceInfo->hostApi = hostApiIndex; deviceInfo->maxInputChannels = 0; + wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1; deviceInfo->maxOutputChannels = 0; + wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1; deviceInfo->defaultLowInputLatency = defaultLowLatency; deviceInfo->defaultLowOutputLatency = defaultLowLatency; @@ -843,8 +1011,14 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd goto error; if( deviceInfoInitializationSucceeded ){ - if( (*hostApi)->info.defaultInputDevice == paNoDevice ) + if( (*hostApi)->info.defaultInputDevice == paNoDevice ){ + /* if there is currently no default device, use the first one available */ + (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; + + }else if( winMmeDeviceId == waveInPreferredDevice ){ + /* set the default device to the system preferred device */ (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; + } winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId; (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; @@ -865,7 +1039,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd deviceInfo->hostApi = hostApiIndex; deviceInfo->maxInputChannels = 0; + wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1; deviceInfo->maxOutputChannels = 0; + wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1; deviceInfo->defaultLowInputLatency = defaultLowLatency; deviceInfo->defaultLowOutputLatency = defaultLowLatency; @@ -878,8 +1054,14 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd goto error; if( deviceInfoInitializationSucceeded ){ - if( (*hostApi)->info.defaultOutputDevice == paNoDevice ) + if( (*hostApi)->info.defaultOutputDevice == paNoDevice ){ + /* if there is currently no default device, use the first one available */ + (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; + + }else if( winMmeDeviceId == waveOutPreferredDevice ){ + /* set the default device to the system preferred device */ (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; + } winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId; (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; @@ -891,7 +1073,6 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd } } - InitializeDefaultDeviceIdsFromEnv( winMmeHostApi ); (*hostApi)->Terminate = Terminate; @@ -941,6 +1122,34 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) } +static PaError IsInputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount ) +{ + PaError result = paNoError; + + if( channelCount > 0 + && deviceInfo->deviceInputChannelCountIsKnown + && channelCount > deviceInfo->inheritedDeviceInfo.maxInputChannels ){ + + result = paInvalidChannelCount; + } + + return result; +} + +static PaError IsOutputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount ) +{ + PaError result = paNoError; + + if( channelCount > 0 + && deviceInfo->deviceOutputChannelCountIsKnown + && channelCount > deviceInfo->inheritedDeviceInfo.maxOutputChannels ){ + + result = paInvalidChannelCount; + } + + return result; +} + static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, @@ -985,13 +1194,19 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ]; /* check that input device can support inputChannelCount */ - if( inputStreamInfo->devices[i].channelCount <= 0 - || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels ) + if( inputStreamInfo->devices[i].channelCount < 1 ) return paInvalidChannelCount; + paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo, + inputStreamInfo->devices[i].channelCount ); + if( paerror != paNoError ) + return paerror; + /* test for valid sample rate, see comment above */ winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device ); - paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate ); + paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, + winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate, + ((inputStreamInfo) ? inputStreamInfo->flags : 0) ); if( paerror != paNoError ) return paInvalidSampleRate; } @@ -1007,12 +1222,15 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ]; /* check that input device can support inputChannelCount */ - if( inputChannelCount > inputDeviceInfo->maxInputChannels ) - return paInvalidChannelCount; + paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo, inputChannelCount ); + if( paerror != paNoError ) + return paerror; /* test for valid sample rate, see comment above */ winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device ); - paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate ); + paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, + winMmeInputDeviceId, inputChannelCount, sampleRate, + ((inputStreamInfo) ? inputStreamInfo->flags : 0) ); if( paerror != paNoError ) return paInvalidSampleRate; } @@ -1040,13 +1258,19 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ]; /* check that output device can support outputChannelCount */ - if( outputStreamInfo->devices[i].channelCount <= 0 - || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels ) + if( outputStreamInfo->devices[i].channelCount < 1 ) return paInvalidChannelCount; + paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo, + outputStreamInfo->devices[i].channelCount ); + if( paerror != paNoError ) + return paerror; + /* test for valid sample rate, see comment above */ winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device ); - paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate ); + paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, + winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate, + ((outputStreamInfo) ? outputStreamInfo->flags : 0) ); if( paerror != paNoError ) return paInvalidSampleRate; } @@ -1062,12 +1286,15 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ]; /* check that output device can support outputChannelCount */ - if( outputChannelCount > outputDeviceInfo->maxOutputChannels ) - return paInvalidChannelCount; + paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo, outputChannelCount ); + if( paerror != paNoError ) + return paerror; /* test for valid sample rate, see comment above */ winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device ); - paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate ); + paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, + winMmeOutputDeviceId, outputChannelCount, sampleRate, + ((outputStreamInfo) ? outputStreamInfo->flags : 0) ); if( paerror != paNoError ) return paInvalidSampleRate; } @@ -1491,9 +1718,10 @@ typedef struct static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers ); static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi, PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, + unsigned long winMmeSpecificFlags, unsigned long bytesPerHostSample, double sampleRate, PaWinMmeDeviceAndChannelCount *devices, - unsigned int deviceCount, int isInput ); + unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput ); static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError ); static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, unsigned long hostBufferCount, @@ -1515,15 +1743,16 @@ static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionH static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi, PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, + unsigned long winMmeSpecificFlags, unsigned long bytesPerHostSample, double sampleRate, PaWinMmeDeviceAndChannelCount *devices, - unsigned int deviceCount, int isInput ) + unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput ) { PaError result; MMRESULT mmresult; - unsigned long bytesPerFrame; - WAVEFORMATEX wfx; - signed int i; + signed int i, j; + PaSampleFormat sampleFormat; + int waveFormatTag; /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers() has already been called to zero some fields */ @@ -1551,64 +1780,98 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0; } - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nSamplesPerSec = (DWORD) sampleRate; - wfx.cbSize = 0; - + /* @todo at the moment we only use 16 bit sample format */ + sampleFormat = paInt16; + waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags ); + for( i = 0; i < (signed int)deviceCount; ++i ) { - UINT winMmeDeviceId; + PaWinWaveFormat waveFormat; + UINT winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device ); + + /* @todo: consider providing a flag or #define to not try waveformat extensible + this could just initialize j to 1 the first time round. */ - winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device ); - wfx.nChannels = (WORD)devices[i].channelCount; + for( j = 0; j < 2; ++j ) + { + if( j == 0 ) + { + /* first, attempt to open the device using WAVEFORMATEXTENSIBLE, + if this fails we fall back to WAVEFORMATEX */ - bytesPerFrame = wfx.nChannels * bytesPerHostSample; + PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount, + sampleFormat, waveFormatTag, sampleRate, channelMask ); - wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate); - wfx.nBlockAlign = (WORD)bytesPerFrame; - wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8); + } + else + { + /* retry with WAVEFORMATEX */ - /* REVIEW: consider not firing an event for input when a full duplex - stream is being used. this would probably depend on the - neverDropInput flag. */ + PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount, + sampleFormat, waveFormatTag, sampleRate ); + } - if( isInput ) - mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx, + /* REVIEW: consider not firing an event for input when a full duplex + stream is being used. this would probably depend on the + neverDropInput flag. */ + + if( isInput ) + { + mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, + (WAVEFORMATEX*)&waveFormat, (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT ); - else - mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx, + } + else + { + mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, + (WAVEFORMATEX*)&waveFormat, (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT ); + } - if( mmresult != MMSYSERR_NOERROR ) - { - switch( mmresult ) + if( mmresult == MMSYSERR_NOERROR ) { - case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ - result = paDeviceUnavailable; - break; - case MMSYSERR_NODRIVER: /* No device driver is present. */ - result = paDeviceUnavailable; - break; - case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ - result = paInsufficientMemory; - break; + break; /* success */ + } + else if( j == 0 ) + { + continue; /* try again with WAVEFORMATEX */ + } + else + { + switch( mmresult ) + { + case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ + result = paDeviceUnavailable; + break; + case MMSYSERR_NODRIVER: /* No device driver is present. */ + result = paDeviceUnavailable; + break; + case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */ + result = paInsufficientMemory; + break; - case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ - /* falls through */ - case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ - /* falls through */ - default: - result = paUnanticipatedHostError; - if( isInput ) - { - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - else - { - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } + case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ + /* falls through */ + + case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ + /* This can also occur if we try to open the device with an unsupported + * number of channels. This is attempted when device*ChannelCountIsKnown is + * set to 0. + */ + /* falls through */ + default: + result = paUnanticipatedHostError; + if( isInput ) + { + PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); + } + else + { + PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); + } + } + goto error; } - goto error; } } @@ -1850,6 +2113,7 @@ struct PaWinMmeStream static PaError ValidateWinMmeSpecificStreamInfo( const PaStreamParameters *streamParameters, const PaWinMmeStreamInfo *streamInfo, + unsigned long *winMmeSpecificFlags, char *throttleProcessingThreadOnOverload, unsigned long *deviceCount ) { @@ -1861,6 +2125,8 @@ static PaError ValidateWinMmeSpecificStreamInfo( return paIncompatibleHostApiSpecificStreamInfo; } + *winMmeSpecificFlags = streamInfo->flags; + if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread ) *throttleProcessingThreadOnOverload = 0; @@ -1926,12 +2192,20 @@ static PaError ValidateInputChannelCounts( unsigned long deviceCount ) { unsigned int i; + PaWinMmeDeviceInfo *inputDeviceInfo; + PaError paerror; for( i=0; i < deviceCount; ++i ) { - if( devices[i].channelCount < 1 || devices[i].channelCount - > hostApi->deviceInfos[ devices[i].device ]->maxInputChannels ) + if( devices[i].channelCount < 1 ) return paInvalidChannelCount; + + inputDeviceInfo = + (PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ]; + + paerror = IsInputChannelCountSupported( inputDeviceInfo, devices[i].channelCount ); + if( paerror != paNoError ) + return paerror; } return paNoError; @@ -1943,12 +2217,20 @@ static PaError ValidateOutputChannelCounts( unsigned long deviceCount ) { unsigned int i; + PaWinMmeDeviceInfo *outputDeviceInfo; + PaError paerror; for( i=0; i < deviceCount; ++i ) { - if( devices[i].channelCount < 1 || devices[i].channelCount - > hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels ) + if( devices[i].channelCount < 1 ) return paInvalidChannelCount; + + outputDeviceInfo = + (PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ]; + + paerror = IsOutputChannelCountSupported( outputDeviceInfo, devices[i].channelCount ); + if( paerror != paNoError ) + return paerror; } return paNoError; @@ -1981,14 +2263,17 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, PaSampleFormat inputSampleFormat, outputSampleFormat; double suggestedInputLatency, suggestedOutputLatency; PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo; + PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask; unsigned long framesPerHostInputBuffer; unsigned long hostInputBufferCount; unsigned long framesPerHostOutputBuffer; unsigned long hostOutputBufferCount; unsigned long framesPerBufferProcessorCall; PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */ + unsigned long winMmeSpecificInputFlags = 0; unsigned long inputDeviceCount = 0; PaWinMmeDeviceAndChannelCount *outputDevices = 0; + unsigned long winMmeSpecificOutputFlags = 0; unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */ char throttleProcessingThreadOnOverload = 1; @@ -2004,6 +2289,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, /* validate input hostApiSpecificStreamInfo */ inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo; result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo, + &winMmeSpecificInputFlags, &throttleProcessingThreadOnOverload, &inputDeviceCount ); if( result != paNoError ) return result; @@ -2019,6 +2305,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, hostInputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); + + if( inputDeviceCount != 1 ){ + /* always use direct speakers when using multi-device multichannel mode */ + inputChannelMask = PAWIN_SPEAKER_DIRECTOUT; + } + else + { + if( inputStreamInfo && inputStreamInfo->flags & paWinMmeUseChannelMask ) + inputChannelMask = inputStreamInfo->channelMask; + else + inputChannelMask = PaWin_DefaultChannelMask( inputDevices[0].channelCount ); + } } else { @@ -2041,6 +2339,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, /* validate output hostApiSpecificStreamInfo */ outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo; result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo, + &winMmeSpecificOutputFlags, &throttleProcessingThreadOnOverload, &outputDeviceCount ); if( result != paNoError ) return result; @@ -2056,6 +2355,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, hostOutputSampleFormat = PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); + + if( outputDeviceCount != 1 ){ + /* always use direct speakers when using multi-device multichannel mode */ + outputChannelMask = PAWIN_SPEAKER_DIRECTOUT; + } + else + { + if( outputStreamInfo && outputStreamInfo->flags & paWinMmeUseChannelMask ) + outputChannelMask = outputStreamInfo->channelMask; + else + outputChannelMask = PaWin_DefaultChannelMask( outputDevices[0].channelCount ); + } } else { @@ -2078,6 +2389,14 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, return paInvalidFlag; /* unexpected platform specific flag */ + /* always disable clipping and dithering if we are outputting a raw spdif stream */ + if( (winMmeSpecificOutputFlags & paWinMmeWaveFormatDolbyAc3Spdif) + || (winMmeSpecificOutputFlags & paWinMmeWaveFormatWmaSpdif) ){ + + streamFlags = streamFlags | paClipOff | paDitherOff; + } + + result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount, &framesPerHostOutputBuffer, &hostOutputBufferCount, inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo, @@ -2176,16 +2495,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, if( inputParameters ) { result = InitializeWaveHandles( winMmeHostApi, &stream->input, + winMmeSpecificInputFlags, stream->bufferProcessor.bytesPerHostInputSample, sampleRate, - inputDevices, inputDeviceCount, 1 /* isInput */ ); + inputDevices, inputDeviceCount, inputChannelMask, 1 /* isInput */ ); if( result != paNoError ) goto error; } if( outputParameters ) { result = InitializeWaveHandles( winMmeHostApi, &stream->output, + winMmeSpecificOutputFlags, stream->bufferProcessor.bytesPerHostOutputSample, sampleRate, - outputDevices, outputDeviceCount, 0 /* isInput */ ); + outputDevices, outputDeviceCount, outputChannelMask, 0 /* isInput */ ); if( result != paNoError ) goto error; } @@ -2340,6 +2661,7 @@ static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream ) for( i=0; i < stream->input.deviceCount; ++i ) { + stream->input.waveHeaders[i][ stream->input.currentBufferIndex ].dwFlags &= ~WHDR_DONE; mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i], &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ], sizeof(WAVEHDR) ); @@ -2642,7 +2964,7 @@ static DWORD WINAPI ProcessingThreadProc( void *pArg ) for( i=0; iinput.deviceCount; ++i ) { /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; + int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData + @@ -2663,7 +2985,7 @@ static DWORD WINAPI ProcessingThreadProc( void *pArg ) for( i=0; ioutput.deviceCount; ++i ) { /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; + int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + @@ -2866,6 +3188,7 @@ static PaError StartStream( PaStream *s ) { for( j=0; jinput.deviceCount; ++j ) { + stream->input.waveHeaders[j][i].dwFlags &= ~WHDR_DONE; mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) ); if( mmresult != MMSYSERR_NOERROR ) { @@ -2912,7 +3235,7 @@ static PaError StartStream( PaStream *s ) for( j=0; joutput.deviceCount; ++j ) { /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[j][i].dwUser; + int channelCount = (int)stream->output.waveHeaders[j][i].dwUser; PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, stream->output.waveHeaders[j][i].lpData + @@ -2988,7 +3311,7 @@ static PaError StartStream( PaStream *s ) if( result != paNoError ) goto error; /* Create thread that waits for audio buffers to be ready for processing. */ - stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ); + stream->processingThread = CREATE_THREAD; if( !stream->processingThread ) { result = paUnanticipatedHostError; @@ -3120,7 +3443,7 @@ static PaError StopStream( PaStream *s ) for( i=0; ioutput.deviceCount; ++i ) { /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; + int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + @@ -3344,7 +3667,7 @@ static PaError ReadStream( PaStream* s, } else { - userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount ); + userBuffer = (void*)alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount ); if( !userBuffer ) return paInsufficientMemory; for( i = 0; ibufferProcessor.inputChannelCount; ++i ) @@ -3372,7 +3695,7 @@ static PaError ReadStream( PaStream* s, for( i=0; iinput.deviceCount; ++i ) { /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; + int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser; PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData + @@ -3448,7 +3771,7 @@ static PaError WriteStream( PaStream* s, } else { - userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount ); + userBuffer = (const void*)alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount ); if( !userBuffer ) return paInsufficientMemory; for( i = 0; ibufferProcessor.outputChannelCount; ++i ) @@ -3477,7 +3800,7 @@ static PaError WriteStream( PaStream* s, for( i=0; ioutput.deviceCount; ++i ) { /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; + int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser; PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData + -- cgit v1.2.1