~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« 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: 2014-01-28 18:23:36 UTC
  • mfrom: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
Tags: 1.3.0-1
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

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
 
}