From 2e416ee0095f1bf608f849f156d564e0f45fb8ab Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Mon, 2 Feb 2004 12:18:59 +0000 Subject: merged in version_0_37_1test6 svn path=/trunk/; revision=1305 --- pd/portaudio/pa_win_wmme/pa_win_wmme.c | 2672 -------------------------------- 1 file changed, 2672 deletions(-) delete mode 100644 pd/portaudio/pa_win_wmme/pa_win_wmme.c (limited to 'pd/portaudio/pa_win_wmme/pa_win_wmme.c') diff --git a/pd/portaudio/pa_win_wmme/pa_win_wmme.c b/pd/portaudio/pa_win_wmme/pa_win_wmme.c deleted file mode 100644 index 2b1cc57d..00000000 --- a/pd/portaudio/pa_win_wmme/pa_win_wmme.c +++ /dev/null @@ -1,2672 +0,0 @@ -/* - * $Id: pa_win_wmme.c,v 1.6.2.44 2003/03/18 14:27:58 rossbencina Exp $ - * pa_win_wmme.c - * Implementation of PortAudio for Windows MultiMedia Extensions (WMME) - * - * PortAudio Portable Real-Time Audio Library - * Latest Version at: http://www.portaudio.com - * - * Authors: Ross Bencina and Phil Burk - * Copyright (c) 1999-2000 Ross Bencina and 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. - * - */ - -/* Modification History: - PLB = Phil Burk - JM = Julien Maillard - RDB = Ross Bencina - PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer) - PLB20010413 - check for excessive numbers of channels - PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC - including conditional inclusion of memory.h, - and explicit typecasting on memory allocation - PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory - PLB20010816 - pass process instead of thread to SetPriorityClass() - PLB20010927 - use number of frames instead of real-time for CPULoad calculation. - JM20020118 - prevent hung thread when buffers underflow. - PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount - RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init - RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices - refactoring, renaming and fixed a few edge case bugs - RDB20020531 - converted to V19 framework - ** NOTE maintanance history is now stored in CVS ** -*/ - -/** @file - - @todo Handle case where user supplied full duplex buffer sizes are not compatible - (must be common multiples) - - @todo Fix buffer catch up code, can sometimes get stuck - - @todo Implement "close sample rate matching" if needed - is this really needed - in mme? - - @todo Investigate supporting host buffer formats > 16 bits - - @todo Implement buffer size and number of buffers code, - this code should generate defaults the way the old code did - - @todo implement underflow/overflow streamCallback statusFlags, paNeverDropInput. - - @todo Fix fixmes - - @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.) - - @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable - - @todo implement IsFormatSupported - - @todo implement PaDeviceInfo.defaultSampleRate; - - @todo define UNICODE and _UNICODE in the project settings and see what breaks -*/ - -#include -#include -#include -#include -#include -#include -#include -/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */ -#ifndef __MWERKS__ -#include -#include -#endif /* __MWERKS__ */ - -#include "portaudio.h" -#include "pa_trace.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 "pa_win_wmme.h" - -/************************************************* Constants ********/ - -/* Switches for debugging. */ -#define PA_SIMULATE_UNDERFLOW_ (0) /* Set to one to force an underflow of the output buffer. */ - -#define PA_USE_HIGH_LATENCY_ (0) /* For debugging glitches. */ - -#if PA_USE_HIGH_LATENCY_ - #define PA_MIN_MSEC_PER_HOST_BUFFER_ (100) - #define PA_MAX_MSEC_PER_HOST_BUFFER_ (300) /* Do not exceed unless user buffer exceeds */ - #define PA_MIN_NUM_HOST_BUFFERS_ (4) - #define PA_MAX_NUM_HOST_BUFFERS_ (16) /* OK to exceed if necessary */ - #define PA_WIN_9X_LATENCY_ (400) -#else - #define PA_MIN_MSEC_PER_HOST_BUFFER_ (10) - #define PA_MAX_MSEC_PER_HOST_BUFFER_ (100) /* Do not exceed unless user buffer exceeds */ - #define PA_MIN_NUM_HOST_BUFFERS_ (3) - #define PA_MAX_NUM_HOST_BUFFERS_ (16) /* OK to exceed if necessary */ - #define PA_WIN_9X_LATENCY_ (200) -#endif - -#define PA_MIN_TIMEOUT_MSEC_ (1000) - -/* -** Use higher latency for NT because it is even worse at real-time -** operation than Win9x. -*/ -#define PA_WIN_NT_LATENCY_ (PA_WIN_9X_LATENCY_ * 2) -#define PA_WIN_WDM_LATENCY_ (PA_WIN_9X_LATENCY_) - - -static const char constInputMapperSuffix_[] = " - Input"; -static const char constOutputMapperSuffix_[] = " - Output"; - - -typedef struct PaWinMmeStream PaWinMmeStream; /* forward reference */ - -/* prototypes for functions declared in this file */ - -PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ); -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** stream, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -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, void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - -static PaError UpdateStreamTime( PaWinMmeStream *stream ); - -/* macros for setting last host error information */ - -#ifdef UNICODE - -#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \ - { \ - wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \ - char mmeErrorText[ MAXERRORLENGTH ]; \ - waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \ - WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\ - mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \ - PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ - } - -#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \ - { \ - wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \ - char mmeErrorText[ MAXERRORLENGTH ]; \ - waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \ - WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\ - mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \ - PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ - } - -#else /* !UNICODE */ - -#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \ - { \ - char mmeErrorText[ MAXERRORLENGTH ]; \ - waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \ - PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ - } - -#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \ - { \ - char mmeErrorText[ MAXERRORLENGTH ]; \ - waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \ - PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \ - } - -#endif /* UNICODE */ - - -static void PaMme_SetLastSystemError( DWORD errorCode ) -{ - LPVOID lpMsgBuf; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - errorCode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, - NULL - ); - PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf ); - LocalFree( lpMsgBuf ); -} - -#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \ - PaMme_SetLastSystemError( errorCode ) - - -/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - int numInputDevices, numOutputDevices; - - /** winMmeDeviceIds is an array of WinMme device ids. - fields in the range [0, numInputDevices) are input device ids, - and [numInputDevices, numInputDevices + numOutputDevices) are output - device ids. - */ - int *winMmeDeviceIds; -} -PaWinMmeHostApiRepresentation; - - -/************************************************************************* - * Returns recommended device ID. - * On the PC, the recommended device can be specified by the user by - * setting an environment variable. For example, to use device #1. - * - * set PA_RECOMMENDED_OUTPUT_DEVICE=1 - * - * The user should first determine the available device ID by using - * the supplied application "pa_devs". - */ -#define PA_ENV_BUF_SIZE_ (32) -#define PA_REC_IN_DEV_ENV_NAME_ ("PA_RECOMMENDED_INPUT_DEVICE") -#define PA_REC_OUT_DEV_ENV_NAME_ ("PA_RECOMMENDED_OUTPUT_DEVICE") -static PaDeviceIndex GetEnvDefaultDeviceID( char *envName ) -{ - PaDeviceIndex recommendedIndex = paNoDevice; - DWORD hresult; - char envbuf[PA_ENV_BUF_SIZE_]; - -#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */ - - /* Let user determine default device by setting environment variable. */ - hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ ); - if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) ) - { - recommendedIndex = atoi( envbuf ); - } -#endif - - return recommendedIndex; -} - -static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi ) -{ - PaDeviceIndex device; - - /* input */ - device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ ); - if( device != paNoDevice && - ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) && - hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 ) - { - hostApi->inheritedHostApiRep.info.defaultInputDevice = device; - } - - /* output */ - device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ ); - if( device != paNoDevice && - ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) && - hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 ) - { - hostApi->inheritedHostApiRep.info.defaultOutputDevice = device; - } -} - - -/** Convert external PA ID to a windows multimedia device ID -*/ -static int LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device ) -{ - assert( device >= 0 && device < hostApi->numInputDevices + hostApi->numOutputDevices ); - - return hostApi->winMmeDeviceIds[ device ]; -} - - -static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaDeviceInfo *deviceInfo, int winMmeInputDeviceId, int *success ) -{ - PaError result = paNoError; - char *deviceName; /* non-const ptr */ - MMRESULT mmresult; - WAVEINCAPS wic; - - *success = 0; - - mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) ); - if( mmresult == MMSYSERR_NOMEM ) - { - result = paInsufficientMemory; - goto error; - } - else if( mmresult != MMSYSERR_NOERROR ) - { - /* instead of returning paUnanticipatedHostError we return - paNoError, but leave success set as 0. This allows - Pa_Initialize to just ignore this device, without failing - the entire initialisation process. - */ - return paNoError; - } - - if( winMmeInputDeviceId == WAVE_MAPPER ) - { - /* Append I/O suffix to WAVE_MAPPER device. */ - deviceName = (char *)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, strlen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, wic.szPname ); - strcat( deviceName, constInputMapperSuffix_ ); - } - else - { - deviceName = (char*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, strlen( wic.szPname ) + 1 ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, wic.szPname ); - } - 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( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) ) - { - PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels )); - deviceInfo->maxInputChannels = 2; - } - - deviceInfo->defaultSampleRate = 0.; /* @todo IMPLEMENT ME */ - - *success = 1; - -error: - return result; -} - - -static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi, - PaDeviceInfo *deviceInfo, int winMmeOutputDeviceId, int *success ) -{ - PaError result = paNoError; - char *deviceName; /* non-const ptr */ - MMRESULT mmresult; - WAVEOUTCAPS woc; - - *success = 0; - - mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) ); - if( mmresult == MMSYSERR_NOMEM ) - { - result = paInsufficientMemory; - goto error; - } - else if( mmresult != MMSYSERR_NOERROR ) - { - /* instead of returning paUnanticipatedHostError we return - paNoError, but leave success set as 0. This allows - Pa_Initialize to just ignore this device, without failing - the entire initialisation process. - */ - return paNoError; - } - - if( winMmeOutputDeviceId == WAVE_MAPPER ) - { - /* Append I/O suffix to WAVE_MAPPER device. */ - deviceName = (char *)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, strlen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, woc.szPname ); - strcat( deviceName, constOutputMapperSuffix_ ); - } - else - { - deviceName = (char*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, strlen( woc.szPname ) + 1 ); - if( !deviceName ) - { - result = paInsufficientMemory; - goto error; - } - strcpy( deviceName, woc.szPname ); - } - 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( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) ) - { - PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels )); - deviceInfo->maxOutputChannels = 2; - } - - deviceInfo->defaultSampleRate = 0.; /* @todo IMPLEMENT ME */ - - *success = 1; - -error: - return result; -} - - -PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i; - PaWinMmeHostApiRepresentation *winMmeHostApi; - int numInputDevices, numOutputDevices, maximumPossibleNumDevices; - PaDeviceInfo *deviceInfoArray; - int deviceInfoInitializationSucceeded; - - winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) ); - if( !winMmeHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - winMmeHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !winMmeHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - *hostApi = &winMmeHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - (*hostApi)->info.type = paMME; - (*hostApi)->info.name = "MME"; - - - /* initialise device counts and default devices under the assumption that - there are no devices. These values are incremented below if and when - devices are successfully initialized. - */ - (*hostApi)->info.deviceCount = 0; - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; - winMmeHostApi->numInputDevices = 0; - winMmeHostApi->numOutputDevices = 0; - - - maximumPossibleNumDevices = 0; - - numInputDevices = waveInGetNumDevs(); - if( numInputDevices > 0 ) - maximumPossibleNumDevices += numInputDevices + 1; /* assume there is a WAVE_MAPPER */ - - numOutputDevices = waveOutGetNumDevs(); - if( numOutputDevices > 0 ) - maximumPossibleNumDevices += numOutputDevices + 1; /* assume there is a WAVE_MAPPER */ - - - if( maximumPossibleNumDevices > 0 ){ - - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleNumDevices ); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, sizeof(PaDeviceInfo) * maximumPossibleNumDevices ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - winMmeHostApi->winMmeDeviceIds = (int*)PaUtil_GroupAllocateMemory( - winMmeHostApi->allocations, sizeof(int) * maximumPossibleNumDevices ); - if( !winMmeHostApi->winMmeDeviceIds ) - { - result = paInsufficientMemory; - goto error; - } - - if( numInputDevices > 0 ){ - // -1 is the WAVE_MAPPER - for( i = -1; i < numInputDevices; ++i ){ - PaDeviceInfo *deviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = 0; - - deviceInfo->defaultLowInputLatency = 0.; /* @todo IMPLEMENT ME */ - deviceInfo->defaultLowOutputLatency = 0.; /* @todo IMPLEMENT ME */ - deviceInfo->defaultHighInputLatency = 0.; /* @todo IMPLEMENT ME */ - deviceInfo->defaultHighOutputLatency = 0.; /* @todo IMPLEMENT ME */ - - result = InitializeInputDeviceInfo( winMmeHostApi, deviceInfo, i, &deviceInfoInitializationSucceeded ); - if( result != paNoError ) - goto error; - - if( deviceInfoInitializationSucceeded ){ - if( (*hostApi)->info.defaultInputDevice == paNoDevice ) - (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; - - winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = i; - (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; - - winMmeHostApi->numInputDevices++; - (*hostApi)->info.deviceCount++; - } - } - } - - if( numOutputDevices > 0 ){ - // -1 is the WAVE_MAPPER - for( i = -1; i < numOutputDevices; ++i ){ - PaDeviceInfo *deviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - deviceInfo->maxInputChannels = 0; - deviceInfo->maxOutputChannels = 0; - - deviceInfo->defaultLowInputLatency = 0.; /* @todo IMPLEMENT ME */ - deviceInfo->defaultLowOutputLatency = 0.; /* @todo IMPLEMENT ME */ - deviceInfo->defaultHighInputLatency = 0.; /* @todo IMPLEMENT ME */ - deviceInfo->defaultHighOutputLatency = 0.; /* @todo IMPLEMENT ME */ - - result = InitializeOutputDeviceInfo( winMmeHostApi, deviceInfo, i, &deviceInfoInitializationSucceeded ); - if( result != paNoError ) - goto error; - - if( deviceInfoInitializationSucceeded ){ - if( (*hostApi)->info.defaultOutputDevice == paNoDevice ) - (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; - - winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = i; - (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; - - winMmeHostApi->numOutputDevices++; - (*hostApi)->info.deviceCount++; - } - } - } - } - - - InitializeDefaultDeviceIdsFromEnv( winMmeHostApi ); - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyReadWrite, PaUtil_DummyReadWrite, PaUtil_DummyGetAvailable, PaUtil_DummyGetAvailable ); - - PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - return result; - -error: - if( winMmeHostApi ) - { - if( winMmeHostApi->allocations ) - { - PaUtil_FreeAllAllocations( winMmeHostApi->allocations ); - PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations ); - } - - PaUtil_FreeMemory( winMmeHostApi ); - } - - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; - - if( winMmeHostApi->allocations ) - { - PaUtil_FreeAllAllocations( winMmeHostApi->allocations ); - PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations ); - } - - PaUtil_FreeMemory( winMmeHostApi ); -} - - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - - if( inputParameters ) - { - inputChannelCount = 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 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 */ - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = 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 inputChannelCount */ - if( outputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* validate outputStreamInfo */ - if( outputParameters->hostApiSpecificStreamInfo ) - return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - } - - /* - IMPLEMENT ME: - - check that input device can support inputSampleFormat, or that - we have the capability to convert from outputSampleFormat 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 a full duplex stream is requested, check that the combination - of input and output parameters is supported - - - check that the device supports sampleRate - */ - - return paFormatIsSupported; -} - - -/* CalculateBufferSettings() fills the framesPerHostInputBuffer, numHostInputBuffers, - framesPerHostOutputBuffer and numHostOutputBuffers parameters based on the values - of the other parameters. - -*/ - -static PaError CalculateBufferSettings( - unsigned long *framesPerHostInputBuffer, unsigned long *numHostInputBuffers, - unsigned long *framesPerHostOutputBuffer, unsigned long *numHostOutputBuffers, - int inputChannelCount, PaSampleFormat hostInputSampleFormat, - PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo, - int outputChannelCount, PaSampleFormat hostOutputSampleFormat, - PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo, - double sampleRate, unsigned long framesPerBuffer ) -{ - PaError result = paNoError; - - if( inputChannelCount > 0 ) - { - if( inputStreamInfo ) - { - if( inputStreamInfo->flags & PaWinMmeUseLowLevelLatencyParameters ) - { - if( inputStreamInfo->numBuffers <= 0 - || inputStreamInfo->framesPerBuffer <= 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - - *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer; - *numHostInputBuffers = inputStreamInfo->numBuffers; - } - } - else - { - /* hardwire for now, FIXME */ - /* don't forget that there will be one more buffer than the number required to achieve the requested latency */ - *framesPerHostInputBuffer = 4096; - *numHostInputBuffers = 4; - - /* - Need to determine the right heuristic for mapping latency in - seconds to buffer sizes and number of buffers. - - - for output don't allocate less than 1+1 buffers - - - for input don't allocate less than 2+1 buffers - (less than 1+1 if input only) - - - don't allocate buffers smaller than framesPerBuffer - - - if the client doesn't care about the buffer size use a power - of two buffer size. otherwise use a multiple of the user - buffer size. if the user buffer size is a power of 2, it - might be wise to make the host buffer size a power of 2 too. - - - there probably shouldn't be too many buffers ( 3 to 10 seems - reasonable). - - - aside from a limit on what constitutes a "reasonable" number - of buffers, there should be as many buffers as possible, - because this will place a less bursty load on CPU resources - - - the host buffers should be as big as practical (ie multiple - user buffers per host buffer). - - - . One way to achieve the above is to say: Try to have 8 - host buffers, and host buffers cannot be larger than 32k - unless the user buffer size requires it" - I say 32k because buffers larger than this are known to - crash some drivers (Turtle Beach for example.) - - just some idle rambling: - - if( framesPerBuffer == 0 ){ - // use a power of two buffer size - - }else{ - latencySamples = ceil(requestedLatency * sampleRate) - - numBuffers = ceil(latencySamples / framesPerBuffer); - - bufferSize = framesPerBuffer; - - minBuffers = ( inputChannelCount > 0 && outputChannelCount > 0 ) ? 2 : 1; - - if( numBuffers <= minBuffers ){ - numBuffers = minBuffers; - }else{ - make buffer size a multiple of framesPerBuffer until numBuffers is - greater than 4 and less than 10. - } - } - */ - } - } - else - { - *framesPerHostInputBuffer = 0; - *numHostInputBuffers = 0; - } - - if( outputChannelCount > 0 ) - { - if( outputStreamInfo ) - { - if( outputStreamInfo->flags & PaWinMmeUseLowLevelLatencyParameters ) - { - if( outputStreamInfo->numBuffers <= 0 - || outputStreamInfo->framesPerBuffer <= 0 ) - { - result = paIncompatibleHostApiSpecificStreamInfo; - goto error; - } - - *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer; - *numHostOutputBuffers = outputStreamInfo->numBuffers; - } - } - else - { - /* hardwire for now, FIXME */ - /* don't forget that there will be one more buffer than the number required to achieve the requested latency */ - *framesPerHostOutputBuffer = 4096; - *numHostOutputBuffers = 4; - } - } - else - { - *framesPerHostOutputBuffer = 0; - *numHostOutputBuffers = 0; - } - -error: - return result; -} - - - -typedef HWAVEIN MmeHandle; - -static PaError InitializeBufferSet( WAVEHDR **bufferSet, int numBuffers, int bufferBytes, - int isInput, /* if 0, then output */ - MmeHandle mmeWaveHandle, int numDeviceChannels ) -{ - PaError result = paNoError; - MMRESULT mmresult; - int i; - - *bufferSet = 0; - - /* Allocate an array to hold the buffer pointers. */ - *bufferSet = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*numBuffers ); - if( !*bufferSet ) - { - result = paInsufficientMemory; - goto error; - } - - for( i=0; i don't throtte, non-0 -> throttle */ - int processingThreadPriority; - int highThreadPriority; - int throttledThreadPriority; - - volatile int isActive; - volatile int stopProcessing; /* stop thread once existing buffers have been returned */ - volatile int abortProcessing; /* stop thread immediately */ - - DWORD allBuffersDurationMs; /* used to calculate timeouts */ - - /* @todo FIXME: we no longer need the following for GetStreamTime support */ - /* GetStreamTime() support ------------- */ - - PaTime streamPosition; - long previousStreamPosition; /* used to track frames played. */ -} -PaWinMmeStream; - - -/* the following macros are intended to improve the readability of the following code */ -#define PA_IS_INPUT_STREAM_( stream ) ( stream ->hWaveIns ) -#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->hWaveOuts ) -#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->hWaveIns && stream ->hWaveOuts ) - - -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; - PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi; - PaWinMmeStream *stream = 0; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - double suggestedInputLatency, suggestedOutputLatency; - PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo; - unsigned long bytesPerInputFrame, bytesPerOutputFrame; - unsigned long framesPerHostInputBuffer; - unsigned long numHostInputBuffers; - unsigned long framesPerHostOutputBuffer; - unsigned long numHostOutputBuffers; - unsigned long framesPerBufferProcessorCall; - int lockInited = 0; - int bufferEventInited = 0; - int abortEventInited = 0; - WAVEFORMATEX wfx; - MMRESULT mmresult; - unsigned int i; - int channelCount; - PaWinMmeDeviceAndChannelCount *inputDevices = 0; - unsigned long numInputDevices = (inputParameters) ? 1 : 0; - PaWinMmeDeviceAndChannelCount *outputDevices = 0; - unsigned long numOutputDevices = (outputParameters) ? 1 : 0; - char noHighPriorityProcessClass = 0; - char useTimeCriticalProcessingThreadPriority = 0; - char throttleProcessingThreadOnOverload = 1; - - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - suggestedInputLatency = inputParameters->suggestedLatency; - - /* check that input device can support inputChannelCount */ - if( (inputParameters->device != paUseHostApiSpecificDeviceSpecification) && - (inputChannelCount > hostApi->deviceInfos[ inputParameters->device ]->maxInputChannels) ) - return paInvalidChannelCount; - - - /* validate input hostApiSpecificStreamInfo */ - inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo; - if( inputStreamInfo ) - { - if( inputStreamInfo->size != sizeof( PaWinMmeStreamInfo ) - || inputStreamInfo->version != 1 ) - { - return paIncompatibleHostApiSpecificStreamInfo; - } - - if( inputStreamInfo->flags & PaWinMmeNoHighPriorityProcessClass ) - noHighPriorityProcessClass = 1; - if( inputStreamInfo->flags & PaWinMmeDontThrottleOverloadedProcessingThread ) - throttleProcessingThreadOnOverload = 0; - if( inputStreamInfo->flags & PaWinMmeUseTimeCriticalThreadPriority ) - useTimeCriticalProcessingThreadPriority = 1; - - /* validate multidevice fields */ - - if( inputStreamInfo->flags & PaWinMmeUseMultipleDevices ) - { - int totalChannels = 0; - for( i=0; i< inputStreamInfo->deviceCount; ++i ) - { - /* validate that the device number is within range, and that - the number of channels is legal */ - PaDeviceIndex hostApiDevice; - - if( inputParameters->device != paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - channelCount = inputStreamInfo->devices[i].channelCount; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, - inputStreamInfo->devices[i].device, hostApi ); - if( result != paNoError ) - return result; - - if( channelCount < 1 || channelCount > hostApi->deviceInfos[ hostApiDevice ]->maxInputChannels ) - return paInvalidChannelCount; - - /* FIXME this validation might be easier and better if there was a pautil - function which performed the validation in pa_front:ValidateOpenStreamParameters() */ - - totalChannels += channelCount; - } - - if( totalChannels != inputChannelCount ) - { - /* inputChannelCount must match total channels specified by multiple devices */ - return paInvalidChannelCount; /* REVIEW use of this error code */ - } - - inputDevices = inputStreamInfo->devices; - numInputDevices = inputStreamInfo->deviceCount; - } - } - - /* FIXME: establish which host formats are available */ - hostInputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat ); - - } - else - { - inputChannelCount = 0; - inputSampleFormat = -1; - suggestedInputLatency = 0.; - inputStreamInfo = 0; - hostInputSampleFormat = -1; - } - - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - suggestedOutputLatency = outputParameters->suggestedLatency; - - /* check that input device can support inputChannelCount */ - if( (outputParameters->device != paUseHostApiSpecificDeviceSpecification) && - (inputChannelCount > hostApi->deviceInfos[ outputParameters->device ]->maxOutputChannels) ) - return paInvalidChannelCount; - - - /* validate input hostApiSpecificStreamInfo */ - outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo; - if( outputStreamInfo ) - { - if( outputStreamInfo->size != sizeof( PaWinMmeStreamInfo ) - || outputStreamInfo->version != 1 ) - { - return paIncompatibleHostApiSpecificStreamInfo; - } - - if( outputStreamInfo->flags & PaWinMmeNoHighPriorityProcessClass ) - noHighPriorityProcessClass = 1; - if( outputStreamInfo->flags & PaWinMmeDontThrottleOverloadedProcessingThread ) - throttleProcessingThreadOnOverload = 0; - if( outputStreamInfo->flags & PaWinMmeUseTimeCriticalThreadPriority ) - useTimeCriticalProcessingThreadPriority = 1; - - /* validate multidevice fields */ - - if( outputStreamInfo->flags & PaWinMmeUseMultipleDevices ) - { - int totalChannels = 0; - for( i=0; i< outputStreamInfo->deviceCount; ++i ) - { - /* validate that the device number is within range, and that - the number of channels is legal */ - PaDeviceIndex hostApiDevice; - - if( outputParameters->device != paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - channelCount = outputStreamInfo->devices[i].channelCount; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, - outputStreamInfo->devices[i].device, - hostApi ); - if( result != paNoError ) - return result; - - if( channelCount < 1 || channelCount > hostApi->deviceInfos[ hostApiDevice ]->maxOutputChannels ) - return paInvalidChannelCount; - - /* FIXME this validation might be easier and better if there was a pautil - function which performed the validation in pa_front:ValidateOpenStreamParameters() */ - - totalChannels += channelCount; - } - - if( totalChannels != outputChannelCount ) - { - /* outputChannelCount must match total channels specified by multiple devices */ - return paInvalidChannelCount; /* REVIEW use of this error code */ - } - - outputDevices = outputStreamInfo->devices; - numOutputDevices = outputStreamInfo->deviceCount; - } - } - - /* FIXME: establish which host formats are available */ - hostOutputSampleFormat = - PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat ); - } - else - { - outputChannelCount = 0; - outputSampleFormat = -1; - outputStreamInfo = 0; - hostOutputSampleFormat = -1; - suggestedOutputLatency = 0.; - } - - - /* - IMPLEMENT ME: - - alter sampleRate to a close allowable rate if possible / necessary - */ - - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ) - return paInvalidFlag; /* unexpected platform specific flag */ - - - result = CalculateBufferSettings( &framesPerHostInputBuffer, &numHostInputBuffers, - &framesPerHostOutputBuffer, &numHostOutputBuffers, - inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo, - outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo, - sampleRate, framesPerBuffer ); - if( result != paNoError ) - goto error; - - - stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) ); - if( !stream ) - { - result = paInsufficientMemory; - goto error; - } - - stream->hWaveIns = 0; - stream->inputBuffers = 0; - stream->hWaveOuts = 0; - stream->outputBuffers = 0; - stream->processingThread = 0; - - stream->noHighPriorityProcessClass = noHighPriorityProcessClass; - stream->useTimeCriticalProcessingThreadPriority = useTimeCriticalProcessingThreadPriority; - stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload; - - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &winMmeHostApi->callbackStreamInterface, streamCallback, userData ); - - stream->streamRepresentation.streamInfo.inputLatency = (double)(framesPerHostInputBuffer * (numHostInputBuffers-1)) / sampleRate; - stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerHostOutputBuffer * (numHostOutputBuffers-1)) / sampleRate; - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - - if( inputParameters && outputParameters ) /* full duplex */ - { - /* - either host input and output buffers must be the same size, or the - larger one must be an integer multiple of the smaller one. - FIXME: should this return an error if the host specific latency - settings don't fulfill these constraints? rb: probably - */ - - if( framesPerHostInputBuffer < framesPerHostOutputBuffer ) - { - assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); - - framesPerBufferProcessorCall = framesPerHostInputBuffer; - } - else - { - assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); - - framesPerBufferProcessorCall = framesPerHostOutputBuffer; - } - } - else if( inputParameters ) - { - framesPerBufferProcessorCall = framesPerHostInputBuffer; - } - else if( outputParameters ) - { - framesPerBufferProcessorCall = framesPerHostOutputBuffer; - } - - stream->framesPerInputBuffer = framesPerHostInputBuffer; - stream->framesPerOutputBuffer = framesPerHostOutputBuffer; - - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, hostInputSampleFormat, - outputChannelCount, outputSampleFormat, hostOutputSampleFormat, - sampleRate, streamFlags, framesPerBuffer, - framesPerBufferProcessorCall, paUtilFixedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ) - goto error; - - stream->isActive = 0; - - stream->streamPosition = 0.; - stream->previousStreamPosition = 0; - - - stream->bufferEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - if( stream->bufferEvent == NULL ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - bufferEventInited = 1; - - if( inputParameters ) - { - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nSamplesPerSec = (DWORD) sampleRate; - wfx.cbSize = 0; - - stream->numInputDevices = numInputDevices; - stream->hWaveIns = (HWAVEIN*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * stream->numInputDevices ); - if( !stream->hWaveIns ) - { - result = paInsufficientMemory; - goto error; - } - - for( i = 0; i < stream->numInputDevices; ++i ) - stream->hWaveIns[i] = 0; - - for( i = 0; i < stream->numInputDevices; ++i ) - { - int inputWinMmeId; - - if( inputDevices ) - { - PaDeviceIndex hostApiDevice; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, - inputDevices[i].device, hostApi ); - if( result != paNoError ) - return result; - - inputWinMmeId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, hostApiDevice ); - wfx.nChannels = (WORD) inputDevices[i].channelCount; - } - else - { - inputWinMmeId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device ); - wfx.nChannels = (WORD) inputChannelCount; - } - - bytesPerInputFrame = wfx.nChannels * stream->bufferProcessor.bytesPerHostInputSample; - - wfx.nAvgBytesPerSec = (DWORD)(bytesPerInputFrame * sampleRate); - wfx.nBlockAlign = (WORD)bytesPerInputFrame; - wfx.wBitsPerSample = (WORD)((bytesPerInputFrame/wfx.nChannels) * 8); - - /* REVIEW: consider not firing an event for input when a full duplex stream is being used */ - - mmresult = waveInOpen( &stream->hWaveIns[i], inputWinMmeId, &wfx, - (DWORD)stream->bufferEvent, (DWORD) stream, CALLBACK_EVENT ); - if( mmresult != MMSYSERR_NOERROR ) - { - switch( mmresult ) - { - case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ - result = paDeviceUnavailable; - break; - case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ - result = paInternalError; /* portaudio should ensure that only good device ids are used */ - 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 WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ - result = paInternalError; /* REVIEW: port audio shouldn't get this far without using compatible format info */ - break; - default: - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - goto error; - } - } - } - - if( outputParameters ) - { - wfx.wFormatTag = WAVE_FORMAT_PCM; - wfx.nSamplesPerSec = (DWORD) sampleRate; - wfx.cbSize = 0; - - stream->numOutputDevices = numOutputDevices; - stream->hWaveOuts = (HWAVEOUT*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * stream->numOutputDevices ); - if( !stream->hWaveOuts ) - { - result = paInsufficientMemory; - goto error; - } - - for( i = 0; i < stream->numOutputDevices; ++i ) - stream->hWaveOuts[i] = 0; - - for( i = 0; i < stream->numOutputDevices; ++i ) - { - int outputWinMmeId; - - if( outputDevices ) - { - PaDeviceIndex hostApiDevice; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, - outputDevices[i].device, hostApi ); - if( result != paNoError ) - return result; - - outputWinMmeId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, hostApiDevice ); - wfx.nChannels = (WORD) outputDevices[i].channelCount; - } - else - { - outputWinMmeId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device ); - wfx.nChannels = (WORD) outputChannelCount; - } - - bytesPerOutputFrame = wfx.nChannels * stream->bufferProcessor.bytesPerHostOutputSample; - - wfx.nAvgBytesPerSec = (DWORD)(bytesPerOutputFrame * sampleRate); - wfx.nBlockAlign = (WORD)bytesPerOutputFrame; - wfx.wBitsPerSample = (WORD)((bytesPerOutputFrame/wfx.nChannels) * 8); - - mmresult = waveOutOpen( &stream->hWaveOuts[i], outputWinMmeId, &wfx, - (DWORD)stream->bufferEvent, (DWORD) stream, CALLBACK_EVENT ); - if( mmresult != MMSYSERR_NOERROR ) - { - switch( mmresult ) - { - case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */ - result = paDeviceUnavailable; - break; - case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */ - result = paInternalError; /* portaudio should ensure that only good device ids are used */ - 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 WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */ - result = paInternalError; /* REVIEW: port audio shouldn't get this far without using compatible format info */ - break; - default: - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - goto error; - } - } - } - - if( PA_IS_INPUT_STREAM_(stream) ) - { - stream->inputBuffers = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * stream->numInputDevices ); - if( stream->inputBuffers == 0 ) - { - result = paInsufficientMemory; - goto error; - } - - for( i =0; i < stream->numInputDevices; ++i ) - stream->inputBuffers[i] = 0; - - stream->numInputBuffers = numHostInputBuffers; - - for( i =0; i < stream->numInputDevices; ++i ) - { - int hostInputBufferBytes = Pa_GetSampleSize( hostInputSampleFormat ) * - framesPerHostInputBuffer * - ((inputDevices) ? inputDevices[i].channelCount : inputChannelCount); - if( hostInputBufferBytes < 0 ) - { - result = paInternalError; - goto error; - } - - result = InitializeBufferSet( &stream->inputBuffers[i], numHostInputBuffers, hostInputBufferBytes, - 1 /* isInput */, - (MmeHandle)stream->hWaveIns[i], - ((inputDevices) ? inputDevices[i].channelCount : inputChannelCount) ); - - if( result != paNoError ) - goto error; - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - stream->outputBuffers = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * stream->numOutputDevices ); - if( stream->outputBuffers == 0 ) - { - result = paInsufficientMemory; - goto error; - } - - for( i =0; i < stream->numOutputDevices; ++i ) - stream->outputBuffers[i] = 0; - - stream->numOutputBuffers = numHostOutputBuffers; - - for( i=0; i < stream->numOutputDevices; ++i ) - { - int hostOutputBufferBytes = Pa_GetSampleSize( hostOutputSampleFormat ) * - framesPerHostOutputBuffer * - ((outputDevices) ? outputDevices[i].channelCount : outputChannelCount); - if( hostOutputBufferBytes < 0 ) - { - result = paInternalError; - goto error; - } - - result = InitializeBufferSet( &stream->outputBuffers[i], numHostOutputBuffers, hostOutputBufferBytes, - 0 /* not isInput */, - (MmeHandle)stream->hWaveOuts[i], - ((outputDevices) ? outputDevices[i].channelCount : outputChannelCount) ); - - if( result != paNoError ) - goto error; - } - } - - stream->abortEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); - if( stream->abortEvent == NULL ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - abortEventInited = 1; - - InitializeCriticalSection( &stream->lock ); - lockInited = 1; - - if( PA_IS_OUTPUT_STREAM_(stream) ) - stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->numOutputBuffers) / sampleRate); - else - stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->numInputBuffers) / sampleRate); - - - *s = (PaStream*)stream; - - return result; - -error: - if( lockInited ) - DeleteCriticalSection( &stream->lock ); - - if( abortEventInited ) - CloseHandle( stream->abortEvent ); - - - if( stream->inputBuffers ) - { - for( i =0 ; i< stream->numInputDevices; ++i ) - { - if( stream->inputBuffers[i] ) - { - TerminateBufferSet( &stream->inputBuffers[i], stream->numInputBuffers, - 1 /* isInput */, (MmeHandle)stream->hWaveIns[i] ); - } - } - - PaUtil_FreeMemory( stream->inputBuffers ); - } - - if( stream->outputBuffers ) - { - for( i =0 ; i< stream->numOutputDevices; ++i ) - { - if( stream->outputBuffers[i] ) - { - TerminateBufferSet( &stream->outputBuffers[i], stream->numOutputBuffers, - 0 /* not isInput */, (MmeHandle)stream->hWaveOuts[i] ); - } - } - - PaUtil_FreeMemory( stream->outputBuffers ); - } - - if( stream->hWaveIns ) - { - for( i =0 ; i< stream->numInputDevices; ++i ) - { - if( stream->hWaveIns[i] ) - waveInClose( stream->hWaveIns[i] ); - } - - PaUtil_FreeMemory( stream->hWaveIns ); - } - - if( stream->hWaveOuts ) - { - for( i =0 ; i< stream->numOutputDevices; ++i ) - { - if( stream->hWaveOuts[i] ) - waveOutClose( stream->hWaveOuts[i] ); - } - - PaUtil_FreeMemory( stream->hWaveOuts ); - } - - if( bufferEventInited ) - CloseHandle( stream->bufferEvent ); - - if( stream ) - PaUtil_FreeMemory( stream ); - - return result; -} - - -/* return non-zero if any output buffers are queued */ -static int OutputBuffersAreQueued( PaWinMmeStream *stream ) -{ - int result = 0; - unsigned int i, j; - - if( PA_IS_OUTPUT_STREAM_( stream ) ) - { - for( i=0; inumOutputBuffers; ++i ) - { - for( j=0; j < stream->numOutputDevices; ++j ) - { - if( !( stream->outputBuffers[ j ][ i ].dwFlags & WHDR_DONE) ) - { - result++; - } - } - } - } - - return result; -} - - -static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - MMRESULT mmresult; - unsigned int i; - - for( i=0; i< stream->numInputDevices; ++i ) - { - mmresult = waveInAddBuffer( stream->hWaveIns[i], - &stream->inputBuffers[i][ stream->currentInputBufferIndex ], - sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - } - stream->currentInputBufferIndex = (stream->currentInputBufferIndex+1 >= stream->numInputBuffers) ? - 0 : stream->currentInputBufferIndex+1; - - stream->framesUsedInCurrentInputBuffer = 0; - - return result; -} - - -static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream ) -{ - PaError result = paNoError; - MMRESULT mmresult; - unsigned int i; - - for( i=0; i< stream->numOutputDevices; ++i ) - { - mmresult = waveOutWrite( stream->hWaveOuts[i], - &stream->outputBuffers[i][ stream->currentOutputBufferIndex ], - sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - } - - stream->currentOutputBufferIndex = (stream->currentOutputBufferIndex+1 >= stream->numOutputBuffers) ? - 0 : stream->currentOutputBufferIndex+1; - - stream->framesUsedInCurrentOutputBuffer = 0; - - return result; -} - - -static DWORD WINAPI ProcessingThreadProc( void *pArg ) -{ - PaWinMmeStream *stream = (PaWinMmeStream *)pArg; - HANDLE events[2]; - int numEvents = 0; - DWORD result = paNoError; - DWORD waitResult; -/** @todo: -Gordon Gidluck: -> function: ProcessingThreadProc() -> line #1665 DWORD timeout = stream->allBuffersDurationMs * 0.5; -> conversion from 'double ' to 'unsigned long ', possible loss of data -*/ - DWORD timeout = stream->allBuffersDurationMs * 0.5; - DWORD numTimeouts = 0; - int hostBuffersAvailable; - signed int hostInputBufferIndex, hostOutputBufferIndex; - int callbackResult; - int done = 0; - unsigned int channel, i, j; - unsigned long framesProcessed; - - /* prepare event array for call to WaitForMultipleObjects() */ - events[numEvents++] = stream->bufferEvent; - events[numEvents++] = stream->abortEvent; - - /* loop until something causes us to stop */ - while( !done ) - { - /* wait for MME to signal that a buffer is available, or for - the PA abort event to be signaled */ - waitResult = WaitForMultipleObjects( numEvents, events, FALSE, timeout ); - if( waitResult == WAIT_FAILED ) - { - result = paUnanticipatedHostError; - /* FIXME/REVIEW: can't return host error info from an asyncronous thread */ - done = 1; - } - else if( waitResult == WAIT_TIMEOUT ) - { - /* if a timeout is encountered, continue */ - numTimeouts += 1; - } - - if( stream->abortProcessing ) - { - /* Pa_AbortStream() has been called, stop processing immediately */ - done = 1; - } - else if( stream->stopProcessing ) - { - /* Pa_StopStream() has been called or the user callback returned - non-zero, processing will continue until all output buffers - are marked as done. The stream will stop immediately if it - is input-only. - */ - - if( !OutputBuffersAreQueued( stream ) ) - { - done = 1; /* Will cause thread to return. */ - } - } - else - { - hostBuffersAvailable = 1; - - /* process all available host buffers */ - do - { - hostInputBufferIndex = -1; - hostOutputBufferIndex = -1; - - if( PA_IS_INPUT_STREAM_(stream)) - { - hostInputBufferIndex = stream->currentInputBufferIndex; - for( i=0; inumInputDevices; ++i ) - { - if( !(stream->inputBuffers[i][ stream->currentInputBufferIndex ].dwFlags & WHDR_DONE) ) - { - hostInputBufferIndex = -1; - break; - } - } - - if( hostInputBufferIndex != -1 ) - { - /* if all of the other buffers are also ready then we dicard all but the - most recent. */ - int inputCatchUp = 1; - - for( i=0; i < stream->numInputBuffers && inputCatchUp == 1; ++i ) - { - for( j=0; jnumInputDevices; ++j ) - { - if( !(stream->inputBuffers[ j ][ i ].dwFlags & WHDR_DONE) ) - { - inputCatchUp = 0; - break; - } - } - } - - if( inputCatchUp ) - { - for( i=0; i < stream->numInputBuffers - 1; ++i ) - { - result = AdvanceToNextInputBuffer( stream ); - if( result != paNoError ) - done = 1; - } - } - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - hostOutputBufferIndex = stream->currentOutputBufferIndex; - for( i=0; inumOutputDevices; ++i ) - { - if( !(stream->outputBuffers[i][ stream->currentOutputBufferIndex ].dwFlags & WHDR_DONE) ) - { - hostOutputBufferIndex = -1; - break; - } - } - - if( hostOutputBufferIndex != - 1 ) - { - /* if all of the other buffers are also ready, catch up by copying - the most recently generated buffer into all but one of the output - buffers */ - int outputCatchUp = 1; - - for( i=0; i < stream->numOutputBuffers && outputCatchUp == 1; ++i ) - { - for( j=0; jnumOutputDevices; ++j ) - { - if( !(stream->outputBuffers[ j ][ i ].dwFlags & WHDR_DONE) ) - { - outputCatchUp = 0; - break; - } - } - } - - if( outputCatchUp ) - { - /* FIXME: this is an output underflow buffer slip and should be flagged as such */ - unsigned int previousBufferIndex = (stream->currentOutputBufferIndex==0) - ? stream->numOutputBuffers - 1 - : stream->currentOutputBufferIndex - 1; - - for( i=0; i < stream->numOutputBuffers - 1; ++i ) - { - for( j=0; jnumOutputDevices; ++j ) - { - if( stream->outputBuffers[j][ stream->currentOutputBufferIndex ].lpData - != stream->outputBuffers[j][ previousBufferIndex ].lpData ) - { - CopyMemory( stream->outputBuffers[j][ stream->currentOutputBufferIndex ].lpData, - stream->outputBuffers[j][ previousBufferIndex ].lpData, - stream->outputBuffers[j][ stream->currentOutputBufferIndex ].dwBufferLength ); - } - } - - result = AdvanceToNextOutputBuffer( stream ); - if( result != paNoError ) - done = 1; - } - } - } - } - - - if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) || - (!PA_IS_FULL_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) ) - { - PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /* @todo implement inputBufferAdcTime and currentTime */ - - - if( hostOutputBufferIndex != -1 ){ - MMTIME time; - double now; - long totalRingFrames; - long ringPosition; - long playbackPosition; - - time.wType = TIME_SAMPLES; - waveOutGetPosition( stream->hWaveOuts[0], &time, sizeof(MMTIME) ); - now = PaUtil_GetTime(); - - totalRingFrames = stream->numOutputBuffers * stream->bufferProcessor.framesPerHostBuffer; - - ringPosition = stream->currentOutputBufferIndex * stream->bufferProcessor.framesPerHostBuffer; - - playbackPosition = time.u.sample % totalRingFrames; - - if( playbackPosition >= ringPosition ){ - timeInfo.outputBufferDacTime = - now + ((double)( ringPosition + (totalRingFrames - playbackPosition) ) * stream->bufferProcessor.samplePeriod ); - }else{ - timeInfo.outputBufferDacTime = - now + ((double)( ringPosition - playbackPosition ) * stream->bufferProcessor.samplePeriod ); - } - } - - - PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer ); - - PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo ); - - if( hostInputBufferIndex != -1 ) - { - PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - - channel = 0; - for( i=0; inumInputDevices; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->inputBuffers[i][ hostInputBufferIndex ].dwUser; - - PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel, - stream->inputBuffers[i][ hostInputBufferIndex ].lpData + - stream->framesUsedInCurrentInputBuffer * channelCount * - stream->bufferProcessor.bytesPerHostInputSample, - channelCount ); - - - channel += channelCount; - } - } - - if( hostOutputBufferIndex != -1 ) - { - PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ ); - - channel = 0; - for( i=0; inumOutputDevices; ++i ) - { - /* we have stored the number of channels in the buffer in dwUser */ - int channelCount = stream->outputBuffers[i][ hostOutputBufferIndex ].dwUser; - - PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel, - stream->outputBuffers[i][ hostOutputBufferIndex ].lpData + - stream->framesUsedInCurrentOutputBuffer * channelCount * - stream->bufferProcessor.bytesPerHostOutputSample, - channelCount ); - - /* we have stored the number of channels in the buffer in dwUser */ - channel += channelCount; - } - } - - framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult ); - - stream->framesUsedInCurrentInputBuffer += framesProcessed; - stream->framesUsedInCurrentOutputBuffer += framesProcessed; - - PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed ); - - if( callbackResult == paContinue ) - { - /* nothing special to do */ - } - else if( callbackResult == paAbort ) - { - stream->abortProcessing = 1; - done = 1; - /* FIXME: should probably do a reset here */ - result = paNoError; - } - else - { - /* User cllback has asked us to stop with paComplete or other non-zero value */ - stream->stopProcessing = 1; /* stop once currently queued audio has finished */ - result = paNoError; - } - - /* - FIXME: the following code is incorrect, because stopProcessing should - still queue the current buffer. - */ - if( stream->stopProcessing == 0 && stream->abortProcessing == 0 ) - { - if( stream->throttleProcessingThreadOnOverload != 0 ) - { - if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. ) - { - if( stream->processingThreadPriority != stream->throttledThreadPriority ) - { - SetThreadPriority( stream->processingThread, stream->throttledThreadPriority ); - stream->processingThreadPriority = stream->throttledThreadPriority; - } - -/** @todo: -Gordon Gidluck: -> function: ProcessingThreadProc() -> line #1947/1948 Sleep( stream->bufferProcessor.framesPerHostBuffer * -> stream->bufferProcessor.samplePeriod * .25 ); -> conversion from 'double ' to 'unsigned long ', possible loss of data -> integral size mismatch in argument; conversion supplied -*/ - /* sleep for a quater of a buffer's duration to give other processes a go */ - Sleep( stream->bufferProcessor.framesPerHostBuffer * - stream->bufferProcessor.samplePeriod * .25 ); - } - else - { - if( stream->processingThreadPriority != stream->highThreadPriority ) - { - SetThreadPriority( stream->processingThread, stream->highThreadPriority ); - stream->processingThreadPriority = stream->highThreadPriority; - } - } - } - - if( PA_IS_INPUT_STREAM_(stream) && - stream->framesUsedInCurrentInputBuffer == stream->framesPerInputBuffer ) - { - result = AdvanceToNextInputBuffer( stream ); - if( result != paNoError ) - done = 1; - } - - if( PA_IS_OUTPUT_STREAM_(stream) && - stream->framesUsedInCurrentOutputBuffer == stream->framesPerOutputBuffer ) - { - result = AdvanceToNextOutputBuffer( stream ); - if( result != paNoError ) - done = 1; - } - } - } - else - { - hostBuffersAvailable = 0; - } - } - while( hostBuffersAvailable && - stream->stopProcessing == 0 && - stream->abortProcessing == 0 && - !done ); - } - - result = UpdateStreamTime( stream ); - if( result != paNoError ) - done = 1; - } - - stream->isActive = 0; - - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - - 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; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - MMRESULT mmresult; - unsigned int i; - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; inumInputDevices; ++i ) - { - TerminateBufferSet( &stream->inputBuffers[i], stream->numInputBuffers, - 1 /* isInput */, (MmeHandle)stream->hWaveIns[i] ); - } - - PaUtil_FreeMemory( stream->inputBuffers ); - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i=0; inumOutputDevices; ++i ) - { - TerminateBufferSet( &stream->outputBuffers[i], stream->numOutputBuffers, - 0 /* not isInput */, (MmeHandle)stream->hWaveOuts[i] ); - } - - PaUtil_FreeMemory( stream->outputBuffers ); - } - - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; inumInputDevices; ++i ) - { - mmresult = waveInClose( stream->hWaveIns[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - - PaUtil_FreeMemory( stream->hWaveIns ); - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i=0; inumOutputDevices; ++i ) - { - mmresult = waveOutClose( stream->hWaveOuts[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - - PaUtil_FreeMemory( stream->hWaveOuts ); - } - - if( CloseHandle( stream->bufferEvent ) == 0 ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - - if( CloseHandle( stream->abortEvent ) == 0 ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - - DeleteCriticalSection( &stream->lock ); - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - PaUtil_FreeMemory( stream ); - -error: - /* FIXME: consider how to best clean up on failure */ - return result; -} - - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - MMRESULT mmresult; - unsigned int i, j; - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; inumInputBuffers; ++i ) - { - for( j=0; jnumInputDevices; ++j ) - { - mmresult = waveInAddBuffer( stream->hWaveIns[j], &stream->inputBuffers[j][i], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - } - stream->currentInputBufferIndex = 0; - stream->framesUsedInCurrentInputBuffer = 0; - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i=0; inumOutputDevices; ++i ) - { - if( (mmresult = waveOutPause( stream->hWaveOuts[i] )) != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - - for( i=0; inumOutputBuffers; ++i ) - { - for( j=0; jnumOutputDevices; ++j ) - { - ZeroMemory( stream->outputBuffers[j][i].lpData, stream->outputBuffers[j][i].dwBufferLength ); - mmresult = waveOutWrite( stream->hWaveOuts[j], &stream->outputBuffers[j][i], sizeof(WAVEHDR) ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - } - stream->currentOutputBufferIndex = 0; - stream->framesUsedInCurrentOutputBuffer = 0; - } - - stream->streamPosition = 0.; - stream->previousStreamPosition = 0; - - stream->isActive = 1; - stream->stopProcessing = 0; - stream->abortProcessing = 0; - - if( ResetEvent( stream->bufferEvent ) == 0 ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - - if( ResetEvent( stream->abortEvent ) == 0 ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - - /* Create thread that waits for audio buffers to be ready for processing. */ - stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId ); - if( !stream->processingThread ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - - /* I used to pass the thread which was failing. I now pass GetCurrentProcess(). - * This fix could improve latency for some applications. It could also result in CPU - * starvation if the callback did too much processing. - * I also added result checks, so we might see more failures at initialization. - * Thanks to Alberto di Bene for spotting this. - */ - /* REVIEW: should we reset the priority class when the stream has stopped? - - would be best to refcount priority boosts incase more than one - stream is open - */ - - if( !stream->noHighPriorityProcessClass ) - { -#ifndef WIN32_PLATFORM_PSPC /* no SetPriorityClass or HIGH_PRIORITY_CLASS on PocketPC */ - - if( !SetPriorityClass( GetCurrentProcess(), HIGH_PRIORITY_CLASS ) ) /* PLB20010816 */ - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } -#endif - } - - if( stream->useTimeCriticalProcessingThreadPriority ) - stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL; - else - stream->highThreadPriority = THREAD_PRIORITY_HIGHEST; - - stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL; - - if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() ); - goto error; - } - stream->processingThreadPriority = stream->highThreadPriority; - - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; i < stream->numInputDevices; ++i ) - { - mmresult = waveInStart( stream->hWaveIns[i] ); - PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult)); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - goto error; - } - } - } - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i=0; i < stream->numOutputDevices; ++i ) - { - if( (mmresult = waveOutRestart( stream->hWaveOuts[i] )) != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - goto error; - } - } - } - - return result; - -error: - /* FIXME: implement recovery as best we can - This should involve rolling back to a state as-if this function had never been called - */ - return result; -} - - -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - int timeout; - DWORD waitResult; - MMRESULT mmresult; - unsigned int i; - - /* - FIXME: the error checking in this function needs review. the basic - idea is to return from this function in a known state - for example - there is no point avoiding calling waveInReset just because - the thread times out. - */ - - - /* Tell processing thread to stop generating more data and to let current data play out. */ - stream->stopProcessing = 1; - - /* Calculate timeOut longer than longest time it could take to return all buffers. */ - timeout = stream->allBuffersDurationMs * 1.5; - if( timeout < PA_MIN_TIMEOUT_MSEC_ ) - timeout = PA_MIN_TIMEOUT_MSEC_; - - PA_DEBUG(("WinMME StopStream: waiting for background thread.\n")); - - waitResult = WaitForSingleObject( stream->processingThread, timeout ); - if( waitResult == WAIT_TIMEOUT ) - { - /* try to abort */ - stream->abortProcessing = 1; - SetEvent( stream->abortEvent ); - waitResult = WaitForSingleObject( stream->processingThread, timeout ); - if( waitResult == WAIT_TIMEOUT ) - { - PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n")); - result = paTimedOut; - } - } - - CloseHandle( stream->processingThread ); - stream->processingThread = NULL; - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i =0; i < stream->numOutputDevices; ++i ) - { - mmresult = waveOutReset( stream->hWaveOuts[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - } - } - } - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; i < stream->numInputDevices; ++i ) - { - mmresult = waveInReset( stream->hWaveIns[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - result = paUnanticipatedHostError; - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - } - } - } - - stream->isActive = 0; - - return result; -} - - -static PaError AbortStream( PaStream *s ) -{ - PaError result = paNoError; - PaWinMmeStream *stream = (PaWinMmeStream*)s; - int timeout; - DWORD waitResult; - MMRESULT mmresult; - unsigned int i; - - /* - FIXME: the error checking in this function needs review. the basic - idea is to return from this function in a known state - for example - there is no point avoiding calling waveInReset just because - the thread times out. - */ - - /* Tell processing thread to abort immediately */ - stream->abortProcessing = 1; - SetEvent( stream->abortEvent ); - - /* Calculate timeOut longer than longest time it could take to return all buffers. */ - timeout = stream->allBuffersDurationMs * 1.5; - if( timeout < PA_MIN_TIMEOUT_MSEC_ ) - timeout = PA_MIN_TIMEOUT_MSEC_; - - if( PA_IS_OUTPUT_STREAM_(stream) ) - { - for( i =0; i < stream->numOutputDevices; ++i ) - { - mmresult = waveOutReset( stream->hWaveOuts[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - return paUnanticipatedHostError; - } - } - } - - if( PA_IS_INPUT_STREAM_(stream) ) - { - for( i=0; i < stream->numInputDevices; ++i ) - { - mmresult = waveInReset( stream->hWaveIns[i] ); - if( mmresult != MMSYSERR_NOERROR ) - { - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - return paUnanticipatedHostError; - } - } - } - - - PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n")); - - waitResult = WaitForSingleObject( stream->processingThread, timeout ); - if( waitResult == WAIT_TIMEOUT ) - { - PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n")); - return paTimedOut; - } - - CloseHandle( stream->processingThread ); - stream->processingThread = NULL; - - stream->isActive = 0; - - return result; -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - return ( stream->processingThread == NULL ); -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - return stream->isActive; -} - - -/* UpdateStreamTime() must be called periodically because mmtime.u.sample - is a DWORD and can wrap and lose sync after a few hours. - */ -static PaError UpdateStreamTime( PaWinMmeStream *stream ) -{ - MMRESULT mmresult; - MMTIME mmtime; - mmtime.wType = TIME_SAMPLES; - - if( stream->hWaveOuts ) - { - /* assume that all devices have the same position */ - mmresult = waveOutGetPosition( stream->hWaveOuts[0], &mmtime, sizeof(mmtime) ); - - if( mmresult != MMSYSERR_NOERROR ) - { - PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ); - return paUnanticipatedHostError; - } - } - else - { - /* assume that all devices have the same position */ - mmresult = waveInGetPosition( stream->hWaveIns[0], &mmtime, sizeof(mmtime) ); - - if( mmresult != MMSYSERR_NOERROR ) - { - PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ); - return paUnanticipatedHostError; - } - } - - - /* This data has two variables and is shared by foreground and background. - * So we need to make it thread safe. */ - EnterCriticalSection( &stream->lock ); - stream->streamPosition += ((long)mmtime.u.sample) - stream->previousStreamPosition; - stream->previousStreamPosition = (long)mmtime.u.sample; - LeaveCriticalSection( &stream->lock ); - - return paNoError; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ -/* - new behavior for GetStreamTime is to return a stream based seconds clock - used for the outTime parameter to the callback. - FIXME: delete this comment when the other unnecessary related code has - been cleaned from this file. - - PaWinMmeStream *stream = (PaWinMmeStream*)s; - PaError error = UpdateStreamTime( stream ); - - if( error == paNoError ) - return stream->streamPosition; - else - return 0; -*/ - (void) s; /* unused parameter */ - return PaUtil_GetTime(); -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - 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 ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - (void) stream; /* unused parameters */ - (void) buffer; - (void) frames; - - return paNoError; -} - - -static PaError WriteStream( PaStream* s, - void *buffer, - unsigned long frames ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - (void) stream; /* unused parameters */ - (void) buffer; - (void) frames; - - return paNoError; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - (void) stream; /* unused parameter */ - - return 0; -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaWinMmeStream *stream = (PaWinMmeStream*)s; - - /* IMPLEMENT ME, see portaudio.h for required behavior*/ - (void) stream; /* unused parameter */ - - return 0; -} - - - - -- cgit v1.2.1