2
* $Id: pa_win_wmme.c 1432 2009-12-09 01:31:44Z rossb $
4
* Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
6
* PortAudio Portable Real-Time Audio Library
7
* Latest Version at: http://www.portaudio.com
9
* Authors: Ross Bencina and Phil Burk
10
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
12
* Permission is hereby granted, free of charge, to any person obtaining
13
* a copy of this software and associated documentation files
14
* (the "Software"), to deal in the Software without restriction,
15
* including without limitation the rights to use, copy, modify, merge,
16
* publish, distribute, sublicense, and/or sell copies of the Software,
17
* and to permit persons to whom the Software is furnished to do so,
18
* subject to the following conditions:
20
* The above copyright notice and this permission notice shall be
21
* included in all copies or substantial portions of the Software.
23
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
27
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
28
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33
* The text above constitutes the entire PortAudio license; however,
34
* the PortAudio community also makes the following non-binding requests:
36
* Any person wishing to distribute modifications to the Software is
37
* requested to send the modifications to the original developer so that
38
* they can be incorporated into the canonical version. It is also
39
* requested that these non-binding requests be included along with the
43
/* Modification History:
47
PLB20010402 - sDevicePtrs now allocates based on sizeof(pointer)
48
PLB20010413 - check for excessive numbers of channels
49
PLB20010422 - apply Mike Berry's changes for CodeWarrior on PC
50
including conditional inclusion of memory.h,
51
and explicit typecasting on memory allocation
52
PLB20010802 - use GlobalAlloc for sDevicesPtr instead of PaHost_AllocFastMemory
53
PLB20010816 - pass process instead of thread to SetPriorityClass()
54
PLB20010927 - use number of frames instead of real-time for CPULoad calculation.
55
JM20020118 - prevent hung thread when buffers underflow.
56
PLB20020321 - detect Win XP versus NT, 9x; fix DBUG typo; removed init of CurrentCount
57
RDB20020411 - various renaming cleanups, factored streamData alloc and cpu usage init
58
RDB20020417 - stopped counting WAVE_MAPPER when there were no real devices
59
refactoring, renaming and fixed a few edge case bugs
60
RDB20020531 - converted to V19 framework
61
** NOTE maintanance history is now stored in CVS **
67
@brief Win32 host API implementation for the Windows MultiMedia Extensions (WMME) audio API.
69
@todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now,
70
needs to be reviewed and tested.)
72
@todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput.
74
@todo BUG: PA_MME_SET_LAST_WAVEIN/OUT_ERROR is used in functions which may
75
be called asynchronously from the callback thread. this is bad.
77
@todo implement inputBufferAdcTime in callback thread
79
@todo review/fix error recovery and cleanup in marked functions
81
@todo implement timeInfo for stream priming
83
@todo handle the case where the callback returns paAbort or paComplete during stream priming.
85
@todo review input overflow and output underflow handling in ReadStream and WriteStream
87
Non-critical stuff for the future:
89
@todo Investigate supporting host buffer formats > 16 bits
91
@todo define UNICODE and _UNICODE in the project settings and see what breaks
93
@todo refactor conversion of MMSYSTEM errors into PA arrors into a single function.
95
@todo cleanup WAVEFORMATEXTENSIBLE retry in InitializeWaveHandles to not use a for loop
102
For both callback and blocking read/write streams we open the MME devices
103
in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever
104
it has finished with a buffer (either filled it for input, or played it
105
for output). Where necessary we block waiting for Event objects using
106
WaitMultipleObjects().
108
When implementing a PA callback stream, we set up a high priority thread
109
which waits on the MME buffer Events and drains/fills the buffers when
112
When implementing a PA blocking read/write stream, we simply wait on these
113
Events (when necessary) inside the ReadStream() and WriteStream() functions.
120
#include <mmsystem.h>
125
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
129
#endif /* __MWERKS__ */
131
#include "portaudio.h"
132
#include "pa_trace.h"
134
#include "pa_allocation.h"
135
#include "pa_hostapi.h"
136
#include "pa_stream.h"
137
#include "pa_cpuload.h"
138
#include "pa_process.h"
139
#include "pa_debugprint.h"
141
#include "pa_win_wmme.h"
142
#include "pa_win_waveformat.h"
144
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
145
#include "pa_win_wdmks_utils.h"
146
#ifndef DRV_QUERYDEVICEINTERFACE
147
#define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12)
149
#ifndef DRV_QUERYDEVICEINTERFACESIZE
150
#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
152
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
154
/* use CreateThread for CYGWIN, _beginthreadex for all others */
155
#if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
156
#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
157
#define PA_THREAD_FUNC static unsigned WINAPI
158
#define PA_THREAD_ID unsigned
160
#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
161
#define PA_THREAD_FUNC static DWORD WINAPI
162
#define PA_THREAD_ID DWORD
164
#if (defined(_WIN32_WCE))
165
#pragma comment(lib, "Coredll.lib")
166
#elif (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
167
#pragma comment(lib, "winmm.lib")
171
provided in newer platform sdks
175
#define DWORD_PTR unsigned __int64
177
#define DWORD_PTR unsigned long
181
/************************************************* Constants ********/
183
#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */
185
#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
186
#define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4)
187
#define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4)
188
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (4)
189
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (4)
190
#define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
191
#define PA_MME_MAX_HOST_BUFFER_SECS_ (0.3) /* Do not exceed unless user buffer exceeds */
192
#define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
194
#define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.2)
195
#define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (2)
196
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ (3)
197
#define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_ (2)
198
#define PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_ (16)
199
#define PA_MME_MAX_HOST_BUFFER_SECS_ (0.1) /* Do not exceed unless user buffer exceeds */
200
#define PA_MME_MAX_HOST_BUFFER_BYTES_ (32 * 1024) /* Has precedence over PA_MME_MAX_HOST_BUFFER_SECS_, some drivers are known to crash with buffer sizes > 32k */
203
/* Use higher latency for NT because it is even worse at real-time
204
operation than Win9x.
206
#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
207
#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_)
210
#define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
212
static const char constInputMapperSuffix_[] = " - Input";
213
static const char constOutputMapperSuffix_[] = " - Output";
216
copies TCHAR string to explicit char string
218
char *StrTCpyToC(char *to, const TCHAR *from)
220
#if !defined(_UNICODE) && !defined(UNICODE)
221
return strcpy(to, from);
223
int count = wcslen(from);
225
if (WideCharToMultiByte(CP_ACP, 0, from, count, to, count, NULL, NULL) == 0)
232
returns length of TCHAR string
234
size_t StrTLen(const TCHAR *str)
236
#if !defined(_UNICODE) && !defined(UNICODE)
243
/********************************************************************/
245
typedef struct PaWinMmeStream PaWinMmeStream; /* forward declaration */
247
/* prototypes for functions declared in this file */
252
#endif /* __cplusplus */
254
PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
258
#endif /* __cplusplus */
260
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
261
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
263
const PaStreamParameters *inputParameters,
264
const PaStreamParameters *outputParameters,
266
unsigned long framesPerBuffer,
267
PaStreamFlags streamFlags,
268
PaStreamCallback *streamCallback,
270
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
271
const PaStreamParameters *inputParameters,
272
const PaStreamParameters *outputParameters,
274
static PaError CloseStream( PaStream* stream );
275
static PaError StartStream( PaStream *stream );
276
static PaError StopStream( PaStream *stream );
277
static PaError AbortStream( PaStream *stream );
278
static PaError IsStreamStopped( PaStream *s );
279
static PaError IsStreamActive( PaStream *stream );
280
static PaTime GetStreamTime( PaStream *stream );
281
static double GetStreamCpuLoad( PaStream* stream );
282
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
283
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
284
static signed long GetStreamReadAvailable( PaStream* stream );
285
static signed long GetStreamWriteAvailable( PaStream* stream );
288
/* macros for setting last host error information */
292
#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
294
wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
295
char mmeErrorText[ MAXERRORLENGTH ]; \
296
waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
297
WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
298
mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
299
PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
302
#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
304
wchar_t mmeErrorTextWide[ MAXERRORLENGTH ]; \
305
char mmeErrorText[ MAXERRORLENGTH ]; \
306
waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH ); \
307
WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
308
mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL ); \
309
PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
314
#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
316
char mmeErrorText[ MAXERRORLENGTH ]; \
317
waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
318
PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
321
#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
323
char mmeErrorText[ MAXERRORLENGTH ]; \
324
waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH ); \
325
PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText ); \
331
static void PaMme_SetLastSystemError( DWORD errorCode )
335
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
338
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
343
PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf );
344
LocalFree( lpMsgBuf );
347
#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \
348
PaMme_SetLastSystemError( errorCode )
351
/* PaError returning wrappers for some commonly used win32 functions
352
note that we allow passing a null ptr to have no effect.
355
static PaError CreateEventWithPaError( HANDLE *handle,
356
LPSECURITY_ATTRIBUTES lpEventAttributes,
361
PaError result = paNoError;
365
*handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
366
if( *handle == NULL )
368
result = paUnanticipatedHostError;
369
PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
376
static PaError ResetEventWithPaError( HANDLE handle )
378
PaError result = paNoError;
382
if( ResetEvent( handle ) == 0 )
384
result = paUnanticipatedHostError;
385
PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
393
static PaError CloseHandleWithPaError( HANDLE handle )
395
PaError result = paNoError;
399
if( CloseHandle( handle ) == 0 )
401
result = paUnanticipatedHostError;
402
PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
410
/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
414
PaUtilHostApiRepresentation inheritedHostApiRep;
415
PaUtilStreamInterface callbackStreamInterface;
416
PaUtilStreamInterface blockingStreamInterface;
418
PaUtilAllocationGroup *allocations;
420
int inputDeviceCount, outputDeviceCount;
422
/** winMmeDeviceIds is an array of WinMme device ids.
423
fields in the range [0, inputDeviceCount) are input device ids,
424
and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output
427
UINT *winMmeDeviceIds;
429
PaWinMmeHostApiRepresentation;
434
PaDeviceInfo inheritedDeviceInfo;
435
DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
436
char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
437
char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
442
/*************************************************************************
443
* Returns recommended device ID.
444
* On the PC, the recommended device can be specified by the user by
445
* setting an environment variable. For example, to use device #1.
447
* set PA_RECOMMENDED_OUTPUT_DEVICE=1
449
* The user should first determine the available device ID by using
450
* the supplied application "pa_devs".
452
#define PA_ENV_BUF_SIZE_ (32)
453
#define PA_REC_IN_DEV_ENV_NAME_ ("PA_RECOMMENDED_INPUT_DEVICE")
454
#define PA_REC_OUT_DEV_ENV_NAME_ ("PA_RECOMMENDED_OUTPUT_DEVICE")
455
static PaDeviceIndex GetEnvDefaultDeviceID( char *envName )
457
PaDeviceIndex recommendedIndex = paNoDevice;
459
char envbuf[PA_ENV_BUF_SIZE_];
461
#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */
463
/* Let user determine default device by setting environment variable. */
464
hresult = GetEnvironmentVariable( envName, envbuf, PA_ENV_BUF_SIZE_ );
465
if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) )
467
recommendedIndex = atoi( envbuf );
471
return recommendedIndex;
475
static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi )
477
PaDeviceIndex device;
480
device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ );
481
if( device != paNoDevice &&
482
( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
483
hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 )
485
hostApi->inheritedHostApiRep.info.defaultInputDevice = device;
489
device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ );
490
if( device != paNoDevice &&
491
( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
492
hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 )
494
hostApi->inheritedHostApiRep.info.defaultOutputDevice = device;
499
/** Convert external PA ID to a windows multimedia device ID
501
static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device )
503
assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount );
505
return hostApi->winMmeDeviceIds[ device ];
509
static int SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( PaSampleFormat sampleFormat, unsigned long winMmeSpecificFlags )
511
int waveFormatTag = 0;
513
if( winMmeSpecificFlags & paWinMmeWaveFormatDolbyAc3Spdif )
514
waveFormatTag = PAWIN_WAVE_FORMAT_DOLBY_AC3_SPDIF;
515
else if( winMmeSpecificFlags & paWinMmeWaveFormatWmaSpdif )
516
waveFormatTag = PAWIN_WAVE_FORMAT_WMA_SPDIF;
518
waveFormatTag = PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat );
520
return waveFormatTag;
524
static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
528
switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
530
case MMSYSERR_NOERROR:
532
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
533
return paDeviceUnavailable;
534
case MMSYSERR_NODRIVER: /* No device driver is present. */
535
return paDeviceUnavailable;
536
case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
537
return paInsufficientMemory;
538
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
539
return paSampleFormatNotSupported;
541
case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
544
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
545
return paUnanticipatedHostError;
550
static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
554
switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
556
case MMSYSERR_NOERROR:
558
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
559
return paDeviceUnavailable;
560
case MMSYSERR_NODRIVER: /* No device driver is present. */
561
return paDeviceUnavailable;
562
case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
563
return paInsufficientMemory;
564
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
565
return paSampleFormatNotSupported;
567
case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
570
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
571
return paUnanticipatedHostError;
576
static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
577
PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
578
int winMmeDeviceId, int channels, double sampleRate, unsigned long winMmeSpecificFlags )
580
PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
581
PaWinWaveFormat waveFormat;
582
PaSampleFormat sampleFormat;
585
/* @todo at the moment we only query with 16 bit sample format and directout speaker config*/
587
sampleFormat = paInt16;
588
waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
590
if( waveFormatTag == PaWin_SampleFormatToLinearWaveFormatTag( paInt16 ) ){
592
/* attempt bypass querying the device for linear formats */
594
if( sampleRate == 11025.0
595
&& ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
596
|| (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
601
if( sampleRate == 22050.0
602
&& ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
603
|| (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
608
if( sampleRate == 44100.0
609
&& ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
610
|| (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
617
/* first, attempt to query the device using WAVEFORMATEXTENSIBLE,
618
if this fails we fall back to WAVEFORMATEX */
620
PaWin_InitializeWaveFormatExtensible( &waveFormat, channels, sampleFormat, waveFormatTag,
621
sampleRate, PAWIN_SPEAKER_DIRECTOUT );
623
if( waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat ) == paNoError )
626
PaWin_InitializeWaveFormatEx( &waveFormat, channels, sampleFormat, waveFormatTag, sampleRate );
628
return waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat );
632
#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
633
static double defaultSampleRateSearchOrder_[] =
634
{ 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
635
16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
637
static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId,
638
PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels )
640
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
643
deviceInfo->defaultSampleRate = 0.;
645
for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
647
double sampleRate = defaultSampleRateSearchOrder_[ i ];
648
PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate, 0 );
649
if( paerror == paNoError )
651
deviceInfo->defaultSampleRate = sampleRate;
658
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
659
static int QueryWaveInKSFilterMaxChannels( int waveInDeviceId, int *maxChannels )
662
DWORD devicePathSize;
665
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
666
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
669
devicePath = PaUtil_AllocateMemory( devicePathSize );
673
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
674
if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACE,
675
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
677
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 1 );
680
*maxChannels = count;
685
PaUtil_FreeMemory( devicePath );
689
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
692
static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
693
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
695
PaError result = paNoError;
696
char *deviceName; /* non-const ptr */
699
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
703
mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
704
if( mmresult == MMSYSERR_NOMEM )
706
result = paInsufficientMemory;
709
else if( mmresult != MMSYSERR_NOERROR )
711
/* instead of returning paUnanticipatedHostError we return
712
paNoError, but leave success set as 0. This allows
713
Pa_Initialize to just ignore this device, without failing
714
the entire initialisation process.
719
if( winMmeInputDeviceId == WAVE_MAPPER )
721
/* Append I/O suffix to WAVE_MAPPER device. */
722
deviceName = (char *)PaUtil_GroupAllocateMemory(
723
winMmeHostApi->allocations, StrTLen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) );
726
result = paInsufficientMemory;
729
StrTCpyToC( deviceName, wic.szPname );
730
strcat( deviceName, constInputMapperSuffix_ );
734
deviceName = (char*)PaUtil_GroupAllocateMemory(
735
winMmeHostApi->allocations, StrTLen( wic.szPname ) + 1 );
738
result = paInsufficientMemory;
741
StrTCpyToC( deviceName, wic.szPname );
743
deviceInfo->name = deviceName;
745
if( wic.wChannels == 0xFFFF || wic.wChannels < 1 || wic.wChannels > 255 ){
746
/* For Windows versions using WDM (possibly Windows 98 ME and later)
747
* the kernel mixer sits between the application and the driver. As a result,
748
* wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
749
* When this happens we assume the device is stereo and set a flag
750
* so that other channel counts can be tried with OpenStream -- i.e. when
751
* device*ChannelCountIsKnown is false, OpenStream will try whatever
752
* channel count you supply.
753
* see also InitializeOutputDeviceInfo() below.
756
PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", wic.wChannels ));
757
deviceInfo->maxInputChannels = 2;
758
winMmeDeviceInfo->deviceInputChannelCountIsKnown = 0;
760
deviceInfo->maxInputChannels = wic.wChannels;
761
winMmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
764
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
765
winMmeDeviceInfo->deviceInputChannelCountIsKnown =
766
QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels );
767
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
769
winMmeDeviceInfo->dwFormats = wic.dwFormats;
771
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
772
QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
781
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
782
static int QueryWaveOutKSFilterMaxChannels( int waveOutDeviceId, int *maxChannels )
785
DWORD devicePathSize;
788
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
789
(DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
792
devicePath = PaUtil_AllocateMemory( devicePathSize );
796
/* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
797
if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACE,
798
(DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
800
int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 0 );
803
*maxChannels = count;
808
PaUtil_FreeMemory( devicePath );
812
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
815
static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
816
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
818
PaError result = paNoError;
819
char *deviceName; /* non-const ptr */
822
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
823
int wdmksDeviceOutputChannelCountIsKnown;
827
mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
828
if( mmresult == MMSYSERR_NOMEM )
830
result = paInsufficientMemory;
833
else if( mmresult != MMSYSERR_NOERROR )
835
/* instead of returning paUnanticipatedHostError we return
836
paNoError, but leave success set as 0. This allows
837
Pa_Initialize to just ignore this device, without failing
838
the entire initialisation process.
843
if( winMmeOutputDeviceId == WAVE_MAPPER )
845
/* Append I/O suffix to WAVE_MAPPER device. */
846
deviceName = (char *)PaUtil_GroupAllocateMemory(
847
winMmeHostApi->allocations, StrTLen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) );
850
result = paInsufficientMemory;
853
StrTCpyToC( deviceName, woc.szPname );
854
strcat( deviceName, constOutputMapperSuffix_ );
858
deviceName = (char*)PaUtil_GroupAllocateMemory(
859
winMmeHostApi->allocations, StrTLen( woc.szPname ) + 1 );
862
result = paInsufficientMemory;
865
StrTCpyToC( deviceName, woc.szPname );
867
deviceInfo->name = deviceName;
869
if( woc.wChannels == 0xFFFF || woc.wChannels < 1 || woc.wChannels > 255 ){
870
/* For Windows versions using WDM (possibly Windows 98 ME and later)
871
* the kernel mixer sits between the application and the driver. As a result,
872
* wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
873
* When this happens we assume the device is stereo and set a flag
874
* so that other channel counts can be tried with OpenStream -- i.e. when
875
* device*ChannelCountIsKnown is false, OpenStream will try whatever
876
* channel count you supply.
877
* see also InitializeInputDeviceInfo() above.
880
PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", woc.wChannels ));
881
deviceInfo->maxOutputChannels = 2;
882
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 0;
884
deviceInfo->maxOutputChannels = woc.wChannels;
885
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
888
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
889
wdmksDeviceOutputChannelCountIsKnown = QueryWaveOutKSFilterMaxChannels(
890
winMmeOutputDeviceId, &deviceInfo->maxOutputChannels );
891
if( wdmksDeviceOutputChannelCountIsKnown && !winMmeDeviceInfo->deviceOutputChannelCountIsKnown )
892
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
893
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
895
winMmeDeviceInfo->dwFormats = woc.dwFormats;
897
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
898
QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
907
static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
910
osvi.dwOSVersionInfoSize = sizeof( osvi );
911
GetVersionEx( &osvi );
914
if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
916
*defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
918
else if(osvi.dwMajorVersion >= 5)
920
*defaultLowLatency = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
924
*defaultLowLatency = PA_MME_WIN_9X_DEFAULT_LATENCY_;
927
*defaultHighLatency = *defaultLowLatency * 2;
931
PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
933
PaError result = paNoError;
935
PaWinMmeHostApiRepresentation *winMmeHostApi;
936
int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
937
PaWinMmeDeviceInfo *deviceInfoArray;
938
int deviceInfoInitializationSucceeded;
939
PaTime defaultLowLatency, defaultHighLatency;
940
DWORD waveInPreferredDevice, waveOutPreferredDevice;
941
DWORD preferredDeviceStatusFlags;
943
winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
946
result = paInsufficientMemory;
950
winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
951
if( !winMmeHostApi->allocations )
953
result = paInsufficientMemory;
957
*hostApi = &winMmeHostApi->inheritedHostApiRep;
958
(*hostApi)->info.structVersion = 1;
959
(*hostApi)->info.type = paMME;
960
(*hostApi)->info.name = "MME";
963
/* initialise device counts and default devices under the assumption that
964
there are no devices. These values are incremented below if and when
965
devices are successfully initialized.
967
(*hostApi)->info.deviceCount = 0;
968
(*hostApi)->info.defaultInputDevice = paNoDevice;
969
(*hostApi)->info.defaultOutputDevice = paNoDevice;
970
winMmeHostApi->inputDeviceCount = 0;
971
winMmeHostApi->outputDeviceCount = 0;
973
#if !defined(DRVM_MAPPER_PREFERRED_GET)
974
/* DRVM_MAPPER_PREFERRED_GET is defined in mmddk.h but we avoid a dependency on the DDK by defining it here */
975
#define DRVM_MAPPER_PREFERRED_GET (0x2000+21)
978
/* the following calls assume that if wave*Message fails the preferred device parameter won't be modified */
979
preferredDeviceStatusFlags = 0;
980
waveInPreferredDevice = -1;
981
waveInMessage( (HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&waveInPreferredDevice, (DWORD_PTR)&preferredDeviceStatusFlags );
983
preferredDeviceStatusFlags = 0;
984
waveOutPreferredDevice = -1;
985
waveOutMessage( (HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&waveOutPreferredDevice, (DWORD_PTR)&preferredDeviceStatusFlags );
987
maximumPossibleDeviceCount = 0;
989
inputDeviceCount = waveInGetNumDevs();
990
if( inputDeviceCount > 0 )
991
maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
993
outputDeviceCount = waveOutGetNumDevs();
994
if( outputDeviceCount > 0 )
995
maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
998
if( maximumPossibleDeviceCount > 0 ){
1000
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1001
winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
1002
if( !(*hostApi)->deviceInfos )
1004
result = paInsufficientMemory;
1008
/* allocate all device info structs in a contiguous block */
1009
deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
1010
winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
1011
if( !deviceInfoArray )
1013
result = paInsufficientMemory;
1017
winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
1018
winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
1019
if( !winMmeHostApi->winMmeDeviceIds )
1021
result = paInsufficientMemory;
1025
GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
1027
if( inputDeviceCount > 0 ){
1028
/* -1 is the WAVE_MAPPER */
1029
for( i = -1; i < inputDeviceCount; ++i ){
1030
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
1031
PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
1032
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
1033
deviceInfo->structVersion = 2;
1034
deviceInfo->hostApi = hostApiIndex;
1036
deviceInfo->maxInputChannels = 0;
1037
wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
1038
deviceInfo->maxOutputChannels = 0;
1039
wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
1041
deviceInfo->defaultLowInputLatency = defaultLowLatency;
1042
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
1043
deviceInfo->defaultHighInputLatency = defaultHighLatency;
1044
deviceInfo->defaultHighOutputLatency = defaultHighLatency;
1046
result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
1047
winMmeDeviceId, &deviceInfoInitializationSucceeded );
1048
if( result != paNoError )
1051
if( deviceInfoInitializationSucceeded ){
1052
if( (*hostApi)->info.defaultInputDevice == paNoDevice ){
1053
/* if there is currently no default device, use the first one available */
1054
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
1056
}else if( winMmeDeviceId == waveInPreferredDevice ){
1057
/* set the default device to the system preferred device */
1058
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
1061
winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
1062
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
1064
winMmeHostApi->inputDeviceCount++;
1065
(*hostApi)->info.deviceCount++;
1070
if( outputDeviceCount > 0 ){
1071
/* -1 is the WAVE_MAPPER */
1072
for( i = -1; i < outputDeviceCount; ++i ){
1073
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
1074
PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
1075
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
1076
deviceInfo->structVersion = 2;
1077
deviceInfo->hostApi = hostApiIndex;
1079
deviceInfo->maxInputChannels = 0;
1080
wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
1081
deviceInfo->maxOutputChannels = 0;
1082
wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
1084
deviceInfo->defaultLowInputLatency = defaultLowLatency;
1085
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
1086
deviceInfo->defaultHighInputLatency = defaultHighLatency;
1087
deviceInfo->defaultHighOutputLatency = defaultHighLatency;
1089
result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
1090
winMmeDeviceId, &deviceInfoInitializationSucceeded );
1091
if( result != paNoError )
1094
if( deviceInfoInitializationSucceeded ){
1095
if( (*hostApi)->info.defaultOutputDevice == paNoDevice ){
1096
/* if there is currently no default device, use the first one available */
1097
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
1099
}else if( winMmeDeviceId == waveOutPreferredDevice ){
1100
/* set the default device to the system preferred device */
1101
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
1104
winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
1105
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
1107
winMmeHostApi->outputDeviceCount++;
1108
(*hostApi)->info.deviceCount++;
1114
InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
1116
(*hostApi)->Terminate = Terminate;
1117
(*hostApi)->OpenStream = OpenStream;
1118
(*hostApi)->IsFormatSupported = IsFormatSupported;
1120
PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
1121
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1122
GetStreamTime, GetStreamCpuLoad,
1123
PaUtil_DummyRead, PaUtil_DummyWrite,
1124
PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1126
PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream,
1127
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1128
GetStreamTime, PaUtil_DummyGetCpuLoad,
1129
ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1136
if( winMmeHostApi->allocations )
1138
PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
1139
PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
1142
PaUtil_FreeMemory( winMmeHostApi );
1149
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1151
PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
1153
if( winMmeHostApi->allocations )
1155
PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
1156
PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
1159
PaUtil_FreeMemory( winMmeHostApi );
1163
static PaError IsInputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
1165
PaError result = paNoError;
1167
if( channelCount > 0
1168
&& deviceInfo->deviceInputChannelCountIsKnown
1169
&& channelCount > deviceInfo->inheritedDeviceInfo.maxInputChannels ){
1171
result = paInvalidChannelCount;
1177
static PaError IsOutputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
1179
PaError result = paNoError;
1181
if( channelCount > 0
1182
&& deviceInfo->deviceOutputChannelCountIsKnown
1183
&& channelCount > deviceInfo->inheritedDeviceInfo.maxOutputChannels ){
1185
result = paInvalidChannelCount;
1191
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1192
const PaStreamParameters *inputParameters,
1193
const PaStreamParameters *outputParameters,
1196
PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
1197
PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1198
int inputChannelCount, outputChannelCount;
1199
int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount;
1200
PaSampleFormat inputSampleFormat, outputSampleFormat;
1201
PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
1202
UINT winMmeInputDeviceId, winMmeOutputDeviceId;
1206
/* The calls to QueryFormatSupported below are intended to detect invalid
1207
sample rates. If we assume that the channel count and format are OK,
1208
then the only thing that could fail is the sample rate. This isn't
1209
strictly true, but I can't think of a better way to test that the
1210
sample rate is valid.
1213
if( inputParameters )
1215
inputChannelCount = inputParameters->channelCount;
1216
inputSampleFormat = inputParameters->sampleFormat;
1217
inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
1219
/* all standard sample formats are supported by the buffer adapter,
1220
this implementation doesn't support any custom sample formats */
1221
if( inputSampleFormat & paCustomFormat )
1222
return paSampleFormatNotSupported;
1224
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification
1225
&& inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
1227
inputMultipleDeviceChannelCount = 0;
1228
for( i=0; i< inputStreamInfo->deviceCount; ++i )
1230
inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
1232
inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
1234
/* check that input device can support inputChannelCount */
1235
if( inputStreamInfo->devices[i].channelCount < 1 )
1236
return paInvalidChannelCount;
1238
paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo,
1239
inputStreamInfo->devices[i].channelCount );
1240
if( paerror != paNoError )
1243
/* test for valid sample rate, see comment above */
1244
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
1245
paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx,
1246
winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate,
1247
((inputStreamInfo) ? inputStreamInfo->flags : 0) );
1248
if( paerror != paNoError )
1249
return paInvalidSampleRate;
1252
if( inputMultipleDeviceChannelCount != inputChannelCount )
1253
return paIncompatibleHostApiSpecificStreamInfo;
1257
if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
1258
return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */
1260
inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
1262
/* check that input device can support inputChannelCount */
1263
paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo, inputChannelCount );
1264
if( paerror != paNoError )
1267
/* test for valid sample rate, see comment above */
1268
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
1269
paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx,
1270
winMmeInputDeviceId, inputChannelCount, sampleRate,
1271
((inputStreamInfo) ? inputStreamInfo->flags : 0) );
1272
if( paerror != paNoError )
1273
return paInvalidSampleRate;
1277
if( outputParameters )
1279
outputChannelCount = outputParameters->channelCount;
1280
outputSampleFormat = outputParameters->sampleFormat;
1281
outputStreamInfo = outputParameters->hostApiSpecificStreamInfo;
1283
/* all standard sample formats are supported by the buffer adapter,
1284
this implementation doesn't support any custom sample formats */
1285
if( outputSampleFormat & paCustomFormat )
1286
return paSampleFormatNotSupported;
1288
if( outputParameters->device == paUseHostApiSpecificDeviceSpecification
1289
&& outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
1291
outputMultipleDeviceChannelCount = 0;
1292
for( i=0; i< outputStreamInfo->deviceCount; ++i )
1294
outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
1296
outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
1298
/* check that output device can support outputChannelCount */
1299
if( outputStreamInfo->devices[i].channelCount < 1 )
1300
return paInvalidChannelCount;
1302
paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo,
1303
outputStreamInfo->devices[i].channelCount );
1304
if( paerror != paNoError )
1307
/* test for valid sample rate, see comment above */
1308
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
1309
paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx,
1310
winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate,
1311
((outputStreamInfo) ? outputStreamInfo->flags : 0) );
1312
if( paerror != paNoError )
1313
return paInvalidSampleRate;
1316
if( outputMultipleDeviceChannelCount != outputChannelCount )
1317
return paIncompatibleHostApiSpecificStreamInfo;
1321
if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
1322
return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */
1324
outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
1326
/* check that output device can support outputChannelCount */
1327
paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo, outputChannelCount );
1328
if( paerror != paNoError )
1331
/* test for valid sample rate, see comment above */
1332
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
1333
paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx,
1334
winMmeOutputDeviceId, outputChannelCount, sampleRate,
1335
((outputStreamInfo) ? outputStreamInfo->flags : 0) );
1336
if( paerror != paNoError )
1337
return paInvalidSampleRate;
1342
- if a full duplex stream is requested, check that the combination
1343
of input and output parameters is supported
1345
- check that the device supports sampleRate
1347
for mme all we can do is test that the input and output devices
1348
support the requested sample rate and number of channels. we
1349
cannot test for full duplex compatibility.
1352
return paFormatIsSupported;
1357
static void SelectBufferSizeAndCount( unsigned long baseBufferSize,
1358
unsigned long requestedLatency,
1359
unsigned long baseBufferCount, unsigned long minimumBufferCount,
1360
unsigned long maximumBufferSize, unsigned long *hostBufferSize,
1361
unsigned long *hostBufferCount )
1363
unsigned long sizeMultiplier, bufferCount, latency;
1364
unsigned long nextLatency, nextBufferSize;
1365
int baseBufferSizeIsPowerOfTwo;
1368
bufferCount = baseBufferCount;
1370
/* count-1 below because latency is always determined by one less
1371
than the total number of buffers.
1373
latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
1375
if( latency > requestedLatency )
1378
/* reduce number of buffers without falling below suggested latency */
1380
nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
1381
while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
1384
nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
1387
}else if( latency < requestedLatency ){
1389
baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1)));
1390
if( baseBufferSizeIsPowerOfTwo ){
1392
/* double size of buffers without exceeding requestedLatency */
1394
nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
1395
nextLatency = nextBufferSize * (bufferCount-1);
1396
while( nextBufferSize <= maximumBufferSize
1397
&& nextLatency < requestedLatency )
1399
sizeMultiplier *= 2;
1400
nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
1401
nextLatency = nextBufferSize * (bufferCount-1);
1406
/* increase size of buffers upto first excess of requestedLatency */
1408
nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
1409
nextLatency = nextBufferSize * (bufferCount-1);
1410
while( nextBufferSize <= maximumBufferSize
1411
&& nextLatency < requestedLatency )
1414
nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
1415
nextLatency = nextBufferSize * (bufferCount-1);
1418
if( nextLatency < requestedLatency )
1422
/* increase number of buffers until requestedLatency is reached */
1424
latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
1425
while( latency < requestedLatency )
1428
latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
1432
*hostBufferSize = baseBufferSize * sizeMultiplier;
1433
*hostBufferCount = bufferCount;
1437
static void ReselectBufferCount( unsigned long bufferSize,
1438
unsigned long requestedLatency,
1439
unsigned long baseBufferCount, unsigned long minimumBufferCount,
1440
unsigned long *hostBufferCount )
1442
unsigned long bufferCount, latency;
1443
unsigned long nextLatency;
1445
bufferCount = baseBufferCount;
1447
/* count-1 below because latency is always determined by one less
1448
than the total number of buffers.
1450
latency = bufferSize * (bufferCount-1);
1452
if( latency > requestedLatency )
1454
/* reduce number of buffers without falling below suggested latency */
1456
nextLatency = bufferSize * (bufferCount-2);
1457
while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
1460
nextLatency = bufferSize * (bufferCount-2);
1463
}else if( latency < requestedLatency ){
1465
/* increase number of buffers until requestedLatency is reached */
1467
latency = bufferSize * (bufferCount-1);
1468
while( latency < requestedLatency )
1471
latency = bufferSize * (bufferCount-1);
1475
*hostBufferCount = bufferCount;
1479
/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount,
1480
framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values
1481
of the other parameters.
1484
static PaError CalculateBufferSettings(
1485
unsigned long *framesPerHostInputBuffer, unsigned long *hostInputBufferCount,
1486
unsigned long *framesPerHostOutputBuffer, unsigned long *hostOutputBufferCount,
1487
int inputChannelCount, PaSampleFormat hostInputSampleFormat,
1488
PaTime suggestedInputLatency, PaWinMmeStreamInfo *inputStreamInfo,
1489
int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
1490
PaTime suggestedOutputLatency, PaWinMmeStreamInfo *outputStreamInfo,
1491
double sampleRate, unsigned long framesPerBuffer )
1493
PaError result = paNoError;
1494
int effectiveInputChannelCount, effectiveOutputChannelCount;
1495
int hostInputFrameSize = 0;
1498
if( inputChannelCount > 0 )
1500
int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat );
1501
if( hostInputSampleSize < 0 )
1503
result = hostInputSampleSize;
1508
&& ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
1510
/* set effectiveInputChannelCount to the largest number of
1511
channels on any one device.
1513
effectiveInputChannelCount = 0;
1514
for( i=0; i< inputStreamInfo->deviceCount; ++i )
1516
if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount )
1517
effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount;
1522
effectiveInputChannelCount = inputChannelCount;
1525
hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount;
1528
&& ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
1530
if( inputStreamInfo->bufferCount <= 0
1531
|| inputStreamInfo->framesPerBuffer <= 0 )
1533
result = paIncompatibleHostApiSpecificStreamInfo;
1537
*framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer;
1538
*hostInputBufferCount = inputStreamInfo->bufferCount;
1542
unsigned long hostBufferSizeBytes, hostBufferCount;
1543
unsigned long minimumBufferCount = (outputChannelCount > 0)
1544
? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
1545
: PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
1547
unsigned long maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostInputFrameSize);
1548
if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
1549
maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
1551
/* compute the following in bytes, then convert back to frames */
1553
SelectBufferSizeAndCount(
1554
((framesPerBuffer == paFramesPerBufferUnspecified)
1555
? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
1556
: framesPerBuffer ) * hostInputFrameSize, /* baseBufferSize */
1557
((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
1558
4, /* baseBufferCount */
1559
minimumBufferCount, maximumBufferSize,
1560
&hostBufferSizeBytes, &hostBufferCount );
1562
*framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
1563
*hostInputBufferCount = hostBufferCount;
1568
*framesPerHostInputBuffer = 0;
1569
*hostInputBufferCount = 0;
1572
if( outputChannelCount > 0 )
1574
if( outputStreamInfo
1575
&& ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
1577
if( outputStreamInfo->bufferCount <= 0
1578
|| outputStreamInfo->framesPerBuffer <= 0 )
1580
result = paIncompatibleHostApiSpecificStreamInfo;
1584
*framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer;
1585
*hostOutputBufferCount = outputStreamInfo->bufferCount;
1588
if( inputChannelCount > 0 ) /* full duplex */
1590
if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer )
1593
&& ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
1595
/* a custom StreamInfo was used for specifying both input
1596
and output buffer sizes, the larger buffer size
1597
must be a multiple of the smaller buffer size */
1599
if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer )
1601
if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 )
1603
result = paIncompatibleHostApiSpecificStreamInfo;
1609
assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer );
1610
if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 )
1612
result = paIncompatibleHostApiSpecificStreamInfo;
1619
/* a custom StreamInfo was not used for specifying the input buffer size,
1620
so use the output buffer size, and approximately the same latency. */
1622
*framesPerHostInputBuffer = *framesPerHostOutputBuffer;
1623
*hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
1625
if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ )
1626
*hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
1633
unsigned long hostBufferSizeBytes, hostBufferCount;
1634
unsigned long minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
1635
unsigned long maximumBufferSize;
1636
int hostOutputFrameSize;
1637
int hostOutputSampleSize;
1639
hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat );
1640
if( hostOutputSampleSize < 0 )
1642
result = hostOutputSampleSize;
1646
if( outputStreamInfo
1647
&& ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
1649
/* set effectiveOutputChannelCount to the largest number of
1650
channels on any one device.
1652
effectiveOutputChannelCount = 0;
1653
for( i=0; i< outputStreamInfo->deviceCount; ++i )
1655
if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount )
1656
effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount;
1661
effectiveOutputChannelCount = outputChannelCount;
1664
hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount;
1666
maximumBufferSize = (long) ((PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate) * hostOutputFrameSize);
1667
if( maximumBufferSize > PA_MME_MAX_HOST_BUFFER_BYTES_ )
1668
maximumBufferSize = PA_MME_MAX_HOST_BUFFER_BYTES_;
1671
/* compute the following in bytes, then convert back to frames */
1673
SelectBufferSizeAndCount(
1674
((framesPerBuffer == paFramesPerBufferUnspecified)
1675
? PA_MME_MIN_HOST_BUFFER_FRAMES_WHEN_UNSPECIFIED_
1676
: framesPerBuffer ) * hostOutputFrameSize, /* baseBufferSize */
1677
((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
1678
4, /* baseBufferCount */
1681
&hostBufferSizeBytes, &hostBufferCount );
1683
*framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize;
1684
*hostOutputBufferCount = hostBufferCount;
1687
if( inputChannelCount > 0 )
1689
/* ensure that both input and output buffer sizes are the same.
1690
if they don't match at this stage, choose the smallest one
1691
and use that for input and output
1694
if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer )
1696
if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
1698
unsigned long framesPerHostBuffer = *framesPerHostInputBuffer;
1700
minimumBufferCount = PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_;
1701
ReselectBufferCount(
1702
framesPerHostBuffer * hostOutputFrameSize, /* bufferSize */
1703
((unsigned long)(suggestedOutputLatency * sampleRate)) * hostOutputFrameSize, /* suggestedLatency */
1704
4, /* baseBufferCount */
1708
*framesPerHostOutputBuffer = framesPerHostBuffer;
1709
*hostOutputBufferCount = hostBufferCount;
1713
unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer;
1715
minimumBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
1716
ReselectBufferCount(
1717
framesPerHostBuffer * hostInputFrameSize, /* bufferSize */
1718
((unsigned long)(suggestedInputLatency * sampleRate)) * hostInputFrameSize, /* suggestedLatency */
1719
4, /* baseBufferCount */
1723
*framesPerHostInputBuffer = framesPerHostBuffer;
1724
*hostInputBufferCount = hostBufferCount;
1732
*framesPerHostOutputBuffer = 0;
1733
*hostOutputBufferCount = 0;
1745
unsigned int deviceCount;
1746
/* unsigned int channelCount; */
1747
WAVEHDR **waveHeaders; /* waveHeaders[device][buffer] */
1748
unsigned int bufferCount;
1749
unsigned int currentBufferIndex;
1750
unsigned int framesPerBuffer;
1751
unsigned int framesUsedInCurrentBuffer;
1752
}PaWinMmeSingleDirectionHandlesAndBuffers;
1754
/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */
1756
static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers );
1757
static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
1758
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
1759
unsigned long winMmeSpecificFlags,
1760
unsigned long bytesPerHostSample,
1761
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
1762
unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput );
1763
static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
1764
static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
1765
unsigned long hostBufferCount,
1766
PaSampleFormat hostSampleFormat,
1767
unsigned long framesPerHostBuffer,
1768
PaWinMmeDeviceAndChannelCount *devices,
1770
static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput );
1773
static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
1775
handlesAndBuffers->bufferEvent = 0;
1776
handlesAndBuffers->waveHandles = 0;
1777
handlesAndBuffers->deviceCount = 0;
1778
handlesAndBuffers->waveHeaders = 0;
1779
handlesAndBuffers->bufferCount = 0;
1782
static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
1783
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
1784
unsigned long winMmeSpecificFlags,
1785
unsigned long bytesPerHostSample,
1786
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
1787
unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput )
1792
PaSampleFormat sampleFormat;
1795
/* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
1796
has already been called to zero some fields */
1798
result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
1799
if( result != paNoError ) goto error;
1802
handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount );
1804
handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount );
1805
if( !handlesAndBuffers->waveHandles )
1807
result = paInsufficientMemory;
1811
handlesAndBuffers->deviceCount = deviceCount;
1813
for( i = 0; i < (signed int)deviceCount; ++i )
1816
((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0;
1818
((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
1821
/* @todo at the moment we only use 16 bit sample format */
1822
sampleFormat = paInt16;
1823
waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
1825
for( i = 0; i < (signed int)deviceCount; ++i )
1827
PaWinWaveFormat waveFormat;
1828
UINT winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
1830
/* @todo: consider providing a flag or #define to not try waveformat extensible
1831
this could just initialize j to 1 the first time round. */
1833
for( j = 0; j < 2; ++j )
1837
/* first, attempt to open the device using WAVEFORMATEXTENSIBLE,
1838
if this fails we fall back to WAVEFORMATEX */
1840
PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount,
1841
sampleFormat, waveFormatTag, sampleRate, channelMask );
1846
/* retry with WAVEFORMATEX */
1848
PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount,
1849
sampleFormat, waveFormatTag, sampleRate );
1852
/* REVIEW: consider not firing an event for input when a full duplex
1853
stream is being used. this would probably depend on the
1854
neverDropInput flag. */
1858
mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
1859
(WAVEFORMATEX*)&waveFormat,
1860
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
1864
mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
1865
(WAVEFORMATEX*)&waveFormat,
1866
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
1869
if( mmresult == MMSYSERR_NOERROR )
1871
break; /* success */
1875
continue; /* try again with WAVEFORMATEX */
1881
case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
1882
result = paDeviceUnavailable;
1884
case MMSYSERR_NODRIVER: /* No device driver is present. */
1885
result = paDeviceUnavailable;
1887
case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
1888
result = paInsufficientMemory;
1891
case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
1894
case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
1895
/* This can also occur if we try to open the device with an unsupported
1896
* number of channels. This is attempted when device*ChannelCountIsKnown is
1901
result = paUnanticipatedHostError;
1904
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
1908
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
1919
TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ );
1925
static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError )
1927
PaError result = paNoError;
1931
if( handlesAndBuffers->waveHandles )
1933
for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
1937
if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
1938
mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
1940
mmresult = MMSYSERR_NOERROR;
1944
if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
1945
mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
1947
mmresult = MMSYSERR_NOERROR;
1950
if( mmresult != MMSYSERR_NOERROR &&
1951
!currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */
1953
result = paUnanticipatedHostError;
1956
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
1960
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
1962
/* note that we don't break here, we try to continue closing devices */
1966
PaUtil_FreeMemory( handlesAndBuffers->waveHandles );
1967
handlesAndBuffers->waveHandles = 0;
1970
if( handlesAndBuffers->bufferEvent )
1972
result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
1973
handlesAndBuffers->bufferEvent = 0;
1980
static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
1981
unsigned long hostBufferCount,
1982
PaSampleFormat hostSampleFormat,
1983
unsigned long framesPerHostBuffer,
1984
PaWinMmeDeviceAndChannelCount *devices,
1987
PaError result = paNoError;
1989
WAVEHDR *deviceWaveHeaders;
1992
/* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
1993
has already been called to zero some fields */
1996
/* allocate an array of pointers to arrays of wave headers, one array of
1997
wave headers per device */
1998
handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount );
1999
if( !handlesAndBuffers->waveHeaders )
2001
result = paInsufficientMemory;
2005
for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
2006
handlesAndBuffers->waveHeaders[i] = 0;
2008
handlesAndBuffers->bufferCount = hostBufferCount;
2010
for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
2012
int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) *
2013
framesPerHostBuffer * devices[i].channelCount;
2014
if( bufferBytes < 0 )
2016
result = paInternalError;
2020
/* Allocate an array of wave headers for device i */
2021
deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount );
2022
if( !deviceWaveHeaders )
2024
result = paInsufficientMemory;
2028
for( j=0; j < (signed int)hostBufferCount; ++j )
2029
deviceWaveHeaders[j].lpData = 0;
2031
handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders;
2033
/* Allocate a buffer for each wave header */
2034
for( j=0; j < (signed int)hostBufferCount; ++j )
2036
deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes );
2037
if( !deviceWaveHeaders[j].lpData )
2039
result = paInsufficientMemory;
2042
deviceWaveHeaders[j].dwBufferLength = bufferBytes;
2043
deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */
2047
mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
2048
if( mmresult != MMSYSERR_NOERROR )
2050
result = paUnanticipatedHostError;
2051
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
2057
mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
2058
if( mmresult != MMSYSERR_NOERROR )
2060
result = paUnanticipatedHostError;
2061
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
2065
deviceWaveHeaders[j].dwUser = devices[i].channelCount;
2072
TerminateWaveHeaders( handlesAndBuffers, isInput );
2078
static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput )
2081
WAVEHDR *deviceWaveHeaders;
2083
if( handlesAndBuffers->waveHeaders )
2085
for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
2087
deviceWaveHeaders = handlesAndBuffers->waveHeaders[i]; /* wave headers for device i */
2088
if( deviceWaveHeaders )
2090
for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j )
2092
if( deviceWaveHeaders[j].lpData )
2094
if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF )
2097
waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
2099
waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
2102
PaUtil_FreeMemory( deviceWaveHeaders[j].lpData );
2106
PaUtil_FreeMemory( deviceWaveHeaders );
2110
PaUtil_FreeMemory( handlesAndBuffers->waveHeaders );
2111
handlesAndBuffers->waveHeaders = 0;
2117
/* PaWinMmeStream - a stream data structure specifically for this implementation */
2118
/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */
2119
struct PaWinMmeStream
2121
PaUtilStreamRepresentation streamRepresentation;
2122
PaUtilCpuLoadMeasurer cpuLoadMeasurer;
2123
PaUtilBufferProcessor bufferProcessor;
2125
int primeStreamUsingCallback;
2127
PaWinMmeSingleDirectionHandlesAndBuffers input;
2128
PaWinMmeSingleDirectionHandlesAndBuffers output;
2130
/* Processing thread management -------------- */
2132
HANDLE processingThread;
2133
PA_THREAD_ID processingThreadId;
2135
char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
2136
int processingThreadPriority;
2137
int highThreadPriority;
2138
int throttledThreadPriority;
2139
unsigned long throttledSleepMsecs;
2142
volatile int isActive;
2143
volatile int stopProcessing; /* stop thread once existing buffers have been returned */
2144
volatile int abortProcessing; /* stop thread immediately */
2146
DWORD allBuffersDurationMs; /* used to calculate timeouts */
2149
/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
2151
static PaError ValidateWinMmeSpecificStreamInfo(
2152
const PaStreamParameters *streamParameters,
2153
const PaWinMmeStreamInfo *streamInfo,
2154
unsigned long *winMmeSpecificFlags,
2155
char *throttleProcessingThreadOnOverload,
2156
unsigned long *deviceCount )
2160
if( streamInfo->size != sizeof( PaWinMmeStreamInfo )
2161
|| streamInfo->version != 1 )
2163
return paIncompatibleHostApiSpecificStreamInfo;
2166
*winMmeSpecificFlags = streamInfo->flags;
2168
if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
2169
*throttleProcessingThreadOnOverload = 0;
2171
if( streamInfo->flags & paWinMmeUseMultipleDevices )
2173
if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
2174
return paInvalidDevice;
2176
*deviceCount = streamInfo->deviceCount;
2183
static PaError RetrieveDevicesFromStreamParameters(
2184
struct PaUtilHostApiRepresentation *hostApi,
2185
const PaStreamParameters *streamParameters,
2186
const PaWinMmeStreamInfo *streamInfo,
2187
PaWinMmeDeviceAndChannelCount *devices,
2188
unsigned long deviceCount )
2190
PaError result = paNoError;
2192
int totalChannelCount;
2193
PaDeviceIndex hostApiDevice;
2195
if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
2197
totalChannelCount = 0;
2198
for( i=0; i < deviceCount; ++i )
2200
/* validate that the device number is within range */
2201
result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice,
2202
streamInfo->devices[i].device, hostApi );
2203
if( result != paNoError )
2206
devices[i].device = hostApiDevice;
2207
devices[i].channelCount = streamInfo->devices[i].channelCount;
2209
totalChannelCount += devices[i].channelCount;
2212
if( totalChannelCount != streamParameters->channelCount )
2214
/* channelCount must match total channels specified by multiple devices */
2215
return paInvalidChannelCount; /* REVIEW use of this error code */
2220
devices[0].device = streamParameters->device;
2221
devices[0].channelCount = streamParameters->channelCount;
2227
static PaError ValidateInputChannelCounts(
2228
struct PaUtilHostApiRepresentation *hostApi,
2229
PaWinMmeDeviceAndChannelCount *devices,
2230
unsigned long deviceCount )
2233
PaWinMmeDeviceInfo *inputDeviceInfo;
2236
for( i=0; i < deviceCount; ++i )
2238
if( devices[i].channelCount < 1 )
2239
return paInvalidChannelCount;
2242
(PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
2244
paerror = IsInputChannelCountSupported( inputDeviceInfo, devices[i].channelCount );
2245
if( paerror != paNoError )
2252
static PaError ValidateOutputChannelCounts(
2253
struct PaUtilHostApiRepresentation *hostApi,
2254
PaWinMmeDeviceAndChannelCount *devices,
2255
unsigned long deviceCount )
2258
PaWinMmeDeviceInfo *outputDeviceInfo;
2261
for( i=0; i < deviceCount; ++i )
2263
if( devices[i].channelCount < 1 )
2264
return paInvalidChannelCount;
2267
(PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
2269
paerror = IsOutputChannelCountSupported( outputDeviceInfo, devices[i].channelCount );
2270
if( paerror != paNoError )
2278
/* the following macros are intended to improve the readability of the following code */
2279
#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles )
2280
#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles )
2281
#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles )
2282
#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) )
2284
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
2286
const PaStreamParameters *inputParameters,
2287
const PaStreamParameters *outputParameters,
2289
unsigned long framesPerBuffer,
2290
PaStreamFlags streamFlags,
2291
PaStreamCallback *streamCallback,
2295
PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
2296
PaWinMmeStream *stream = 0;
2297
int bufferProcessorIsInitialized = 0;
2298
int streamRepresentationIsInitialized = 0;
2299
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
2300
int inputChannelCount, outputChannelCount;
2301
PaSampleFormat inputSampleFormat, outputSampleFormat;
2302
double suggestedInputLatency, suggestedOutputLatency;
2303
PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
2304
PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
2305
unsigned long framesPerHostInputBuffer;
2306
unsigned long hostInputBufferCount;
2307
unsigned long framesPerHostOutputBuffer;
2308
unsigned long hostOutputBufferCount;
2309
unsigned long framesPerBufferProcessorCall;
2310
PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
2311
unsigned long winMmeSpecificInputFlags = 0;
2312
unsigned long inputDeviceCount = 0;
2313
PaWinMmeDeviceAndChannelCount *outputDevices = 0;
2314
unsigned long winMmeSpecificOutputFlags = 0;
2315
unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
2316
char throttleProcessingThreadOnOverload = 1;
2319
if( inputParameters )
2321
inputChannelCount = inputParameters->channelCount;
2322
inputSampleFormat = inputParameters->sampleFormat;
2323
suggestedInputLatency = inputParameters->suggestedLatency;
2325
inputDeviceCount = 1;
2327
/* validate input hostApiSpecificStreamInfo */
2328
inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
2329
result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo,
2330
&winMmeSpecificInputFlags,
2331
&throttleProcessingThreadOnOverload,
2332
&inputDeviceCount );
2333
if( result != paNoError ) return result;
2335
inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount );
2336
if( !inputDevices ) return paInsufficientMemory;
2338
result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount );
2339
if( result != paNoError ) return result;
2341
result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount );
2342
if( result != paNoError ) return result;
2344
hostInputSampleFormat =
2345
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
2347
if( inputDeviceCount != 1 ){
2348
/* always use direct speakers when using multi-device multichannel mode */
2349
inputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
2353
if( inputStreamInfo && inputStreamInfo->flags & paWinMmeUseChannelMask )
2354
inputChannelMask = inputStreamInfo->channelMask;
2356
inputChannelMask = PaWin_DefaultChannelMask( inputDevices[0].channelCount );
2361
inputChannelCount = 0;
2362
inputSampleFormat = 0;
2363
suggestedInputLatency = 0.;
2364
inputStreamInfo = 0;
2365
hostInputSampleFormat = 0;
2369
if( outputParameters )
2371
outputChannelCount = outputParameters->channelCount;
2372
outputSampleFormat = outputParameters->sampleFormat;
2373
suggestedOutputLatency = outputParameters->suggestedLatency;
2375
outputDeviceCount = 1;
2377
/* validate output hostApiSpecificStreamInfo */
2378
outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
2379
result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo,
2380
&winMmeSpecificOutputFlags,
2381
&throttleProcessingThreadOnOverload,
2382
&outputDeviceCount );
2383
if( result != paNoError ) return result;
2385
outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount );
2386
if( !outputDevices ) return paInsufficientMemory;
2388
result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount );
2389
if( result != paNoError ) return result;
2391
result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount );
2392
if( result != paNoError ) return result;
2394
hostOutputSampleFormat =
2395
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
2397
if( outputDeviceCount != 1 ){
2398
/* always use direct speakers when using multi-device multichannel mode */
2399
outputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
2403
if( outputStreamInfo && outputStreamInfo->flags & paWinMmeUseChannelMask )
2404
outputChannelMask = outputStreamInfo->channelMask;
2406
outputChannelMask = PaWin_DefaultChannelMask( outputDevices[0].channelCount );
2411
outputChannelCount = 0;
2412
outputSampleFormat = 0;
2413
outputStreamInfo = 0;
2414
hostOutputSampleFormat = 0;
2415
suggestedOutputLatency = 0.;
2421
- alter sampleRate to a close allowable rate if possible / necessary
2425
/* validate platform specific flags */
2426
if( (streamFlags & paPlatformSpecificFlags) != 0 )
2427
return paInvalidFlag; /* unexpected platform specific flag */
2430
/* always disable clipping and dithering if we are outputting a raw spdif stream */
2431
if( (winMmeSpecificOutputFlags & paWinMmeWaveFormatDolbyAc3Spdif)
2432
|| (winMmeSpecificOutputFlags & paWinMmeWaveFormatWmaSpdif) ){
2434
streamFlags = streamFlags | paClipOff | paDitherOff;
2438
result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount,
2439
&framesPerHostOutputBuffer, &hostOutputBufferCount,
2440
inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo,
2441
outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo,
2442
sampleRate, framesPerBuffer );
2443
if( result != paNoError ) goto error;
2446
stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) );
2449
result = paInsufficientMemory;
2453
InitializeSingleDirectionHandlesAndBuffers( &stream->input );
2454
InitializeSingleDirectionHandlesAndBuffers( &stream->output );
2456
stream->abortEvent = 0;
2457
stream->processingThread = 0;
2459
stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload;
2461
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2463
? &winMmeHostApi->callbackStreamInterface
2464
: &winMmeHostApi->blockingStreamInterface ),
2465
streamCallback, userData );
2466
streamRepresentationIsInitialized = 1;
2468
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
2471
if( inputParameters && outputParameters ) /* full duplex */
2473
if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
2475
assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
2477
framesPerBufferProcessorCall = framesPerHostInputBuffer;
2481
assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
2483
framesPerBufferProcessorCall = framesPerHostOutputBuffer;
2486
else if( inputParameters )
2488
framesPerBufferProcessorCall = framesPerHostInputBuffer;
2490
else if( outputParameters )
2492
framesPerBufferProcessorCall = framesPerHostOutputBuffer;
2495
stream->input.framesPerBuffer = framesPerHostInputBuffer;
2496
stream->output.framesPerBuffer = framesPerHostOutputBuffer;
2498
result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2499
inputChannelCount, inputSampleFormat, hostInputSampleFormat,
2500
outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
2501
sampleRate, streamFlags, framesPerBuffer,
2502
framesPerBufferProcessorCall, paUtilFixedHostBufferSize,
2503
streamCallback, userData );
2504
if( result != paNoError ) goto error;
2506
bufferProcessorIsInitialized = 1;
2508
stream->streamRepresentation.streamInfo.inputLatency =
2509
(double)(PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
2510
+(framesPerHostInputBuffer * (hostInputBufferCount-1))) / sampleRate;
2511
stream->streamRepresentation.streamInfo.outputLatency =
2512
(double)(PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
2513
+(framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
2514
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2516
stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
2518
/* time to sleep when throttling due to >100% cpu usage.
2519
-a quater of a buffer's duration */
2520
stream->throttledSleepMsecs =
2521
(unsigned long)(stream->bufferProcessor.framesPerHostBuffer *
2522
stream->bufferProcessor.samplePeriod * .25 * 1000);
2524
stream->isStopped = 1;
2525
stream->isActive = 0;
2528
/* for maximum compatibility with multi-device multichannel drivers,
2529
we first open all devices, then we prepare all buffers, finally
2530
we start all devices ( in StartStream() ). teardown in reverse order.
2533
if( inputParameters )
2535
result = InitializeWaveHandles( winMmeHostApi, &stream->input,
2536
winMmeSpecificInputFlags,
2537
stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
2538
inputDevices, inputDeviceCount, inputChannelMask, 1 /* isInput */ );
2539
if( result != paNoError ) goto error;
2542
if( outputParameters )
2544
result = InitializeWaveHandles( winMmeHostApi, &stream->output,
2545
winMmeSpecificOutputFlags,
2546
stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
2547
outputDevices, outputDeviceCount, outputChannelMask, 0 /* isInput */ );
2548
if( result != paNoError ) goto error;
2551
if( inputParameters )
2553
result = InitializeWaveHeaders( &stream->input, hostInputBufferCount,
2554
hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ );
2555
if( result != paNoError ) goto error;
2558
if( outputParameters )
2560
result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount,
2561
hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ );
2562
if( result != paNoError ) goto error;
2564
stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate);
2568
stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
2572
if( streamCallback )
2574
/* abort event is only needed for callback streams */
2575
result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL );
2576
if( result != paNoError ) goto error;
2579
*s = (PaStream*)stream;
2587
if( stream->abortEvent )
2588
CloseHandle( stream->abortEvent );
2590
TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
2591
TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
2593
TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ );
2594
TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ );
2596
if( bufferProcessorIsInitialized )
2597
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2599
if( streamRepresentationIsInitialized )
2600
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2602
PaUtil_FreeMemory( stream );
2609
/* return non-zero if all current buffers are done */
2610
static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
2614
for( i=0; i < deviceCount; ++i )
2616
if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
2625
static int CurrentInputBuffersAreDone( PaWinMmeStream *stream )
2627
return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex );
2630
static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream )
2632
return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex );
2636
/* return non-zero if any buffers are queued */
2637
static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
2641
if( handlesAndBuffers->waveHandles )
2643
for( i=0; i < handlesAndBuffers->bufferCount; ++i )
2645
for( j=0; j < handlesAndBuffers->deviceCount; ++j )
2647
if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) )
2659
#define PA_CIRCULAR_INCREMENT_( current, max )\
2660
( (((current) + 1) >= (max)) ? (0) : (current+1) )
2662
#define PA_CIRCULAR_DECREMENT_( current, max )\
2663
( ((current) == 0) ? ((max)-1) : (current-1) )
2666
static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
2668
signed long result = 0;
2671
if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
2673
/* we could calculate the following in O(1) if we kept track of the
2675
result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer;
2677
i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount );
2678
while( i != handlesAndBuffers->currentBufferIndex )
2680
if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) )
2682
result += handlesAndBuffers->framesPerBuffer;
2683
i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount );
2694
static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
2696
PaError result = paNoError;
2700
for( i=0; i < stream->input.deviceCount; ++i )
2702
stream->input.waveHeaders[i][ stream->input.currentBufferIndex ].dwFlags &= ~WHDR_DONE;
2703
mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
2704
&stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
2706
if( mmresult != MMSYSERR_NOERROR )
2708
result = paUnanticipatedHostError;
2709
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
2713
stream->input.currentBufferIndex =
2714
PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount );
2716
stream->input.framesUsedInCurrentBuffer = 0;
2722
static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream )
2724
PaError result = paNoError;
2728
for( i=0; i < stream->output.deviceCount; ++i )
2730
mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i],
2731
&stream->output.waveHeaders[i][ stream->output.currentBufferIndex ],
2733
if( mmresult != MMSYSERR_NOERROR )
2735
result = paUnanticipatedHostError;
2736
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
2740
stream->output.currentBufferIndex =
2741
PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
2743
stream->output.framesUsedInCurrentBuffer = 0;
2749
/* requeue all but the most recent input with the driver. Used for catching
2750
up after a total input buffer underrun */
2751
static PaError CatchUpInputBuffers( PaWinMmeStream *stream )
2753
PaError result = paNoError;
2756
for( i=0; i < stream->input.bufferCount - 1; ++i )
2758
result = AdvanceToNextInputBuffer( stream );
2759
if( result != paNoError )
2767
/* take the most recent output and duplicate it to all other output buffers
2768
and requeue them. Used for catching up after a total output buffer underrun.
2770
static PaError CatchUpOutputBuffers( PaWinMmeStream *stream )
2772
PaError result = paNoError;
2774
unsigned int previousBufferIndex =
2775
PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
2777
for( i=0; i < stream->output.bufferCount - 1; ++i )
2779
for( j=0; j < stream->output.deviceCount; ++j )
2781
if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData
2782
!= stream->output.waveHeaders[j][ previousBufferIndex ].lpData )
2784
CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData,
2785
stream->output.waveHeaders[j][ previousBufferIndex ].lpData,
2786
stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength );
2790
result = AdvanceToNextOutputBuffer( stream );
2791
if( result != paNoError )
2799
PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
2801
PaWinMmeStream *stream = (PaWinMmeStream *)pArg;
2804
DWORD result = paNoError;
2806
DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
2807
int hostBuffersAvailable;
2808
signed int hostInputBufferIndex, hostOutputBufferIndex;
2809
PaStreamCallbackFlags statusFlags;
2812
unsigned int channel, i;
2813
unsigned long framesProcessed;
2815
/* prepare event array for call to WaitForMultipleObjects() */
2816
if( stream->input.bufferEvent )
2817
events[eventCount++] = stream->input.bufferEvent;
2818
if( stream->output.bufferEvent )
2819
events[eventCount++] = stream->output.bufferEvent;
2820
events[eventCount++] = stream->abortEvent;
2822
statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
2824
/* loop until something causes us to stop */
2826
/* wait for MME to signal that a buffer is available, or for
2827
the PA abort event to be signaled.
2829
When this indicates that one or more buffers are available
2830
NoBuffersAreQueued() and Current*BuffersAreDone are used below to
2831
poll for additional done buffers. NoBuffersAreQueued() will fail
2832
to identify an underrun/overflow if the driver doesn't mark all done
2833
buffers prior to signalling the event. Some drivers do this
2834
(eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a
2835
huge problem, it just means that we won't always be able to detect
2838
waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout );
2839
if( waitResult == WAIT_FAILED )
2841
result = paUnanticipatedHostError;
2842
/** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */
2845
else if( waitResult == WAIT_TIMEOUT )
2847
/* if a timeout is encountered, continue */
2850
if( stream->abortProcessing )
2852
/* Pa_AbortStream() has been called, stop processing immediately */
2855
else if( stream->stopProcessing )
2857
/* Pa_StopStream() has been called or the user callback returned
2858
non-zero, processing will continue until all output buffers
2859
are marked as done. The stream will stop immediately if it
2863
if( PA_IS_OUTPUT_STREAM_(stream) )
2865
if( NoBuffersAreQueued( &stream->output ) )
2866
done = 1; /* Will cause thread to return. */
2870
/* input only stream */
2871
done = 1; /* Will cause thread to return. */
2876
hostBuffersAvailable = 1;
2878
/* process all available host buffers */
2881
hostInputBufferIndex = -1;
2882
hostOutputBufferIndex = -1;
2884
if( PA_IS_INPUT_STREAM_(stream) )
2886
if( CurrentInputBuffersAreDone( stream ) )
2888
if( NoBuffersAreQueued( &stream->input ) )
2891
if all of the other buffers are also ready then
2892
we discard all but the most recent. This is an
2893
input buffer overflow. FIXME: these buffers should
2894
be passed to the callback in a paNeverDropInput
2897
note that it is also possible for an input overflow
2898
to happen while the callback is processing a buffer.
2899
that is handled further down.
2901
result = CatchUpInputBuffers( stream );
2902
if( result != paNoError )
2905
statusFlags |= paInputOverflow;
2908
hostInputBufferIndex = stream->input.currentBufferIndex;
2912
if( PA_IS_OUTPUT_STREAM_(stream) )
2914
if( CurrentOutputBuffersAreDone( stream ) )
2916
/* ok, we have an output buffer */
2918
if( NoBuffersAreQueued( &stream->output ) )
2921
if all of the other buffers are also ready, catch up by copying
2922
the most recently generated buffer into all but one of the output
2925
note that this catch up code only handles the case where all
2926
buffers have been played out due to this thread not having
2927
woken up at all. a more common case occurs when this thread
2928
is woken up, processes one buffer, but takes too long, and as
2929
a result all the other buffers have become un-queued. that
2930
case is handled further down.
2933
result = CatchUpOutputBuffers( stream );
2934
if( result != paNoError )
2937
statusFlags |= paOutputUnderflow;
2940
hostOutputBufferIndex = stream->output.currentBufferIndex;
2945
if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
2946
(PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
2948
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
2951
if( PA_IS_OUTPUT_STREAM_(stream) )
2953
/* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime
2954
from the current wave out position */
2956
double timeBeforeGetPosition, timeAfterGetPosition;
2958
long framesInBufferRing;
2960
long playbackPosition;
2961
HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
2963
mmtime.wType = TIME_SAMPLES;
2964
timeBeforeGetPosition = PaUtil_GetTime();
2965
waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
2966
timeAfterGetPosition = PaUtil_GetTime();
2968
timeInfo.currentTime = timeAfterGetPosition;
2970
/* approximate time at which wave out position was measured
2971
as half way between timeBeforeGetPosition and timeAfterGetPosition */
2972
time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
2974
framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
2975
playbackPosition = mmtime.u.sample % framesInBufferRing;
2977
writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
2978
+ stream->output.framesUsedInCurrentBuffer;
2980
if( playbackPosition >= writePosition ){
2981
timeInfo.outputBufferDacTime =
2982
time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
2984
timeInfo.outputBufferDacTime =
2985
time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod );
2990
PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
2992
PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags );
2994
/* reset status flags once they have been passed to the buffer processor */
2997
if( PA_IS_INPUT_STREAM_(stream) )
2999
PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
3002
for( i=0; i<stream->input.deviceCount; ++i )
3004
/* we have stored the number of channels in the buffer in dwUser */
3005
int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
3007
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
3008
stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
3009
stream->input.framesUsedInCurrentBuffer * channelCount *
3010
stream->bufferProcessor.bytesPerHostInputSample,
3014
channel += channelCount;
3018
if( PA_IS_OUTPUT_STREAM_(stream) )
3020
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
3023
for( i=0; i<stream->output.deviceCount; ++i )
3025
/* we have stored the number of channels in the buffer in dwUser */
3026
int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
3028
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
3029
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
3030
stream->output.framesUsedInCurrentBuffer * channelCount *
3031
stream->bufferProcessor.bytesPerHostOutputSample,
3034
channel += channelCount;
3038
callbackResult = paContinue;
3039
framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
3041
stream->input.framesUsedInCurrentBuffer += framesProcessed;
3042
stream->output.framesUsedInCurrentBuffer += framesProcessed;
3044
PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
3046
if( callbackResult == paContinue )
3048
/* nothing special to do */
3050
else if( callbackResult == paAbort )
3052
stream->abortProcessing = 1;
3054
/** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */
3059
/* User callback has asked us to stop with paComplete or other non-zero value */
3060
stream->stopProcessing = 1; /* stop once currently queued audio has finished */
3065
if( PA_IS_INPUT_STREAM_(stream)
3066
&& stream->stopProcessing == 0 && stream->abortProcessing == 0
3067
&& stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
3069
if( NoBuffersAreQueued( &stream->input ) )
3071
/** @todo need to handle PaNeverDropInput here where necessary */
3072
result = CatchUpInputBuffers( stream );
3073
if( result != paNoError )
3076
statusFlags |= paInputOverflow;
3079
result = AdvanceToNextInputBuffer( stream );
3080
if( result != paNoError )
3085
if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
3087
if( stream->stopProcessing &&
3088
stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer )
3090
/* zero remaining samples in output output buffer and flush */
3092
stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor,
3093
stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
3095
/* we send the entire buffer to the output devices, but we could
3096
just send a partial buffer, rather than zeroing the unused
3101
if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
3103
/* check for underflow before enquing the just-generated buffer,
3104
but recover from underflow after enquing it. This ensures
3105
that the most recent audio segment is repeated */
3106
int outputUnderflow = NoBuffersAreQueued( &stream->output );
3108
result = AdvanceToNextOutputBuffer( stream );
3109
if( result != paNoError )
3112
if( outputUnderflow && !done && !stream->stopProcessing )
3114
/* Recover from underflow in the case where the
3115
underflow occured while processing the buffer
3118
result = CatchUpOutputBuffers( stream );
3119
if( result != paNoError )
3122
statusFlags |= paOutputUnderflow;
3127
if( stream->throttleProcessingThreadOnOverload != 0 )
3129
if( stream->stopProcessing || stream->abortProcessing )
3131
if( stream->processingThreadPriority != stream->highThreadPriority )
3133
SetThreadPriority( stream->processingThread, stream->highThreadPriority );
3134
stream->processingThreadPriority = stream->highThreadPriority;
3137
else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. )
3139
if( stream->processingThreadPriority != stream->throttledThreadPriority )
3141
SetThreadPriority( stream->processingThread, stream->throttledThreadPriority );
3142
stream->processingThreadPriority = stream->throttledThreadPriority;
3145
/* sleep to give other processes a go */
3146
Sleep( stream->throttledSleepMsecs );
3150
if( stream->processingThreadPriority != stream->highThreadPriority )
3152
SetThreadPriority( stream->processingThread, stream->highThreadPriority );
3153
stream->processingThreadPriority = stream->highThreadPriority;
3160
hostBuffersAvailable = 0;
3163
while( hostBuffersAvailable &&
3164
stream->stopProcessing == 0 &&
3165
stream->abortProcessing == 0 &&
3171
stream->isActive = 0;
3173
if( stream->streamRepresentation.streamFinishedCallback != 0 )
3174
stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3176
PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
3183
When CloseStream() is called, the multi-api layer ensures that
3184
the stream has already been stopped or aborted.
3186
static PaError CloseStream( PaStream* s )
3189
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3191
result = CloseHandleWithPaError( stream->abortEvent );
3192
if( result != paNoError ) goto error;
3194
TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
3195
TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
3197
TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
3198
TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
3200
PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
3201
PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
3202
PaUtil_FreeMemory( stream );
3205
/** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */
3210
static PaError StartStream( PaStream *s )
3213
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3217
unsigned int channel;
3218
unsigned long framesProcessed;
3219
PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
3221
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
3223
if( PA_IS_INPUT_STREAM_(stream) )
3225
for( i=0; i<stream->input.bufferCount; ++i )
3227
for( j=0; j<stream->input.deviceCount; ++j )
3229
stream->input.waveHeaders[j][i].dwFlags &= ~WHDR_DONE;
3230
mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
3231
if( mmresult != MMSYSERR_NOERROR )
3233
result = paUnanticipatedHostError;
3234
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
3239
stream->input.currentBufferIndex = 0;
3240
stream->input.framesUsedInCurrentBuffer = 0;
3243
if( PA_IS_OUTPUT_STREAM_(stream) )
3245
for( i=0; i<stream->output.deviceCount; ++i )
3247
if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
3249
result = paUnanticipatedHostError;
3250
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3255
for( i=0; i<stream->output.bufferCount; ++i )
3257
if( stream->primeStreamUsingCallback )
3260
stream->output.framesUsedInCurrentBuffer = 0;
3263
PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
3265
paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0));
3267
if( stream->input.bufferCount > 0 )
3268
PaUtil_SetNoInput( &stream->bufferProcessor );
3270
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
3273
for( j=0; j<stream->output.deviceCount; ++j )
3275
/* we have stored the number of channels in the buffer in dwUser */
3276
int channelCount = (int)stream->output.waveHeaders[j][i].dwUser;
3278
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
3279
stream->output.waveHeaders[j][i].lpData +
3280
stream->output.framesUsedInCurrentBuffer * channelCount *
3281
stream->bufferProcessor.bytesPerHostOutputSample,
3284
/* we have stored the number of channels in the buffer in dwUser */
3285
channel += channelCount;
3288
callbackResult = paContinue;
3289
framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
3290
stream->output.framesUsedInCurrentBuffer += framesProcessed;
3292
if( callbackResult != paContinue )
3294
/** @todo fix this, what do we do if callback result is non-zero during stream
3297
for complete: play out primed waveHeaders as usual
3298
for abort: clean up immediately.
3302
}while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer );
3307
for( j=0; j<stream->output.deviceCount; ++j )
3309
ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
3313
/* we queue all channels of a single buffer frame (accross all
3314
devices, because some multidevice multichannel drivers work
3316
for( j=0; j<stream->output.deviceCount; ++j )
3318
mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) );
3319
if( mmresult != MMSYSERR_NOERROR )
3321
result = paUnanticipatedHostError;
3322
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3327
stream->output.currentBufferIndex = 0;
3328
stream->output.framesUsedInCurrentBuffer = 0;
3332
stream->isStopped = 0;
3333
stream->isActive = 1;
3334
stream->stopProcessing = 0;
3335
stream->abortProcessing = 0;
3337
result = ResetEventWithPaError( stream->input.bufferEvent );
3338
if( result != paNoError ) goto error;
3340
result = ResetEventWithPaError( stream->output.bufferEvent );
3341
if( result != paNoError ) goto error;
3344
if( stream->streamRepresentation.streamCallback )
3346
/* callback stream */
3348
result = ResetEventWithPaError( stream->abortEvent );
3349
if( result != paNoError ) goto error;
3351
/* Create thread that waits for audio buffers to be ready for processing. */
3352
stream->processingThread = CREATE_THREAD;
3353
if( !stream->processingThread )
3355
result = paUnanticipatedHostError;
3356
PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
3360
/** @todo could have mme specific stream parameters to allow the user
3361
to set the callback thread priorities */
3362
stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
3363
stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL;
3365
if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) )
3367
result = paUnanticipatedHostError;
3368
PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
3371
stream->processingThreadPriority = stream->highThreadPriority;
3375
/* blocking read/write stream */
3379
if( PA_IS_INPUT_STREAM_(stream) )
3381
for( i=0; i < stream->input.deviceCount; ++i )
3383
mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] );
3384
PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
3385
if( mmresult != MMSYSERR_NOERROR )
3387
result = paUnanticipatedHostError;
3388
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
3394
if( PA_IS_OUTPUT_STREAM_(stream) )
3396
for( i=0; i < stream->output.deviceCount; ++i )
3398
if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
3400
result = paUnanticipatedHostError;
3401
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3410
/** @todo FIXME: implement recovery as best we can
3411
This should involve rolling back to a state as-if this function had never been called
3417
static PaError StopStream( PaStream *s )
3419
PaError result = paNoError;
3420
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3424
signed int hostOutputBufferIndex;
3425
unsigned int channel, waitCount, i;
3428
REVIEW: the error checking in this function needs review. the basic
3429
idea is to return from this function in a known state - for example
3430
there is no point avoiding calling waveInReset just because
3431
the thread times out.
3434
if( stream->processingThread )
3436
/* callback stream */
3438
/* Tell processing thread to stop generating more data and to let current data play out. */
3439
stream->stopProcessing = 1;
3441
/* Calculate timeOut longer than longest time it could take to return all buffers. */
3442
timeout = (int)(stream->allBuffersDurationMs * 1.5);
3443
if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
3444
timeout = PA_MME_MIN_TIMEOUT_MSEC_;
3446
PA_DEBUG(("WinMME StopStream: waiting for background thread.\n"));
3448
waitResult = WaitForSingleObject( stream->processingThread, timeout );
3449
if( waitResult == WAIT_TIMEOUT )
3452
stream->abortProcessing = 1;
3453
SetEvent( stream->abortEvent );
3454
waitResult = WaitForSingleObject( stream->processingThread, timeout );
3455
if( waitResult == WAIT_TIMEOUT )
3457
PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n"));
3458
result = paTimedOut;
3462
CloseHandle( stream->processingThread );
3463
stream->processingThread = NULL;
3467
/* blocking read / write stream */
3469
if( PA_IS_OUTPUT_STREAM_(stream) )
3471
if( stream->output.framesUsedInCurrentBuffer > 0 )
3473
/* there are still unqueued frames in the current buffer, so flush them */
3475
hostOutputBufferIndex = stream->output.currentBufferIndex;
3477
PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
3478
stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
3481
for( i=0; i<stream->output.deviceCount; ++i )
3483
/* we have stored the number of channels in the buffer in dwUser */
3484
int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
3486
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
3487
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
3488
stream->output.framesUsedInCurrentBuffer * channelCount *
3489
stream->bufferProcessor.bytesPerHostOutputSample,
3492
channel += channelCount;
3495
PaUtil_ZeroOutput( &stream->bufferProcessor,
3496
stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
3498
/* we send the entire buffer to the output devices, but we could
3499
just send a partial buffer, rather than zeroing the unused
3502
AdvanceToNextOutputBuffer( stream );
3506
timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
3507
if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
3508
timeout = PA_MME_MIN_TIMEOUT_MSEC_;
3511
while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
3513
/* wait for MME to signal that a buffer is available */
3514
waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
3515
if( waitResult == WAIT_FAILED )
3519
else if( waitResult == WAIT_TIMEOUT )
3529
if( PA_IS_OUTPUT_STREAM_(stream) )
3531
for( i =0; i < stream->output.deviceCount; ++i )
3533
mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
3534
if( mmresult != MMSYSERR_NOERROR )
3536
result = paUnanticipatedHostError;
3537
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3542
if( PA_IS_INPUT_STREAM_(stream) )
3544
for( i=0; i < stream->input.deviceCount; ++i )
3546
mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
3547
if( mmresult != MMSYSERR_NOERROR )
3549
result = paUnanticipatedHostError;
3550
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
3555
stream->isStopped = 1;
3556
stream->isActive = 0;
3562
static PaError AbortStream( PaStream *s )
3564
PaError result = paNoError;
3565
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3572
REVIEW: the error checking in this function needs review. the basic
3573
idea is to return from this function in a known state - for example
3574
there is no point avoiding calling waveInReset just because
3575
the thread times out.
3578
if( stream->processingThread )
3580
/* callback stream */
3582
/* Tell processing thread to abort immediately */
3583
stream->abortProcessing = 1;
3584
SetEvent( stream->abortEvent );
3588
if( PA_IS_OUTPUT_STREAM_(stream) )
3590
for( i =0; i < stream->output.deviceCount; ++i )
3592
mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
3593
if( mmresult != MMSYSERR_NOERROR )
3595
PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3596
return paUnanticipatedHostError;
3601
if( PA_IS_INPUT_STREAM_(stream) )
3603
for( i=0; i < stream->input.deviceCount; ++i )
3605
mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
3606
if( mmresult != MMSYSERR_NOERROR )
3608
PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
3609
return paUnanticipatedHostError;
3615
if( stream->processingThread )
3617
/* callback stream */
3619
PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
3621
/* Calculate timeOut longer than longest time it could take to return all buffers. */
3622
timeout = (int)(stream->allBuffersDurationMs * 1.5);
3623
if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
3624
timeout = PA_MME_MIN_TIMEOUT_MSEC_;
3626
waitResult = WaitForSingleObject( stream->processingThread, timeout );
3627
if( waitResult == WAIT_TIMEOUT )
3629
PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n"));
3633
CloseHandle( stream->processingThread );
3634
stream->processingThread = NULL;
3637
stream->isStopped = 1;
3638
stream->isActive = 0;
3644
static PaError IsStreamStopped( PaStream *s )
3646
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3648
return stream->isStopped;
3652
static PaError IsStreamActive( PaStream *s )
3654
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3656
return stream->isActive;
3660
static PaTime GetStreamTime( PaStream *s )
3662
(void) s; /* unused parameter */
3664
return PaUtil_GetTime();
3668
static double GetStreamCpuLoad( PaStream* s )
3670
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3672
return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
3677
As separate stream interfaces are used for blocking and callback
3678
streams, the following functions can be guaranteed to only be called
3679
for blocking streams.
3682
static PaError ReadStream( PaStream* s,
3684
unsigned long frames )
3686
PaError result = paNoError;
3687
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3689
unsigned long framesRead = 0;
3690
unsigned long framesProcessed;
3691
signed int hostInputBufferIndex;
3693
DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
3694
unsigned int channel, i;
3696
if( PA_IS_INPUT_STREAM_(stream) )
3698
/* make a local copy of the user buffer pointer(s). this is necessary
3699
because PaUtil_CopyInput() advances these pointers every time
3702
if( stream->bufferProcessor.userInputIsInterleaved )
3704
userBuffer = buffer;
3708
userBuffer = (void*)alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
3710
return paInsufficientMemory;
3711
for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
3712
((void**)userBuffer)[i] = ((void**)buffer)[i];
3716
if( CurrentInputBuffersAreDone( stream ) )
3718
if( NoBuffersAreQueued( &stream->input ) )
3720
/** @todo REVIEW: consider what to do if the input overflows.
3721
do we requeue all of the buffers? should we be running
3722
a thread to make sure they are always queued? */
3724
result = paInputOverflowed;
3727
hostInputBufferIndex = stream->input.currentBufferIndex;
3729
PaUtil_SetInputFrameCount( &stream->bufferProcessor,
3730
stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
3733
for( i=0; i<stream->input.deviceCount; ++i )
3735
/* we have stored the number of channels in the buffer in dwUser */
3736
int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
3738
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
3739
stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
3740
stream->input.framesUsedInCurrentBuffer * channelCount *
3741
stream->bufferProcessor.bytesPerHostInputSample,
3744
channel += channelCount;
3747
framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
3749
stream->input.framesUsedInCurrentBuffer += framesProcessed;
3750
if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
3752
result = AdvanceToNextInputBuffer( stream );
3753
if( result != paNoError )
3757
framesRead += framesProcessed;
3760
/* wait for MME to signal that a buffer is available */
3761
waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout );
3762
if( waitResult == WAIT_FAILED )
3764
result = paUnanticipatedHostError;
3767
else if( waitResult == WAIT_TIMEOUT )
3769
/* if a timeout is encountered, continue,
3770
perhaps we should give up eventually
3774
}while( framesRead < frames );
3778
result = paCanNotReadFromAnOutputOnlyStream;
3785
static PaError WriteStream( PaStream* s,
3787
unsigned long frames )
3789
PaError result = paNoError;
3790
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3791
const void *userBuffer;
3792
unsigned long framesWritten = 0;
3793
unsigned long framesProcessed;
3794
signed int hostOutputBufferIndex;
3796
DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
3797
unsigned int channel, i;
3800
if( PA_IS_OUTPUT_STREAM_(stream) )
3802
/* make a local copy of the user buffer pointer(s). this is necessary
3803
because PaUtil_CopyOutput() advances these pointers every time
3806
if( stream->bufferProcessor.userOutputIsInterleaved )
3808
userBuffer = buffer;
3812
userBuffer = (const void*)alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
3814
return paInsufficientMemory;
3815
for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
3816
((const void**)userBuffer)[i] = ((const void**)buffer)[i];
3820
if( CurrentOutputBuffersAreDone( stream ) )
3822
if( NoBuffersAreQueued( &stream->output ) )
3824
/** @todo REVIEW: consider what to do if the output
3825
underflows. do we requeue all the existing buffers with
3826
zeros? should we run a separate thread to keep the buffers
3827
enqueued at all times? */
3829
result = paOutputUnderflowed;
3832
hostOutputBufferIndex = stream->output.currentBufferIndex;
3834
PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
3835
stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
3838
for( i=0; i<stream->output.deviceCount; ++i )
3840
/* we have stored the number of channels in the buffer in dwUser */
3841
int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
3843
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
3844
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
3845
stream->output.framesUsedInCurrentBuffer * channelCount *
3846
stream->bufferProcessor.bytesPerHostOutputSample,
3849
channel += channelCount;
3852
framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
3854
stream->output.framesUsedInCurrentBuffer += framesProcessed;
3855
if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
3857
result = AdvanceToNextOutputBuffer( stream );
3858
if( result != paNoError )
3862
framesWritten += framesProcessed;
3866
/* wait for MME to signal that a buffer is available */
3867
waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
3868
if( waitResult == WAIT_FAILED )
3870
result = paUnanticipatedHostError;
3873
else if( waitResult == WAIT_TIMEOUT )
3875
/* if a timeout is encountered, continue,
3876
perhaps we should give up eventually
3880
}while( framesWritten < frames );
3884
result = paCanNotWriteToAnInputOnlyStream;
3891
static signed long GetStreamReadAvailable( PaStream* s )
3893
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3895
if( PA_IS_INPUT_STREAM_(stream) )
3896
return GetAvailableFrames( &stream->input );
3898
return paCanNotReadFromAnOutputOnlyStream;
3902
static signed long GetStreamWriteAvailable( PaStream* s )
3904
PaWinMmeStream *stream = (PaWinMmeStream*)s;
3906
if( PA_IS_OUTPUT_STREAM_(stream) )
3907
return GetAvailableFrames( &stream->output );
3909
return paCanNotWriteToAnInputOnlyStream;
3913
/* NOTE: the following functions are MME-stream specific, and are called directly
3914
by client code. We need to check for many more error conditions here because
3915
we don't have the benefit of pa_front.c's parameter checking.
3918
static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s )
3921
PaUtilHostApiRepresentation *hostApi;
3922
PaWinMmeHostApiRepresentation *winMmeHostApi;
3924
result = PaUtil_ValidateStreamPointer( s );
3925
if( result != paNoError )
3928
result = PaUtil_GetHostApiRepresentation( &hostApi, paMME );
3929
if( result != paNoError )
3932
winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
3934
/* note, the following would be easier if there was a generic way of testing
3935
that a stream belongs to a specific host API */
3937
if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
3938
|| PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
3940
/* s is a WinMME stream */
3941
*stream = (PaWinMmeStream *)s;
3946
return paIncompatibleStreamHostApi;
3951
int PaWinMME_GetStreamInputHandleCount( PaStream* s )
3953
PaWinMmeStream *stream;
3954
PaError result = GetWinMMEStreamPointer( &stream, s );
3956
if( result == paNoError )
3957
return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0;
3963
HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex )
3965
PaWinMmeStream *stream;
3966
PaError result = GetWinMMEStreamPointer( &stream, s );
3968
if( result == paNoError
3969
&& PA_IS_INPUT_STREAM_(stream)
3971
&& (unsigned int)handleIndex < stream->input.deviceCount )
3972
return ((HWAVEIN*)stream->input.waveHandles)[handleIndex];
3978
int PaWinMME_GetStreamOutputHandleCount( PaStream* s)
3980
PaWinMmeStream *stream;
3981
PaError result = GetWinMMEStreamPointer( &stream, s );
3983
if( result == paNoError )
3984
return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0;
3990
HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex )
3992
PaWinMmeStream *stream;
3993
PaError result = GetWinMMEStreamPointer( &stream, s );
3995
if( result == paNoError
3996
&& PA_IS_OUTPUT_STREAM_(stream)
3998
&& (unsigned int)handleIndex < stream->output.deviceCount )
3999
return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex];