~ubuntu-branches/debian/sid/sflphone/sid

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/third_party/portaudio/src/hostapi/wmme/pa_win_wmme.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2013-06-02 18:04:11 UTC
  • mfrom: (1.1.9)
  • Revision ID: package-import@ubuntu.com-20130602180411-3rcpy8c1zdlo8y0s
Tags: 1.2.2-1
* New upstream release
* changeset_rb68857a4b485b7d43f92714cd5792595ff895f82.diff - fix QTest
* pjproject ./configure --disable-sound --disable-video

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * $Id: pa_win_wmme.c 1432 2009-12-09 01:31:44Z rossb $
 
3
 * pa_win_wmme.c
 
4
 * Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
 
5
 *
 
6
 * PortAudio Portable Real-Time Audio Library
 
7
 * Latest Version at: http://www.portaudio.com
 
8
 *
 
9
 * Authors: Ross Bencina and Phil Burk
 
10
 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
 
11
 *
 
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:
 
19
 *
 
20
 * The above copyright notice and this permission notice shall be
 
21
 * included in all copies or substantial portions of the Software.
 
22
 *
 
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.
 
30
 */
 
31
 
 
32
/*
 
33
 * The text above constitutes the entire PortAudio license; however,
 
34
 * the PortAudio community also makes the following non-binding requests:
 
35
 *
 
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
 
40
 * license above.
 
41
 */
 
42
 
 
43
/* Modification History:
 
44
 PLB = Phil Burk
 
45
 JM = Julien Maillard
 
46
 RDB = Ross Bencina
 
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 **
 
62
*/
 
63
 
 
64
/** @file
 
65
        @ingroup hostapi_src
 
66
 
 
67
    @brief Win32 host API implementation for the Windows MultiMedia Extensions (WMME) audio API.
 
68
 
 
69
        @todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now,
 
70
            needs to be reviewed and tested.)
 
71
 
 
72
    @todo implement paInputUnderflow, paOutputOverflow streamCallback statusFlags, paNeverDropInput.
 
73
 
 
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.
 
76
 
 
77
    @todo implement inputBufferAdcTime in callback thread
 
78
 
 
79
    @todo review/fix error recovery and cleanup in marked functions
 
80
 
 
81
    @todo implement timeInfo for stream priming
 
82
 
 
83
    @todo handle the case where the callback returns paAbort or paComplete during stream priming.
 
84
 
 
85
    @todo review input overflow and output underflow handling in ReadStream and WriteStream
 
86
 
 
87
Non-critical stuff for the future:
 
88
 
 
89
    @todo Investigate supporting host buffer formats > 16 bits
 
90
 
 
91
    @todo define UNICODE and _UNICODE in the project settings and see what breaks
 
92
 
 
93
    @todo refactor conversion of MMSYSTEM errors into PA arrors into a single function.
 
94
 
 
95
    @todo cleanup WAVEFORMATEXTENSIBLE retry in InitializeWaveHandles to not use a for loop
 
96
 
 
97
*/
 
98
 
 
99
/*
 
100
    How it works:
 
101
 
 
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().
 
107
 
 
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
 
110
    they are ready.
 
111
 
 
112
    When implementing a PA blocking read/write stream, we simply wait on these
 
113
    Events (when necessary) inside the ReadStream() and WriteStream() functions.
 
114
*/
 
115
 
 
116
#include <stdio.h>
 
117
#include <stdlib.h>
 
118
#include <math.h>
 
119
#include <windows.h>
 
120
#include <mmsystem.h>
 
121
#ifndef UNDER_CE
 
122
#include <process.h>
 
123
#endif
 
124
#include <assert.h>
 
125
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
 
126
#ifndef __MWERKS__
 
127
#include <malloc.h>
 
128
#include <memory.h>
 
129
#endif /* __MWERKS__ */
 
130
 
 
131
#include "portaudio.h"
 
132
#include "pa_trace.h"
 
133
#include "pa_util.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"
 
140
 
 
141
#include "pa_win_wmme.h"
 
142
#include "pa_win_waveformat.h"
 
143
 
 
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)
 
148
#endif
 
149
#ifndef DRV_QUERYDEVICEINTERFACESIZE
 
150
#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
 
151
#endif
 
152
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
 
153
 
 
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
 
159
#else
 
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
 
163
#endif
 
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")
 
168
#endif
 
169
 
 
170
/*
 
171
 provided in newer platform sdks
 
172
 */
 
173
#ifndef DWORD_PTR
 
174
    #if defined(_WIN64)
 
175
        #define DWORD_PTR unsigned __int64
 
176
    #else
 
177
        #define DWORD_PTR unsigned long
 
178
    #endif
 
179
#endif
 
180
 
 
181
/************************************************* Constants ********/
 
182
 
 
183
#define PA_MME_USE_HIGH_DEFAULT_LATENCY_    (0)  /* For debugging glitches. */
 
184
 
 
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 */
 
193
#else
 
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 */
 
201
#endif
 
202
 
 
203
/* Use higher latency for NT because it is even worse at real-time
 
204
   operation than Win9x.
 
205
*/
 
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_)
 
208
 
 
209
 
 
210
#define PA_MME_MIN_TIMEOUT_MSEC_        (1000)
 
211
 
 
212
static const char constInputMapperSuffix_[] = " - Input";
 
213
static const char constOutputMapperSuffix_[] = " - Output";
 
214
 
 
215
/*
 
216
copies TCHAR string to explicit char string
 
217
*/
 
218
char *StrTCpyToC(char *to, const TCHAR *from)
 
219
{
 
220
#if !defined(_UNICODE) && !defined(UNICODE)
 
221
        return strcpy(to, from);
 
222
#else
 
223
        int count = wcslen(from);
 
224
        if (count != 0)
 
225
                if (WideCharToMultiByte(CP_ACP, 0, from, count, to, count, NULL, NULL) == 0)
 
226
                        return NULL;
 
227
        return to;
 
228
#endif
 
229
}
 
230
 
 
231
/*
 
232
returns length of TCHAR string
 
233
*/
 
234
size_t StrTLen(const TCHAR *str)
 
235
{
 
236
#if !defined(_UNICODE) && !defined(UNICODE)
 
237
        return strlen(str);
 
238
#else
 
239
        return wcslen(str);
 
240
#endif
 
241
}
 
242
 
 
243
/********************************************************************/
 
244
 
 
245
typedef struct PaWinMmeStream PaWinMmeStream;     /* forward declaration */
 
246
 
 
247
/* prototypes for functions declared in this file */
 
248
 
 
249
#ifdef __cplusplus
 
250
extern "C"
 
251
{
 
252
#endif /* __cplusplus */
 
253
 
 
254
PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
 
255
 
 
256
#ifdef __cplusplus
 
257
}
 
258
#endif /* __cplusplus */
 
259
 
 
260
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
 
261
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
262
                           PaStream** stream,
 
263
                           const PaStreamParameters *inputParameters,
 
264
                           const PaStreamParameters *outputParameters,
 
265
                           double sampleRate,
 
266
                           unsigned long framesPerBuffer,
 
267
                           PaStreamFlags streamFlags,
 
268
                           PaStreamCallback *streamCallback,
 
269
                           void *userData );
 
270
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
271
                                  const PaStreamParameters *inputParameters,
 
272
                                  const PaStreamParameters *outputParameters,
 
273
                                  double sampleRate );
 
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 );
 
286
 
 
287
 
 
288
/* macros for setting last host error information */
 
289
 
 
290
#ifdef UNICODE
 
291
 
 
292
#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
 
293
    {                                                                   \
 
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 );   \
 
300
    }
 
301
 
 
302
#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
 
303
    {                                                                   \
 
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 );   \
 
310
    }
 
311
 
 
312
#else /* !UNICODE */
 
313
 
 
314
#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
 
315
    {                                                                   \
 
316
        char mmeErrorText[ MAXERRORLENGTH ];                            \
 
317
        waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH );   \
 
318
        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
 
319
    }
 
320
 
 
321
#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
 
322
    {                                                                   \
 
323
        char mmeErrorText[ MAXERRORLENGTH ];                            \
 
324
        waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH );  \
 
325
        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
 
326
    }
 
327
 
 
328
#endif /* UNICODE */
 
329
 
 
330
 
 
331
static void PaMme_SetLastSystemError( DWORD errorCode )
 
332
{
 
333
    char *lpMsgBuf;
 
334
    FormatMessage(
 
335
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
 
336
        NULL,
 
337
        errorCode,
 
338
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 
339
        (LPTSTR) &lpMsgBuf,
 
340
        0,
 
341
        NULL
 
342
    );
 
343
    PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf );
 
344
    LocalFree( lpMsgBuf );
 
345
}
 
346
 
 
347
#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \
 
348
    PaMme_SetLastSystemError( errorCode )
 
349
 
 
350
 
 
351
/* PaError returning wrappers for some commonly used win32 functions
 
352
    note that we allow passing a null ptr to have no effect.
 
353
*/
 
354
 
 
355
static PaError CreateEventWithPaError( HANDLE *handle,
 
356
        LPSECURITY_ATTRIBUTES lpEventAttributes,
 
357
        BOOL bManualReset,
 
358
        BOOL bInitialState,
 
359
        LPCTSTR lpName )
 
360
{
 
361
    PaError result = paNoError;
 
362
 
 
363
    *handle = NULL;
 
364
 
 
365
    *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
 
366
    if( *handle == NULL )
 
367
    {
 
368
        result = paUnanticipatedHostError;
 
369
        PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
 
370
    }
 
371
 
 
372
    return result;
 
373
}
 
374
 
 
375
 
 
376
static PaError ResetEventWithPaError( HANDLE handle )
 
377
{
 
378
    PaError result = paNoError;
 
379
 
 
380
    if( handle )
 
381
    {
 
382
        if( ResetEvent( handle ) == 0 )
 
383
        {
 
384
            result = paUnanticipatedHostError;
 
385
            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
 
386
        }
 
387
    }
 
388
 
 
389
    return result;
 
390
}
 
391
 
 
392
 
 
393
static PaError CloseHandleWithPaError( HANDLE handle )
 
394
{
 
395
    PaError result = paNoError;
 
396
 
 
397
    if( handle )
 
398
    {
 
399
        if( CloseHandle( handle ) == 0 )
 
400
        {
 
401
            result = paUnanticipatedHostError;
 
402
            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
 
403
        }
 
404
    }
 
405
 
 
406
    return result;
 
407
}
 
408
 
 
409
 
 
410
/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
 
411
 
 
412
typedef struct
 
413
{
 
414
    PaUtilHostApiRepresentation inheritedHostApiRep;
 
415
    PaUtilStreamInterface callbackStreamInterface;
 
416
    PaUtilStreamInterface blockingStreamInterface;
 
417
 
 
418
    PaUtilAllocationGroup *allocations;
 
419
 
 
420
    int inputDeviceCount, outputDeviceCount;
 
421
 
 
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
 
425
        device ids.
 
426
     */
 
427
    UINT *winMmeDeviceIds;
 
428
}
 
429
PaWinMmeHostApiRepresentation;
 
430
 
 
431
 
 
432
typedef struct
 
433
{
 
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)*/
 
438
}
 
439
PaWinMmeDeviceInfo;
 
440
 
 
441
 
 
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.
 
446
 *
 
447
 *    set PA_RECOMMENDED_OUTPUT_DEVICE=1
 
448
 *
 
449
 * The user should first determine the available device ID by using
 
450
 * the supplied application "pa_devs".
 
451
 */
 
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 )
 
456
{
 
457
    PaDeviceIndex recommendedIndex = paNoDevice;
 
458
    DWORD   hresult;
 
459
    char    envbuf[PA_ENV_BUF_SIZE_];
 
460
 
 
461
#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */
 
462
 
 
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_) )
 
466
    {
 
467
        recommendedIndex = atoi( envbuf );
 
468
    }
 
469
#endif
 
470
 
 
471
    return recommendedIndex;
 
472
}
 
473
 
 
474
 
 
475
static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi )
 
476
{
 
477
    PaDeviceIndex device;
 
478
 
 
479
    /* input */
 
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 )
 
484
    {
 
485
        hostApi->inheritedHostApiRep.info.defaultInputDevice = device;
 
486
    }
 
487
 
 
488
    /* output */
 
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 )
 
493
    {
 
494
        hostApi->inheritedHostApiRep.info.defaultOutputDevice = device;
 
495
    }
 
496
}
 
497
 
 
498
 
 
499
/** Convert external PA ID to a windows multimedia device ID
 
500
*/
 
501
static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device )
 
502
{
 
503
    assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount );
 
504
 
 
505
        return hostApi->winMmeDeviceIds[ device ];
 
506
}
 
507
 
 
508
 
 
509
static int SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( PaSampleFormat sampleFormat, unsigned long winMmeSpecificFlags )
 
510
{
 
511
    int waveFormatTag = 0;
 
512
 
 
513
    if( winMmeSpecificFlags & paWinMmeWaveFormatDolbyAc3Spdif )
 
514
        waveFormatTag = PAWIN_WAVE_FORMAT_DOLBY_AC3_SPDIF;
 
515
    else if( winMmeSpecificFlags & paWinMmeWaveFormatWmaSpdif )
 
516
        waveFormatTag = PAWIN_WAVE_FORMAT_WMA_SPDIF;
 
517
    else
 
518
        waveFormatTag = PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat );
 
519
 
 
520
    return waveFormatTag;
 
521
}
 
522
 
 
523
 
 
524
static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
 
525
{
 
526
    MMRESULT mmresult;
 
527
 
 
528
    switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
 
529
    {
 
530
        case MMSYSERR_NOERROR:
 
531
            return paNoError;
 
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;
 
540
 
 
541
        case MMSYSERR_BADDEVICEID:      /* Specified device identifier is out of range. */
 
542
            /* falls through */
 
543
        default:
 
544
            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
545
            return paUnanticipatedHostError;
 
546
    }
 
547
}
 
548
 
 
549
 
 
550
static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
 
551
{
 
552
    MMRESULT mmresult;
 
553
 
 
554
    switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
 
555
    {
 
556
        case MMSYSERR_NOERROR:
 
557
            return paNoError;
 
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;
 
566
 
 
567
        case MMSYSERR_BADDEVICEID:      /* Specified device identifier is out of range. */
 
568
            /* falls through */
 
569
        default:
 
570
            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
 
571
            return paUnanticipatedHostError;
 
572
    }
 
573
}
 
574
 
 
575
 
 
576
static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
 
577
        PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
 
578
        int winMmeDeviceId, int channels, double sampleRate, unsigned long winMmeSpecificFlags )
 
579
{
 
580
    PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
 
581
    PaWinWaveFormat waveFormat;
 
582
    PaSampleFormat sampleFormat;
 
583
    int waveFormatTag;
 
584
 
 
585
    /* @todo at the moment we only query with 16 bit sample format and directout speaker config*/
 
586
 
 
587
    sampleFormat = paInt16;
 
588
    waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
 
589
 
 
590
    if( waveFormatTag == PaWin_SampleFormatToLinearWaveFormatTag( paInt16 ) ){
 
591
 
 
592
        /* attempt bypass querying the device for linear formats */
 
593
 
 
594
        if( sampleRate == 11025.0
 
595
            && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
 
596
                || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
 
597
 
 
598
            return paNoError;
 
599
        }
 
600
 
 
601
        if( sampleRate == 22050.0
 
602
            && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
 
603
                || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
 
604
 
 
605
            return paNoError;
 
606
        }
 
607
 
 
608
        if( sampleRate == 44100.0
 
609
            && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
 
610
                || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
 
611
 
 
612
            return paNoError;
 
613
        }
 
614
    }
 
615
 
 
616
 
 
617
    /* first, attempt to query the device using WAVEFORMATEXTENSIBLE,
 
618
       if this fails we fall back to WAVEFORMATEX */
 
619
 
 
620
    PaWin_InitializeWaveFormatExtensible( &waveFormat, channels, sampleFormat, waveFormatTag,
 
621
            sampleRate, PAWIN_SPEAKER_DIRECTOUT );
 
622
 
 
623
    if( waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat ) == paNoError )
 
624
        return paNoError;
 
625
 
 
626
    PaWin_InitializeWaveFormatEx( &waveFormat, channels, sampleFormat, waveFormatTag, sampleRate );
 
627
 
 
628
    return waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat );
 
629
}
 
630
 
 
631
 
 
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 };
 
636
 
 
637
static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId,
 
638
        PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels )
 
639
{
 
640
    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
 
641
    int i;
 
642
 
 
643
    deviceInfo->defaultSampleRate = 0.;
 
644
 
 
645
    for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
 
646
    {
 
647
        double sampleRate = defaultSampleRateSearchOrder_[ i ];
 
648
        PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate, 0 );
 
649
        if( paerror == paNoError )
 
650
        {
 
651
            deviceInfo->defaultSampleRate = sampleRate;
 
652
            break;
 
653
        }
 
654
    }
 
655
}
 
656
 
 
657
 
 
658
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
 
659
static int QueryWaveInKSFilterMaxChannels( int waveInDeviceId, int *maxChannels )
 
660
{
 
661
    void *devicePath;
 
662
    DWORD devicePathSize;
 
663
    int result = 0;
 
664
 
 
665
    if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
 
666
            (DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
 
667
        return 0;
 
668
 
 
669
    devicePath = PaUtil_AllocateMemory( devicePathSize );
 
670
    if( !devicePath )
 
671
        return 0;
 
672
 
 
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 )
 
676
    {
 
677
        int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 1  );
 
678
        if( count > 0 )
 
679
        {
 
680
            *maxChannels = count;
 
681
            result = 1;
 
682
        }
 
683
    }
 
684
 
 
685
    PaUtil_FreeMemory( devicePath );
 
686
 
 
687
    return result;
 
688
}
 
689
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
 
690
 
 
691
 
 
692
static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
 
693
        PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
 
694
{
 
695
    PaError result = paNoError;
 
696
    char *deviceName; /* non-const ptr */
 
697
    MMRESULT mmresult;
 
698
    WAVEINCAPS wic;
 
699
    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
 
700
 
 
701
    *success = 0;
 
702
 
 
703
    mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
 
704
    if( mmresult == MMSYSERR_NOMEM )
 
705
    {
 
706
        result = paInsufficientMemory;
 
707
        goto error;
 
708
    }
 
709
    else if( mmresult != MMSYSERR_NOERROR )
 
710
    {
 
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.
 
715
        */
 
716
        return paNoError;
 
717
    }
 
718
 
 
719
    if( winMmeInputDeviceId == WAVE_MAPPER )
 
720
    {
 
721
        /* Append I/O suffix to WAVE_MAPPER device. */
 
722
        deviceName = (char *)PaUtil_GroupAllocateMemory(
 
723
                    winMmeHostApi->allocations, StrTLen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_) );
 
724
        if( !deviceName )
 
725
        {
 
726
            result = paInsufficientMemory;
 
727
            goto error;
 
728
        }
 
729
        StrTCpyToC( deviceName, wic.szPname );
 
730
        strcat( deviceName, constInputMapperSuffix_ );
 
731
    }
 
732
    else
 
733
    {
 
734
        deviceName = (char*)PaUtil_GroupAllocateMemory(
 
735
                    winMmeHostApi->allocations, StrTLen( wic.szPname ) + 1 );
 
736
        if( !deviceName )
 
737
        {
 
738
            result = paInsufficientMemory;
 
739
            goto error;
 
740
        }
 
741
        StrTCpyToC( deviceName, wic.szPname  );
 
742
    }
 
743
    deviceInfo->name = deviceName;
 
744
 
 
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.
 
754
     */
 
755
 
 
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;
 
759
    }else{
 
760
        deviceInfo->maxInputChannels = wic.wChannels;
 
761
        winMmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
 
762
    }
 
763
 
 
764
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
 
765
    winMmeDeviceInfo->deviceInputChannelCountIsKnown =
 
766
            QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels );
 
767
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
 
768
 
 
769
    winMmeDeviceInfo->dwFormats = wic.dwFormats;
 
770
 
 
771
    DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
 
772
            QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
 
773
 
 
774
    *success = 1;
 
775
 
 
776
error:
 
777
    return result;
 
778
}
 
779
 
 
780
 
 
781
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
 
782
static int QueryWaveOutKSFilterMaxChannels( int waveOutDeviceId, int *maxChannels )
 
783
{
 
784
    void *devicePath;
 
785
    DWORD devicePathSize;
 
786
    int result = 0;
 
787
 
 
788
    if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
 
789
            (DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
 
790
        return 0;
 
791
 
 
792
    devicePath = PaUtil_AllocateMemory( devicePathSize );
 
793
    if( !devicePath )
 
794
        return 0;
 
795
 
 
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 )
 
799
    {
 
800
        int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 0  );
 
801
        if( count > 0 )
 
802
        {
 
803
            *maxChannels = count;
 
804
            result = 1;
 
805
        }
 
806
    }
 
807
 
 
808
    PaUtil_FreeMemory( devicePath );
 
809
 
 
810
    return result;
 
811
}
 
812
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
 
813
 
 
814
 
 
815
static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
 
816
        PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
 
817
{
 
818
    PaError result = paNoError;
 
819
    char *deviceName; /* non-const ptr */
 
820
    MMRESULT mmresult;
 
821
    WAVEOUTCAPS woc;
 
822
    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
 
823
    int wdmksDeviceOutputChannelCountIsKnown;
 
824
 
 
825
    *success = 0;
 
826
 
 
827
    mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
 
828
    if( mmresult == MMSYSERR_NOMEM )
 
829
    {
 
830
        result = paInsufficientMemory;
 
831
        goto error;
 
832
    }
 
833
    else if( mmresult != MMSYSERR_NOERROR )
 
834
    {
 
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.
 
839
        */
 
840
        return paNoError;
 
841
    }
 
842
 
 
843
    if( winMmeOutputDeviceId == WAVE_MAPPER )
 
844
    {
 
845
        /* Append I/O suffix to WAVE_MAPPER device. */
 
846
        deviceName = (char *)PaUtil_GroupAllocateMemory(
 
847
                    winMmeHostApi->allocations, StrTLen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_) );
 
848
        if( !deviceName )
 
849
        {
 
850
            result = paInsufficientMemory;
 
851
            goto error;
 
852
        }
 
853
        StrTCpyToC( deviceName, woc.szPname );
 
854
        strcat( deviceName, constOutputMapperSuffix_ );
 
855
    }
 
856
    else
 
857
    {
 
858
        deviceName = (char*)PaUtil_GroupAllocateMemory(
 
859
                    winMmeHostApi->allocations, StrTLen( woc.szPname ) + 1 );
 
860
        if( !deviceName )
 
861
        {
 
862
            result = paInsufficientMemory;
 
863
            goto error;
 
864
        }
 
865
        StrTCpyToC( deviceName, woc.szPname  );
 
866
    }
 
867
    deviceInfo->name = deviceName;
 
868
 
 
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.
 
878
     */
 
879
 
 
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;
 
883
    }else{
 
884
        deviceInfo->maxOutputChannels = woc.wChannels;
 
885
        winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
 
886
    }
 
887
 
 
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 */
 
894
 
 
895
    winMmeDeviceInfo->dwFormats = woc.dwFormats;
 
896
 
 
897
    DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
 
898
            QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
 
899
 
 
900
    *success = 1;
 
901
 
 
902
error:
 
903
    return result;
 
904
}
 
905
 
 
906
 
 
907
static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
 
908
{
 
909
    OSVERSIONINFO osvi;
 
910
    osvi.dwOSVersionInfoSize = sizeof( osvi );
 
911
        GetVersionEx( &osvi );
 
912
 
 
913
    /* Check for NT */
 
914
    if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
 
915
    {
 
916
        *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
 
917
    }
 
918
    else if(osvi.dwMajorVersion >= 5)
 
919
    {
 
920
        *defaultLowLatency  = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
 
921
    }
 
922
    else
 
923
    {
 
924
        *defaultLowLatency  = PA_MME_WIN_9X_DEFAULT_LATENCY_;
 
925
    }
 
926
 
 
927
    *defaultHighLatency = *defaultLowLatency * 2;
 
928
}
 
929
 
 
930
 
 
931
PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
 
932
{
 
933
    PaError result = paNoError;
 
934
    int i;
 
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;
 
942
 
 
943
    winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
 
944
    if( !winMmeHostApi )
 
945
    {
 
946
        result = paInsufficientMemory;
 
947
        goto error;
 
948
    }
 
949
 
 
950
    winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
 
951
    if( !winMmeHostApi->allocations )
 
952
    {
 
953
        result = paInsufficientMemory;
 
954
        goto error;
 
955
    }
 
956
 
 
957
    *hostApi = &winMmeHostApi->inheritedHostApiRep;
 
958
    (*hostApi)->info.structVersion = 1;
 
959
    (*hostApi)->info.type = paMME;
 
960
    (*hostApi)->info.name = "MME";
 
961
 
 
962
 
 
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.
 
966
    */
 
967
    (*hostApi)->info.deviceCount = 0;
 
968
    (*hostApi)->info.defaultInputDevice = paNoDevice;
 
969
    (*hostApi)->info.defaultOutputDevice = paNoDevice;
 
970
    winMmeHostApi->inputDeviceCount = 0;
 
971
    winMmeHostApi->outputDeviceCount = 0;
 
972
 
 
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)
 
976
#endif
 
977
 
 
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 );
 
982
 
 
983
    preferredDeviceStatusFlags = 0;
 
984
    waveOutPreferredDevice = -1;
 
985
    waveOutMessage( (HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&waveOutPreferredDevice, (DWORD_PTR)&preferredDeviceStatusFlags );
 
986
 
 
987
    maximumPossibleDeviceCount = 0;
 
988
 
 
989
    inputDeviceCount = waveInGetNumDevs();
 
990
    if( inputDeviceCount > 0 )
 
991
        maximumPossibleDeviceCount += inputDeviceCount + 1;     /* assume there is a WAVE_MAPPER */
 
992
 
 
993
    outputDeviceCount = waveOutGetNumDevs();
 
994
    if( outputDeviceCount > 0 )
 
995
            maximumPossibleDeviceCount += outputDeviceCount + 1;        /* assume there is a WAVE_MAPPER */
 
996
 
 
997
 
 
998
    if( maximumPossibleDeviceCount > 0 ){
 
999
 
 
1000
        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
 
1001
                winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
 
1002
        if( !(*hostApi)->deviceInfos )
 
1003
        {
 
1004
            result = paInsufficientMemory;
 
1005
            goto error;
 
1006
        }
 
1007
 
 
1008
        /* allocate all device info structs in a contiguous block */
 
1009
        deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
 
1010
                winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
 
1011
        if( !deviceInfoArray )
 
1012
        {
 
1013
            result = paInsufficientMemory;
 
1014
            goto error;
 
1015
        }
 
1016
 
 
1017
        winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
 
1018
                winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
 
1019
        if( !winMmeHostApi->winMmeDeviceIds )
 
1020
        {
 
1021
            result = paInsufficientMemory;
 
1022
            goto error;
 
1023
        }
 
1024
 
 
1025
        GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
 
1026
 
 
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;
 
1035
 
 
1036
                deviceInfo->maxInputChannels = 0;
 
1037
                wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
 
1038
                deviceInfo->maxOutputChannels = 0;
 
1039
                wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
 
1040
 
 
1041
                deviceInfo->defaultLowInputLatency = defaultLowLatency;
 
1042
                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
 
1043
                deviceInfo->defaultHighInputLatency = defaultHighLatency;
 
1044
                deviceInfo->defaultHighOutputLatency = defaultHighLatency;
 
1045
 
 
1046
                result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
 
1047
                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
 
1048
                if( result != paNoError )
 
1049
                    goto error;
 
1050
 
 
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;
 
1055
 
 
1056
                    }else if( winMmeDeviceId == waveInPreferredDevice ){
 
1057
                        /* set the default device to the system preferred device */
 
1058
                        (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
 
1059
                    }
 
1060
 
 
1061
                    winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
 
1062
                    (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
 
1063
 
 
1064
                    winMmeHostApi->inputDeviceCount++;
 
1065
                    (*hostApi)->info.deviceCount++;
 
1066
                }
 
1067
            }
 
1068
        }
 
1069
 
 
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;
 
1078
 
 
1079
                deviceInfo->maxInputChannels = 0;
 
1080
                wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
 
1081
                deviceInfo->maxOutputChannels = 0;
 
1082
                wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
 
1083
 
 
1084
                deviceInfo->defaultLowInputLatency = defaultLowLatency;
 
1085
                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
 
1086
                deviceInfo->defaultHighInputLatency = defaultHighLatency;
 
1087
                deviceInfo->defaultHighOutputLatency = defaultHighLatency;
 
1088
 
 
1089
                result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
 
1090
                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
 
1091
                if( result != paNoError )
 
1092
                    goto error;
 
1093
 
 
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;
 
1098
 
 
1099
                    }else if( winMmeDeviceId == waveOutPreferredDevice ){
 
1100
                        /* set the default device to the system preferred device */
 
1101
                        (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
 
1102
                    }
 
1103
 
 
1104
                    winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
 
1105
                    (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
 
1106
 
 
1107
                    winMmeHostApi->outputDeviceCount++;
 
1108
                    (*hostApi)->info.deviceCount++;
 
1109
                }
 
1110
            }
 
1111
        }
 
1112
    }
 
1113
 
 
1114
    InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
 
1115
 
 
1116
    (*hostApi)->Terminate = Terminate;
 
1117
    (*hostApi)->OpenStream = OpenStream;
 
1118
    (*hostApi)->IsFormatSupported = IsFormatSupported;
 
1119
 
 
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 );
 
1125
 
 
1126
    PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream,
 
1127
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
 
1128
                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
 
1129
                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
 
1130
 
 
1131
    return result;
 
1132
 
 
1133
error:
 
1134
    if( winMmeHostApi )
 
1135
    {
 
1136
        if( winMmeHostApi->allocations )
 
1137
        {
 
1138
            PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
 
1139
            PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
 
1140
        }
 
1141
 
 
1142
        PaUtil_FreeMemory( winMmeHostApi );
 
1143
    }
 
1144
 
 
1145
    return result;
 
1146
}
 
1147
 
 
1148
 
 
1149
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
 
1150
{
 
1151
    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
 
1152
 
 
1153
    if( winMmeHostApi->allocations )
 
1154
    {
 
1155
        PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
 
1156
        PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
 
1157
    }
 
1158
 
 
1159
    PaUtil_FreeMemory( winMmeHostApi );
 
1160
}
 
1161
 
 
1162
 
 
1163
static PaError IsInputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
 
1164
{
 
1165
    PaError result = paNoError;
 
1166
 
 
1167
    if( channelCount > 0
 
1168
            && deviceInfo->deviceInputChannelCountIsKnown
 
1169
            && channelCount > deviceInfo->inheritedDeviceInfo.maxInputChannels ){
 
1170
 
 
1171
        result = paInvalidChannelCount;
 
1172
    }
 
1173
 
 
1174
    return result;
 
1175
}
 
1176
 
 
1177
static PaError IsOutputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
 
1178
{
 
1179
    PaError result = paNoError;
 
1180
 
 
1181
    if( channelCount > 0
 
1182
            && deviceInfo->deviceOutputChannelCountIsKnown
 
1183
            && channelCount > deviceInfo->inheritedDeviceInfo.maxOutputChannels ){
 
1184
 
 
1185
        result = paInvalidChannelCount;
 
1186
    }
 
1187
 
 
1188
    return result;
 
1189
}
 
1190
 
 
1191
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
 
1192
                                  const PaStreamParameters *inputParameters,
 
1193
                                  const PaStreamParameters *outputParameters,
 
1194
                                  double sampleRate )
 
1195
{
 
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;
 
1203
    unsigned int i;
 
1204
    PaError paerror;
 
1205
 
 
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.
 
1211
    */
 
1212
 
 
1213
    if( inputParameters )
 
1214
    {
 
1215
        inputChannelCount = inputParameters->channelCount;
 
1216
        inputSampleFormat = inputParameters->sampleFormat;
 
1217
        inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
 
1218
 
 
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;
 
1223
 
 
1224
        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification
 
1225
                && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
 
1226
        {
 
1227
            inputMultipleDeviceChannelCount = 0;
 
1228
            for( i=0; i< inputStreamInfo->deviceCount; ++i )
 
1229
            {
 
1230
                inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
 
1231
 
 
1232
                inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
 
1233
 
 
1234
                /* check that input device can support inputChannelCount */
 
1235
                if( inputStreamInfo->devices[i].channelCount < 1 )
 
1236
                    return paInvalidChannelCount;
 
1237
 
 
1238
                paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo,
 
1239
                        inputStreamInfo->devices[i].channelCount );
 
1240
                if( paerror != paNoError )
 
1241
                    return paerror;
 
1242
 
 
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;
 
1250
            }
 
1251
 
 
1252
            if( inputMultipleDeviceChannelCount != inputChannelCount )
 
1253
                return paIncompatibleHostApiSpecificStreamInfo;
 
1254
        }
 
1255
        else
 
1256
        {
 
1257
            if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
 
1258
                return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */
 
1259
 
 
1260
            inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
 
1261
 
 
1262
            /* check that input device can support inputChannelCount */
 
1263
            paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo, inputChannelCount );
 
1264
            if( paerror != paNoError )
 
1265
                return paerror;
 
1266
 
 
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;
 
1274
        }
 
1275
    }
 
1276
 
 
1277
    if( outputParameters )
 
1278
    {
 
1279
        outputChannelCount = outputParameters->channelCount;
 
1280
        outputSampleFormat = outputParameters->sampleFormat;
 
1281
        outputStreamInfo = outputParameters->hostApiSpecificStreamInfo;
 
1282
 
 
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;
 
1287
 
 
1288
        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification
 
1289
                && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
 
1290
        {
 
1291
            outputMultipleDeviceChannelCount = 0;
 
1292
            for( i=0; i< outputStreamInfo->deviceCount; ++i )
 
1293
            {
 
1294
                outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
 
1295
 
 
1296
                outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
 
1297
 
 
1298
                /* check that output device can support outputChannelCount */
 
1299
                if( outputStreamInfo->devices[i].channelCount < 1 )
 
1300
                    return paInvalidChannelCount;
 
1301
 
 
1302
                paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo,
 
1303
                        outputStreamInfo->devices[i].channelCount );
 
1304
                if( paerror != paNoError )
 
1305
                    return paerror;
 
1306
 
 
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;
 
1314
            }
 
1315
 
 
1316
            if( outputMultipleDeviceChannelCount != outputChannelCount )
 
1317
                return paIncompatibleHostApiSpecificStreamInfo;
 
1318
        }
 
1319
        else
 
1320
        {
 
1321
            if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
 
1322
                return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */
 
1323
 
 
1324
            outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
 
1325
 
 
1326
            /* check that output device can support outputChannelCount */
 
1327
            paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo, outputChannelCount );
 
1328
            if( paerror != paNoError )
 
1329
                return paerror;
 
1330
 
 
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;
 
1338
        }
 
1339
    }
 
1340
 
 
1341
    /*
 
1342
            - if a full duplex stream is requested, check that the combination
 
1343
                of input and output parameters is supported
 
1344
 
 
1345
            - check that the device supports sampleRate
 
1346
 
 
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.
 
1350
    */
 
1351
 
 
1352
    return paFormatIsSupported;
 
1353
}
 
1354
 
 
1355
 
 
1356
 
 
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 )
 
1362
{
 
1363
    unsigned long sizeMultiplier, bufferCount, latency;
 
1364
    unsigned long nextLatency, nextBufferSize;
 
1365
    int baseBufferSizeIsPowerOfTwo;
 
1366
 
 
1367
    sizeMultiplier = 1;
 
1368
    bufferCount = baseBufferCount;
 
1369
 
 
1370
    /* count-1 below because latency is always determined by one less
 
1371
        than the total number of buffers.
 
1372
    */
 
1373
    latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
 
1374
 
 
1375
    if( latency > requestedLatency )
 
1376
    {
 
1377
 
 
1378
        /* reduce number of buffers without falling below suggested latency */
 
1379
 
 
1380
        nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
 
1381
        while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
 
1382
        {
 
1383
            --bufferCount;
 
1384
            nextLatency = (baseBufferSize * sizeMultiplier) * (bufferCount-2);
 
1385
        }
 
1386
 
 
1387
    }else if( latency < requestedLatency ){
 
1388
 
 
1389
        baseBufferSizeIsPowerOfTwo = (! (baseBufferSize & (baseBufferSize - 1)));
 
1390
        if( baseBufferSizeIsPowerOfTwo ){
 
1391
 
 
1392
            /* double size of buffers without exceeding requestedLatency */
 
1393
 
 
1394
            nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
 
1395
            nextLatency = nextBufferSize * (bufferCount-1);
 
1396
            while( nextBufferSize <= maximumBufferSize
 
1397
                    && nextLatency < requestedLatency )
 
1398
            {
 
1399
                sizeMultiplier *= 2;
 
1400
                nextBufferSize = (baseBufferSize * (sizeMultiplier*2));
 
1401
                nextLatency = nextBufferSize * (bufferCount-1);
 
1402
            }
 
1403
 
 
1404
        }else{
 
1405
 
 
1406
            /* increase size of buffers upto first excess of requestedLatency */
 
1407
 
 
1408
            nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
 
1409
            nextLatency = nextBufferSize * (bufferCount-1);
 
1410
            while( nextBufferSize <= maximumBufferSize
 
1411
                    && nextLatency < requestedLatency )
 
1412
            {
 
1413
                ++sizeMultiplier;
 
1414
                nextBufferSize = (baseBufferSize * (sizeMultiplier+1));
 
1415
                nextLatency = nextBufferSize * (bufferCount-1);
 
1416
            }
 
1417
 
 
1418
            if( nextLatency < requestedLatency )
 
1419
                ++sizeMultiplier;
 
1420
        }
 
1421
 
 
1422
        /* increase number of buffers until requestedLatency is reached */
 
1423
 
 
1424
        latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
 
1425
        while( latency < requestedLatency )
 
1426
        {
 
1427
            ++bufferCount;
 
1428
            latency = (baseBufferSize * sizeMultiplier) * (bufferCount-1);
 
1429
        }
 
1430
    }
 
1431
 
 
1432
    *hostBufferSize = baseBufferSize * sizeMultiplier;
 
1433
    *hostBufferCount = bufferCount;
 
1434
}
 
1435
 
 
1436
 
 
1437
static void ReselectBufferCount( unsigned long bufferSize,
 
1438
    unsigned long requestedLatency,
 
1439
    unsigned long baseBufferCount, unsigned long minimumBufferCount,
 
1440
    unsigned long *hostBufferCount )
 
1441
{
 
1442
    unsigned long bufferCount, latency;
 
1443
    unsigned long nextLatency;
 
1444
 
 
1445
    bufferCount = baseBufferCount;
 
1446
 
 
1447
    /* count-1 below because latency is always determined by one less
 
1448
        than the total number of buffers.
 
1449
    */
 
1450
    latency = bufferSize * (bufferCount-1);
 
1451
 
 
1452
    if( latency > requestedLatency )
 
1453
    {
 
1454
        /* reduce number of buffers without falling below suggested latency */
 
1455
 
 
1456
        nextLatency = bufferSize * (bufferCount-2);
 
1457
        while( bufferCount > minimumBufferCount && nextLatency >= requestedLatency )
 
1458
        {
 
1459
            --bufferCount;
 
1460
            nextLatency = bufferSize * (bufferCount-2);
 
1461
        }
 
1462
 
 
1463
    }else if( latency < requestedLatency ){
 
1464
 
 
1465
        /* increase number of buffers until requestedLatency is reached */
 
1466
 
 
1467
        latency = bufferSize * (bufferCount-1);
 
1468
        while( latency < requestedLatency )
 
1469
        {
 
1470
            ++bufferCount;
 
1471
            latency = bufferSize * (bufferCount-1);
 
1472
        }
 
1473
    }
 
1474
 
 
1475
    *hostBufferCount = bufferCount;
 
1476
}
 
1477
 
 
1478
 
 
1479
/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount,
 
1480
   framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values
 
1481
   of the other parameters.
 
1482
*/
 
1483
 
 
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 )
 
1492
{
 
1493
    PaError result = paNoError;
 
1494
    int effectiveInputChannelCount, effectiveOutputChannelCount;
 
1495
    int hostInputFrameSize = 0;
 
1496
    unsigned int i;
 
1497
 
 
1498
    if( inputChannelCount > 0 )
 
1499
    {
 
1500
        int hostInputSampleSize = Pa_GetSampleSize( hostInputSampleFormat );
 
1501
        if( hostInputSampleSize < 0 )
 
1502
        {
 
1503
            result = hostInputSampleSize;
 
1504
            goto error;
 
1505
        }
 
1506
 
 
1507
        if( inputStreamInfo
 
1508
                && ( inputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
 
1509
        {
 
1510
            /* set effectiveInputChannelCount to the largest number of
 
1511
                channels on any one device.
 
1512
            */
 
1513
            effectiveInputChannelCount = 0;
 
1514
            for( i=0; i< inputStreamInfo->deviceCount; ++i )
 
1515
            {
 
1516
                if( inputStreamInfo->devices[i].channelCount > effectiveInputChannelCount )
 
1517
                    effectiveInputChannelCount = inputStreamInfo->devices[i].channelCount;
 
1518
            }
 
1519
        }
 
1520
        else
 
1521
        {
 
1522
            effectiveInputChannelCount = inputChannelCount;
 
1523
        }
 
1524
 
 
1525
        hostInputFrameSize = hostInputSampleSize * effectiveInputChannelCount;
 
1526
 
 
1527
        if( inputStreamInfo
 
1528
                && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
 
1529
        {
 
1530
            if( inputStreamInfo->bufferCount <= 0
 
1531
                    || inputStreamInfo->framesPerBuffer <= 0 )
 
1532
            {
 
1533
                result = paIncompatibleHostApiSpecificStreamInfo;
 
1534
                goto error;
 
1535
            }
 
1536
 
 
1537
            *framesPerHostInputBuffer = inputStreamInfo->framesPerBuffer;
 
1538
            *hostInputBufferCount = inputStreamInfo->bufferCount;
 
1539
        }
 
1540
        else
 
1541
        {
 
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_;
 
1546
 
 
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_;
 
1550
 
 
1551
            /* compute the following in bytes, then convert back to frames */
 
1552
 
 
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 );
 
1561
 
 
1562
            *framesPerHostInputBuffer = hostBufferSizeBytes / hostInputFrameSize;
 
1563
            *hostInputBufferCount = hostBufferCount;
 
1564
        }
 
1565
    }
 
1566
    else
 
1567
    {
 
1568
        *framesPerHostInputBuffer = 0;
 
1569
        *hostInputBufferCount = 0;
 
1570
    }
 
1571
 
 
1572
    if( outputChannelCount > 0 )
 
1573
    {
 
1574
        if( outputStreamInfo
 
1575
                && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
 
1576
        {
 
1577
            if( outputStreamInfo->bufferCount <= 0
 
1578
                    || outputStreamInfo->framesPerBuffer <= 0 )
 
1579
            {
 
1580
                result = paIncompatibleHostApiSpecificStreamInfo;
 
1581
                goto error;
 
1582
            }
 
1583
 
 
1584
            *framesPerHostOutputBuffer = outputStreamInfo->framesPerBuffer;
 
1585
            *hostOutputBufferCount = outputStreamInfo->bufferCount;
 
1586
 
 
1587
 
 
1588
            if( inputChannelCount > 0 ) /* full duplex */
 
1589
            {
 
1590
                if( *framesPerHostInputBuffer != *framesPerHostOutputBuffer )
 
1591
                {
 
1592
                    if( inputStreamInfo
 
1593
                            && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
 
1594
                    {
 
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 */
 
1598
 
 
1599
                        if( *framesPerHostInputBuffer < *framesPerHostOutputBuffer )
 
1600
                        {
 
1601
                            if( *framesPerHostOutputBuffer % *framesPerHostInputBuffer != 0 )
 
1602
                            {
 
1603
                                result = paIncompatibleHostApiSpecificStreamInfo;
 
1604
                                goto error;
 
1605
                            }
 
1606
                        }
 
1607
                        else
 
1608
                        {
 
1609
                            assert( *framesPerHostInputBuffer > *framesPerHostOutputBuffer );
 
1610
                            if( *framesPerHostInputBuffer % *framesPerHostOutputBuffer != 0 )
 
1611
                            {
 
1612
                                result = paIncompatibleHostApiSpecificStreamInfo;
 
1613
                                goto error;
 
1614
                            }
 
1615
                        }
 
1616
                    }
 
1617
                    else
 
1618
                    {
 
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. */
 
1621
 
 
1622
                        *framesPerHostInputBuffer = *framesPerHostOutputBuffer;
 
1623
                        *hostInputBufferCount = (((unsigned long)(suggestedInputLatency * sampleRate)) / *framesPerHostInputBuffer) + 1;
 
1624
 
 
1625
                        if( *hostInputBufferCount < PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ )
 
1626
                            *hostInputBufferCount = PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_;
 
1627
                    }
 
1628
                }
 
1629
            }
 
1630
        }
 
1631
        else
 
1632
        {
 
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;
 
1638
 
 
1639
            hostOutputSampleSize = Pa_GetSampleSize( hostOutputSampleFormat );
 
1640
            if( hostOutputSampleSize < 0 )
 
1641
            {
 
1642
                result = hostOutputSampleSize;
 
1643
                goto error;
 
1644
            }
 
1645
 
 
1646
            if( outputStreamInfo
 
1647
                && ( outputStreamInfo->flags & paWinMmeUseMultipleDevices ) )
 
1648
            {
 
1649
                /* set effectiveOutputChannelCount to the largest number of
 
1650
                    channels on any one device.
 
1651
                */
 
1652
                effectiveOutputChannelCount = 0;
 
1653
                for( i=0; i< outputStreamInfo->deviceCount; ++i )
 
1654
                {
 
1655
                    if( outputStreamInfo->devices[i].channelCount > effectiveOutputChannelCount )
 
1656
                        effectiveOutputChannelCount = outputStreamInfo->devices[i].channelCount;
 
1657
                }
 
1658
            }
 
1659
            else
 
1660
            {
 
1661
                effectiveOutputChannelCount = outputChannelCount;
 
1662
            }
 
1663
 
 
1664
            hostOutputFrameSize = hostOutputSampleSize * effectiveOutputChannelCount;
 
1665
 
 
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_;
 
1669
 
 
1670
 
 
1671
            /* compute the following in bytes, then convert back to frames */
 
1672
 
 
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 */
 
1679
                minimumBufferCount,
 
1680
                maximumBufferSize,
 
1681
                &hostBufferSizeBytes, &hostBufferCount );
 
1682
 
 
1683
            *framesPerHostOutputBuffer = hostBufferSizeBytes / hostOutputFrameSize;
 
1684
            *hostOutputBufferCount = hostBufferCount;
 
1685
 
 
1686
 
 
1687
            if( inputChannelCount > 0 )
 
1688
            {
 
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
 
1692
                */
 
1693
 
 
1694
                if( *framesPerHostOutputBuffer != *framesPerHostInputBuffer )
 
1695
                {
 
1696
                    if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
 
1697
                    {
 
1698
                        unsigned long framesPerHostBuffer = *framesPerHostInputBuffer;
 
1699
 
 
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 */
 
1705
                            minimumBufferCount,
 
1706
                            &hostBufferCount );
 
1707
 
 
1708
                        *framesPerHostOutputBuffer = framesPerHostBuffer;
 
1709
                        *hostOutputBufferCount = hostBufferCount;
 
1710
                    }
 
1711
                    else
 
1712
                    {
 
1713
                        unsigned long framesPerHostBuffer = *framesPerHostOutputBuffer;
 
1714
 
 
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 */
 
1720
                            minimumBufferCount,
 
1721
                            &hostBufferCount );
 
1722
 
 
1723
                        *framesPerHostInputBuffer = framesPerHostBuffer;
 
1724
                        *hostInputBufferCount = hostBufferCount;
 
1725
                    }
 
1726
                }
 
1727
            }
 
1728
        }
 
1729
    }
 
1730
    else
 
1731
    {
 
1732
        *framesPerHostOutputBuffer = 0;
 
1733
        *hostOutputBufferCount = 0;
 
1734
    }
 
1735
 
 
1736
error:
 
1737
    return result;
 
1738
}
 
1739
 
 
1740
 
 
1741
typedef struct
 
1742
{
 
1743
    HANDLE bufferEvent;
 
1744
    void *waveHandles;
 
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;
 
1753
 
 
1754
/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */
 
1755
 
 
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,
 
1769
        int isInput );
 
1770
static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput );
 
1771
 
 
1772
 
 
1773
static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
 
1774
{
 
1775
    handlesAndBuffers->bufferEvent = 0;
 
1776
    handlesAndBuffers->waveHandles = 0;
 
1777
    handlesAndBuffers->deviceCount = 0;
 
1778
    handlesAndBuffers->waveHeaders = 0;
 
1779
    handlesAndBuffers->bufferCount = 0;
 
1780
}
 
1781
 
 
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 )
 
1788
{
 
1789
    PaError result;
 
1790
    MMRESULT mmresult;
 
1791
    signed int i, j;
 
1792
    PaSampleFormat sampleFormat;
 
1793
    int waveFormatTag;
 
1794
 
 
1795
    /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
 
1796
        has already been called to zero some fields */
 
1797
 
 
1798
    result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
 
1799
    if( result != paNoError ) goto error;
 
1800
 
 
1801
    if( isInput )
 
1802
        handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount );
 
1803
    else
 
1804
        handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount );
 
1805
    if( !handlesAndBuffers->waveHandles )
 
1806
    {
 
1807
        result = paInsufficientMemory;
 
1808
        goto error;
 
1809
    }
 
1810
 
 
1811
    handlesAndBuffers->deviceCount = deviceCount;
 
1812
 
 
1813
    for( i = 0; i < (signed int)deviceCount; ++i )
 
1814
    {
 
1815
        if( isInput )
 
1816
            ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0;
 
1817
        else
 
1818
            ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
 
1819
    }
 
1820
 
 
1821
    /* @todo at the moment we only use 16 bit sample format */
 
1822
    sampleFormat = paInt16;
 
1823
    waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
 
1824
 
 
1825
    for( i = 0; i < (signed int)deviceCount; ++i )
 
1826
    {
 
1827
        PaWinWaveFormat waveFormat;
 
1828
        UINT winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
 
1829
 
 
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. */
 
1832
 
 
1833
        for( j = 0; j < 2; ++j )
 
1834
        {
 
1835
            if( j == 0 )
 
1836
            {
 
1837
                /* first, attempt to open the device using WAVEFORMATEXTENSIBLE,
 
1838
                    if this fails we fall back to WAVEFORMATEX */
 
1839
 
 
1840
                PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount,
 
1841
                        sampleFormat, waveFormatTag, sampleRate, channelMask );
 
1842
 
 
1843
            }
 
1844
            else
 
1845
            {
 
1846
                /* retry with WAVEFORMATEX */
 
1847
 
 
1848
                PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount,
 
1849
                        sampleFormat, waveFormatTag, sampleRate );
 
1850
            }
 
1851
 
 
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. */
 
1855
 
 
1856
            if( isInput )
 
1857
            {
 
1858
                mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
 
1859
                                    (WAVEFORMATEX*)&waveFormat,
 
1860
                               (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
 
1861
            }
 
1862
            else
 
1863
            {
 
1864
                mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
 
1865
                                    (WAVEFORMATEX*)&waveFormat,
 
1866
                                (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
 
1867
            }
 
1868
 
 
1869
            if( mmresult == MMSYSERR_NOERROR )
 
1870
            {
 
1871
                break; /* success */
 
1872
            }
 
1873
            else if( j == 0 )
 
1874
            {
 
1875
                continue; /* try again with WAVEFORMATEX */
 
1876
            }
 
1877
            else
 
1878
            {
 
1879
                switch( mmresult )
 
1880
                {
 
1881
                    case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
 
1882
                        result = paDeviceUnavailable;
 
1883
                        break;
 
1884
                    case MMSYSERR_NODRIVER:         /* No device driver is present. */
 
1885
                        result = paDeviceUnavailable;
 
1886
                        break;
 
1887
                    case MMSYSERR_NOMEM:            /* Unable to allocate or lock memory. */
 
1888
                        result = paInsufficientMemory;
 
1889
                        break;
 
1890
 
 
1891
                    case MMSYSERR_BADDEVICEID:  /* Specified device identifier is out of range. */
 
1892
                        /* falls through */
 
1893
 
 
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
 
1897
                                                     * set to 0.
 
1898
                                                     */
 
1899
                        /* falls through */
 
1900
                    default:
 
1901
                        result = paUnanticipatedHostError;
 
1902
                        if( isInput )
 
1903
                        {
 
1904
                            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
1905
                        }
 
1906
                        else
 
1907
                        {
 
1908
                            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
 
1909
                        }
 
1910
                }
 
1911
                goto error;
 
1912
            }
 
1913
        }
 
1914
    }
 
1915
 
 
1916
    return result;
 
1917
 
 
1918
error:
 
1919
    TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ );
 
1920
 
 
1921
    return result;
 
1922
}
 
1923
 
 
1924
 
 
1925
static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError )
 
1926
{
 
1927
    PaError result = paNoError;
 
1928
    MMRESULT mmresult;
 
1929
    signed int i;
 
1930
 
 
1931
    if( handlesAndBuffers->waveHandles )
 
1932
    {
 
1933
        for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
 
1934
        {
 
1935
            if( isInput )
 
1936
            {
 
1937
                if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
 
1938
                    mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
 
1939
                else
 
1940
                    mmresult = MMSYSERR_NOERROR;
 
1941
            }
 
1942
            else
 
1943
            {
 
1944
                if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
 
1945
                    mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
 
1946
                else
 
1947
                    mmresult = MMSYSERR_NOERROR;
 
1948
            }
 
1949
 
 
1950
            if( mmresult != MMSYSERR_NOERROR &&
 
1951
                !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */
 
1952
            {
 
1953
                result = paUnanticipatedHostError;
 
1954
                if( isInput )
 
1955
                {
 
1956
                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
1957
                }
 
1958
                else
 
1959
                {
 
1960
                    PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
 
1961
                }
 
1962
                /* note that we don't break here, we try to continue closing devices */
 
1963
            }
 
1964
        }
 
1965
 
 
1966
        PaUtil_FreeMemory( handlesAndBuffers->waveHandles );
 
1967
        handlesAndBuffers->waveHandles = 0;
 
1968
    }
 
1969
 
 
1970
    if( handlesAndBuffers->bufferEvent )
 
1971
    {
 
1972
        result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
 
1973
        handlesAndBuffers->bufferEvent = 0;
 
1974
    }
 
1975
 
 
1976
    return result;
 
1977
}
 
1978
 
 
1979
 
 
1980
static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
 
1981
        unsigned long hostBufferCount,
 
1982
        PaSampleFormat hostSampleFormat,
 
1983
        unsigned long framesPerHostBuffer,
 
1984
        PaWinMmeDeviceAndChannelCount *devices,
 
1985
        int isInput )
 
1986
{
 
1987
    PaError result = paNoError;
 
1988
    MMRESULT mmresult;
 
1989
    WAVEHDR *deviceWaveHeaders;
 
1990
    signed int i, j;
 
1991
 
 
1992
    /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
 
1993
        has already been called to zero some fields */
 
1994
 
 
1995
 
 
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 )
 
2000
    {
 
2001
        result = paInsufficientMemory;
 
2002
        goto error;
 
2003
    }
 
2004
 
 
2005
    for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
 
2006
        handlesAndBuffers->waveHeaders[i] = 0;
 
2007
 
 
2008
    handlesAndBuffers->bufferCount = hostBufferCount;
 
2009
 
 
2010
    for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
 
2011
    {
 
2012
        int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) *
 
2013
                framesPerHostBuffer * devices[i].channelCount;
 
2014
        if( bufferBytes < 0 )
 
2015
        {
 
2016
            result = paInternalError;
 
2017
            goto error;
 
2018
        }
 
2019
 
 
2020
        /* Allocate an array of wave headers for device i */
 
2021
        deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount );
 
2022
        if( !deviceWaveHeaders )
 
2023
        {
 
2024
            result = paInsufficientMemory;
 
2025
            goto error;
 
2026
        }
 
2027
 
 
2028
        for( j=0; j < (signed int)hostBufferCount; ++j )
 
2029
            deviceWaveHeaders[j].lpData = 0;
 
2030
 
 
2031
        handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders;
 
2032
 
 
2033
        /* Allocate a buffer for each wave header */
 
2034
        for( j=0; j < (signed int)hostBufferCount; ++j )
 
2035
        {
 
2036
            deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes );
 
2037
            if( !deviceWaveHeaders[j].lpData )
 
2038
            {
 
2039
                result = paInsufficientMemory;
 
2040
                goto error;
 
2041
            }
 
2042
            deviceWaveHeaders[j].dwBufferLength = bufferBytes;
 
2043
            deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */
 
2044
 
 
2045
            if( isInput )
 
2046
            {
 
2047
                mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
 
2048
                if( mmresult != MMSYSERR_NOERROR )
 
2049
                {
 
2050
                    result = paUnanticipatedHostError;
 
2051
                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
2052
                    goto error;
 
2053
                }
 
2054
            }
 
2055
            else /* output */
 
2056
            {
 
2057
                mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
 
2058
                if( mmresult != MMSYSERR_NOERROR )
 
2059
                {
 
2060
                    result = paUnanticipatedHostError;
 
2061
                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
2062
                    goto error;
 
2063
                }
 
2064
            }
 
2065
            deviceWaveHeaders[j].dwUser = devices[i].channelCount;
 
2066
        }
 
2067
    }
 
2068
 
 
2069
    return result;
 
2070
 
 
2071
error:
 
2072
    TerminateWaveHeaders( handlesAndBuffers, isInput );
 
2073
 
 
2074
    return result;
 
2075
}
 
2076
 
 
2077
 
 
2078
static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput )
 
2079
{
 
2080
    signed int i, j;
 
2081
    WAVEHDR *deviceWaveHeaders;
 
2082
 
 
2083
    if( handlesAndBuffers->waveHeaders )
 
2084
    {
 
2085
        for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
 
2086
        {
 
2087
            deviceWaveHeaders = handlesAndBuffers->waveHeaders[i];  /* wave headers for device i */
 
2088
            if( deviceWaveHeaders )
 
2089
            {
 
2090
                for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j )
 
2091
                {
 
2092
                    if( deviceWaveHeaders[j].lpData )
 
2093
                    {
 
2094
                        if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF )
 
2095
                        {
 
2096
                            if( isInput )
 
2097
                                waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
 
2098
                            else
 
2099
                                waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
 
2100
                        }
 
2101
 
 
2102
                        PaUtil_FreeMemory( deviceWaveHeaders[j].lpData );
 
2103
                    }
 
2104
                }
 
2105
 
 
2106
                PaUtil_FreeMemory( deviceWaveHeaders );
 
2107
            }
 
2108
        }
 
2109
 
 
2110
        PaUtil_FreeMemory( handlesAndBuffers->waveHeaders );
 
2111
        handlesAndBuffers->waveHeaders = 0;
 
2112
    }
 
2113
}
 
2114
 
 
2115
 
 
2116
 
 
2117
/* PaWinMmeStream - a stream data structure specifically for this implementation */
 
2118
/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */
 
2119
struct PaWinMmeStream
 
2120
{
 
2121
    PaUtilStreamRepresentation streamRepresentation;
 
2122
    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
 
2123
    PaUtilBufferProcessor bufferProcessor;
 
2124
 
 
2125
    int primeStreamUsingCallback;
 
2126
 
 
2127
    PaWinMmeSingleDirectionHandlesAndBuffers input;
 
2128
    PaWinMmeSingleDirectionHandlesAndBuffers output;
 
2129
 
 
2130
    /* Processing thread management -------------- */
 
2131
    HANDLE abortEvent;
 
2132
    HANDLE processingThread;
 
2133
    PA_THREAD_ID processingThreadId;
 
2134
 
 
2135
    char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
 
2136
    int processingThreadPriority;
 
2137
    int highThreadPriority;
 
2138
    int throttledThreadPriority;
 
2139
    unsigned long throttledSleepMsecs;
 
2140
 
 
2141
    int isStopped;
 
2142
    volatile int isActive;
 
2143
    volatile int stopProcessing; /* stop thread once existing buffers have been returned */
 
2144
    volatile int abortProcessing; /* stop thread immediately */
 
2145
 
 
2146
    DWORD allBuffersDurationMs; /* used to calculate timeouts */
 
2147
};
 
2148
 
 
2149
/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
 
2150
 
 
2151
static PaError ValidateWinMmeSpecificStreamInfo(
 
2152
        const PaStreamParameters *streamParameters,
 
2153
        const PaWinMmeStreamInfo *streamInfo,
 
2154
        unsigned long *winMmeSpecificFlags,
 
2155
        char *throttleProcessingThreadOnOverload,
 
2156
        unsigned long *deviceCount )
 
2157
{
 
2158
        if( streamInfo )
 
2159
        {
 
2160
            if( streamInfo->size != sizeof( PaWinMmeStreamInfo )
 
2161
                    || streamInfo->version != 1 )
 
2162
            {
 
2163
                return paIncompatibleHostApiSpecificStreamInfo;
 
2164
            }
 
2165
 
 
2166
        *winMmeSpecificFlags = streamInfo->flags;
 
2167
 
 
2168
            if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
 
2169
                *throttleProcessingThreadOnOverload = 0;
 
2170
 
 
2171
            if( streamInfo->flags & paWinMmeUseMultipleDevices )
 
2172
            {
 
2173
                if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
 
2174
                    return paInvalidDevice;
 
2175
 
 
2176
                        *deviceCount = streamInfo->deviceCount;
 
2177
                }
 
2178
        }
 
2179
 
 
2180
        return paNoError;
 
2181
}
 
2182
 
 
2183
static PaError RetrieveDevicesFromStreamParameters(
 
2184
        struct PaUtilHostApiRepresentation *hostApi,
 
2185
        const PaStreamParameters *streamParameters,
 
2186
        const PaWinMmeStreamInfo *streamInfo,
 
2187
        PaWinMmeDeviceAndChannelCount *devices,
 
2188
        unsigned long deviceCount )
 
2189
{
 
2190
    PaError result = paNoError;
 
2191
    unsigned int i;
 
2192
    int totalChannelCount;
 
2193
    PaDeviceIndex hostApiDevice;
 
2194
 
 
2195
        if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
 
2196
        {
 
2197
                totalChannelCount = 0;
 
2198
            for( i=0; i < deviceCount; ++i )
 
2199
            {
 
2200
                /* validate that the device number is within range */
 
2201
                result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice,
 
2202
                                streamInfo->devices[i].device, hostApi );
 
2203
                if( result != paNoError )
 
2204
                    return result;
 
2205
 
 
2206
                devices[i].device = hostApiDevice;
 
2207
                devices[i].channelCount = streamInfo->devices[i].channelCount;
 
2208
 
 
2209
                totalChannelCount += devices[i].channelCount;
 
2210
            }
 
2211
 
 
2212
            if( totalChannelCount != streamParameters->channelCount )
 
2213
            {
 
2214
                /* channelCount must match total channels specified by multiple devices */
 
2215
                return paInvalidChannelCount; /* REVIEW use of this error code */
 
2216
            }
 
2217
        }
 
2218
        else
 
2219
        {
 
2220
            devices[0].device = streamParameters->device;
 
2221
            devices[0].channelCount = streamParameters->channelCount;
 
2222
        }
 
2223
 
 
2224
    return result;
 
2225
}
 
2226
 
 
2227
static PaError ValidateInputChannelCounts(
 
2228
        struct PaUtilHostApiRepresentation *hostApi,
 
2229
        PaWinMmeDeviceAndChannelCount *devices,
 
2230
        unsigned long deviceCount )
 
2231
{
 
2232
    unsigned int i;
 
2233
    PaWinMmeDeviceInfo *inputDeviceInfo;
 
2234
    PaError paerror;
 
2235
 
 
2236
        for( i=0; i < deviceCount; ++i )
 
2237
        {
 
2238
        if( devices[i].channelCount < 1 )
 
2239
                return paInvalidChannelCount;
 
2240
 
 
2241
        inputDeviceInfo =
 
2242
                (PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
 
2243
 
 
2244
        paerror = IsInputChannelCountSupported( inputDeviceInfo, devices[i].channelCount );
 
2245
        if( paerror != paNoError )
 
2246
            return paerror;
 
2247
        }
 
2248
 
 
2249
    return paNoError;
 
2250
}
 
2251
 
 
2252
static PaError ValidateOutputChannelCounts(
 
2253
        struct PaUtilHostApiRepresentation *hostApi,
 
2254
        PaWinMmeDeviceAndChannelCount *devices,
 
2255
        unsigned long deviceCount )
 
2256
{
 
2257
    unsigned int i;
 
2258
    PaWinMmeDeviceInfo *outputDeviceInfo;
 
2259
    PaError paerror;
 
2260
 
 
2261
        for( i=0; i < deviceCount; ++i )
 
2262
        {
 
2263
        if( devices[i].channelCount < 1 )
 
2264
                return paInvalidChannelCount;
 
2265
 
 
2266
        outputDeviceInfo =
 
2267
                (PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
 
2268
 
 
2269
        paerror = IsOutputChannelCountSupported( outputDeviceInfo, devices[i].channelCount );
 
2270
        if( paerror != paNoError )
 
2271
            return paerror;
 
2272
        }
 
2273
 
 
2274
    return paNoError;
 
2275
}
 
2276
 
 
2277
 
 
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) )
 
2283
 
 
2284
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
 
2285
                           PaStream** s,
 
2286
                           const PaStreamParameters *inputParameters,
 
2287
                           const PaStreamParameters *outputParameters,
 
2288
                           double sampleRate,
 
2289
                           unsigned long framesPerBuffer,
 
2290
                           PaStreamFlags streamFlags,
 
2291
                           PaStreamCallback *streamCallback,
 
2292
                           void *userData )
 
2293
{
 
2294
    PaError result;
 
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;
 
2317
 
 
2318
 
 
2319
    if( inputParameters )
 
2320
    {
 
2321
                inputChannelCount = inputParameters->channelCount;
 
2322
        inputSampleFormat = inputParameters->sampleFormat;
 
2323
        suggestedInputLatency = inputParameters->suggestedLatency;
 
2324
 
 
2325
        inputDeviceCount = 1;
 
2326
 
 
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;
 
2334
 
 
2335
                inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount );
 
2336
        if( !inputDevices ) return paInsufficientMemory;
 
2337
 
 
2338
                result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount );
 
2339
                if( result != paNoError ) return result;
 
2340
 
 
2341
                result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount );
 
2342
                if( result != paNoError ) return result;
 
2343
 
 
2344
        hostInputSampleFormat =
 
2345
            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
 
2346
 
 
2347
        if( inputDeviceCount != 1 ){
 
2348
            /* always use direct speakers when using multi-device multichannel mode */
 
2349
            inputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
 
2350
        }
 
2351
        else
 
2352
        {
 
2353
            if( inputStreamInfo && inputStreamInfo->flags & paWinMmeUseChannelMask )
 
2354
                inputChannelMask = inputStreamInfo->channelMask;
 
2355
            else
 
2356
                inputChannelMask = PaWin_DefaultChannelMask( inputDevices[0].channelCount );
 
2357
        }
 
2358
        }
 
2359
    else
 
2360
    {
 
2361
        inputChannelCount = 0;
 
2362
        inputSampleFormat = 0;
 
2363
        suggestedInputLatency = 0.;
 
2364
        inputStreamInfo = 0;
 
2365
        hostInputSampleFormat = 0;
 
2366
    }
 
2367
 
 
2368
 
 
2369
    if( outputParameters )
 
2370
    {
 
2371
        outputChannelCount = outputParameters->channelCount;
 
2372
        outputSampleFormat = outputParameters->sampleFormat;
 
2373
        suggestedOutputLatency = outputParameters->suggestedLatency;
 
2374
 
 
2375
        outputDeviceCount = 1;
 
2376
 
 
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;
 
2384
 
 
2385
                outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount );
 
2386
        if( !outputDevices ) return paInsufficientMemory;
 
2387
 
 
2388
                result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount );
 
2389
                if( result != paNoError ) return result;
 
2390
 
 
2391
                result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount );
 
2392
                if( result != paNoError ) return result;
 
2393
 
 
2394
        hostOutputSampleFormat =
 
2395
            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
 
2396
 
 
2397
        if( outputDeviceCount != 1 ){
 
2398
            /* always use direct speakers when using multi-device multichannel mode */
 
2399
            outputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
 
2400
        }
 
2401
        else
 
2402
        {
 
2403
            if( outputStreamInfo && outputStreamInfo->flags & paWinMmeUseChannelMask )
 
2404
                outputChannelMask = outputStreamInfo->channelMask;
 
2405
            else
 
2406
                outputChannelMask = PaWin_DefaultChannelMask( outputDevices[0].channelCount );
 
2407
        }
 
2408
    }
 
2409
    else
 
2410
    {
 
2411
        outputChannelCount = 0;
 
2412
        outputSampleFormat = 0;
 
2413
        outputStreamInfo = 0;
 
2414
        hostOutputSampleFormat = 0;
 
2415
        suggestedOutputLatency = 0.;
 
2416
    }
 
2417
 
 
2418
 
 
2419
    /*
 
2420
        IMPLEMENT ME:
 
2421
            - alter sampleRate to a close allowable rate if possible / necessary
 
2422
    */
 
2423
 
 
2424
 
 
2425
    /* validate platform specific flags */
 
2426
    if( (streamFlags & paPlatformSpecificFlags) != 0 )
 
2427
        return paInvalidFlag; /* unexpected platform specific flag */
 
2428
 
 
2429
 
 
2430
    /* always disable clipping and dithering if we are outputting a raw spdif stream */
 
2431
    if( (winMmeSpecificOutputFlags & paWinMmeWaveFormatDolbyAc3Spdif)
 
2432
            || (winMmeSpecificOutputFlags & paWinMmeWaveFormatWmaSpdif) ){
 
2433
 
 
2434
        streamFlags = streamFlags | paClipOff | paDitherOff;
 
2435
    }
 
2436
 
 
2437
 
 
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;
 
2444
 
 
2445
 
 
2446
    stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) );
 
2447
    if( !stream )
 
2448
    {
 
2449
        result = paInsufficientMemory;
 
2450
        goto error;
 
2451
    }
 
2452
 
 
2453
    InitializeSingleDirectionHandlesAndBuffers( &stream->input );
 
2454
    InitializeSingleDirectionHandlesAndBuffers( &stream->output );
 
2455
 
 
2456
    stream->abortEvent = 0;
 
2457
    stream->processingThread = 0;
 
2458
 
 
2459
    stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload;
 
2460
 
 
2461
    PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
 
2462
                                           ( (streamCallback)
 
2463
                                            ? &winMmeHostApi->callbackStreamInterface
 
2464
                                            : &winMmeHostApi->blockingStreamInterface ),
 
2465
                                           streamCallback, userData );
 
2466
    streamRepresentationIsInitialized = 1;
 
2467
 
 
2468
    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
 
2469
 
 
2470
 
 
2471
    if( inputParameters && outputParameters ) /* full duplex */
 
2472
    {
 
2473
        if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
 
2474
        {
 
2475
            assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
 
2476
 
 
2477
            framesPerBufferProcessorCall = framesPerHostInputBuffer;
 
2478
        }
 
2479
        else
 
2480
        {
 
2481
            assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
 
2482
 
 
2483
            framesPerBufferProcessorCall = framesPerHostOutputBuffer;
 
2484
        }
 
2485
    }
 
2486
    else if( inputParameters )
 
2487
    {
 
2488
        framesPerBufferProcessorCall = framesPerHostInputBuffer;
 
2489
    }
 
2490
    else if( outputParameters )
 
2491
    {
 
2492
        framesPerBufferProcessorCall = framesPerHostOutputBuffer;
 
2493
    }
 
2494
 
 
2495
    stream->input.framesPerBuffer = framesPerHostInputBuffer;
 
2496
    stream->output.framesPerBuffer = framesPerHostOutputBuffer;
 
2497
 
 
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;
 
2505
 
 
2506
    bufferProcessorIsInitialized = 1;
 
2507
 
 
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;
 
2515
 
 
2516
    stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
 
2517
 
 
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);
 
2523
 
 
2524
    stream->isStopped = 1;
 
2525
    stream->isActive = 0;
 
2526
 
 
2527
 
 
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.
 
2531
    */
 
2532
 
 
2533
    if( inputParameters )
 
2534
    {
 
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;
 
2540
    }
 
2541
 
 
2542
    if( outputParameters )
 
2543
    {
 
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;
 
2549
    }
 
2550
 
 
2551
    if( inputParameters )
 
2552
    {
 
2553
        result = InitializeWaveHeaders( &stream->input, hostInputBufferCount,
 
2554
                hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ );
 
2555
        if( result != paNoError ) goto error;
 
2556
    }
 
2557
 
 
2558
    if( outputParameters )
 
2559
    {
 
2560
        result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount,
 
2561
                hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ );
 
2562
        if( result != paNoError ) goto error;
 
2563
 
 
2564
        stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate);
 
2565
    }
 
2566
    else
 
2567
    {
 
2568
        stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
 
2569
    }
 
2570
 
 
2571
 
 
2572
    if( streamCallback )
 
2573
    {
 
2574
        /* abort event is only needed for callback streams */
 
2575
        result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL );
 
2576
        if( result != paNoError ) goto error;
 
2577
    }
 
2578
 
 
2579
    *s = (PaStream*)stream;
 
2580
 
 
2581
    return result;
 
2582
 
 
2583
error:
 
2584
 
 
2585
    if( stream )
 
2586
    {
 
2587
        if( stream->abortEvent )
 
2588
            CloseHandle( stream->abortEvent );
 
2589
 
 
2590
        TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
 
2591
        TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
 
2592
 
 
2593
        TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ );
 
2594
        TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ );
 
2595
 
 
2596
        if( bufferProcessorIsInitialized )
 
2597
            PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
 
2598
 
 
2599
        if( streamRepresentationIsInitialized )
 
2600
            PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
 
2601
 
 
2602
        PaUtil_FreeMemory( stream );
 
2603
    }
 
2604
 
 
2605
    return result;
 
2606
}
 
2607
 
 
2608
 
 
2609
/* return non-zero if all current buffers are done */
 
2610
static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
 
2611
{
 
2612
    unsigned int i;
 
2613
 
 
2614
    for( i=0; i < deviceCount; ++i )
 
2615
    {
 
2616
        if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
 
2617
        {
 
2618
            return 0;
 
2619
        }
 
2620
    }
 
2621
 
 
2622
    return 1;
 
2623
}
 
2624
 
 
2625
static int CurrentInputBuffersAreDone( PaWinMmeStream *stream )
 
2626
{
 
2627
    return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex );
 
2628
}
 
2629
 
 
2630
static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream )
 
2631
{
 
2632
    return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex );
 
2633
}
 
2634
 
 
2635
 
 
2636
/* return non-zero if any buffers are queued */
 
2637
static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
 
2638
{
 
2639
    unsigned int i, j;
 
2640
 
 
2641
    if( handlesAndBuffers->waveHandles )
 
2642
    {
 
2643
        for( i=0; i < handlesAndBuffers->bufferCount; ++i )
 
2644
        {
 
2645
            for( j=0; j < handlesAndBuffers->deviceCount; ++j )
 
2646
            {
 
2647
                if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) )
 
2648
                {
 
2649
                    return 0;
 
2650
                }
 
2651
            }
 
2652
        }
 
2653
    }
 
2654
 
 
2655
    return 1;
 
2656
}
 
2657
 
 
2658
 
 
2659
#define PA_CIRCULAR_INCREMENT_( current, max )\
 
2660
    ( (((current) + 1) >= (max)) ? (0) : (current+1) )
 
2661
 
 
2662
#define PA_CIRCULAR_DECREMENT_( current, max )\
 
2663
    ( ((current) == 0) ? ((max)-1) : (current-1) )
 
2664
 
 
2665
 
 
2666
static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
 
2667
{
 
2668
    signed long result = 0;
 
2669
    unsigned int i;
 
2670
 
 
2671
    if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
 
2672
    {
 
2673
        /* we could calculate the following in O(1) if we kept track of the
 
2674
            last done buffer */
 
2675
        result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer;
 
2676
 
 
2677
        i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount );
 
2678
        while( i != handlesAndBuffers->currentBufferIndex )
 
2679
        {
 
2680
            if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) )
 
2681
            {
 
2682
                result += handlesAndBuffers->framesPerBuffer;
 
2683
                i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount );
 
2684
            }
 
2685
            else
 
2686
                break;
 
2687
        }
 
2688
    }
 
2689
 
 
2690
    return result;
 
2691
}
 
2692
 
 
2693
 
 
2694
static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
 
2695
{
 
2696
    PaError result = paNoError;
 
2697
    MMRESULT mmresult;
 
2698
    unsigned int i;
 
2699
 
 
2700
    for( i=0; i < stream->input.deviceCount; ++i )
 
2701
    {
 
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 ],
 
2705
                                    sizeof(WAVEHDR) );
 
2706
        if( mmresult != MMSYSERR_NOERROR )
 
2707
        {
 
2708
            result = paUnanticipatedHostError;
 
2709
            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
2710
        }
 
2711
    }
 
2712
 
 
2713
    stream->input.currentBufferIndex =
 
2714
            PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount );
 
2715
 
 
2716
    stream->input.framesUsedInCurrentBuffer = 0;
 
2717
 
 
2718
    return result;
 
2719
}
 
2720
 
 
2721
 
 
2722
static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream )
 
2723
{
 
2724
    PaError result = paNoError;
 
2725
    MMRESULT mmresult;
 
2726
    unsigned int i;
 
2727
 
 
2728
    for( i=0; i < stream->output.deviceCount; ++i )
 
2729
    {
 
2730
        mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i],
 
2731
                                 &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ],
 
2732
                                 sizeof(WAVEHDR) );
 
2733
        if( mmresult != MMSYSERR_NOERROR )
 
2734
        {
 
2735
            result = paUnanticipatedHostError;
 
2736
            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
 
2737
        }
 
2738
    }
 
2739
 
 
2740
    stream->output.currentBufferIndex =
 
2741
            PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
 
2742
 
 
2743
    stream->output.framesUsedInCurrentBuffer = 0;
 
2744
 
 
2745
    return result;
 
2746
}
 
2747
 
 
2748
 
 
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 )
 
2752
{
 
2753
    PaError result = paNoError;
 
2754
    unsigned int i;
 
2755
 
 
2756
    for( i=0; i < stream->input.bufferCount - 1; ++i )
 
2757
    {
 
2758
        result = AdvanceToNextInputBuffer( stream );
 
2759
        if( result != paNoError )
 
2760
            break;
 
2761
    }
 
2762
 
 
2763
    return result;
 
2764
}
 
2765
 
 
2766
 
 
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.
 
2769
*/
 
2770
static PaError CatchUpOutputBuffers( PaWinMmeStream *stream )
 
2771
{
 
2772
    PaError result = paNoError;
 
2773
    unsigned int i, j;
 
2774
    unsigned int previousBufferIndex =
 
2775
            PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
 
2776
 
 
2777
    for( i=0; i < stream->output.bufferCount - 1; ++i )
 
2778
    {
 
2779
        for( j=0; j < stream->output.deviceCount; ++j )
 
2780
        {
 
2781
            if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData
 
2782
                    != stream->output.waveHeaders[j][ previousBufferIndex ].lpData )
 
2783
            {
 
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 );
 
2787
            }
 
2788
        }
 
2789
 
 
2790
        result = AdvanceToNextOutputBuffer( stream );
 
2791
        if( result != paNoError )
 
2792
            break;
 
2793
    }
 
2794
 
 
2795
    return result;
 
2796
}
 
2797
 
 
2798
 
 
2799
PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
 
2800
{
 
2801
    PaWinMmeStream *stream = (PaWinMmeStream *)pArg;
 
2802
    HANDLE events[3];
 
2803
    int eventCount = 0;
 
2804
    DWORD result = paNoError;
 
2805
    DWORD waitResult;
 
2806
    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
 
2807
    int hostBuffersAvailable;
 
2808
    signed int hostInputBufferIndex, hostOutputBufferIndex;
 
2809
    PaStreamCallbackFlags statusFlags;
 
2810
    int callbackResult;
 
2811
    int done = 0;
 
2812
    unsigned int channel, i;
 
2813
    unsigned long framesProcessed;
 
2814
 
 
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;
 
2821
 
 
2822
    statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
 
2823
 
 
2824
    /* loop until something causes us to stop */
 
2825
    do{
 
2826
        /* wait for MME to signal that a buffer is available, or for
 
2827
            the PA abort event to be signaled.
 
2828
 
 
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
 
2836
          underflow/overflow.
 
2837
        */
 
2838
        waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout );
 
2839
        if( waitResult == WAIT_FAILED )
 
2840
        {
 
2841
            result = paUnanticipatedHostError;
 
2842
            /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread */
 
2843
            done = 1;
 
2844
        }
 
2845
        else if( waitResult == WAIT_TIMEOUT )
 
2846
        {
 
2847
            /* if a timeout is encountered, continue */
 
2848
        }
 
2849
 
 
2850
        if( stream->abortProcessing )
 
2851
        {
 
2852
            /* Pa_AbortStream() has been called, stop processing immediately */
 
2853
            done = 1;
 
2854
        }
 
2855
        else if( stream->stopProcessing )
 
2856
        {
 
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
 
2860
                is input-only.
 
2861
            */
 
2862
 
 
2863
            if( PA_IS_OUTPUT_STREAM_(stream) )
 
2864
            {
 
2865
                if( NoBuffersAreQueued( &stream->output ) )
 
2866
                    done = 1; /* Will cause thread to return. */
 
2867
            }
 
2868
            else
 
2869
            {
 
2870
                /* input only stream */
 
2871
                done = 1; /* Will cause thread to return. */
 
2872
            }
 
2873
        }
 
2874
        else
 
2875
        {
 
2876
            hostBuffersAvailable = 1;
 
2877
 
 
2878
            /* process all available host buffers */
 
2879
            do
 
2880
            {
 
2881
                hostInputBufferIndex = -1;
 
2882
                hostOutputBufferIndex = -1;
 
2883
 
 
2884
                if( PA_IS_INPUT_STREAM_(stream) )
 
2885
                {
 
2886
                    if( CurrentInputBuffersAreDone( stream ) )
 
2887
                    {
 
2888
                        if( NoBuffersAreQueued( &stream->input ) )
 
2889
                        {
 
2890
                            /** @todo
 
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
 
2895
                               stream.
 
2896
 
 
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.
 
2900
                            */
 
2901
                            result = CatchUpInputBuffers( stream );
 
2902
                            if( result != paNoError )
 
2903
                                done = 1;
 
2904
 
 
2905
                            statusFlags |= paInputOverflow;
 
2906
                        }
 
2907
 
 
2908
                        hostInputBufferIndex = stream->input.currentBufferIndex;
 
2909
                    }
 
2910
                }
 
2911
 
 
2912
                if( PA_IS_OUTPUT_STREAM_(stream) )
 
2913
                {
 
2914
                    if( CurrentOutputBuffersAreDone( stream ) )
 
2915
                    {
 
2916
                        /* ok, we have an output buffer */
 
2917
 
 
2918
                        if( NoBuffersAreQueued( &stream->output ) )
 
2919
                        {
 
2920
                            /*
 
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
 
2923
                            buffers.
 
2924
 
 
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.
 
2931
                            */
 
2932
 
 
2933
                            result = CatchUpOutputBuffers( stream );
 
2934
                            if( result != paNoError )
 
2935
                                done = 1;
 
2936
 
 
2937
                            statusFlags |= paOutputUnderflow;
 
2938
                        }
 
2939
 
 
2940
                        hostOutputBufferIndex = stream->output.currentBufferIndex;
 
2941
                    }
 
2942
                }
 
2943
 
 
2944
 
 
2945
                if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
 
2946
                        (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
 
2947
                {
 
2948
                    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
 
2949
 
 
2950
 
 
2951
                    if( PA_IS_OUTPUT_STREAM_(stream) )
 
2952
                    {
 
2953
                        /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime
 
2954
                            from the current wave out position */
 
2955
                        MMTIME mmtime;
 
2956
                        double timeBeforeGetPosition, timeAfterGetPosition;
 
2957
                        double time;
 
2958
                        long framesInBufferRing;
 
2959
                        long writePosition;
 
2960
                        long playbackPosition;
 
2961
                        HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
 
2962
 
 
2963
                        mmtime.wType = TIME_SAMPLES;
 
2964
                        timeBeforeGetPosition = PaUtil_GetTime();
 
2965
                        waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
 
2966
                        timeAfterGetPosition = PaUtil_GetTime();
 
2967
 
 
2968
                        timeInfo.currentTime = timeAfterGetPosition;
 
2969
 
 
2970
                        /* approximate time at which wave out position was measured
 
2971
                            as half way between timeBeforeGetPosition and timeAfterGetPosition */
 
2972
                        time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
 
2973
 
 
2974
                        framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
 
2975
                        playbackPosition = mmtime.u.sample % framesInBufferRing;
 
2976
 
 
2977
                        writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
 
2978
                                + stream->output.framesUsedInCurrentBuffer;
 
2979
 
 
2980
                        if( playbackPosition >= writePosition ){
 
2981
                            timeInfo.outputBufferDacTime =
 
2982
                                    time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
 
2983
                        }else{
 
2984
                            timeInfo.outputBufferDacTime =
 
2985
                                    time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod );
 
2986
                        }
 
2987
                    }
 
2988
 
 
2989
 
 
2990
                    PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
 
2991
 
 
2992
                    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags  );
 
2993
 
 
2994
                    /* reset status flags once they have been passed to the buffer processor */
 
2995
                    statusFlags = 0;
 
2996
 
 
2997
                    if( PA_IS_INPUT_STREAM_(stream) )
 
2998
                    {
 
2999
                        PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
 
3000
 
 
3001
                        channel = 0;
 
3002
                        for( i=0; i<stream->input.deviceCount; ++i )
 
3003
                        {
 
3004
                             /* we have stored the number of channels in the buffer in dwUser */
 
3005
                            int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
 
3006
 
 
3007
                            PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
 
3008
                                    stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
 
3009
                                        stream->input.framesUsedInCurrentBuffer * channelCount *
 
3010
                                        stream->bufferProcessor.bytesPerHostInputSample,
 
3011
                                    channelCount );
 
3012
 
 
3013
 
 
3014
                            channel += channelCount;
 
3015
                        }
 
3016
                    }
 
3017
 
 
3018
                    if( PA_IS_OUTPUT_STREAM_(stream) )
 
3019
                    {
 
3020
                        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
 
3021
 
 
3022
                        channel = 0;
 
3023
                        for( i=0; i<stream->output.deviceCount; ++i )
 
3024
                        {
 
3025
                            /* we have stored the number of channels in the buffer in dwUser */
 
3026
                            int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
 
3027
 
 
3028
                            PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
 
3029
                                    stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
 
3030
                                        stream->output.framesUsedInCurrentBuffer * channelCount *
 
3031
                                        stream->bufferProcessor.bytesPerHostOutputSample,
 
3032
                                    channelCount );
 
3033
 
 
3034
                            channel += channelCount;
 
3035
                        }
 
3036
                    }
 
3037
 
 
3038
                    callbackResult = paContinue;
 
3039
                    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
 
3040
 
 
3041
                    stream->input.framesUsedInCurrentBuffer += framesProcessed;
 
3042
                    stream->output.framesUsedInCurrentBuffer += framesProcessed;
 
3043
 
 
3044
                    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
 
3045
 
 
3046
                    if( callbackResult == paContinue )
 
3047
                    {
 
3048
                        /* nothing special to do */
 
3049
                    }
 
3050
                    else if( callbackResult == paAbort )
 
3051
                    {
 
3052
                        stream->abortProcessing = 1;
 
3053
                        done = 1;
 
3054
                        /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort */
 
3055
                        result = paNoError;
 
3056
                    }
 
3057
                    else
 
3058
                    {
 
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 */
 
3061
                        result = paNoError;
 
3062
                    }
 
3063
 
 
3064
 
 
3065
                    if( PA_IS_INPUT_STREAM_(stream)
 
3066
                            && stream->stopProcessing == 0 && stream->abortProcessing == 0
 
3067
                            && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
 
3068
                    {
 
3069
                        if( NoBuffersAreQueued( &stream->input ) )
 
3070
                        {
 
3071
                            /** @todo need to handle PaNeverDropInput here where necessary */
 
3072
                            result = CatchUpInputBuffers( stream );
 
3073
                            if( result != paNoError )
 
3074
                                done = 1;
 
3075
 
 
3076
                            statusFlags |= paInputOverflow;
 
3077
                        }
 
3078
 
 
3079
                        result = AdvanceToNextInputBuffer( stream );
 
3080
                        if( result != paNoError )
 
3081
                            done = 1;
 
3082
                    }
 
3083
 
 
3084
 
 
3085
                    if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
 
3086
                    {
 
3087
                        if( stream->stopProcessing &&
 
3088
                                stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer )
 
3089
                        {
 
3090
                            /* zero remaining samples in output output buffer and flush */
 
3091
 
 
3092
                            stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor,
 
3093
                                    stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
 
3094
 
 
3095
                            /* we send the entire buffer to the output devices, but we could
 
3096
                                just send a partial buffer, rather than zeroing the unused
 
3097
                                samples.
 
3098
                            */
 
3099
                        }
 
3100
 
 
3101
                        if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
 
3102
                        {
 
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 );
 
3107
 
 
3108
                            result = AdvanceToNextOutputBuffer( stream );
 
3109
                            if( result != paNoError )
 
3110
                                done = 1;
 
3111
 
 
3112
                            if( outputUnderflow && !done && !stream->stopProcessing )
 
3113
                            {
 
3114
                                /* Recover from underflow in the case where the
 
3115
                                    underflow occured while processing the buffer
 
3116
                                    we just finished */
 
3117
 
 
3118
                                result = CatchUpOutputBuffers( stream );
 
3119
                                if( result != paNoError )
 
3120
                                    done = 1;
 
3121
 
 
3122
                                statusFlags |= paOutputUnderflow;
 
3123
                            }
 
3124
                        }
 
3125
                    }
 
3126
 
 
3127
                    if( stream->throttleProcessingThreadOnOverload != 0 )
 
3128
                    {
 
3129
                        if( stream->stopProcessing || stream->abortProcessing )
 
3130
                        {
 
3131
                            if( stream->processingThreadPriority != stream->highThreadPriority )
 
3132
                            {
 
3133
                                SetThreadPriority( stream->processingThread, stream->highThreadPriority );
 
3134
                                stream->processingThreadPriority = stream->highThreadPriority;
 
3135
                            }
 
3136
                        }
 
3137
                        else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. )
 
3138
                        {
 
3139
                            if( stream->processingThreadPriority != stream->throttledThreadPriority )
 
3140
                            {
 
3141
                                SetThreadPriority( stream->processingThread, stream->throttledThreadPriority );
 
3142
                                stream->processingThreadPriority = stream->throttledThreadPriority;
 
3143
                            }
 
3144
 
 
3145
                            /* sleep to give other processes a go */
 
3146
                            Sleep( stream->throttledSleepMsecs );
 
3147
                        }
 
3148
                        else
 
3149
                        {
 
3150
                            if( stream->processingThreadPriority != stream->highThreadPriority )
 
3151
                            {
 
3152
                                SetThreadPriority( stream->processingThread, stream->highThreadPriority );
 
3153
                                stream->processingThreadPriority = stream->highThreadPriority;
 
3154
                            }
 
3155
                        }
 
3156
                    }
 
3157
                }
 
3158
                else
 
3159
                {
 
3160
                    hostBuffersAvailable = 0;
 
3161
                }
 
3162
            }
 
3163
            while( hostBuffersAvailable &&
 
3164
                    stream->stopProcessing == 0 &&
 
3165
                    stream->abortProcessing == 0 &&
 
3166
                    !done );
 
3167
        }
 
3168
    }
 
3169
    while( !done );
 
3170
 
 
3171
    stream->isActive = 0;
 
3172
 
 
3173
    if( stream->streamRepresentation.streamFinishedCallback != 0 )
 
3174
        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
 
3175
 
 
3176
    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
 
3177
 
 
3178
    return result;
 
3179
}
 
3180
 
 
3181
 
 
3182
/*
 
3183
    When CloseStream() is called, the multi-api layer ensures that
 
3184
    the stream has already been stopped or aborted.
 
3185
*/
 
3186
static PaError CloseStream( PaStream* s )
 
3187
{
 
3188
    PaError result;
 
3189
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3190
 
 
3191
    result = CloseHandleWithPaError( stream->abortEvent );
 
3192
    if( result != paNoError ) goto error;
 
3193
 
 
3194
    TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
 
3195
    TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
 
3196
 
 
3197
    TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
 
3198
    TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
 
3199
 
 
3200
    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
 
3201
    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
 
3202
    PaUtil_FreeMemory( stream );
 
3203
 
 
3204
error:
 
3205
    /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */
 
3206
    return result;
 
3207
}
 
3208
 
 
3209
 
 
3210
static PaError StartStream( PaStream *s )
 
3211
{
 
3212
    PaError result;
 
3213
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3214
    MMRESULT mmresult;
 
3215
    unsigned int i, j;
 
3216
    int callbackResult;
 
3217
        unsigned int channel;
 
3218
        unsigned long framesProcessed;
 
3219
        PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
 
3220
 
 
3221
    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
 
3222
 
 
3223
    if( PA_IS_INPUT_STREAM_(stream) )
 
3224
    {
 
3225
        for( i=0; i<stream->input.bufferCount; ++i )
 
3226
        {
 
3227
            for( j=0; j<stream->input.deviceCount; ++j )
 
3228
            {
 
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 )
 
3232
                {
 
3233
                    result = paUnanticipatedHostError;
 
3234
                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
3235
                    goto error;
 
3236
                }
 
3237
            }
 
3238
        }
 
3239
        stream->input.currentBufferIndex = 0;
 
3240
        stream->input.framesUsedInCurrentBuffer = 0;
 
3241
    }
 
3242
 
 
3243
    if( PA_IS_OUTPUT_STREAM_(stream) )
 
3244
    {
 
3245
        for( i=0; i<stream->output.deviceCount; ++i )
 
3246
        {
 
3247
            if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
 
3248
            {
 
3249
                result = paUnanticipatedHostError;
 
3250
                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
 
3251
                goto error;
 
3252
            }
 
3253
        }
 
3254
 
 
3255
        for( i=0; i<stream->output.bufferCount; ++i )
 
3256
        {
 
3257
            if( stream->primeStreamUsingCallback )
 
3258
            {
 
3259
 
 
3260
                stream->output.framesUsedInCurrentBuffer = 0;
 
3261
                do{
 
3262
 
 
3263
                    PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
 
3264
                            &timeInfo,
 
3265
                            paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0));
 
3266
 
 
3267
                    if( stream->input.bufferCount > 0 )
 
3268
                        PaUtil_SetNoInput( &stream->bufferProcessor );
 
3269
 
 
3270
                    PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
 
3271
 
 
3272
                    channel = 0;
 
3273
                    for( j=0; j<stream->output.deviceCount; ++j )
 
3274
                    {
 
3275
                        /* we have stored the number of channels in the buffer in dwUser */
 
3276
                        int channelCount = (int)stream->output.waveHeaders[j][i].dwUser;
 
3277
 
 
3278
                        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
 
3279
                                stream->output.waveHeaders[j][i].lpData +
 
3280
                                stream->output.framesUsedInCurrentBuffer * channelCount *
 
3281
                                stream->bufferProcessor.bytesPerHostOutputSample,
 
3282
                                channelCount );
 
3283
 
 
3284
                        /* we have stored the number of channels in the buffer in dwUser */
 
3285
                        channel += channelCount;
 
3286
                    }
 
3287
 
 
3288
                    callbackResult = paContinue;
 
3289
                    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
 
3290
                    stream->output.framesUsedInCurrentBuffer += framesProcessed;
 
3291
 
 
3292
                    if( callbackResult != paContinue )
 
3293
                    {
 
3294
                        /** @todo fix this, what do we do if callback result is non-zero during stream
 
3295
                            priming?
 
3296
 
 
3297
                            for complete: play out primed waveHeaders as usual
 
3298
                            for abort: clean up immediately.
 
3299
                       */
 
3300
                    }
 
3301
 
 
3302
                }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer );
 
3303
 
 
3304
            }
 
3305
            else
 
3306
            {
 
3307
                for( j=0; j<stream->output.deviceCount; ++j )
 
3308
                {
 
3309
                    ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
 
3310
                }
 
3311
            }
 
3312
 
 
3313
            /* we queue all channels of a single buffer frame (accross all
 
3314
                devices, because some multidevice multichannel drivers work
 
3315
                better this way */
 
3316
            for( j=0; j<stream->output.deviceCount; ++j )
 
3317
            {
 
3318
                mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) );
 
3319
                if( mmresult != MMSYSERR_NOERROR )
 
3320
                {
 
3321
                    result = paUnanticipatedHostError;
 
3322
                    PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
 
3323
                    goto error;
 
3324
                }
 
3325
            }
 
3326
        }
 
3327
        stream->output.currentBufferIndex = 0;
 
3328
        stream->output.framesUsedInCurrentBuffer = 0;
 
3329
    }
 
3330
 
 
3331
 
 
3332
    stream->isStopped = 0;
 
3333
    stream->isActive = 1;
 
3334
    stream->stopProcessing = 0;
 
3335
    stream->abortProcessing = 0;
 
3336
 
 
3337
    result = ResetEventWithPaError( stream->input.bufferEvent );
 
3338
    if( result != paNoError ) goto error;
 
3339
 
 
3340
    result = ResetEventWithPaError( stream->output.bufferEvent );
 
3341
    if( result != paNoError ) goto error;
 
3342
 
 
3343
 
 
3344
    if( stream->streamRepresentation.streamCallback )
 
3345
    {
 
3346
        /* callback stream */
 
3347
 
 
3348
        result = ResetEventWithPaError( stream->abortEvent );
 
3349
        if( result != paNoError ) goto error;
 
3350
 
 
3351
        /* Create thread that waits for audio buffers to be ready for processing. */
 
3352
        stream->processingThread = CREATE_THREAD;
 
3353
        if( !stream->processingThread )
 
3354
        {
 
3355
            result = paUnanticipatedHostError;
 
3356
            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
 
3357
            goto error;
 
3358
        }
 
3359
 
 
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;
 
3364
 
 
3365
        if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) )
 
3366
        {
 
3367
            result = paUnanticipatedHostError;
 
3368
            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
 
3369
            goto error;
 
3370
        }
 
3371
        stream->processingThreadPriority = stream->highThreadPriority;
 
3372
    }
 
3373
    else
 
3374
    {
 
3375
        /* blocking read/write stream */
 
3376
 
 
3377
    }
 
3378
 
 
3379
    if( PA_IS_INPUT_STREAM_(stream) )
 
3380
    {
 
3381
        for( i=0; i < stream->input.deviceCount; ++i )
 
3382
        {
 
3383
            mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] );
 
3384
            PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
 
3385
            if( mmresult != MMSYSERR_NOERROR )
 
3386
            {
 
3387
                result = paUnanticipatedHostError;
 
3388
                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
3389
                goto error;
 
3390
            }
 
3391
        }
 
3392
    }
 
3393
 
 
3394
    if( PA_IS_OUTPUT_STREAM_(stream) )
 
3395
    {
 
3396
        for( i=0; i < stream->output.deviceCount; ++i )
 
3397
        {
 
3398
            if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
 
3399
            {
 
3400
                result = paUnanticipatedHostError;
 
3401
                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
 
3402
                goto error;
 
3403
            }
 
3404
        }
 
3405
    }
 
3406
 
 
3407
    return result;
 
3408
 
 
3409
error:
 
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
 
3412
    */
 
3413
    return result;
 
3414
}
 
3415
 
 
3416
 
 
3417
static PaError StopStream( PaStream *s )
 
3418
{
 
3419
    PaError result = paNoError;
 
3420
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3421
    int timeout;
 
3422
    DWORD waitResult;
 
3423
    MMRESULT mmresult;
 
3424
    signed int hostOutputBufferIndex;
 
3425
    unsigned int channel, waitCount, i;
 
3426
 
 
3427
    /** @todo
 
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.
 
3432
    */
 
3433
 
 
3434
    if( stream->processingThread )
 
3435
    {
 
3436
        /* callback stream */
 
3437
 
 
3438
        /* Tell processing thread to stop generating more data and to let current data play out. */
 
3439
        stream->stopProcessing = 1;
 
3440
 
 
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_;
 
3445
 
 
3446
        PA_DEBUG(("WinMME StopStream: waiting for background thread.\n"));
 
3447
 
 
3448
        waitResult = WaitForSingleObject( stream->processingThread, timeout );
 
3449
        if( waitResult == WAIT_TIMEOUT )
 
3450
        {
 
3451
            /* try to abort */
 
3452
            stream->abortProcessing = 1;
 
3453
            SetEvent( stream->abortEvent );
 
3454
            waitResult = WaitForSingleObject( stream->processingThread, timeout );
 
3455
            if( waitResult == WAIT_TIMEOUT )
 
3456
            {
 
3457
                PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n"));
 
3458
                result = paTimedOut;
 
3459
            }
 
3460
        }
 
3461
 
 
3462
        CloseHandle( stream->processingThread );
 
3463
        stream->processingThread = NULL;
 
3464
    }
 
3465
    else
 
3466
    {
 
3467
        /* blocking read / write stream */
 
3468
 
 
3469
        if( PA_IS_OUTPUT_STREAM_(stream) )
 
3470
        {
 
3471
            if( stream->output.framesUsedInCurrentBuffer > 0 )
 
3472
            {
 
3473
                /* there are still unqueued frames in the current buffer, so flush them */
 
3474
 
 
3475
                hostOutputBufferIndex = stream->output.currentBufferIndex;
 
3476
 
 
3477
                PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
 
3478
                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
 
3479
 
 
3480
                channel = 0;
 
3481
                for( i=0; i<stream->output.deviceCount; ++i )
 
3482
                {
 
3483
                    /* we have stored the number of channels in the buffer in dwUser */
 
3484
                    int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
 
3485
 
 
3486
                    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
 
3487
                            stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
 
3488
                                stream->output.framesUsedInCurrentBuffer * channelCount *
 
3489
                                stream->bufferProcessor.bytesPerHostOutputSample,
 
3490
                            channelCount );
 
3491
 
 
3492
                    channel += channelCount;
 
3493
                }
 
3494
 
 
3495
                PaUtil_ZeroOutput( &stream->bufferProcessor,
 
3496
                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
 
3497
 
 
3498
                /* we send the entire buffer to the output devices, but we could
 
3499
                    just send a partial buffer, rather than zeroing the unused
 
3500
                    samples.
 
3501
                */
 
3502
                AdvanceToNextOutputBuffer( stream );
 
3503
            }
 
3504
 
 
3505
 
 
3506
            timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
 
3507
            if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
 
3508
                timeout = PA_MME_MIN_TIMEOUT_MSEC_;
 
3509
 
 
3510
            waitCount = 0;
 
3511
            while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
 
3512
            {
 
3513
                /* wait for MME to signal that a buffer is available */
 
3514
                waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
 
3515
                if( waitResult == WAIT_FAILED )
 
3516
                {
 
3517
                    break;
 
3518
                }
 
3519
                else if( waitResult == WAIT_TIMEOUT )
 
3520
                {
 
3521
                    /* keep waiting */
 
3522
                }
 
3523
 
 
3524
                ++waitCount;
 
3525
            }
 
3526
        }
 
3527
    }
 
3528
 
 
3529
    if( PA_IS_OUTPUT_STREAM_(stream) )
 
3530
    {
 
3531
        for( i =0; i < stream->output.deviceCount; ++i )
 
3532
        {
 
3533
            mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
 
3534
            if( mmresult != MMSYSERR_NOERROR )
 
3535
            {
 
3536
                result = paUnanticipatedHostError;
 
3537
                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
 
3538
            }
 
3539
        }
 
3540
    }
 
3541
 
 
3542
    if( PA_IS_INPUT_STREAM_(stream) )
 
3543
    {
 
3544
        for( i=0; i < stream->input.deviceCount; ++i )
 
3545
        {
 
3546
            mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
 
3547
            if( mmresult != MMSYSERR_NOERROR )
 
3548
            {
 
3549
                result = paUnanticipatedHostError;
 
3550
                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
3551
            }
 
3552
        }
 
3553
    }
 
3554
 
 
3555
    stream->isStopped = 1;
 
3556
    stream->isActive = 0;
 
3557
 
 
3558
    return result;
 
3559
}
 
3560
 
 
3561
 
 
3562
static PaError AbortStream( PaStream *s )
 
3563
{
 
3564
    PaError result = paNoError;
 
3565
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3566
    int timeout;
 
3567
    DWORD waitResult;
 
3568
    MMRESULT mmresult;
 
3569
    unsigned int i;
 
3570
 
 
3571
    /** @todo
 
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.
 
3576
    */
 
3577
 
 
3578
    if( stream->processingThread )
 
3579
    {
 
3580
        /* callback stream */
 
3581
 
 
3582
        /* Tell processing thread to abort immediately */
 
3583
        stream->abortProcessing = 1;
 
3584
        SetEvent( stream->abortEvent );
 
3585
    }
 
3586
 
 
3587
 
 
3588
    if( PA_IS_OUTPUT_STREAM_(stream) )
 
3589
    {
 
3590
        for( i =0; i < stream->output.deviceCount; ++i )
 
3591
        {
 
3592
            mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
 
3593
            if( mmresult != MMSYSERR_NOERROR )
 
3594
            {
 
3595
                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
 
3596
                return paUnanticipatedHostError;
 
3597
            }
 
3598
        }
 
3599
    }
 
3600
 
 
3601
    if( PA_IS_INPUT_STREAM_(stream) )
 
3602
    {
 
3603
        for( i=0; i < stream->input.deviceCount; ++i )
 
3604
        {
 
3605
            mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
 
3606
            if( mmresult != MMSYSERR_NOERROR )
 
3607
            {
 
3608
                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
 
3609
                return paUnanticipatedHostError;
 
3610
            }
 
3611
        }
 
3612
    }
 
3613
 
 
3614
 
 
3615
    if( stream->processingThread )
 
3616
    {
 
3617
        /* callback stream */
 
3618
 
 
3619
        PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
 
3620
 
 
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_;
 
3625
 
 
3626
        waitResult = WaitForSingleObject( stream->processingThread, timeout );
 
3627
        if( waitResult == WAIT_TIMEOUT )
 
3628
        {
 
3629
            PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n"));
 
3630
            return paTimedOut;
 
3631
        }
 
3632
 
 
3633
        CloseHandle( stream->processingThread );
 
3634
        stream->processingThread = NULL;
 
3635
    }
 
3636
 
 
3637
    stream->isStopped = 1;
 
3638
    stream->isActive = 0;
 
3639
 
 
3640
    return result;
 
3641
}
 
3642
 
 
3643
 
 
3644
static PaError IsStreamStopped( PaStream *s )
 
3645
{
 
3646
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3647
 
 
3648
    return stream->isStopped;
 
3649
}
 
3650
 
 
3651
 
 
3652
static PaError IsStreamActive( PaStream *s )
 
3653
{
 
3654
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3655
 
 
3656
    return stream->isActive;
 
3657
}
 
3658
 
 
3659
 
 
3660
static PaTime GetStreamTime( PaStream *s )
 
3661
{
 
3662
    (void) s; /* unused parameter */
 
3663
 
 
3664
    return PaUtil_GetTime();
 
3665
}
 
3666
 
 
3667
 
 
3668
static double GetStreamCpuLoad( PaStream* s )
 
3669
{
 
3670
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3671
 
 
3672
    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
 
3673
}
 
3674
 
 
3675
 
 
3676
/*
 
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.
 
3680
*/
 
3681
 
 
3682
static PaError ReadStream( PaStream* s,
 
3683
                           void *buffer,
 
3684
                           unsigned long frames )
 
3685
{
 
3686
    PaError result = paNoError;
 
3687
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3688
    void *userBuffer;
 
3689
    unsigned long framesRead = 0;
 
3690
    unsigned long framesProcessed;
 
3691
    signed int hostInputBufferIndex;
 
3692
    DWORD waitResult;
 
3693
    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
 
3694
    unsigned int channel, i;
 
3695
 
 
3696
    if( PA_IS_INPUT_STREAM_(stream) )
 
3697
    {
 
3698
        /* make a local copy of the user buffer pointer(s). this is necessary
 
3699
            because PaUtil_CopyInput() advances these pointers every time
 
3700
            it is called.
 
3701
        */
 
3702
        if( stream->bufferProcessor.userInputIsInterleaved )
 
3703
        {
 
3704
            userBuffer = buffer;
 
3705
        }
 
3706
        else
 
3707
        {
 
3708
            userBuffer = (void*)alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
 
3709
            if( !userBuffer )
 
3710
                return paInsufficientMemory;
 
3711
            for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
 
3712
                ((void**)userBuffer)[i] = ((void**)buffer)[i];
 
3713
        }
 
3714
 
 
3715
        do{
 
3716
            if( CurrentInputBuffersAreDone( stream ) )
 
3717
            {
 
3718
                if( NoBuffersAreQueued( &stream->input ) )
 
3719
                {
 
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? */
 
3723
 
 
3724
                    result = paInputOverflowed;
 
3725
                }
 
3726
 
 
3727
                hostInputBufferIndex = stream->input.currentBufferIndex;
 
3728
 
 
3729
                PaUtil_SetInputFrameCount( &stream->bufferProcessor,
 
3730
                        stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
 
3731
 
 
3732
                channel = 0;
 
3733
                for( i=0; i<stream->input.deviceCount; ++i )
 
3734
                {
 
3735
                    /* we have stored the number of channels in the buffer in dwUser */
 
3736
                    int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
 
3737
 
 
3738
                    PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
 
3739
                            stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
 
3740
                                stream->input.framesUsedInCurrentBuffer * channelCount *
 
3741
                                stream->bufferProcessor.bytesPerHostInputSample,
 
3742
                            channelCount );
 
3743
 
 
3744
                    channel += channelCount;
 
3745
                }
 
3746
 
 
3747
                framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
 
3748
 
 
3749
                stream->input.framesUsedInCurrentBuffer += framesProcessed;
 
3750
                if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
 
3751
                {
 
3752
                    result = AdvanceToNextInputBuffer( stream );
 
3753
                    if( result != paNoError )
 
3754
                        break;
 
3755
                }
 
3756
 
 
3757
                framesRead += framesProcessed;
 
3758
 
 
3759
            }else{
 
3760
                /* wait for MME to signal that a buffer is available */
 
3761
                waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout );
 
3762
                if( waitResult == WAIT_FAILED )
 
3763
                {
 
3764
                    result = paUnanticipatedHostError;
 
3765
                    break;
 
3766
                }
 
3767
                else if( waitResult == WAIT_TIMEOUT )
 
3768
                {
 
3769
                    /* if a timeout is encountered, continue,
 
3770
                        perhaps we should give up eventually
 
3771
                    */
 
3772
                }
 
3773
            }
 
3774
        }while( framesRead < frames );
 
3775
    }
 
3776
    else
 
3777
    {
 
3778
        result = paCanNotReadFromAnOutputOnlyStream;
 
3779
    }
 
3780
 
 
3781
    return result;
 
3782
}
 
3783
 
 
3784
 
 
3785
static PaError WriteStream( PaStream* s,
 
3786
                            const void *buffer,
 
3787
                            unsigned long frames )
 
3788
{
 
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;
 
3795
    DWORD waitResult;
 
3796
    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
 
3797
    unsigned int channel, i;
 
3798
 
 
3799
 
 
3800
    if( PA_IS_OUTPUT_STREAM_(stream) )
 
3801
    {
 
3802
        /* make a local copy of the user buffer pointer(s). this is necessary
 
3803
            because PaUtil_CopyOutput() advances these pointers every time
 
3804
            it is called.
 
3805
        */
 
3806
        if( stream->bufferProcessor.userOutputIsInterleaved )
 
3807
        {
 
3808
            userBuffer = buffer;
 
3809
        }
 
3810
        else
 
3811
        {
 
3812
            userBuffer = (const void*)alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
 
3813
            if( !userBuffer )
 
3814
                return paInsufficientMemory;
 
3815
            for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
 
3816
                ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
 
3817
        }
 
3818
 
 
3819
        do{
 
3820
            if( CurrentOutputBuffersAreDone( stream ) )
 
3821
            {
 
3822
                if( NoBuffersAreQueued( &stream->output ) )
 
3823
                {
 
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? */
 
3828
 
 
3829
                    result = paOutputUnderflowed;
 
3830
                }
 
3831
 
 
3832
                hostOutputBufferIndex = stream->output.currentBufferIndex;
 
3833
 
 
3834
                PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
 
3835
                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
 
3836
 
 
3837
                channel = 0;
 
3838
                for( i=0; i<stream->output.deviceCount; ++i )
 
3839
                {
 
3840
                    /* we have stored the number of channels in the buffer in dwUser */
 
3841
                    int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
 
3842
 
 
3843
                    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
 
3844
                            stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
 
3845
                                stream->output.framesUsedInCurrentBuffer * channelCount *
 
3846
                                stream->bufferProcessor.bytesPerHostOutputSample,
 
3847
                            channelCount );
 
3848
 
 
3849
                    channel += channelCount;
 
3850
                }
 
3851
 
 
3852
                framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
 
3853
 
 
3854
                stream->output.framesUsedInCurrentBuffer += framesProcessed;
 
3855
                if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
 
3856
                {
 
3857
                    result = AdvanceToNextOutputBuffer( stream );
 
3858
                    if( result != paNoError )
 
3859
                        break;
 
3860
                }
 
3861
 
 
3862
                framesWritten += framesProcessed;
 
3863
            }
 
3864
            else
 
3865
            {
 
3866
                /* wait for MME to signal that a buffer is available */
 
3867
                waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
 
3868
                if( waitResult == WAIT_FAILED )
 
3869
                {
 
3870
                    result = paUnanticipatedHostError;
 
3871
                    break;
 
3872
                }
 
3873
                else if( waitResult == WAIT_TIMEOUT )
 
3874
                {
 
3875
                    /* if a timeout is encountered, continue,
 
3876
                        perhaps we should give up eventually
 
3877
                    */
 
3878
                }
 
3879
            }
 
3880
        }while( framesWritten < frames );
 
3881
    }
 
3882
    else
 
3883
    {
 
3884
        result = paCanNotWriteToAnInputOnlyStream;
 
3885
    }
 
3886
 
 
3887
    return result;
 
3888
}
 
3889
 
 
3890
 
 
3891
static signed long GetStreamReadAvailable( PaStream* s )
 
3892
{
 
3893
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3894
 
 
3895
    if( PA_IS_INPUT_STREAM_(stream) )
 
3896
        return GetAvailableFrames( &stream->input );
 
3897
    else
 
3898
        return paCanNotReadFromAnOutputOnlyStream;
 
3899
}
 
3900
 
 
3901
 
 
3902
static signed long GetStreamWriteAvailable( PaStream* s )
 
3903
{
 
3904
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
 
3905
 
 
3906
    if( PA_IS_OUTPUT_STREAM_(stream) )
 
3907
        return GetAvailableFrames( &stream->output );
 
3908
    else
 
3909
        return paCanNotWriteToAnInputOnlyStream;
 
3910
}
 
3911
 
 
3912
 
 
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.
 
3916
*/
 
3917
 
 
3918
static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s )
 
3919
{
 
3920
    PaError result;
 
3921
    PaUtilHostApiRepresentation *hostApi;
 
3922
    PaWinMmeHostApiRepresentation *winMmeHostApi;
 
3923
 
 
3924
    result = PaUtil_ValidateStreamPointer( s );
 
3925
    if( result != paNoError )
 
3926
        return result;
 
3927
 
 
3928
    result = PaUtil_GetHostApiRepresentation( &hostApi, paMME );
 
3929
    if( result != paNoError )
 
3930
        return result;
 
3931
 
 
3932
    winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
 
3933
 
 
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 */
 
3936
 
 
3937
    if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
 
3938
            || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
 
3939
    {
 
3940
        /* s is a WinMME stream */
 
3941
        *stream = (PaWinMmeStream *)s;
 
3942
        return paNoError;
 
3943
    }
 
3944
    else
 
3945
    {
 
3946
        return paIncompatibleStreamHostApi;
 
3947
    }
 
3948
}
 
3949
 
 
3950
 
 
3951
int PaWinMME_GetStreamInputHandleCount( PaStream* s )
 
3952
{
 
3953
    PaWinMmeStream *stream;
 
3954
    PaError result = GetWinMMEStreamPointer( &stream, s );
 
3955
 
 
3956
    if( result == paNoError )
 
3957
        return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0;
 
3958
    else
 
3959
        return result;
 
3960
}
 
3961
 
 
3962
 
 
3963
HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex )
 
3964
{
 
3965
    PaWinMmeStream *stream;
 
3966
    PaError result = GetWinMMEStreamPointer( &stream, s );
 
3967
 
 
3968
    if( result == paNoError
 
3969
            && PA_IS_INPUT_STREAM_(stream)
 
3970
            && handleIndex >= 0
 
3971
            && (unsigned int)handleIndex < stream->input.deviceCount )
 
3972
        return ((HWAVEIN*)stream->input.waveHandles)[handleIndex];
 
3973
    else
 
3974
        return 0;
 
3975
}
 
3976
 
 
3977
 
 
3978
int PaWinMME_GetStreamOutputHandleCount( PaStream* s)
 
3979
{
 
3980
    PaWinMmeStream *stream;
 
3981
    PaError result = GetWinMMEStreamPointer( &stream, s );
 
3982
 
 
3983
    if( result == paNoError )
 
3984
        return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0;
 
3985
    else
 
3986
        return result;
 
3987
}
 
3988
 
 
3989
 
 
3990
HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex )
 
3991
{
 
3992
    PaWinMmeStream *stream;
 
3993
    PaError result = GetWinMMEStreamPointer( &stream, s );
 
3994
 
 
3995
    if( result == paNoError
 
3996
            && PA_IS_OUTPUT_STREAM_(stream)
 
3997
            && handleIndex >= 0
 
3998
            && (unsigned int)handleIndex < stream->output.deviceCount )
 
3999
        return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex];
 
4000
    else
 
4001
        return 0;
 
4002
}