To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/sonic-visualiser/sv-dependency-builds .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / src / portaudio_20161030_catalina_patch / src / hostapi / wmme / pa_win_wmme.c @ 164:9fa11135915a

History | View | Annotate | Download (154 KB)

1
/*
2
 * $Id$
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

    
70
/*
71
    How it works:
72

73
    For both callback and blocking read/write streams we open the MME devices
74
    in CALLBACK_EVENT mode. In this mode, MME signals an Event object whenever
75
    it has finished with a buffer (either filled it for input, or played it
76
    for output). Where necessary, we block waiting for Event objects using
77
    WaitMultipleObjects().
78

79
    When implementing a PA callback stream, we set up a high priority thread
80
    which waits on the MME buffer Events and drains/fills the buffers when
81
    they are ready.
82

83
    When implementing a PA blocking read/write stream, we simply wait on these
84
    Events (when necessary) inside the ReadStream() and WriteStream() functions.
85
*/
86

    
87
#include <stdio.h>
88
#include <stdlib.h>
89
#include <limits.h>
90
#include <math.h>
91
#include <windows.h>
92
#include <mmsystem.h>
93
#ifndef UNDER_CE
94
#include <process.h>
95
#endif
96
#include <assert.h>
97
/* PLB20010422 - "memory.h" doesn't work on CodeWarrior for PC. Thanks Mike Berry for the mod. */
98
#ifndef __MWERKS__
99
#include <malloc.h>
100
#include <memory.h>
101
#endif /* __MWERKS__ */
102

    
103
#include "portaudio.h"
104
#include "pa_trace.h"
105
#include "pa_util.h"
106
#include "pa_allocation.h"
107
#include "pa_hostapi.h"
108
#include "pa_stream.h"
109
#include "pa_cpuload.h"
110
#include "pa_process.h"
111
#include "pa_debugprint.h"
112

    
113
#include "pa_win_wmme.h"
114
#include "pa_win_waveformat.h"
115

    
116
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
117
#include "pa_win_wdmks_utils.h"
118
#ifndef DRV_QUERYDEVICEINTERFACE
119
#define DRV_QUERYDEVICEINTERFACE     (DRV_RESERVED + 12)
120
#endif
121
#ifndef DRV_QUERYDEVICEINTERFACESIZE
122
#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
123
#endif
124
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
125

    
126
/* use CreateThread for CYGWIN, _beginthreadex for all others */
127
#if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
128
#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
129
#define PA_THREAD_FUNC static unsigned WINAPI
130
#define PA_THREAD_ID unsigned
131
#else
132
#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
133
#define PA_THREAD_FUNC static DWORD WINAPI
134
#define PA_THREAD_ID DWORD
135
#endif
136
#if (defined(_WIN32_WCE))
137
#pragma comment(lib, "Coredll.lib")
138
#elif (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
139
#pragma comment(lib, "winmm.lib")
140
#endif
141

    
142
/*
143
 provided in newer platform sdks
144
 */
145
#ifndef DWORD_PTR
146
    #if defined(_WIN64)
147
        #define DWORD_PTR unsigned __int64
148
    #else
149
        #define DWORD_PTR unsigned long
150
    #endif
151
#endif
152

    
153
/************************************************* Constants ********/
154

    
155
#define PA_MME_USE_HIGH_DEFAULT_LATENCY_    (0)  /* For debugging glitches. */
156

    
157
#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
158
 #define PA_MME_WIN_9X_DEFAULT_LATENCY_                             (0.4)
159
 #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_                       (4)
160
 #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_                (4)
161
 #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_                (4)
162
 #define PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_        (16)
163
 #define PA_MME_MAX_HOST_BUFFER_SECS_                                                (0.3)       /* Do not exceed unless user buffer exceeds */
164
 #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 */
165
#else
166
 #define PA_MME_WIN_9X_DEFAULT_LATENCY_                             (0.2)
167
 #define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_                       (2)
168
 #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_                (3)         /* always use at least 3 input buffers for full duplex */
169
 #define PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_                (2)
170
 #define PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_        (16)
171
 #define PA_MME_MAX_HOST_BUFFER_SECS_                                                (0.1)       /* Do not exceed unless user buffer exceeds */
172
 #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 */
173
#endif
174

    
175
/* Use higher latency for NT because it is even worse at real-time
176
   operation than Win9x.
177
*/
178
#define PA_MME_WIN_NT_DEFAULT_LATENCY_                              (0.4)
179

    
180
/* Default low latency for WDM based systems. This is based on a rough
181
   survey of workable latency settings using patest_wmme_find_best_latency_params.c.
182
   See pdf attached to ticket 185 for a graph of the survey results:
183
   http://www.portaudio.com/trac/ticket/185
184
   
185
   Workable latencies varied between 40ms and ~80ms on different systems (different
186
   combinations of hardware, 32 and 64 bit, WinXP, Vista and Win7. We didn't
187
   get enough Vista results to know if Vista has systemically worse latency.
188
   For now we choose a safe value across all Windows versions here.
189
*/
190
#define PA_MME_WIN_WDM_DEFAULT_LATENCY_                             (0.090)
191

    
192

    
193
/* When client suggestedLatency could result in many host buffers, we aim to have around 8, 
194
   based off Windows documentation that suggests that the kmixer uses 8 buffers. This choice
195
   is somewhat arbitrary here, since we havn't observed significant stability degredation 
196
   with using either more, or less buffers.     
197
*/
198
#define PA_MME_TARGET_HOST_BUFFER_COUNT_    8
199

    
200
#define PA_MME_MIN_TIMEOUT_MSEC_        (1000)
201

    
202
static const char constInputMapperSuffix_[] = " - Input";
203
static const char constOutputMapperSuffix_[] = " - Output";
204

    
205
/********************************************************************/
206

    
207
/* Copy null-terminated TCHAR string to explicit char string using UTF8 encoding */
208
static char *CopyTCharStringToUtf8CString(char *destination, size_t destLengthBytes, const TCHAR *source)
209
{
210
#if !defined(_UNICODE) && !defined(UNICODE)
211
    return strcpy(destination, source);
212
#else
213
    /* The cbMultiByte parameter ["destLengthBytes" below] is:
214
    """
215
    Size, in bytes, of the buffer indicated by lpMultiByteStr ["destination" below]. 
216
    If this parameter is set to 0, the function returns the required buffer 
217
    size for lpMultiByteStr and makes no use of the output parameter itself.
218
    """
219
    Source: WideCharToMultiByte at MSDN:
220
    http://msdn.microsoft.com/en-us/library/windows/desktop/dd374130(v=vs.85).aspx
221
    */
222
    int intDestLengthBytes; /* cbMultiByte */
223
    /* intDestLengthBytes is an int, destLengthBytes is a size_t. Ensure that we don't overflow
224
    intDestLengthBytes by only using at most INT_MAX bytes of destination buffer.
225
    */
226
    if (destLengthBytes < INT_MAX)
227
    {
228
#pragma warning (disable : 4267) /* "conversion from 'size_t' to 'int', possible loss of data" */
229
        intDestLengthBytes = (int)destLengthBytes; /* destLengthBytes is guaranteed < INT_MAX here */
230
#pragma warning (default : 4267)
231
    }
232
    else
233
    {
234
        intDestLengthBytes = INT_MAX;
235
    }
236
    
237
    if (WideCharToMultiByte(CP_UTF8, 0, source, -1, destination, /*cbMultiByte=*/intDestLengthBytes, NULL, NULL) == 0)
238
        return NULL;
239
    return destination;
240
#endif
241
}
242

    
243
/* returns required length (in bytes) of destination buffer when 
244
   converting TCHAR string to UTF8 bytes, not including the terminating null. */
245
static size_t TCharStringLen(const TCHAR *str)
246
{
247
#if !defined(_UNICODE) && !defined(UNICODE)
248
    return strlen(str);
249
#else
250
    return WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);        
251
#endif
252
}
253

    
254
/********************************************************************/
255

    
256
typedef struct PaWinMmeStream PaWinMmeStream;     /* forward declaration */
257

    
258
/* prototypes for functions declared in this file */
259

    
260
#ifdef __cplusplus
261
extern "C"
262
{
263
#endif /* __cplusplus */
264

    
265
PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
266

    
267
#ifdef __cplusplus
268
}
269
#endif /* __cplusplus */
270

    
271
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
272
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
273
                           PaStream** stream,
274
                           const PaStreamParameters *inputParameters,
275
                           const PaStreamParameters *outputParameters,
276
                           double sampleRate,
277
                           unsigned long framesPerBuffer,
278
                           PaStreamFlags streamFlags,
279
                           PaStreamCallback *streamCallback,
280
                           void *userData );
281
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
282
                                  const PaStreamParameters *inputParameters,
283
                                  const PaStreamParameters *outputParameters,
284
                                  double sampleRate );
285
static PaError CloseStream( PaStream* stream );
286
static PaError StartStream( PaStream *stream );
287
static PaError StopStream( PaStream *stream );
288
static PaError AbortStream( PaStream *stream );
289
static PaError IsStreamStopped( PaStream *s );
290
static PaError IsStreamActive( PaStream *stream );
291
static PaTime GetStreamTime( PaStream *stream );
292
static double GetStreamCpuLoad( PaStream* stream );
293
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
294
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
295
static signed long GetStreamReadAvailable( PaStream* stream );
296
static signed long GetStreamWriteAvailable( PaStream* stream );
297

    
298

    
299
/* macros for setting last host error information */
300

    
301
#ifdef UNICODE
302

    
303
#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
304
    {                                                                   \
305
        wchar_t mmeErrorTextWide[ MAXERRORLENGTH ];                     \
306
        char mmeErrorText[ MAXERRORLENGTH ];                            \
307
        waveInGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH );   \
308
        WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
309
            mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL );  \
310
        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
311
    }
312

    
313
#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
314
    {                                                                   \
315
        wchar_t mmeErrorTextWide[ MAXERRORLENGTH ];                     \
316
        char mmeErrorText[ MAXERRORLENGTH ];                            \
317
        waveOutGetErrorText( mmresult, mmeErrorTextWide, MAXERRORLENGTH );  \
318
        WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
319
            mmeErrorTextWide, -1, mmeErrorText, MAXERRORLENGTH, NULL, NULL );  \
320
        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
321
    }
322
    
323
#else /* !UNICODE */
324

    
325
#define PA_MME_SET_LAST_WAVEIN_ERROR( mmresult ) \
326
    {                                                                   \
327
        char mmeErrorText[ MAXERRORLENGTH ];                            \
328
        waveInGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH );   \
329
        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
330
    }
331

    
332
#define PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult ) \
333
    {                                                                   \
334
        char mmeErrorText[ MAXERRORLENGTH ];                            \
335
        waveOutGetErrorText( mmresult, mmeErrorText, MAXERRORLENGTH );  \
336
        PaUtil_SetLastHostErrorInfo( paMME, mmresult, mmeErrorText );   \
337
    }
338

    
339
#endif /* UNICODE */
340

    
341

    
342
static void PaMme_SetLastSystemError( DWORD errorCode )
343
{
344
    char *lpMsgBuf;
345
    FormatMessage(
346
        FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
347
        NULL,
348
        errorCode,
349
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
350
        (LPTSTR) &lpMsgBuf,
351
        0,
352
        NULL
353
    );
354
    PaUtil_SetLastHostErrorInfo( paMME, errorCode, lpMsgBuf );
355
    LocalFree( lpMsgBuf );
356
}
357

    
358
#define PA_MME_SET_LAST_SYSTEM_ERROR( errorCode ) \
359
    PaMme_SetLastSystemError( errorCode )
360

    
361

    
362
/* PaError returning wrappers for some commonly used win32 functions
363
    note that we allow passing a null ptr to have no effect.
364
*/
365

    
366
static PaError CreateEventWithPaError( HANDLE *handle,
367
        LPSECURITY_ATTRIBUTES lpEventAttributes,
368
        BOOL bManualReset,
369
        BOOL bInitialState,
370
        LPCTSTR lpName )
371
{
372
    PaError result = paNoError;
373

    
374
    *handle = NULL;
375
    
376
    *handle = CreateEvent( lpEventAttributes, bManualReset, bInitialState, lpName );
377
    if( *handle == NULL )
378
    {
379
        result = paUnanticipatedHostError;
380
        PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
381
    }
382

    
383
    return result;
384
}
385

    
386

    
387
static PaError ResetEventWithPaError( HANDLE handle )
388
{
389
    PaError result = paNoError;
390

    
391
    if( handle )
392
    {
393
        if( ResetEvent( handle ) == 0 )
394
        {
395
            result = paUnanticipatedHostError;
396
            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
397
        }
398
    }
399

    
400
    return result;
401
}
402

    
403

    
404
static PaError CloseHandleWithPaError( HANDLE handle )
405
{
406
    PaError result = paNoError;
407
    
408
    if( handle )
409
    {
410
        if( CloseHandle( handle ) == 0 )
411
        {
412
            result = paUnanticipatedHostError;
413
            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
414
        }
415
    }
416
    
417
    return result;
418
}
419

    
420

    
421
/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
422

    
423
typedef struct
424
{
425
    PaUtilHostApiRepresentation inheritedHostApiRep;
426
    PaUtilStreamInterface callbackStreamInterface;
427
    PaUtilStreamInterface blockingStreamInterface;
428

    
429
    PaUtilAllocationGroup *allocations;
430
    
431
    int inputDeviceCount, outputDeviceCount;
432

    
433
    /** winMmeDeviceIds is an array of WinMme device ids.
434
        fields in the range [0, inputDeviceCount) are input device ids,
435
        and [inputDeviceCount, inputDeviceCount + outputDeviceCount) are output
436
        device ids.
437
     */ 
438
    UINT *winMmeDeviceIds;
439
}
440
PaWinMmeHostApiRepresentation;
441

    
442

    
443
typedef struct
444
{
445
    PaDeviceInfo inheritedDeviceInfo;
446
    DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
447
    char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
448
    char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
449
}
450
PaWinMmeDeviceInfo;
451

    
452

    
453
/*************************************************************************
454
 * Returns recommended device ID.
455
 * On the PC, the recommended device can be specified by the user by
456
 * setting an environment variable. For example, to use device #1.
457
 *
458
 *    set PA_RECOMMENDED_OUTPUT_DEVICE=1
459
 *
460
 * The user should first determine the available device ID by using
461
 * the supplied application "pa_devs".
462
 */
463
#define PA_ENV_BUF_SIZE_  (32)
464
#define PA_REC_IN_DEV_ENV_NAME_  ("PA_RECOMMENDED_INPUT_DEVICE")
465
#define PA_REC_OUT_DEV_ENV_NAME_  ("PA_RECOMMENDED_OUTPUT_DEVICE")
466
static PaDeviceIndex GetEnvDefaultDeviceID( char *envName )
467
{
468
    PaDeviceIndex recommendedIndex = paNoDevice;
469
    DWORD   hresult;
470
    char    envbuf[PA_ENV_BUF_SIZE_];
471

    
472
#ifndef WIN32_PLATFORM_PSPC /* no GetEnvironmentVariable on PocketPC */
473

    
474
    /* Let user determine default device by setting environment variable. */
475
    hresult = GetEnvironmentVariableA( envName, envbuf, PA_ENV_BUF_SIZE_ );
476
    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE_) )
477
    {
478
        recommendedIndex = atoi( envbuf );
479
    }
480
#endif
481

    
482
    return recommendedIndex;
483
}
484

    
485

    
486
static void InitializeDefaultDeviceIdsFromEnv( PaWinMmeHostApiRepresentation *hostApi )
487
{
488
    PaDeviceIndex device;
489

    
490
    /* input */
491
    device = GetEnvDefaultDeviceID( PA_REC_IN_DEV_ENV_NAME_ );
492
    if( device != paNoDevice &&
493
            ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
494
            hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxInputChannels > 0 )
495
    {
496
        hostApi->inheritedHostApiRep.info.defaultInputDevice = device;
497
    }
498

    
499
    /* output */
500
    device = GetEnvDefaultDeviceID( PA_REC_OUT_DEV_ENV_NAME_ );
501
    if( device != paNoDevice &&
502
            ( device >= 0 && device < hostApi->inheritedHostApiRep.info.deviceCount ) &&
503
            hostApi->inheritedHostApiRep.deviceInfos[ device ]->maxOutputChannels > 0 )
504
    {
505
        hostApi->inheritedHostApiRep.info.defaultOutputDevice = device;
506
    }
507
}
508

    
509

    
510
/** Convert external PA ID to a windows multimedia device ID
511
*/
512
static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hostApi, PaDeviceIndex device )
513
{
514
    assert( device >= 0 && device < hostApi->inputDeviceCount + hostApi->outputDeviceCount );
515

    
516
    return hostApi->winMmeDeviceIds[ device ];
517
}
518

    
519

    
520
static int SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( PaSampleFormat sampleFormat, unsigned long winMmeSpecificFlags )
521
{
522
    int waveFormatTag = 0;
523

    
524
    if( winMmeSpecificFlags & paWinMmeWaveFormatDolbyAc3Spdif )
525
        waveFormatTag = PAWIN_WAVE_FORMAT_DOLBY_AC3_SPDIF;
526
    else if( winMmeSpecificFlags & paWinMmeWaveFormatWmaSpdif )
527
        waveFormatTag = PAWIN_WAVE_FORMAT_WMA_SPDIF;
528
    else
529
        waveFormatTag = PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat );
530

    
531
    return waveFormatTag;
532
}
533

    
534

    
535
static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
536
{
537
    MMRESULT mmresult;
538
    
539
    switch( mmresult = waveInOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
540
    {
541
        case MMSYSERR_NOERROR:
542
            return paNoError;
543
        case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
544
            return paDeviceUnavailable;
545
        case MMSYSERR_NODRIVER:            /* No device driver is present. */
546
            return paDeviceUnavailable;
547
        case MMSYSERR_NOMEM:            /* Unable to allocate or lock memory. */
548
            return paInsufficientMemory;
549
        case WAVERR_BADFORMAT:      /* Attempted to open with an unsupported waveform-audio format. */
550
            return paSampleFormatNotSupported;
551
                    
552
        case MMSYSERR_BADDEVICEID:        /* Specified device identifier is out of range. */
553
            /* falls through */
554
        default:
555
            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
556
            return paUnanticipatedHostError;
557
    }
558
}
559

    
560

    
561
static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
562
{
563
    MMRESULT mmresult;
564
    
565
    switch( mmresult = waveOutOpen( NULL, deviceId, waveFormatEx, 0, 0, WAVE_FORMAT_QUERY ) )
566
    {
567
        case MMSYSERR_NOERROR:
568
            return paNoError;
569
        case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
570
            return paDeviceUnavailable;
571
        case MMSYSERR_NODRIVER:            /* No device driver is present. */
572
            return paDeviceUnavailable;
573
        case MMSYSERR_NOMEM:            /* Unable to allocate or lock memory. */
574
            return paInsufficientMemory;
575
        case WAVERR_BADFORMAT:      /* Attempted to open with an unsupported waveform-audio format. */
576
            return paSampleFormatNotSupported;
577
                    
578
        case MMSYSERR_BADDEVICEID:        /* Specified device identifier is out of range. */
579
            /* falls through */
580
        default:
581
            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
582
            return paUnanticipatedHostError;
583
    }
584
}
585

    
586

    
587
static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
588
        PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
589
        int winMmeDeviceId, int channels, double sampleRate, unsigned long winMmeSpecificFlags )
590
{
591
    PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
592
    PaWinWaveFormat waveFormat;
593
    PaSampleFormat sampleFormat;
594
    int waveFormatTag;
595
    
596
    /* @todo at the moment we only query with 16 bit sample format and directout speaker config*/
597

    
598
    sampleFormat = paInt16;
599
    waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
600

    
601
    if( waveFormatTag == PaWin_SampleFormatToLinearWaveFormatTag( paInt16 ) ){
602
    
603
        /* attempt bypass querying the device for linear formats */
604

    
605
        if( sampleRate == 11025.0
606
            && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
607
                || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
608

    
609
            return paNoError;
610
        }
611

    
612
        if( sampleRate == 22050.0
613
            && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
614
                || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
615

    
616
            return paNoError;
617
        }
618

    
619
        if( sampleRate == 44100.0
620
            && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
621
                || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
622

    
623
            return paNoError;
624
        }
625
    }
626

    
627

    
628
    /* first, attempt to query the device using WAVEFORMATEXTENSIBLE, 
629
       if this fails we fall back to WAVEFORMATEX */
630

    
631
    PaWin_InitializeWaveFormatExtensible( &waveFormat, channels, sampleFormat, waveFormatTag,
632
            sampleRate, PAWIN_SPEAKER_DIRECTOUT );
633

    
634
    if( waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat ) == paNoError )
635
        return paNoError;
636

    
637
    PaWin_InitializeWaveFormatEx( &waveFormat, channels, sampleFormat, waveFormatTag, sampleRate );
638

    
639
    return waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat );
640
}
641

    
642

    
643
#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_  (13) /* must match array length below */
644
static double defaultSampleRateSearchOrder_[] =
645
    { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
646
        16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
647

    
648
static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int winMmeDeviceId,
649
        PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*), int maxChannels )
650
{
651
    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
652
    int i;
653
    
654
    deviceInfo->defaultSampleRate = 0.;
655

    
656
    for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
657
    {
658
        double sampleRate = defaultSampleRateSearchOrder_[ i ]; 
659
        PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate, 0 );
660
        if( paerror == paNoError )
661
        {
662
            deviceInfo->defaultSampleRate = sampleRate;
663
            break;
664
        }
665
    }
666
}
667

    
668

    
669
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
670
static int QueryWaveInKSFilterMaxChannels( int waveInDeviceId, int *maxChannels )
671
{
672
    void *devicePath;
673
    DWORD devicePathSize;
674
    int result = 0;
675

    
676
    if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
677
            (DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
678
        return 0;
679

    
680
    devicePath = PaUtil_AllocateMemory( devicePathSize );
681
    if( !devicePath )
682
        return 0;
683

    
684
    /* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
685
    if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACE,
686
            (DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
687
    {
688
        int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 1  );
689
        if( count > 0 )
690
        {
691
            *maxChannels = count;
692
            result = 1;
693
        }
694
    }
695

    
696
    PaUtil_FreeMemory( devicePath );
697

    
698
    return result;
699
}
700
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
701

    
702

    
703
static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
704
        PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
705
{
706
    PaError result = paNoError;
707
    char *deviceName; /* non-const ptr */
708
    MMRESULT mmresult;
709
    WAVEINCAPS wic;
710
    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
711
    size_t len;
712
    
713
    *success = 0;
714

    
715
    mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
716
    if( mmresult == MMSYSERR_NOMEM )
717
    {
718
        result = paInsufficientMemory;
719
        goto error;
720
    }
721
    else if( mmresult != MMSYSERR_NOERROR )
722
    {
723
        /* instead of returning paUnanticipatedHostError we return
724
            paNoError, but leave success set as 0. This allows
725
            Pa_Initialize to just ignore this device, without failing
726
            the entire initialisation process.
727
        */
728
        return paNoError;
729
    }           
730

    
731
    /* NOTE: the WAVEOUTCAPS.szPname is a null-terminated array of 32 characters,
732
        so we are limited to displaying only the first 31 characters of the device name. */
733
    if( winMmeInputDeviceId == WAVE_MAPPER )
734
    {
735
        len = TCharStringLen( wic.szPname ) + 1 + sizeof(constInputMapperSuffix_);
736
        /* Append I/O suffix to WAVE_MAPPER device. */
737
        deviceName = (char*)PaUtil_GroupAllocateMemory(
738
                    winMmeHostApi->allocations,
739
                    (long)len );
740
        if( !deviceName )
741
        {
742
            result = paInsufficientMemory;
743
            goto error;
744
        }
745
        CopyTCharStringToUtf8CString( deviceName, len, wic.szPname );
746
        strcat( deviceName, constInputMapperSuffix_ );
747
    }
748
    else
749
    {
750
        len = TCharStringLen( wic.szPname ) + 1;
751
        deviceName = (char*)PaUtil_GroupAllocateMemory(
752
                    winMmeHostApi->allocations,
753
                    (long)len );
754
        if( !deviceName )
755
        {
756
            result = paInsufficientMemory;
757
            goto error;
758
        }
759
        CopyTCharStringToUtf8CString( deviceName, len, wic.szPname  );
760
    }
761
    deviceInfo->name = deviceName;
762

    
763
    if( wic.wChannels == 0xFFFF || wic.wChannels < 1 || wic.wChannels > 255 ){
764
        /* For Windows versions using WDM (possibly Windows 98 ME and later)
765
         * the kernel mixer sits between the application and the driver. As a result,
766
         * wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
767
         * When this happens we assume the device is stereo and set a flag
768
         * so that other channel counts can be tried with OpenStream -- i.e. when
769
         * device*ChannelCountIsKnown is false, OpenStream will try whatever
770
         * channel count you supply.
771
         * see also InitializeOutputDeviceInfo() below.
772
     */
773

    
774
        PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", wic.wChannels ));
775
        deviceInfo->maxInputChannels = 2;
776
        winMmeDeviceInfo->deviceInputChannelCountIsKnown = 0;
777
    }else{
778
        deviceInfo->maxInputChannels = wic.wChannels;
779
        winMmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
780
    }
781

    
782
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
783
    winMmeDeviceInfo->deviceInputChannelCountIsKnown = 
784
            QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels );
785
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
786

    
787
    winMmeDeviceInfo->dwFormats = wic.dwFormats;
788

    
789
    DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
790
            QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
791

    
792
    *success = 1;
793
    
794
error:
795
    return result;
796
}
797

    
798

    
799
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
800
static int QueryWaveOutKSFilterMaxChannels( int waveOutDeviceId, int *maxChannels )
801
{
802
    void *devicePath;
803
    DWORD devicePathSize;
804
    int result = 0;
805

    
806
    if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
807
            (DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
808
        return 0;
809

    
810
    devicePath = PaUtil_AllocateMemory( devicePathSize );
811
    if( !devicePath )
812
        return 0;
813

    
814
    /* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
815
    if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACE,
816
            (DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
817
    {
818
        int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 0  );
819
        if( count > 0 )
820
        {
821
            *maxChannels = count;
822
            result = 1;
823
        }
824
    }
825

    
826
    PaUtil_FreeMemory( devicePath );
827

    
828
    return result;
829
}
830
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
831

    
832

    
833
static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
834
        PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
835
{
836
    PaError result = paNoError;
837
    char *deviceName; /* non-const ptr */
838
    MMRESULT mmresult;
839
    WAVEOUTCAPS woc;
840
    PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
841
    size_t len;
842
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
843
    int wdmksDeviceOutputChannelCountIsKnown;
844
#endif
845

    
846
    *success = 0;
847

    
848
    mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
849
    if( mmresult == MMSYSERR_NOMEM )
850
    {
851
        result = paInsufficientMemory;
852
        goto error;
853
    }
854
    else if( mmresult != MMSYSERR_NOERROR )
855
    {
856
        /* instead of returning paUnanticipatedHostError we return
857
            paNoError, but leave success set as 0. This allows
858
            Pa_Initialize to just ignore this device, without failing
859
            the entire initialisation process.
860
        */
861
        return paNoError;
862
    }
863

    
864
    /* NOTE: the WAVEOUTCAPS.szPname is a null-terminated array of 32 characters,
865
        so we are limited to displaying only the first 31 characters of the device name. */
866
    if( winMmeOutputDeviceId == WAVE_MAPPER )
867
    {
868
        /* Append I/O suffix to WAVE_MAPPER device. */
869
        len = TCharStringLen( woc.szPname ) + 1 + sizeof(constOutputMapperSuffix_);
870
        deviceName = (char*)PaUtil_GroupAllocateMemory(
871
                    winMmeHostApi->allocations, 
872
                    (long)len );
873
        if( !deviceName )
874
        {
875
            result = paInsufficientMemory;
876
            goto error;
877
        }
878
        CopyTCharStringToUtf8CString( deviceName, len, woc.szPname );
879
        strcat( deviceName, constOutputMapperSuffix_ );
880
    }
881
    else
882
    {
883
        len = TCharStringLen( woc.szPname ) + 1;
884
        deviceName = (char*)PaUtil_GroupAllocateMemory(
885
                    winMmeHostApi->allocations, 
886
                    (long)len );
887
        if( !deviceName )
888
        {
889
            result = paInsufficientMemory;
890
            goto error;
891
        }
892
        CopyTCharStringToUtf8CString( deviceName, len, woc.szPname );
893
    }
894
    deviceInfo->name = deviceName;
895

    
896
    if( woc.wChannels == 0xFFFF || woc.wChannels < 1 || woc.wChannels > 255 ){
897
        /* For Windows versions using WDM (possibly Windows 98 ME and later)
898
         * the kernel mixer sits between the application and the driver. As a result,
899
         * wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
900
         * When this happens we assume the device is stereo and set a flag
901
         * so that other channel counts can be tried with OpenStream -- i.e. when
902
         * device*ChannelCountIsKnown is false, OpenStream will try whatever
903
         * channel count you supply.
904
         * see also InitializeInputDeviceInfo() above.
905
     */
906

    
907
        PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", woc.wChannels ));
908
        deviceInfo->maxOutputChannels = 2;
909
        winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 0;
910
    }else{
911
        deviceInfo->maxOutputChannels = woc.wChannels;
912
        winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
913
    }
914

    
915
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
916
    wdmksDeviceOutputChannelCountIsKnown = QueryWaveOutKSFilterMaxChannels( 
917
            winMmeOutputDeviceId, &deviceInfo->maxOutputChannels );
918
    if( wdmksDeviceOutputChannelCountIsKnown && !winMmeDeviceInfo->deviceOutputChannelCountIsKnown )
919
        winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
920
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
921

    
922
    winMmeDeviceInfo->dwFormats = woc.dwFormats;
923

    
924
    DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
925
            QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
926

    
927
    *success = 1;
928
    
929
error:
930
    return result;
931
}
932

    
933

    
934
static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighLatency )
935
{
936
/*
937
NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
938
versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
939
Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
940
is is faster, for now we just disable the deprecation warning.
941
See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
942
See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
943
*/
944
#pragma warning (disable : 4996) /* use of GetVersionEx */
945

    
946
    OSVERSIONINFO osvi;
947
    osvi.dwOSVersionInfoSize = sizeof( osvi );
948
    GetVersionEx( &osvi );
949

    
950
    /* Check for NT */
951
    if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
952
    {
953
        *defaultLowLatency = PA_MME_WIN_NT_DEFAULT_LATENCY_;
954
    }
955
    else if(osvi.dwMajorVersion >= 5)
956
    {
957
        *defaultLowLatency  = PA_MME_WIN_WDM_DEFAULT_LATENCY_;
958
    }
959
    else
960
    {
961
        *defaultLowLatency  = PA_MME_WIN_9X_DEFAULT_LATENCY_;
962
    }     
963

    
964
    *defaultHighLatency = *defaultLowLatency * 2;
965

    
966
#pragma warning (default : 4996)
967
}
968

    
969

    
970
PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
971
{
972
    PaError result = paNoError;
973
    int i;
974
    PaWinMmeHostApiRepresentation *winMmeHostApi;
975
    int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
976
    PaWinMmeDeviceInfo *deviceInfoArray;
977
    int deviceInfoInitializationSucceeded;
978
    PaTime defaultLowLatency, defaultHighLatency;
979
    DWORD waveInPreferredDevice, waveOutPreferredDevice;
980
    DWORD preferredDeviceStatusFlags;
981

    
982
    winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
983
    if( !winMmeHostApi )
984
    {
985
        result = paInsufficientMemory;
986
        goto error;
987
    }
988

    
989
    winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
990
    if( !winMmeHostApi->allocations )
991
    {
992
        result = paInsufficientMemory;
993
        goto error;
994
    }
995

    
996
    *hostApi = &winMmeHostApi->inheritedHostApiRep;
997
    (*hostApi)->info.structVersion = 1;
998
    (*hostApi)->info.type = paMME;
999
    (*hostApi)->info.name = "MME";
1000

    
1001
    
1002
    /* initialise device counts and default devices under the assumption that
1003
        there are no devices. These values are incremented below if and when
1004
        devices are successfully initialized.
1005
    */
1006
    (*hostApi)->info.deviceCount = 0;
1007
    (*hostApi)->info.defaultInputDevice = paNoDevice;
1008
    (*hostApi)->info.defaultOutputDevice = paNoDevice;
1009
    winMmeHostApi->inputDeviceCount = 0;
1010
    winMmeHostApi->outputDeviceCount = 0;
1011

    
1012
#if !defined(DRVM_MAPPER_PREFERRED_GET)
1013
/* DRVM_MAPPER_PREFERRED_GET is defined in mmddk.h but we avoid a dependency on the DDK by defining it here */
1014
#define DRVM_MAPPER_PREFERRED_GET    (0x2000+21)
1015
#endif
1016

    
1017
    /* the following calls assume that if wave*Message fails the preferred device parameter won't be modified */
1018
    preferredDeviceStatusFlags = 0;
1019
    waveInPreferredDevice = -1;
1020
    waveInMessage( (HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&waveInPreferredDevice, (DWORD_PTR)&preferredDeviceStatusFlags );
1021

    
1022
    preferredDeviceStatusFlags = 0;
1023
    waveOutPreferredDevice = -1;
1024
    waveOutMessage( (HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&waveOutPreferredDevice, (DWORD_PTR)&preferredDeviceStatusFlags );
1025

    
1026
    maximumPossibleDeviceCount = 0;
1027

    
1028
    inputDeviceCount = waveInGetNumDevs();
1029
    if( inputDeviceCount > 0 )
1030
        maximumPossibleDeviceCount += inputDeviceCount + 1;        /* assume there is a WAVE_MAPPER */
1031

    
1032
    outputDeviceCount = waveOutGetNumDevs();
1033
    if( outputDeviceCount > 0 )
1034
        maximumPossibleDeviceCount += outputDeviceCount + 1;        /* assume there is a WAVE_MAPPER */
1035

    
1036

    
1037
    if( maximumPossibleDeviceCount > 0 ){
1038

    
1039
        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1040
                winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
1041
        if( !(*hostApi)->deviceInfos )
1042
        {
1043
            result = paInsufficientMemory;
1044
            goto error;
1045
        }
1046

    
1047
        /* allocate all device info structs in a contiguous block */
1048
        deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
1049
                winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
1050
        if( !deviceInfoArray )
1051
        {
1052
            result = paInsufficientMemory;
1053
            goto error;
1054
        }
1055

    
1056
        winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
1057
                winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
1058
        if( !winMmeHostApi->winMmeDeviceIds )
1059
        {
1060
            result = paInsufficientMemory;
1061
            goto error;
1062
        }
1063

    
1064
        GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
1065

    
1066
        if( inputDeviceCount > 0 ){
1067
            /* -1 is the WAVE_MAPPER */
1068
            for( i = -1; i < inputDeviceCount; ++i ){
1069
                UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
1070
                PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
1071
                PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
1072
                deviceInfo->structVersion = 2;
1073
                deviceInfo->hostApi = hostApiIndex;
1074

    
1075
                deviceInfo->maxInputChannels = 0;
1076
                wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
1077
                deviceInfo->maxOutputChannels = 0;
1078
                wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
1079

    
1080
                deviceInfo->defaultLowInputLatency = defaultLowLatency;
1081
                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
1082
                deviceInfo->defaultHighInputLatency = defaultHighLatency;
1083
                deviceInfo->defaultHighOutputLatency = defaultHighLatency;
1084

    
1085
                result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
1086
                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
1087
                if( result != paNoError )
1088
                    goto error;
1089

    
1090
                if( deviceInfoInitializationSucceeded ){
1091
                    if( (*hostApi)->info.defaultInputDevice == paNoDevice ){
1092
                        /* if there is currently no default device, use the first one available */
1093
                        (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
1094
                    
1095
                    }else if( winMmeDeviceId == waveInPreferredDevice ){
1096
                        /* set the default device to the system preferred device */
1097
                        (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
1098
                    }
1099

    
1100
                    winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
1101
                    (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
1102

    
1103
                    winMmeHostApi->inputDeviceCount++;
1104
                    (*hostApi)->info.deviceCount++;
1105
                }
1106
            }
1107
        }
1108

    
1109
        if( outputDeviceCount > 0 ){
1110
            /* -1 is the WAVE_MAPPER */
1111
            for( i = -1; i < outputDeviceCount; ++i ){
1112
                UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
1113
                PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
1114
                PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
1115
                deviceInfo->structVersion = 2;
1116
                deviceInfo->hostApi = hostApiIndex;
1117

    
1118
                deviceInfo->maxInputChannels = 0;
1119
                wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
1120
                deviceInfo->maxOutputChannels = 0;
1121
                wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
1122

    
1123
                deviceInfo->defaultLowInputLatency = defaultLowLatency;
1124
                deviceInfo->defaultLowOutputLatency = defaultLowLatency;
1125
                deviceInfo->defaultHighInputLatency = defaultHighLatency;
1126
                deviceInfo->defaultHighOutputLatency = defaultHighLatency; 
1127

    
1128
                result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
1129
                        winMmeDeviceId, &deviceInfoInitializationSucceeded );
1130
                if( result != paNoError )
1131
                    goto error;
1132

    
1133
                if( deviceInfoInitializationSucceeded ){
1134
                    if( (*hostApi)->info.defaultOutputDevice == paNoDevice ){
1135
                        /* if there is currently no default device, use the first one available */
1136
                        (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
1137

    
1138
                    }else if( winMmeDeviceId == waveOutPreferredDevice ){
1139
                        /* set the default device to the system preferred device */
1140
                        (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
1141
                    }
1142

    
1143
                    winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
1144
                    (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
1145

    
1146
                    winMmeHostApi->outputDeviceCount++;
1147
                    (*hostApi)->info.deviceCount++;
1148
                }
1149
            }
1150
        }
1151
    }
1152
    
1153
    InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
1154

    
1155
    (*hostApi)->Terminate = Terminate;
1156
    (*hostApi)->OpenStream = OpenStream;
1157
    (*hostApi)->IsFormatSupported = IsFormatSupported;
1158

    
1159
    PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
1160
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1161
                                      GetStreamTime, GetStreamCpuLoad,
1162
                                      PaUtil_DummyRead, PaUtil_DummyWrite,
1163
                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1164

    
1165
    PaUtil_InitializeStreamInterface( &winMmeHostApi->blockingStreamInterface, CloseStream, StartStream,
1166
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1167
                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
1168
                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1169

    
1170
    return result;
1171

    
1172
error:
1173
    if( winMmeHostApi )
1174
    {
1175
        if( winMmeHostApi->allocations )
1176
        {
1177
            PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
1178
            PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
1179
        }
1180
        
1181
        PaUtil_FreeMemory( winMmeHostApi );
1182
    }
1183

    
1184
    return result;
1185
}
1186

    
1187

    
1188
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1189
{
1190
    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
1191

    
1192
    if( winMmeHostApi->allocations )
1193
    {
1194
        PaUtil_FreeAllAllocations( winMmeHostApi->allocations );
1195
        PaUtil_DestroyAllocationGroup( winMmeHostApi->allocations );
1196
    }
1197

    
1198
    PaUtil_FreeMemory( winMmeHostApi );
1199
}
1200

    
1201

    
1202
static PaError IsInputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
1203
{
1204
    PaError result = paNoError;
1205

    
1206
    if( channelCount > 0
1207
            && deviceInfo->deviceInputChannelCountIsKnown
1208
            && channelCount > deviceInfo->inheritedDeviceInfo.maxInputChannels ){
1209

    
1210
        result = paInvalidChannelCount; 
1211
    }
1212

    
1213
    return result;
1214
}
1215

    
1216
static PaError IsOutputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
1217
{
1218
    PaError result = paNoError;
1219

    
1220
    if( channelCount > 0
1221
            && deviceInfo->deviceOutputChannelCountIsKnown
1222
            && channelCount > deviceInfo->inheritedDeviceInfo.maxOutputChannels ){
1223

    
1224
        result = paInvalidChannelCount; 
1225
    }
1226

    
1227
    return result;
1228
}
1229

    
1230
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1231
                                  const PaStreamParameters *inputParameters,
1232
                                  const PaStreamParameters *outputParameters,
1233
                                  double sampleRate )
1234
{
1235
    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
1236
    PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1237
    int inputChannelCount, outputChannelCount;
1238
    int inputMultipleDeviceChannelCount, outputMultipleDeviceChannelCount;
1239
    PaSampleFormat inputSampleFormat, outputSampleFormat;
1240
    PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
1241
    UINT winMmeInputDeviceId, winMmeOutputDeviceId;
1242
    unsigned int i;
1243
    PaError paerror;
1244

    
1245
    /* The calls to QueryFormatSupported below are intended to detect invalid
1246
        sample rates. If we assume that the channel count and format are OK,
1247
        then the only thing that could fail is the sample rate. This isn't
1248
        strictly true, but I can't think of a better way to test that the
1249
        sample rate is valid.
1250
    */  
1251
    
1252
    if( inputParameters )
1253
    {
1254
        inputChannelCount = inputParameters->channelCount;
1255
        inputSampleFormat = inputParameters->sampleFormat;
1256
        inputStreamInfo = inputParameters->hostApiSpecificStreamInfo;
1257
        
1258
        /* all standard sample formats are supported by the buffer adapter,
1259
             this implementation doesn't support any custom sample formats */
1260
        if( inputSampleFormat & paCustomFormat )
1261
            return paSampleFormatNotSupported;
1262

    
1263
        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification
1264
                && inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
1265
        {
1266
            inputMultipleDeviceChannelCount = 0;
1267
            for( i=0; i< inputStreamInfo->deviceCount; ++i )
1268
            {
1269
                inputMultipleDeviceChannelCount += inputStreamInfo->devices[i].channelCount;
1270
                    
1271
                inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
1272

    
1273
                /* check that input device can support inputChannelCount */
1274
                if( inputStreamInfo->devices[i].channelCount < 1 )
1275
                    return paInvalidChannelCount;
1276

    
1277
                paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo, 
1278
                        inputStreamInfo->devices[i].channelCount );
1279
                if( paerror != paNoError )
1280
                    return paerror;
1281

    
1282
                /* test for valid sample rate, see comment above */
1283
                winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
1284
                paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, 
1285
                        winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate, 
1286
                        ((inputStreamInfo) ? inputStreamInfo->flags : 0) );
1287
                if( paerror != paNoError )
1288
                    return paInvalidSampleRate;
1289
            }
1290
                
1291
            if( inputMultipleDeviceChannelCount != inputChannelCount )
1292
                return paIncompatibleHostApiSpecificStreamInfo;                  
1293
        }
1294
        else
1295
        {
1296
            if( inputStreamInfo && (inputStreamInfo->flags & paWinMmeUseMultipleDevices) )
1297
                return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the input device */
1298

    
1299
            inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
1300

    
1301
            /* check that input device can support inputChannelCount */
1302
            paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo, inputChannelCount );
1303
            if( paerror != paNoError )
1304
                return paerror;
1305

    
1306
            /* test for valid sample rate, see comment above */
1307
            winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
1308
            paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, 
1309
                    winMmeInputDeviceId, inputChannelCount, sampleRate,
1310
                    ((inputStreamInfo) ? inputStreamInfo->flags : 0) );
1311
            if( paerror != paNoError )
1312
                return paInvalidSampleRate;
1313
        }
1314
    }
1315

    
1316
    if( outputParameters )
1317
    {
1318
        outputChannelCount = outputParameters->channelCount;
1319
        outputSampleFormat = outputParameters->sampleFormat;
1320
        outputStreamInfo = outputParameters->hostApiSpecificStreamInfo;
1321

    
1322
        /* all standard sample formats are supported by the buffer adapter,
1323
            this implementation doesn't support any custom sample formats */
1324
        if( outputSampleFormat & paCustomFormat )
1325
            return paSampleFormatNotSupported;
1326

    
1327
        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification
1328
                && outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
1329
        {
1330
            outputMultipleDeviceChannelCount = 0;
1331
            for( i=0; i< outputStreamInfo->deviceCount; ++i )
1332
            {
1333
                outputMultipleDeviceChannelCount += outputStreamInfo->devices[i].channelCount;
1334
                    
1335
                outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
1336

    
1337
                /* check that output device can support outputChannelCount */
1338
                if( outputStreamInfo->devices[i].channelCount < 1 )
1339
                    return paInvalidChannelCount;
1340

    
1341
                paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo, 
1342
                        outputStreamInfo->devices[i].channelCount );
1343
                if( paerror != paNoError )
1344
                    return paerror;
1345

    
1346
                /* test for valid sample rate, see comment above */
1347
                winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
1348
                paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, 
1349
                        winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate,
1350
                        ((outputStreamInfo) ? outputStreamInfo->flags : 0) );
1351
                if( paerror != paNoError )
1352
                    return paInvalidSampleRate;
1353
            }
1354
                
1355
            if( outputMultipleDeviceChannelCount != outputChannelCount )
1356
                return paIncompatibleHostApiSpecificStreamInfo;            
1357
        }
1358
        else
1359
        {
1360
            if( outputStreamInfo && (outputStreamInfo->flags & paWinMmeUseMultipleDevices) )
1361
                return paIncompatibleHostApiSpecificStreamInfo; /* paUseHostApiSpecificDeviceSpecification was not supplied as the output device */
1362

    
1363
            outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
1364

    
1365
            /* check that output device can support outputChannelCount */
1366
            paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo, outputChannelCount );
1367
            if( paerror != paNoError )
1368
                return paerror;
1369

    
1370
            /* test for valid sample rate, see comment above */
1371
            winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
1372
            paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, 
1373
                    winMmeOutputDeviceId, outputChannelCount, sampleRate,
1374
                    ((outputStreamInfo) ? outputStreamInfo->flags : 0) );
1375
            if( paerror != paNoError )
1376
                return paInvalidSampleRate;
1377
        }
1378
    }
1379
    
1380
    /*
1381
            - if a full duplex stream is requested, check that the combination
1382
                of input and output parameters is supported
1383

1384
            - check that the device supports sampleRate
1385

1386
            for mme all we can do is test that the input and output devices
1387
            support the requested sample rate and number of channels. we
1388
            cannot test for full duplex compatibility.
1389
    */                                             
1390

    
1391
    return paFormatIsSupported;
1392
}
1393

    
1394

    
1395
static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames(
1396
        unsigned long suggestedLatencyFrames,
1397
        unsigned long hostBufferSizeFrames,
1398
        unsigned long minimumBufferCount )
1399
{
1400
    /* Calculate the number of buffers of length hostFramesPerBuffer 
1401
       that fit in suggestedLatencyFrames, rounding up to the next integer.
1402

1403
       The value (hostBufferSizeFrames - 1) below is to ensure the buffer count is rounded up.
1404
    */
1405
    unsigned long resultBufferCount = ((suggestedLatencyFrames + (hostBufferSizeFrames - 1)) / hostBufferSizeFrames);
1406

    
1407
    /* We always need one extra buffer for processing while the rest are queued/playing.
1408
       i.e. latency is framesPerBuffer * (bufferCount - 1)
1409
    */
1410
    resultBufferCount += 1;
1411

    
1412
    if( resultBufferCount < minimumBufferCount ) /* clamp to minimum buffer count */
1413
        resultBufferCount = minimumBufferCount;
1414

    
1415
    return resultBufferCount;
1416
}
1417

    
1418

    
1419
static unsigned long ComputeHostBufferSizeGivenHardUpperLimit( 
1420
        unsigned long userFramesPerBuffer,
1421
        unsigned long absoluteMaximumBufferSizeFrames )
1422
{
1423
    static unsigned long primes_[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 
1424
            29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 0 }; /* zero terminated */
1425

    
1426
    unsigned long result = userFramesPerBuffer;
1427
    int i;
1428

    
1429
    assert( absoluteMaximumBufferSizeFrames > 67 ); /* assume maximum is large and we're only factoring by small primes */
1430

    
1431
    /* search for the largest integer factor of userFramesPerBuffer less 
1432
       than or equal to absoluteMaximumBufferSizeFrames */
1433

    
1434
    /* repeatedly divide by smallest prime factors until a buffer size 
1435
       smaller than absoluteMaximumBufferSizeFrames is found */
1436
    while( result > absoluteMaximumBufferSizeFrames ){
1437

    
1438
        /* search for the smallest prime factor of result */
1439
        for( i=0; primes_[i] != 0; ++i ) 
1440
        {
1441
            unsigned long p = primes_[i];
1442
            unsigned long divided = result / p;
1443
            if( divided*p == result )
1444
            {
1445
                result = divided;
1446
                break; /* continue with outer while loop */
1447
            }
1448
        }
1449
        if( primes_[i] == 0 )
1450
        { /* loop failed to find a prime factor, return an approximate result */
1451
            unsigned long d = (userFramesPerBuffer + (absoluteMaximumBufferSizeFrames-1))
1452
                    / absoluteMaximumBufferSizeFrames;
1453
            return userFramesPerBuffer / d;
1454
        }
1455
    }
1456

    
1457
    return result;
1458
}
1459

    
1460

    
1461
static PaError SelectHostBufferSizeFramesAndHostBufferCount(
1462
        unsigned long suggestedLatencyFrames,
1463
        unsigned long userFramesPerBuffer,
1464
        unsigned long minimumBufferCount,
1465
        unsigned long preferredMaximumBufferSizeFrames, /* try not to exceed this. for example, don't exceed when coalescing buffers */
1466
        unsigned long absoluteMaximumBufferSizeFrames,  /* never exceed this, a hard limit */
1467
        unsigned long *hostBufferSizeFrames,
1468
        unsigned long *hostBufferCount )
1469
{
1470
    unsigned long effectiveUserFramesPerBuffer;
1471
    unsigned long numberOfUserBuffersPerHostBuffer;
1472

    
1473

    
1474
    if( userFramesPerBuffer == paFramesPerBufferUnspecified ){
1475

    
1476
        effectiveUserFramesPerBuffer = PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_;
1477

    
1478
    }else{
1479

    
1480
        if( userFramesPerBuffer > absoluteMaximumBufferSizeFrames ){
1481

    
1482
            /* user has requested a user buffer that's larger than absoluteMaximumBufferSizeFrames.
1483
               try to choose a buffer size that is equal or smaller than absoluteMaximumBufferSizeFrames
1484
               but is also an integer factor of userFramesPerBuffer, so as to distribute computation evenly.
1485
               the buffer processor will handle the block adaption between host and user buffer sizes.
1486
               see http://www.portaudio.com/trac/ticket/189 for discussion.
1487
            */
1488

    
1489
            effectiveUserFramesPerBuffer = ComputeHostBufferSizeGivenHardUpperLimit( userFramesPerBuffer, absoluteMaximumBufferSizeFrames );
1490
            assert( effectiveUserFramesPerBuffer <= absoluteMaximumBufferSizeFrames );
1491

    
1492
            /* try to ensure that duration of host buffering is at least as 
1493
                large as duration of user buffer. */
1494
            if( suggestedLatencyFrames < userFramesPerBuffer )
1495
                suggestedLatencyFrames = userFramesPerBuffer; 
1496

    
1497
        }else{
1498

    
1499
            effectiveUserFramesPerBuffer = userFramesPerBuffer;
1500
        }
1501
    }
1502
                        
1503
    /* compute a host buffer count based on suggestedLatencyFrames and our granularity */
1504

    
1505
    *hostBufferSizeFrames = effectiveUserFramesPerBuffer;
1506

    
1507
    *hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
1508
            suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount );
1509

    
1510
    if( *hostBufferSizeFrames >= userFramesPerBuffer )
1511
    {
1512
        /*
1513
            If there are too many host buffers we would like to coalesce 
1514
            them by packing an integer number of user buffers into each host buffer.
1515
            We try to coalesce such that hostBufferCount will lie between 
1516
            PA_MME_TARGET_HOST_BUFFER_COUNT_ and (PA_MME_TARGET_HOST_BUFFER_COUNT_*2)-1.
1517
            We limit coalescing to avoid exceeding either absoluteMaximumBufferSizeFrames and
1518
            preferredMaximumBufferSizeFrames. 
1519

1520
            First, compute a coalescing factor: the number of user buffers per host buffer.
1521
            The goal is to achieve PA_MME_TARGET_HOST_BUFFER_COUNT_ total buffer count.
1522
            Since our latency is computed based on (*hostBufferCount - 1) we compute a
1523
            coalescing factor based on (*hostBufferCount - 1) and (PA_MME_TARGET_HOST_BUFFER_COUNT_-1).
1524

1525
            The + (PA_MME_TARGET_HOST_BUFFER_COUNT_-2) term below is intended to round up.
1526
        */
1527
        numberOfUserBuffersPerHostBuffer = ((*hostBufferCount - 1) + (PA_MME_TARGET_HOST_BUFFER_COUNT_-2)) / (PA_MME_TARGET_HOST_BUFFER_COUNT_ - 1);
1528
        
1529
        if( numberOfUserBuffersPerHostBuffer > 1 )
1530
        {
1531
            unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* minimum of our limits */
1532
                            ? absoluteMaximumBufferSizeFrames
1533
                            : preferredMaximumBufferSizeFrames;
1534

    
1535
            unsigned long maxUserBuffersPerHostBuffer = maxCoalescedBufferSizeFrames / effectiveUserFramesPerBuffer; /* don't coalesce more than this */
1536

    
1537
            if( numberOfUserBuffersPerHostBuffer > maxUserBuffersPerHostBuffer )
1538
                numberOfUserBuffersPerHostBuffer = maxUserBuffersPerHostBuffer;
1539

    
1540
            *hostBufferSizeFrames = effectiveUserFramesPerBuffer * numberOfUserBuffersPerHostBuffer;
1541

    
1542
            /* recompute hostBufferCount to approximate suggestedLatencyFrames now that hostBufferSizeFrames is larger */
1543
            *hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
1544
                    suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount );
1545
        }
1546
    }
1547

    
1548
    return paNoError;
1549
}
1550

    
1551

    
1552
static PaError CalculateMaxHostSampleFrameSizeBytes(
1553
        int channelCount,
1554
        PaSampleFormat hostSampleFormat,
1555
        const PaWinMmeStreamInfo *streamInfo,
1556
        int *hostSampleFrameSizeBytes )
1557
{
1558
    unsigned int i;
1559
    /* PA WMME streams may aggregate multiple WMME devices. When the stream addresses 
1560
       more than one device in a single direction, maxDeviceChannelCount is the maximum 
1561
       number of channels used by a single device.
1562
    */
1563
    int maxDeviceChannelCount = channelCount;
1564
    int hostSampleSizeBytes = Pa_GetSampleSize( hostSampleFormat );
1565
    if( hostSampleSizeBytes < 0 )
1566
    {
1567
        return hostSampleSizeBytes; /* the value of hostSampleSize here is an error code, not a sample size */
1568
    }
1569

    
1570
    if( streamInfo && ( streamInfo->flags & paWinMmeUseMultipleDevices ) )
1571
    {
1572
        maxDeviceChannelCount = streamInfo->devices[0].channelCount;
1573
        for( i=1; i< streamInfo->deviceCount; ++i )
1574
        {
1575
            if( streamInfo->devices[i].channelCount > maxDeviceChannelCount )
1576
                maxDeviceChannelCount = streamInfo->devices[i].channelCount;
1577
        }
1578
    }
1579

    
1580
    *hostSampleFrameSizeBytes = hostSampleSizeBytes * maxDeviceChannelCount;
1581

    
1582
    return paNoError;
1583
}
1584

    
1585

    
1586
/* CalculateBufferSettings() fills the framesPerHostInputBuffer, hostInputBufferCount,
1587
   framesPerHostOutputBuffer and hostOutputBufferCount parameters based on the values
1588
   of the other parameters.
1589
*/
1590

    
1591
static PaError CalculateBufferSettings(
1592
        unsigned long *hostFramesPerInputBuffer, unsigned long *hostInputBufferCount,
1593
        unsigned long *hostFramesPerOutputBuffer, unsigned long *hostOutputBufferCount,
1594
        int inputChannelCount, PaSampleFormat hostInputSampleFormat,
1595
        PaTime suggestedInputLatency, const PaWinMmeStreamInfo *inputStreamInfo,
1596
        int outputChannelCount, PaSampleFormat hostOutputSampleFormat,
1597
        PaTime suggestedOutputLatency, const PaWinMmeStreamInfo *outputStreamInfo,
1598
        double sampleRate, unsigned long userFramesPerBuffer )
1599
{
1600
    PaError result = paNoError;
1601
    
1602
    if( inputChannelCount > 0 ) /* stream has input */
1603
    {
1604
        int hostInputFrameSizeBytes;
1605
        result = CalculateMaxHostSampleFrameSizeBytes( 
1606
                inputChannelCount, hostInputSampleFormat, inputStreamInfo, &hostInputFrameSizeBytes );
1607
        if( result != paNoError )
1608
            goto error;
1609

    
1610
        if( inputStreamInfo
1611
                && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
1612
        {
1613
            /* input - using low level latency parameters if provided */
1614

    
1615
            if( inputStreamInfo->bufferCount <= 0
1616
                    || inputStreamInfo->framesPerBuffer <= 0 )
1617
            {
1618
                result = paIncompatibleHostApiSpecificStreamInfo;
1619
                goto error;
1620
            }
1621

    
1622
            *hostFramesPerInputBuffer = inputStreamInfo->framesPerBuffer;
1623
            *hostInputBufferCount = inputStreamInfo->bufferCount;
1624
        }
1625
        else
1626
        {
1627
            /* input - not using low level latency parameters, so compute 
1628
               hostFramesPerInputBuffer and hostInputBufferCount
1629
               based on userFramesPerBuffer and suggestedInputLatency. */
1630

    
1631
            unsigned long minimumBufferCount = (outputChannelCount > 0)
1632
                    ? PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_
1633
                    : PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
1634

    
1635
            result = SelectHostBufferSizeFramesAndHostBufferCount(
1636
                    (unsigned long)(suggestedInputLatency * sampleRate), /* (truncate) */
1637
                    userFramesPerBuffer,
1638
                    minimumBufferCount,
1639
                    (unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */
1640
                    (PA_MME_MAX_HOST_BUFFER_BYTES_ / hostInputFrameSizeBytes),  /* in frames. a hard limit. note truncation due to 
1641
                                                                                division is intentional here to limit max bytes */
1642
                    hostFramesPerInputBuffer,
1643
                    hostInputBufferCount );
1644
            if( result != paNoError )
1645
                goto error;
1646
        }
1647
    }
1648
    else
1649
    {
1650
        *hostFramesPerInputBuffer = 0;
1651
        *hostInputBufferCount = 0;
1652
    }
1653

    
1654
    if( outputChannelCount > 0 ) /* stream has output */
1655
    {
1656
        if( outputStreamInfo
1657
                && ( outputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
1658
        {
1659
            /* output - using low level latency parameters */
1660

    
1661
            if( outputStreamInfo->bufferCount <= 0
1662
                    || outputStreamInfo->framesPerBuffer <= 0 )
1663
            {
1664
                result = paIncompatibleHostApiSpecificStreamInfo;
1665
                goto error;
1666
            }
1667

    
1668
            *hostFramesPerOutputBuffer = outputStreamInfo->framesPerBuffer;
1669
            *hostOutputBufferCount = outputStreamInfo->bufferCount;
1670

    
1671
            if( inputChannelCount > 0 ) /* full duplex */
1672
            {
1673
                /* harmonize hostFramesPerInputBuffer and hostFramesPerOutputBuffer */
1674

    
1675
                if( *hostFramesPerInputBuffer != *hostFramesPerOutputBuffer )
1676
                {
1677
                    if( inputStreamInfo
1678
                            && ( inputStreamInfo->flags & paWinMmeUseLowLevelLatencyParameters ) )
1679
                    { 
1680
                        /* a custom StreamInfo was used for specifying both input
1681
                            and output buffer sizes. We require that the larger buffer size
1682
                            must be a multiple of the smaller buffer size */
1683

    
1684
                        if( *hostFramesPerInputBuffer < *hostFramesPerOutputBuffer )
1685
                        {
1686
                            if( *hostFramesPerOutputBuffer % *hostFramesPerInputBuffer != 0 )
1687
                            {
1688
                                result = paIncompatibleHostApiSpecificStreamInfo;
1689
                                goto error;
1690
                            }
1691
                        }
1692
                        else
1693
                        {
1694
                            assert( *hostFramesPerInputBuffer > *hostFramesPerOutputBuffer );
1695
                            if( *hostFramesPerInputBuffer % *hostFramesPerOutputBuffer != 0 )
1696
                            {
1697
                                result = paIncompatibleHostApiSpecificStreamInfo;
1698
                                goto error;
1699
                            }
1700
                        }                        
1701
                    }
1702
                    else
1703
                    {
1704
                        /* a custom StreamInfo was not used for specifying the input buffer size,
1705
                            so use the output buffer size, and approximately the suggested input latency. */
1706

    
1707
                        *hostFramesPerInputBuffer = *hostFramesPerOutputBuffer;
1708

    
1709
                        *hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
1710
                                (unsigned long)(suggestedInputLatency * sampleRate), 
1711
                                *hostFramesPerInputBuffer, 
1712
                                PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ );
1713
                    }
1714
                }
1715
            }
1716
        }
1717
        else
1718
        {
1719
            /* output - no low level latency parameters, so compute hostFramesPerOutputBuffer and hostOutputBufferCount
1720
                based on userFramesPerBuffer and suggestedOutputLatency. */
1721

    
1722
            int hostOutputFrameSizeBytes;
1723
            result = CalculateMaxHostSampleFrameSizeBytes( 
1724
                    outputChannelCount, hostOutputSampleFormat, outputStreamInfo, &hostOutputFrameSizeBytes );
1725
            if( result != paNoError )
1726
                goto error;
1727

    
1728
            /* compute the output buffer size and count */
1729

    
1730
            result = SelectHostBufferSizeFramesAndHostBufferCount(
1731
                    (unsigned long)(suggestedOutputLatency * sampleRate), /* (truncate) */
1732
                    userFramesPerBuffer,
1733
                    PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_,
1734
                    (unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */
1735
                    (PA_MME_MAX_HOST_BUFFER_BYTES_ / hostOutputFrameSizeBytes),  /* in frames. a hard limit. note truncation due to 
1736
                                                                                 division is intentional here to limit max bytes */
1737
                    hostFramesPerOutputBuffer,
1738
                    hostOutputBufferCount );
1739
            if( result != paNoError )
1740
                goto error;
1741

    
1742
            if( inputChannelCount > 0 ) /* full duplex */
1743
            {
1744
                /* harmonize hostFramesPerInputBuffer and hostFramesPerOutputBuffer */
1745

    
1746
                /* ensure that both input and output buffer sizes are the same.
1747
                    if they don't match at this stage, choose the smallest one
1748
                    and use that for input and output and recompute the corresponding
1749
                    buffer count accordingly.
1750
                */
1751

    
1752
                if( *hostFramesPerOutputBuffer != *hostFramesPerInputBuffer )
1753
                {
1754
                    if( hostFramesPerInputBuffer < hostFramesPerOutputBuffer )
1755
                    {
1756
                        *hostFramesPerOutputBuffer = *hostFramesPerInputBuffer;
1757

    
1758
                        *hostOutputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
1759
                                (unsigned long)(suggestedOutputLatency * sampleRate), 
1760
                                *hostOutputBufferCount, 
1761
                                PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ );
1762
                    }
1763
                    else
1764
                    {
1765
                        *hostFramesPerInputBuffer = *hostFramesPerOutputBuffer;
1766

    
1767
                        *hostInputBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
1768
                                (unsigned long)(suggestedInputLatency * sampleRate), 
1769
                                *hostFramesPerInputBuffer, 
1770
                                PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_FULL_DUPLEX_ );
1771
                    }
1772
                }   
1773
            }
1774
        }
1775
    }
1776
    else
1777
    {
1778
        *hostFramesPerOutputBuffer = 0;
1779
        *hostOutputBufferCount = 0;
1780
    }
1781

    
1782
error:
1783
    return result;
1784
}
1785

    
1786

    
1787
typedef struct
1788
{
1789
    HANDLE bufferEvent;
1790
    void *waveHandles;
1791
    unsigned int deviceCount;
1792
    /* unsigned int channelCount; */
1793
    WAVEHDR **waveHeaders;                  /* waveHeaders[device][buffer] */
1794
    unsigned int bufferCount;
1795
    unsigned int currentBufferIndex;
1796
    unsigned int framesPerBuffer;
1797
    unsigned int framesUsedInCurrentBuffer;
1798
}PaWinMmeSingleDirectionHandlesAndBuffers;
1799

    
1800
/* prototypes for functions operating on PaWinMmeSingleDirectionHandlesAndBuffers */
1801

    
1802
static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers );
1803
static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
1804
        PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
1805
        unsigned long winMmeSpecificFlags,
1806
        unsigned long bytesPerHostSample,
1807
        double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
1808
        unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput );
1809
static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
1810
static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
1811
        unsigned long hostBufferCount,
1812
        PaSampleFormat hostSampleFormat,
1813
        unsigned long framesPerHostBuffer,
1814
        PaWinMmeDeviceAndChannelCount *devices,
1815
        int isInput );
1816
static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput );
1817

    
1818

    
1819
static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
1820
{
1821
    handlesAndBuffers->bufferEvent = 0;
1822
    handlesAndBuffers->waveHandles = 0;
1823
    handlesAndBuffers->deviceCount = 0;
1824
    handlesAndBuffers->waveHeaders = 0;
1825
    handlesAndBuffers->bufferCount = 0;
1826
}    
1827

    
1828
static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
1829
        PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
1830
        unsigned long winMmeSpecificFlags,
1831
        unsigned long bytesPerHostSample,
1832
        double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
1833
        unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput )
1834
{
1835
    PaError result;
1836
    MMRESULT mmresult;
1837
    signed int i, j;
1838
    PaSampleFormat sampleFormat;
1839
    int waveFormatTag;
1840

    
1841
    /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
1842
        has already been called to zero some fields */       
1843

    
1844
    result = CreateEventWithPaError( &handlesAndBuffers->bufferEvent, NULL, FALSE, FALSE, NULL );
1845
    if( result != paNoError ) goto error;
1846

    
1847
    if( isInput )
1848
        handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEIN) * deviceCount );
1849
    else
1850
        handlesAndBuffers->waveHandles = (void*)PaUtil_AllocateMemory( sizeof(HWAVEOUT) * deviceCount );
1851
    if( !handlesAndBuffers->waveHandles )
1852
    {
1853
        result = paInsufficientMemory;
1854
        goto error;
1855
    }
1856

    
1857
    handlesAndBuffers->deviceCount = deviceCount;
1858

    
1859
    for( i = 0; i < (signed int)deviceCount; ++i )
1860
    {
1861
        if( isInput )
1862
            ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] = 0;
1863
        else
1864
            ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
1865
    }
1866

    
1867
    /* @todo at the moment we only use 16 bit sample format */
1868
    sampleFormat = paInt16;
1869
    waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
1870

    
1871
    for( i = 0; i < (signed int)deviceCount; ++i )
1872
    {
1873
        PaWinWaveFormat waveFormat;
1874
        UINT winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
1875
    
1876
        /* @todo: consider providing a flag or #define to not try waveformat extensible 
1877
           this could just initialize j to 1 the first time round. */
1878

    
1879
        for( j = 0; j < 2; ++j )
1880
        {
1881
            switch(j){
1882
                case 0:     
1883
                    /* first, attempt to open the device using WAVEFORMATEXTENSIBLE, 
1884
                        if this fails we fall back to WAVEFORMATEX */
1885

    
1886
                    PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount, 
1887
                            sampleFormat, waveFormatTag, sampleRate, channelMask );
1888
                    break;
1889
                
1890
                case 1:
1891
                    /* retry with WAVEFORMATEX */
1892

    
1893
                    PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount, 
1894
                            sampleFormat, waveFormatTag, sampleRate );
1895
                    break;
1896
            }
1897

    
1898
            /* REVIEW: consider not firing an event for input when a full duplex
1899
                stream is being used. this would probably depend on the
1900
                neverDropInput flag. */
1901

    
1902
            if( isInput )
1903
            {
1904
                mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, 
1905
                                    (WAVEFORMATEX*)&waveFormat,
1906
                               (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
1907
            }
1908
            else
1909
            {
1910
                mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, 
1911
                                    (WAVEFORMATEX*)&waveFormat,
1912
                                (DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
1913
            }
1914

    
1915
            if( mmresult == MMSYSERR_NOERROR )
1916
            {
1917
                break; /* success */
1918
            }
1919
            else if( j == 0 )
1920
            {
1921
                continue; /* try again with WAVEFORMATEX */
1922
            }
1923
            else
1924
            {
1925
                switch( mmresult )
1926
                {
1927
                    case MMSYSERR_ALLOCATED:    /* Specified resource is already allocated. */
1928
                        result = paDeviceUnavailable;
1929
                        break;
1930
                    case MMSYSERR_NODRIVER:            /* No device driver is present. */
1931
                        result = paDeviceUnavailable;
1932
                        break;
1933
                    case MMSYSERR_NOMEM:            /* Unable to allocate or lock memory. */
1934
                        result = paInsufficientMemory;
1935
                        break;
1936

    
1937
                    case MMSYSERR_BADDEVICEID:        /* Specified device identifier is out of range. */
1938
                        /* falls through */
1939

    
1940
                    case WAVERR_BADFORMAT:      /* Attempted to open with an unsupported waveform-audio format. */
1941
                                                    /* This can also occur if we try to open the device with an unsupported
1942
                                                     * number of channels. This is attempted when device*ChannelCountIsKnown is
1943
                                                     * set to 0. 
1944
                                                     */
1945
                        /* falls through */
1946
                    default:
1947
                        result = paUnanticipatedHostError;
1948
                        if( isInput )
1949
                        {
1950
                            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
1951
                        }
1952
                        else
1953
                        {
1954
                            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
1955
                        }
1956
                }
1957
                goto error;
1958
            }
1959
        }
1960
    }
1961

    
1962
    return result;
1963

    
1964
error:
1965
    TerminateWaveHandles( handlesAndBuffers, isInput, 1 /* currentlyProcessingAnError */ );
1966

    
1967
    return result;
1968
}
1969

    
1970

    
1971
static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError )
1972
{
1973
    PaError result = paNoError;
1974
    MMRESULT mmresult;
1975
    signed int i;
1976
    
1977
    if( handlesAndBuffers->waveHandles )
1978
    {
1979
        for( i = handlesAndBuffers->deviceCount-1; i >= 0; --i )
1980
        {
1981
            if( isInput )
1982
            {
1983
                if( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] )
1984
                    mmresult = waveInClose( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i] );
1985
                else
1986
                    mmresult = MMSYSERR_NOERROR;
1987
            }
1988
            else
1989
            {
1990
                if( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] )
1991
                    mmresult = waveOutClose( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] );
1992
                else
1993
                    mmresult = MMSYSERR_NOERROR;
1994
            }
1995

    
1996
            if( mmresult != MMSYSERR_NOERROR &&
1997
                !currentlyProcessingAnError ) /* don't update the error state if we're already processing an error */
1998
            {
1999
                result = paUnanticipatedHostError;
2000
                if( isInput )
2001
                {
2002
                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
2003
                }
2004
                else
2005
                {
2006
                    PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
2007
                }
2008
                /* note that we don't break here, we try to continue closing devices */
2009
            }
2010
        }
2011

    
2012
        PaUtil_FreeMemory( handlesAndBuffers->waveHandles );
2013
        handlesAndBuffers->waveHandles = 0;
2014
    }
2015

    
2016
    if( handlesAndBuffers->bufferEvent )
2017
    {
2018
        result = CloseHandleWithPaError( handlesAndBuffers->bufferEvent );
2019
        handlesAndBuffers->bufferEvent = 0;
2020
    }
2021
    
2022
    return result;
2023
}
2024

    
2025

    
2026
static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
2027
        unsigned long hostBufferCount,
2028
        PaSampleFormat hostSampleFormat,
2029
        unsigned long framesPerHostBuffer,
2030
        PaWinMmeDeviceAndChannelCount *devices,
2031
        int isInput )
2032
{
2033
    PaError result = paNoError;
2034
    MMRESULT mmresult;
2035
    WAVEHDR *deviceWaveHeaders;
2036
    signed int i, j;
2037

    
2038
    /* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
2039
        has already been called to zero some fields */
2040
        
2041

    
2042
    /* allocate an array of pointers to arrays of wave headers, one array of
2043
        wave headers per device */
2044
    handlesAndBuffers->waveHeaders = (WAVEHDR**)PaUtil_AllocateMemory( sizeof(WAVEHDR*) * handlesAndBuffers->deviceCount );
2045
    if( !handlesAndBuffers->waveHeaders )
2046
    {
2047
        result = paInsufficientMemory;
2048
        goto error;
2049
    }
2050
    
2051
    for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
2052
        handlesAndBuffers->waveHeaders[i] = 0;
2053

    
2054
    handlesAndBuffers->bufferCount = hostBufferCount;
2055

    
2056
    for( i = 0; i < (signed int)handlesAndBuffers->deviceCount; ++i )
2057
    {
2058
        int bufferBytes = Pa_GetSampleSize( hostSampleFormat ) *
2059
                framesPerHostBuffer * devices[i].channelCount;
2060
        if( bufferBytes < 0 )
2061
        {
2062
            result = paInternalError;
2063
            goto error;
2064
        }
2065

    
2066
        /* Allocate an array of wave headers for device i */
2067
        deviceWaveHeaders = (WAVEHDR *) PaUtil_AllocateMemory( sizeof(WAVEHDR)*hostBufferCount );
2068
        if( !deviceWaveHeaders )
2069
        {
2070
            result = paInsufficientMemory;
2071
            goto error;
2072
        }
2073

    
2074
        for( j=0; j < (signed int)hostBufferCount; ++j )
2075
            deviceWaveHeaders[j].lpData = 0;
2076

    
2077
        handlesAndBuffers->waveHeaders[i] = deviceWaveHeaders;
2078

    
2079
        /* Allocate a buffer for each wave header */
2080
        for( j=0; j < (signed int)hostBufferCount; ++j )
2081
        {
2082
            deviceWaveHeaders[j].lpData = (char *)PaUtil_AllocateMemory( bufferBytes );
2083
            if( !deviceWaveHeaders[j].lpData )
2084
            {
2085
                result = paInsufficientMemory;
2086
                goto error;
2087
            }
2088
            deviceWaveHeaders[j].dwBufferLength = bufferBytes;
2089
            deviceWaveHeaders[j].dwUser = 0xFFFFFFFF; /* indicates that *PrepareHeader() has not yet been called, for error clean up code */
2090

    
2091
            if( isInput )
2092
            {
2093
                mmresult = waveInPrepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
2094
                if( mmresult != MMSYSERR_NOERROR )
2095
                {
2096
                    result = paUnanticipatedHostError;
2097
                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
2098
                    goto error;
2099
                }
2100
            }
2101
            else /* output */
2102
            {
2103
                mmresult = waveOutPrepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
2104
                if( mmresult != MMSYSERR_NOERROR )
2105
                {
2106
                    result = paUnanticipatedHostError;
2107
                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
2108
                    goto error;
2109
                }
2110
            }
2111
            deviceWaveHeaders[j].dwUser = devices[i].channelCount;
2112
        }
2113
    }
2114

    
2115
    return result;
2116

    
2117
error:
2118
    TerminateWaveHeaders( handlesAndBuffers, isInput );
2119
    
2120
    return result;
2121
}
2122

    
2123

    
2124
static void TerminateWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput )
2125
{
2126
    signed int i, j;
2127
    WAVEHDR *deviceWaveHeaders;
2128
    
2129
    if( handlesAndBuffers->waveHeaders )
2130
    {
2131
        for( i = handlesAndBuffers->deviceCount-1; i >= 0 ; --i )
2132
        {
2133
            deviceWaveHeaders = handlesAndBuffers->waveHeaders[i];  /* wave headers for device i */
2134
            if( deviceWaveHeaders )
2135
            {
2136
                for( j = handlesAndBuffers->bufferCount-1; j >= 0; --j )
2137
                {
2138
                    if( deviceWaveHeaders[j].lpData )
2139
                    {
2140
                        if( deviceWaveHeaders[j].dwUser != 0xFFFFFFFF )
2141
                        {
2142
                            if( isInput )
2143
                                waveInUnprepareHeader( ((HWAVEIN*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
2144
                            else
2145
                                waveOutUnprepareHeader( ((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], &deviceWaveHeaders[j], sizeof(WAVEHDR) );
2146
                        }
2147

    
2148
                        PaUtil_FreeMemory( deviceWaveHeaders[j].lpData );
2149
                    }
2150
                }
2151

    
2152
                PaUtil_FreeMemory( deviceWaveHeaders );
2153
            }
2154
        }
2155

    
2156
        PaUtil_FreeMemory( handlesAndBuffers->waveHeaders );
2157
        handlesAndBuffers->waveHeaders = 0;
2158
    }
2159
}
2160

    
2161

    
2162

    
2163
/* PaWinMmeStream - a stream data structure specifically for this implementation */
2164
/* note that struct PaWinMmeStream is typedeffed to PaWinMmeStream above. */
2165
struct PaWinMmeStream
2166
{
2167
    PaUtilStreamRepresentation streamRepresentation;
2168
    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
2169
    PaUtilBufferProcessor bufferProcessor;
2170

    
2171
    int primeStreamUsingCallback;
2172

    
2173
    PaWinMmeSingleDirectionHandlesAndBuffers input;
2174
    PaWinMmeSingleDirectionHandlesAndBuffers output;
2175

    
2176
    /* Processing thread management -------------- */
2177
    HANDLE abortEvent;
2178
    HANDLE processingThread;
2179
    PA_THREAD_ID processingThreadId;
2180

    
2181
    char throttleProcessingThreadOnOverload; /* 0 -> don't throtte, non-0 -> throttle */
2182
    int processingThreadPriority;
2183
    int highThreadPriority;
2184
    int throttledThreadPriority;
2185
    unsigned long throttledSleepMsecs;
2186

    
2187
    int isStopped;
2188
    volatile int isActive;
2189
    volatile int stopProcessing; /* stop thread once existing buffers have been returned */
2190
    volatile int abortProcessing; /* stop thread immediately */
2191

    
2192
    DWORD allBuffersDurationMs; /* used to calculate timeouts */
2193
};
2194

    
2195
/* updates deviceCount if PaWinMmeUseMultipleDevices is used */
2196

    
2197
static PaError ValidateWinMmeSpecificStreamInfo(
2198
        const PaStreamParameters *streamParameters,
2199
        const PaWinMmeStreamInfo *streamInfo,
2200
        unsigned long *winMmeSpecificFlags,
2201
        char *throttleProcessingThreadOnOverload,
2202
        unsigned long *deviceCount )
2203
{
2204
    if( streamInfo )
2205
    {
2206
        if( streamInfo->size != sizeof( PaWinMmeStreamInfo )
2207
                || streamInfo->version != 1 )
2208
        {
2209
            return paIncompatibleHostApiSpecificStreamInfo;
2210
        }
2211

    
2212
        *winMmeSpecificFlags = streamInfo->flags;
2213

    
2214
        if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
2215
            *throttleProcessingThreadOnOverload = 0;
2216
            
2217
        if( streamInfo->flags & paWinMmeUseMultipleDevices )
2218
        {
2219
            if( streamParameters->device != paUseHostApiSpecificDeviceSpecification )
2220
                return paInvalidDevice;
2221
    
2222
            *deviceCount = streamInfo->deviceCount;
2223
        }        
2224
    }
2225

    
2226
    return paNoError;
2227
}
2228

    
2229
static PaError RetrieveDevicesFromStreamParameters(
2230
        struct PaUtilHostApiRepresentation *hostApi,
2231
        const PaStreamParameters *streamParameters,
2232
        const PaWinMmeStreamInfo *streamInfo,
2233
        PaWinMmeDeviceAndChannelCount *devices,
2234
        unsigned long deviceCount )
2235
{
2236
    PaError result = paNoError;
2237
    unsigned int i;
2238
    int totalChannelCount;
2239
    PaDeviceIndex hostApiDevice;
2240
    
2241
    if( streamInfo && streamInfo->flags & paWinMmeUseMultipleDevices )
2242
    {
2243
        totalChannelCount = 0;
2244
        for( i=0; i < deviceCount; ++i )
2245
        {
2246
            /* validate that the device number is within range */
2247
            result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice,
2248
                            streamInfo->devices[i].device, hostApi );
2249
            if( result != paNoError )
2250
                return result;
2251
            
2252
            devices[i].device = hostApiDevice;
2253
            devices[i].channelCount = streamInfo->devices[i].channelCount;
2254
    
2255
            totalChannelCount += devices[i].channelCount;
2256
        }
2257
    
2258
        if( totalChannelCount != streamParameters->channelCount )
2259
        {
2260
            /* channelCount must match total channels specified by multiple devices */
2261
            return paInvalidChannelCount; /* REVIEW use of this error code */
2262
        }
2263
    }        
2264
    else
2265
    {                
2266
        devices[0].device = streamParameters->device;
2267
        devices[0].channelCount = streamParameters->channelCount;
2268
    }
2269

    
2270
    return result;
2271
}
2272

    
2273
static PaError ValidateInputChannelCounts(
2274
        struct PaUtilHostApiRepresentation *hostApi,
2275
        PaWinMmeDeviceAndChannelCount *devices,
2276
        unsigned long deviceCount )
2277
{
2278
    unsigned int i;
2279
    PaWinMmeDeviceInfo *inputDeviceInfo;
2280
    PaError paerror;
2281

    
2282
    for( i=0; i < deviceCount; ++i )
2283
    {
2284
        if( devices[i].channelCount < 1 )
2285
            return paInvalidChannelCount;
2286

    
2287
        inputDeviceInfo = 
2288
                (PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
2289

    
2290
        paerror = IsInputChannelCountSupported( inputDeviceInfo, devices[i].channelCount );
2291
        if( paerror != paNoError )
2292
            return paerror;
2293
    }
2294

    
2295
    return paNoError;
2296
}
2297

    
2298
static PaError ValidateOutputChannelCounts(
2299
        struct PaUtilHostApiRepresentation *hostApi,
2300
        PaWinMmeDeviceAndChannelCount *devices,
2301
        unsigned long deviceCount )
2302
{
2303
    unsigned int i;
2304
    PaWinMmeDeviceInfo *outputDeviceInfo;
2305
    PaError paerror;
2306

    
2307
    for( i=0; i < deviceCount; ++i )
2308
    {
2309
        if( devices[i].channelCount < 1 )
2310
            return paInvalidChannelCount;
2311

    
2312
        outputDeviceInfo = 
2313
                (PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
2314

    
2315
        paerror = IsOutputChannelCountSupported( outputDeviceInfo, devices[i].channelCount );
2316
        if( paerror != paNoError )
2317
            return paerror;
2318
    }
2319

    
2320
    return paNoError;
2321
}
2322

    
2323

    
2324
/* the following macros are intended to improve the readability of the following code */
2325
#define PA_IS_INPUT_STREAM_( stream ) ( stream ->input.waveHandles )
2326
#define PA_IS_OUTPUT_STREAM_( stream ) ( stream ->output.waveHandles )
2327
#define PA_IS_FULL_DUPLEX_STREAM_( stream ) ( stream ->input.waveHandles && stream ->output.waveHandles )
2328
#define PA_IS_HALF_DUPLEX_STREAM_( stream ) ( !(stream ->input.waveHandles && stream ->output.waveHandles) )
2329

    
2330
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
2331
                           PaStream** s,
2332
                           const PaStreamParameters *inputParameters,
2333
                           const PaStreamParameters *outputParameters,
2334
                           double sampleRate,
2335
                           unsigned long framesPerBuffer,
2336
                           PaStreamFlags streamFlags,
2337
                           PaStreamCallback *streamCallback,
2338
                           void *userData )
2339
{
2340
    PaError result;
2341
    PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
2342
    PaWinMmeStream *stream = 0;
2343
    int bufferProcessorIsInitialized = 0;
2344
    int streamRepresentationIsInitialized = 0;
2345
    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
2346
    int inputChannelCount, outputChannelCount;
2347
    PaSampleFormat inputSampleFormat, outputSampleFormat;
2348
    double suggestedInputLatency, suggestedOutputLatency;
2349
    PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
2350
    PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
2351
    unsigned long framesPerHostInputBuffer;
2352
    unsigned long hostInputBufferCount;
2353
    unsigned long framesPerHostOutputBuffer;
2354
    unsigned long hostOutputBufferCount;
2355
    unsigned long framesPerBufferProcessorCall;
2356
    PaWinMmeDeviceAndChannelCount *inputDevices = 0;  /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
2357
    unsigned long winMmeSpecificInputFlags = 0;
2358
    unsigned long inputDeviceCount = 0;            
2359
    PaWinMmeDeviceAndChannelCount *outputDevices = 0;
2360
    unsigned long winMmeSpecificOutputFlags = 0;
2361
    unsigned long outputDeviceCount = 0;                /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
2362
    char throttleProcessingThreadOnOverload = 1;
2363

    
2364
    
2365
    if( inputParameters )
2366
    {
2367
        inputChannelCount = inputParameters->channelCount;
2368
        inputSampleFormat = inputParameters->sampleFormat;
2369
        suggestedInputLatency = inputParameters->suggestedLatency;
2370

    
2371
        inputDeviceCount = 1;
2372

    
2373
        /* validate input hostApiSpecificStreamInfo */
2374
        inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
2375
        result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo,
2376
                &winMmeSpecificInputFlags,
2377
                &throttleProcessingThreadOnOverload,
2378
                &inputDeviceCount );
2379
        if( result != paNoError ) return result;
2380

    
2381
        inputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * inputDeviceCount );
2382
        if( !inputDevices ) return paInsufficientMemory;
2383

    
2384
        result = RetrieveDevicesFromStreamParameters( hostApi, inputParameters, inputStreamInfo, inputDevices, inputDeviceCount );
2385
        if( result != paNoError ) return result;
2386

    
2387
        result = ValidateInputChannelCounts( hostApi, inputDevices, inputDeviceCount );
2388
        if( result != paNoError ) return result;
2389

    
2390
        hostInputSampleFormat =
2391
            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
2392

    
2393
        if( inputDeviceCount != 1 ){
2394
            /* always use direct speakers when using multi-device multichannel mode */
2395
            inputChannelMask = PAWIN_SPEAKER_DIRECTOUT;           
2396
        }
2397
        else
2398
        {
2399
            if( inputStreamInfo && inputStreamInfo->flags & paWinMmeUseChannelMask )
2400
                inputChannelMask = inputStreamInfo->channelMask;
2401
            else
2402
                inputChannelMask = PaWin_DefaultChannelMask( inputDevices[0].channelCount );
2403
        }
2404
    }
2405
    else
2406
    {
2407
        inputChannelCount = 0;
2408
        inputSampleFormat = 0;
2409
        suggestedInputLatency = 0.;
2410
        inputStreamInfo = 0;
2411
        hostInputSampleFormat = 0;
2412
    }
2413

    
2414

    
2415
    if( outputParameters )
2416
    {
2417
        outputChannelCount = outputParameters->channelCount;
2418
        outputSampleFormat = outputParameters->sampleFormat;
2419
        suggestedOutputLatency = outputParameters->suggestedLatency;
2420

    
2421
        outputDeviceCount = 1;
2422

    
2423
        /* validate output hostApiSpecificStreamInfo */
2424
        outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
2425
        result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo,
2426
                &winMmeSpecificOutputFlags,
2427
                &throttleProcessingThreadOnOverload,
2428
                &outputDeviceCount );
2429
        if( result != paNoError ) return result;
2430

    
2431
        outputDevices = (PaWinMmeDeviceAndChannelCount*)alloca( sizeof(PaWinMmeDeviceAndChannelCount) * outputDeviceCount );
2432
        if( !outputDevices ) return paInsufficientMemory;
2433

    
2434
        result = RetrieveDevicesFromStreamParameters( hostApi, outputParameters, outputStreamInfo, outputDevices, outputDeviceCount );
2435
        if( result != paNoError ) return result;
2436

    
2437
        result = ValidateOutputChannelCounts( hostApi, outputDevices, outputDeviceCount );
2438
        if( result != paNoError ) return result;
2439

    
2440
        hostOutputSampleFormat =
2441
            PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
2442

    
2443
        if( outputDeviceCount != 1 ){
2444
            /* always use direct speakers when using multi-device multichannel mode */
2445
            outputChannelMask = PAWIN_SPEAKER_DIRECTOUT;           
2446
        }
2447
        else
2448
        {
2449
            if( outputStreamInfo && outputStreamInfo->flags & paWinMmeUseChannelMask )
2450
                outputChannelMask = outputStreamInfo->channelMask;
2451
            else
2452
                outputChannelMask = PaWin_DefaultChannelMask( outputDevices[0].channelCount );
2453
        }
2454
    }
2455
    else
2456
    {
2457
        outputChannelCount = 0;
2458
        outputSampleFormat = 0;
2459
        outputStreamInfo = 0;
2460
        hostOutputSampleFormat = 0;
2461
        suggestedOutputLatency = 0.;
2462
    }
2463

    
2464

    
2465
    /*
2466
        IMPLEMENT ME:
2467
            - alter sampleRate to a close allowable rate if possible / necessary
2468
    */
2469

    
2470

    
2471
    /* validate platform specific flags */
2472
    if( (streamFlags & paPlatformSpecificFlags) != 0 )
2473
        return paInvalidFlag; /* unexpected platform specific flag */
2474

    
2475

    
2476
    /* always disable clipping and dithering if we are outputting a raw spdif stream */
2477
    if( (winMmeSpecificOutputFlags & paWinMmeWaveFormatDolbyAc3Spdif)
2478
            || (winMmeSpecificOutputFlags & paWinMmeWaveFormatWmaSpdif) ){
2479

    
2480
        streamFlags = streamFlags | paClipOff | paDitherOff;
2481
    }
2482

    
2483

    
2484
    result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount,
2485
                &framesPerHostOutputBuffer, &hostOutputBufferCount,
2486
                inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo,
2487
                outputChannelCount, hostOutputSampleFormat, suggestedOutputLatency, outputStreamInfo,
2488
                sampleRate, framesPerBuffer );
2489
    if( result != paNoError ) goto error;
2490

    
2491

    
2492
    stream = (PaWinMmeStream*)PaUtil_AllocateMemory( sizeof(PaWinMmeStream) );
2493
    if( !stream )
2494
    {
2495
        result = paInsufficientMemory;
2496
        goto error;
2497
    }
2498

    
2499
    InitializeSingleDirectionHandlesAndBuffers( &stream->input );
2500
    InitializeSingleDirectionHandlesAndBuffers( &stream->output );
2501

    
2502
    stream->abortEvent = 0;
2503
    stream->processingThread = 0;
2504

    
2505
    stream->throttleProcessingThreadOnOverload = throttleProcessingThreadOnOverload;
2506

    
2507
    PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2508
                                           ( (streamCallback)
2509
                                            ? &winMmeHostApi->callbackStreamInterface
2510
                                            : &winMmeHostApi->blockingStreamInterface ),
2511
                                           streamCallback, userData );
2512
    streamRepresentationIsInitialized = 1;
2513

    
2514
    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
2515

    
2516

    
2517
    if( inputParameters && outputParameters ) /* full duplex */
2518
    {
2519
        if( framesPerHostInputBuffer < framesPerHostOutputBuffer )
2520
        {
2521
            assert( (framesPerHostOutputBuffer % framesPerHostInputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
2522

    
2523
            framesPerBufferProcessorCall = framesPerHostInputBuffer;
2524
        }
2525
        else
2526
        {
2527
            assert( (framesPerHostInputBuffer % framesPerHostOutputBuffer) == 0 ); /* CalculateBufferSettings() should guarantee this condition */
2528
            
2529
            framesPerBufferProcessorCall = framesPerHostOutputBuffer;
2530
        }
2531
    }
2532
    else if( inputParameters )
2533
    {
2534
        framesPerBufferProcessorCall = framesPerHostInputBuffer;
2535
    }
2536
    else if( outputParameters )
2537
    {
2538
        framesPerBufferProcessorCall = framesPerHostOutputBuffer;
2539
    }
2540

    
2541
    stream->input.framesPerBuffer = framesPerHostInputBuffer;
2542
    stream->output.framesPerBuffer = framesPerHostOutputBuffer;
2543

    
2544
    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2545
                    inputChannelCount, inputSampleFormat, hostInputSampleFormat,
2546
                    outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
2547
                    sampleRate, streamFlags, framesPerBuffer,
2548
                    framesPerBufferProcessorCall, paUtilFixedHostBufferSize,
2549
                    streamCallback, userData );
2550
    if( result != paNoError ) goto error;
2551
    
2552
    bufferProcessorIsInitialized = 1;
2553

    
2554
    /* stream info input latency is the minimum buffering latency (unlike suggested and default which are *maximums*) */
2555
    stream->streamRepresentation.streamInfo.inputLatency =
2556
            (double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
2557
                + framesPerHostInputBuffer) / sampleRate;
2558
    stream->streamRepresentation.streamInfo.outputLatency =
2559
            (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
2560
                + (framesPerHostOutputBuffer * (hostOutputBufferCount-1))) / sampleRate;
2561
    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2562

    
2563
    stream->primeStreamUsingCallback = ( (streamFlags&paPrimeOutputBuffersUsingStreamCallback) && streamCallback ) ? 1 : 0;
2564

    
2565
    /* time to sleep when throttling due to >100% cpu usage.
2566
        -a quater of a buffer's duration */
2567
    stream->throttledSleepMsecs =
2568
            (unsigned long)(stream->bufferProcessor.framesPerHostBuffer *
2569
             stream->bufferProcessor.samplePeriod * .25 * 1000);
2570

    
2571
    stream->isStopped = 1;
2572
    stream->isActive = 0;
2573

    
2574

    
2575
    /* for maximum compatibility with multi-device multichannel drivers,
2576
        we first open all devices, then we prepare all buffers, finally
2577
        we start all devices ( in StartStream() ). teardown in reverse order.
2578
    */
2579

    
2580
    if( inputParameters )
2581
    {
2582
        result = InitializeWaveHandles( winMmeHostApi, &stream->input,
2583
                winMmeSpecificInputFlags,
2584
                stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
2585
                inputDevices, inputDeviceCount, inputChannelMask, 1 /* isInput */ );
2586
        if( result != paNoError ) goto error;
2587
    }
2588
    
2589
    if( outputParameters )
2590
    {
2591
        result = InitializeWaveHandles( winMmeHostApi, &stream->output,
2592
                winMmeSpecificOutputFlags,
2593
                stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
2594
                outputDevices, outputDeviceCount, outputChannelMask, 0 /* isInput */ );
2595
        if( result != paNoError ) goto error;
2596
    }
2597

    
2598
    if( inputParameters )
2599
    {
2600
        result = InitializeWaveHeaders( &stream->input, hostInputBufferCount,
2601
                hostInputSampleFormat, framesPerHostInputBuffer, inputDevices, 1 /* isInput */ );
2602
        if( result != paNoError ) goto error;
2603
    }
2604

    
2605
    if( outputParameters )
2606
    {
2607
        result = InitializeWaveHeaders( &stream->output, hostOutputBufferCount,
2608
                hostOutputSampleFormat, framesPerHostOutputBuffer, outputDevices, 0 /* not isInput */ );
2609
        if( result != paNoError ) goto error;
2610

    
2611
        stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostOutputBuffer * stream->output.bufferCount) / sampleRate);
2612
    }
2613
    else
2614
    {
2615
        stream->allBuffersDurationMs = (DWORD) (1000.0 * (framesPerHostInputBuffer * stream->input.bufferCount) / sampleRate);
2616
    }
2617

    
2618
    
2619
    if( streamCallback )
2620
    {
2621
        /* abort event is only needed for callback streams */
2622
        result = CreateEventWithPaError( &stream->abortEvent, NULL, TRUE, FALSE, NULL );
2623
        if( result != paNoError ) goto error;
2624
    }
2625

    
2626
    *s = (PaStream*)stream;
2627

    
2628
    return result;
2629

    
2630
error:
2631

    
2632
    if( stream )
2633
    {
2634
        if( stream->abortEvent )
2635
            CloseHandle( stream->abortEvent );
2636
            
2637
        TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
2638
        TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
2639

    
2640
        TerminateWaveHandles( &stream->output, 0 /* not isInput */, 1 /* currentlyProcessingAnError */ );
2641
        TerminateWaveHandles( &stream->input, 1 /* isInput */, 1 /* currentlyProcessingAnError */ );
2642

    
2643
        if( bufferProcessorIsInitialized )
2644
            PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2645

    
2646
        if( streamRepresentationIsInitialized )
2647
            PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2648

    
2649
        PaUtil_FreeMemory( stream );
2650
    }
2651

    
2652
    return result;
2653
}
2654

    
2655

    
2656
/* return non-zero if all current buffers are done */
2657
static int BuffersAreDone( WAVEHDR **waveHeaders, unsigned int deviceCount, int bufferIndex )
2658
{
2659
    unsigned int i;
2660
    
2661
    for( i=0; i < deviceCount; ++i )
2662
    {
2663
        if( !(waveHeaders[i][ bufferIndex ].dwFlags & WHDR_DONE) )
2664
        {
2665
            return 0;
2666
        }         
2667
    }
2668

    
2669
    return 1;
2670
}
2671

    
2672
static int CurrentInputBuffersAreDone( PaWinMmeStream *stream )
2673
{
2674
    return BuffersAreDone( stream->input.waveHeaders, stream->input.deviceCount, stream->input.currentBufferIndex );
2675
}
2676

    
2677
static int CurrentOutputBuffersAreDone( PaWinMmeStream *stream )
2678
{
2679
    return BuffersAreDone( stream->output.waveHeaders, stream->output.deviceCount, stream->output.currentBufferIndex );
2680
}
2681

    
2682

    
2683
/* return non-zero if any buffers are queued */
2684
static int NoBuffersAreQueued( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
2685
{
2686
    unsigned int i, j;
2687

    
2688
    if( handlesAndBuffers->waveHandles )
2689
    {
2690
        for( i=0; i < handlesAndBuffers->bufferCount; ++i )
2691
        {
2692
            for( j=0; j < handlesAndBuffers->deviceCount; ++j )
2693
            {
2694
                if( !( handlesAndBuffers->waveHeaders[ j ][ i ].dwFlags & WHDR_DONE) )
2695
                {
2696
                    return 0;
2697
                }
2698
            }
2699
        }
2700
    }
2701

    
2702
    return 1;
2703
}
2704

    
2705

    
2706
#define PA_CIRCULAR_INCREMENT_( current, max )\
2707
    ( (((current) + 1) >= (max)) ? (0) : (current+1) )
2708

    
2709
#define PA_CIRCULAR_DECREMENT_( current, max )\
2710
    ( ((current) == 0) ? ((max)-1) : (current-1) )
2711
    
2712

    
2713
static signed long GetAvailableFrames( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers )
2714
{
2715
    signed long result = 0;
2716
    unsigned int i;
2717
    
2718
    if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, handlesAndBuffers->currentBufferIndex ) )
2719
    {
2720
        /* we could calculate the following in O(1) if we kept track of the
2721
            last done buffer */
2722
        result = handlesAndBuffers->framesPerBuffer - handlesAndBuffers->framesUsedInCurrentBuffer;
2723

    
2724
        i = PA_CIRCULAR_INCREMENT_( handlesAndBuffers->currentBufferIndex, handlesAndBuffers->bufferCount );
2725
        while( i != handlesAndBuffers->currentBufferIndex )
2726
        {
2727
            if( BuffersAreDone( handlesAndBuffers->waveHeaders, handlesAndBuffers->deviceCount, i ) )
2728
            {
2729
                result += handlesAndBuffers->framesPerBuffer;
2730
                i = PA_CIRCULAR_INCREMENT_( i, handlesAndBuffers->bufferCount );
2731
            }
2732
            else
2733
                break;
2734
        }
2735
    }
2736

    
2737
    return result;
2738
}
2739

    
2740

    
2741
static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
2742
{
2743
    PaError result = paNoError;
2744
    MMRESULT mmresult;
2745
    unsigned int i;
2746

    
2747
    for( i=0; i < stream->input.deviceCount; ++i )
2748
    {
2749
        stream->input.waveHeaders[i][ stream->input.currentBufferIndex ].dwFlags &= ~WHDR_DONE;
2750
        mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
2751
                                    &stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
2752
                                    sizeof(WAVEHDR) );
2753
        if( mmresult != MMSYSERR_NOERROR )
2754
        {
2755
            result = paUnanticipatedHostError;
2756
            PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
2757
        }
2758
    }
2759

    
2760
    stream->input.currentBufferIndex =
2761
            PA_CIRCULAR_INCREMENT_( stream->input.currentBufferIndex, stream->input.bufferCount );
2762

    
2763
    stream->input.framesUsedInCurrentBuffer = 0;
2764

    
2765
    return result;
2766
}
2767

    
2768

    
2769
static PaError AdvanceToNextOutputBuffer( PaWinMmeStream *stream )
2770
{
2771
    PaError result = paNoError;
2772
    MMRESULT mmresult;
2773
    unsigned int i;
2774

    
2775
    for( i=0; i < stream->output.deviceCount; ++i )
2776
    {
2777
        mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[i],
2778
                                 &stream->output.waveHeaders[i][ stream->output.currentBufferIndex ],
2779
                                 sizeof(WAVEHDR) );
2780
        if( mmresult != MMSYSERR_NOERROR )
2781
        {
2782
            result = paUnanticipatedHostError;
2783
            PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
2784
        }
2785
    }
2786

    
2787
    stream->output.currentBufferIndex =
2788
            PA_CIRCULAR_INCREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
2789

    
2790
    stream->output.framesUsedInCurrentBuffer = 0;
2791
    
2792
    return result;
2793
}
2794

    
2795

    
2796
/* requeue all but the most recent input with the driver. Used for catching
2797
    up after a total input buffer underrun */
2798
static PaError CatchUpInputBuffers( PaWinMmeStream *stream )
2799
{
2800
    PaError result = paNoError;
2801
    unsigned int i;
2802
    
2803
    for( i=0; i < stream->input.bufferCount - 1; ++i )
2804
    {
2805
        result = AdvanceToNextInputBuffer( stream );
2806
        if( result != paNoError )
2807
            break;
2808
    }
2809

    
2810
    return result;
2811
}
2812

    
2813

    
2814
/* take the most recent output and duplicate it to all other output buffers
2815
    and requeue them. Used for catching up after a total output buffer underrun.
2816
*/
2817
static PaError CatchUpOutputBuffers( PaWinMmeStream *stream )
2818
{
2819
    PaError result = paNoError;
2820
    unsigned int i, j;
2821
    unsigned int previousBufferIndex =
2822
            PA_CIRCULAR_DECREMENT_( stream->output.currentBufferIndex, stream->output.bufferCount );
2823

    
2824
    for( i=0; i < stream->output.bufferCount - 1; ++i )
2825
    {
2826
        for( j=0; j < stream->output.deviceCount; ++j )
2827
        {
2828
            if( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData
2829
                    != stream->output.waveHeaders[j][ previousBufferIndex ].lpData )
2830
            {
2831
                CopyMemory( stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].lpData,
2832
                            stream->output.waveHeaders[j][ previousBufferIndex ].lpData,
2833
                            stream->output.waveHeaders[j][ stream->output.currentBufferIndex ].dwBufferLength );
2834
            }
2835
        }
2836

    
2837
        result = AdvanceToNextOutputBuffer( stream );
2838
        if( result != paNoError )
2839
            break;
2840
    }
2841

    
2842
    return result;
2843
}
2844

    
2845

    
2846
PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
2847
{
2848
    PaWinMmeStream *stream = (PaWinMmeStream *)pArg;
2849
    HANDLE events[3];
2850
    int eventCount = 0;
2851
    DWORD result = paNoError;
2852
    DWORD waitResult;
2853
    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
2854
    int hostBuffersAvailable;
2855
    signed int hostInputBufferIndex, hostOutputBufferIndex;
2856
    PaStreamCallbackFlags statusFlags;
2857
    int callbackResult;
2858
    int done = 0;
2859
    unsigned int channel, i;
2860
    unsigned long framesProcessed;
2861
    
2862
    /* prepare event array for call to WaitForMultipleObjects() */
2863
    if( stream->input.bufferEvent )
2864
        events[eventCount++] = stream->input.bufferEvent;
2865
    if( stream->output.bufferEvent )
2866
        events[eventCount++] = stream->output.bufferEvent;
2867
    events[eventCount++] = stream->abortEvent;
2868

    
2869
    statusFlags = 0; /** @todo support paInputUnderflow, paOutputOverflow and paNeverDropInput */
2870
    
2871
    /* loop until something causes us to stop */
2872
    do{
2873
        /* wait for MME to signal that a buffer is available, or for
2874
            the PA abort event to be signaled.
2875

2876
          When this indicates that one or more buffers are available
2877
          NoBuffersAreQueued() and Current*BuffersAreDone are used below to
2878
          poll for additional done buffers. NoBuffersAreQueued() will fail
2879
          to identify an underrun/overflow if the driver doesn't mark all done
2880
          buffers prior to signalling the event. Some drivers do this
2881
          (eg RME Digi96, and others don't eg VIA PC 97 input). This isn't a
2882
          huge problem, it just means that we won't always be able to detect
2883
          underflow/overflow.
2884
        */
2885
        waitResult = WaitForMultipleObjects( eventCount, events, FALSE /* wait all = FALSE */, timeout );
2886
        if( waitResult == WAIT_FAILED )
2887
        {
2888
            result = paUnanticipatedHostError;
2889
            /** @todo FIXME/REVIEW: can't return host error info from an asyncronous thread. see http://www.portaudio.com/trac/ticket/143 */
2890
            done = 1;
2891
        }
2892
        else if( waitResult == WAIT_TIMEOUT )
2893
        {
2894
            /* if a timeout is encountered, continue */
2895
        }
2896

    
2897
        if( stream->abortProcessing )
2898
        {
2899
            /* Pa_AbortStream() has been called, stop processing immediately */
2900
            done = 1;
2901
        }
2902
        else if( stream->stopProcessing )
2903
        {
2904
            /* Pa_StopStream() has been called or the user callback returned
2905
                non-zero, processing will continue until all output buffers
2906
                are marked as done. The stream will stop immediately if it
2907
                is input-only.
2908
            */
2909

    
2910
            if( PA_IS_OUTPUT_STREAM_(stream) )
2911
            {
2912
                if( NoBuffersAreQueued( &stream->output ) )
2913
                    done = 1; /* Will cause thread to return. */
2914
            }
2915
            else
2916
            {
2917
                /* input only stream */
2918
                done = 1; /* Will cause thread to return. */
2919
            }
2920
        }
2921
        else
2922
        {
2923
            hostBuffersAvailable = 1;
2924

    
2925
            /* process all available host buffers */
2926
            do
2927
            {
2928
                hostInputBufferIndex = -1;
2929
                hostOutputBufferIndex = -1;
2930
                
2931
                if( PA_IS_INPUT_STREAM_(stream) )
2932
                {
2933
                    if( CurrentInputBuffersAreDone( stream ) )
2934
                    {
2935
                        if( NoBuffersAreQueued( &stream->input ) )
2936
                        {
2937
                            /** @todo
2938
                               if all of the other buffers are also ready then
2939
                               we discard all but the most recent. This is an
2940
                               input buffer overflow. FIXME: these buffers should
2941
                               be passed to the callback in a paNeverDropInput
2942
                               stream. http://www.portaudio.com/trac/ticket/142
2943

2944
                               note that it is also possible for an input overflow
2945
                               to happen while the callback is processing a buffer.
2946
                               that is handled further down.
2947
                            */
2948
                            result = CatchUpInputBuffers( stream );
2949
                            if( result != paNoError )
2950
                                done = 1;
2951

    
2952
                            statusFlags |= paInputOverflow;
2953
                        }
2954

    
2955
                        hostInputBufferIndex = stream->input.currentBufferIndex;
2956
                    }
2957
                }
2958

    
2959
                if( PA_IS_OUTPUT_STREAM_(stream) )
2960
                {
2961
                    if( CurrentOutputBuffersAreDone( stream ) )
2962
                    {
2963
                        /* ok, we have an output buffer */
2964
                        
2965
                        if( NoBuffersAreQueued( &stream->output ) )
2966
                        {
2967
                            /*
2968
                            if all of the other buffers are also ready, catch up by copying
2969
                            the most recently generated buffer into all but one of the output
2970
                            buffers.
2971

2972
                            note that this catch up code only handles the case where all
2973
                            buffers have been played out due to this thread not having
2974
                            woken up at all. a more common case occurs when this thread
2975
                            is woken up, processes one buffer, but takes too long, and as
2976
                            a result all the other buffers have become un-queued. that
2977
                            case is handled further down.
2978
                            */
2979

    
2980
                            result = CatchUpOutputBuffers( stream );
2981
                            if( result != paNoError )
2982
                                done = 1;
2983

    
2984
                            statusFlags |= paOutputUnderflow;
2985
                        }
2986

    
2987
                        hostOutputBufferIndex = stream->output.currentBufferIndex;
2988
                    }
2989
                }
2990

    
2991
               
2992
                if( (PA_IS_FULL_DUPLEX_STREAM_(stream) && hostInputBufferIndex != -1 && hostOutputBufferIndex != -1) ||
2993
                        (PA_IS_HALF_DUPLEX_STREAM_(stream) && ( hostInputBufferIndex != -1 || hostOutputBufferIndex != -1 ) ) )
2994
                {
2995
                    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
2996

    
2997

    
2998
                    if( PA_IS_OUTPUT_STREAM_(stream) )
2999
                    {
3000
                        /* set timeInfo.currentTime and calculate timeInfo.outputBufferDacTime
3001
                            from the current wave out position */
3002
                        MMTIME mmtime;
3003
                        double timeBeforeGetPosition, timeAfterGetPosition;
3004
                        double time;
3005
                        long framesInBufferRing;                 
3006
                        long writePosition;
3007
                        long playbackPosition;
3008
                        HWAVEOUT firstWaveOutDevice = ((HWAVEOUT*)stream->output.waveHandles)[0];
3009
                        
3010
                        mmtime.wType = TIME_SAMPLES;
3011
                        timeBeforeGetPosition = PaUtil_GetTime();
3012
                        waveOutGetPosition( firstWaveOutDevice, &mmtime, sizeof(MMTIME) );
3013
                        timeAfterGetPosition = PaUtil_GetTime();
3014

    
3015
                        timeInfo.currentTime = timeAfterGetPosition;
3016

    
3017
                        /* approximate time at which wave out position was measured
3018
                            as half way between timeBeforeGetPosition and timeAfterGetPosition */
3019
                        time = timeBeforeGetPosition + (timeAfterGetPosition - timeBeforeGetPosition) * .5;
3020
                        
3021
                        framesInBufferRing = stream->output.bufferCount * stream->bufferProcessor.framesPerHostBuffer;
3022
                        playbackPosition = mmtime.u.sample % framesInBufferRing;
3023

    
3024
                        writePosition = stream->output.currentBufferIndex * stream->bufferProcessor.framesPerHostBuffer
3025
                                + stream->output.framesUsedInCurrentBuffer;
3026
                       
3027
                        if( playbackPosition >= writePosition ){
3028
                            timeInfo.outputBufferDacTime =
3029
                                    time + ((double)( writePosition + (framesInBufferRing - playbackPosition) ) * stream->bufferProcessor.samplePeriod );
3030
                        }else{
3031
                            timeInfo.outputBufferDacTime =
3032
                                    time + ((double)( writePosition - playbackPosition ) * stream->bufferProcessor.samplePeriod );
3033
                        }
3034
                    }
3035

    
3036

    
3037
                    PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
3038

    
3039
                    PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, statusFlags  );
3040

    
3041
                    /* reset status flags once they have been passed to the buffer processor */
3042
                    statusFlags = 0;
3043

    
3044
                    if( PA_IS_INPUT_STREAM_(stream) )
3045
                    {
3046
                        PaUtil_SetInputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
3047

    
3048
                        channel = 0;
3049
                        for( i=0; i<stream->input.deviceCount; ++i )
3050
                        {
3051
                             /* we have stored the number of channels in the buffer in dwUser */
3052
                            int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
3053
                            
3054
                            PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
3055
                                    stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
3056
                                        stream->input.framesUsedInCurrentBuffer * channelCount *
3057
                                        stream->bufferProcessor.bytesPerHostInputSample,
3058
                                    channelCount );
3059
                                    
3060

    
3061
                            channel += channelCount;
3062
                        }
3063
                    }
3064

    
3065
                    if( PA_IS_OUTPUT_STREAM_(stream) )
3066
                    {
3067
                        PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
3068
                        
3069
                        channel = 0;
3070
                        for( i=0; i<stream->output.deviceCount; ++i )
3071
                        {
3072
                            /* we have stored the number of channels in the buffer in dwUser */
3073
                            int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
3074

    
3075
                            PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
3076
                                    stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
3077
                                        stream->output.framesUsedInCurrentBuffer * channelCount *
3078
                                        stream->bufferProcessor.bytesPerHostOutputSample,
3079
                                    channelCount );
3080

    
3081
                            channel += channelCount;
3082
                        }
3083
                    }
3084

    
3085
                    callbackResult = paContinue;
3086
                    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
3087

    
3088
                    stream->input.framesUsedInCurrentBuffer += framesProcessed;
3089
                    stream->output.framesUsedInCurrentBuffer += framesProcessed;
3090

    
3091
                    PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, framesProcessed );
3092

    
3093
                    if( callbackResult == paContinue )
3094
                    {
3095
                        /* nothing special to do */
3096
                    }
3097
                    else if( callbackResult == paAbort )
3098
                    {
3099
                        stream->abortProcessing = 1;
3100
                        done = 1;
3101
                        /** @todo FIXME: should probably reset the output device immediately once the callback returns paAbort 
3102
                            see: http://www.portaudio.com/trac/ticket/141
3103
                        */
3104
                        result = paNoError;
3105
                    }
3106
                    else
3107
                    {
3108
                        /* User callback has asked us to stop with paComplete or other non-zero value */
3109
                        stream->stopProcessing = 1; /* stop once currently queued audio has finished */
3110
                        result = paNoError;
3111
                    }
3112

    
3113

    
3114
                    if( PA_IS_INPUT_STREAM_(stream)
3115
                            && stream->stopProcessing == 0 && stream->abortProcessing == 0
3116
                            && stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
3117
                    {
3118
                        if( NoBuffersAreQueued( &stream->input ) )
3119
                        {
3120
                            /** @todo need to handle PaNeverDropInput here where necessary */
3121
                            result = CatchUpInputBuffers( stream );
3122
                            if( result != paNoError )
3123
                                done = 1;
3124

    
3125
                            statusFlags |= paInputOverflow;
3126
                        }
3127

    
3128
                        result = AdvanceToNextInputBuffer( stream );
3129
                        if( result != paNoError )
3130
                            done = 1;
3131
                    }
3132

    
3133
                    
3134
                    if( PA_IS_OUTPUT_STREAM_(stream) && !stream->abortProcessing )
3135
                    {
3136
                        if( stream->stopProcessing &&
3137
                                stream->output.framesUsedInCurrentBuffer < stream->output.framesPerBuffer )
3138
                        {
3139
                            /* zero remaining samples in output output buffer and flush */
3140

    
3141
                            stream->output.framesUsedInCurrentBuffer += PaUtil_ZeroOutput( &stream->bufferProcessor,
3142
                                    stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
3143

    
3144
                            /* we send the entire buffer to the output devices, but we could
3145
                                just send a partial buffer, rather than zeroing the unused
3146
                                samples.
3147
                            */
3148
                        }
3149

    
3150
                        if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
3151
                        {
3152
                            /* check for underflow before enquing the just-generated buffer,
3153
                                but recover from underflow after enquing it. This ensures
3154
                                that the most recent audio segment is repeated */
3155
                            int outputUnderflow = NoBuffersAreQueued( &stream->output );
3156

    
3157
                            result = AdvanceToNextOutputBuffer( stream );
3158
                            if( result != paNoError )
3159
                                done = 1;
3160

    
3161
                            if( outputUnderflow && !done && !stream->stopProcessing )
3162
                            {
3163
                                /* Recover from underflow in the case where the
3164
                                    underflow occured while processing the buffer
3165
                                    we just finished */
3166

    
3167
                                result = CatchUpOutputBuffers( stream );
3168
                                if( result != paNoError )
3169
                                    done = 1;
3170

    
3171
                                statusFlags |= paOutputUnderflow;
3172
                            }
3173
                        }
3174
                    }
3175
                    
3176
                    if( stream->throttleProcessingThreadOnOverload != 0 )
3177
                    {
3178
                        if( stream->stopProcessing || stream->abortProcessing )
3179
                        {
3180
                            if( stream->processingThreadPriority != stream->highThreadPriority )
3181
                            {
3182
                                SetThreadPriority( stream->processingThread, stream->highThreadPriority );
3183
                                stream->processingThreadPriority = stream->highThreadPriority;
3184
                            }
3185
                        }
3186
                        else if( PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ) > 1. )
3187
                        {
3188
                            if( stream->processingThreadPriority != stream->throttledThreadPriority )
3189
                            {
3190
                                SetThreadPriority( stream->processingThread, stream->throttledThreadPriority );
3191
                                stream->processingThreadPriority = stream->throttledThreadPriority;
3192
                            }
3193

    
3194
                            /* sleep to give other processes a go */
3195
                            Sleep( stream->throttledSleepMsecs );
3196
                        }
3197
                        else
3198
                        {
3199
                            if( stream->processingThreadPriority != stream->highThreadPriority )
3200
                            {
3201
                                SetThreadPriority( stream->processingThread, stream->highThreadPriority );
3202
                                stream->processingThreadPriority = stream->highThreadPriority;
3203
                            }
3204
                        }
3205
                    }
3206
                }
3207
                else
3208
                {
3209
                    hostBuffersAvailable = 0;
3210
                }
3211
            }
3212
            while( hostBuffersAvailable &&
3213
                    stream->stopProcessing == 0 &&
3214
                    stream->abortProcessing == 0 &&
3215
                    !done );
3216
        }
3217
    }
3218
    while( !done );
3219

    
3220
    stream->isActive = 0;
3221

    
3222
    if( stream->streamRepresentation.streamFinishedCallback != 0 )
3223
        stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3224

    
3225
    PaUtil_ResetCpuLoadMeasurer( &stream->cpuLoadMeasurer );
3226
    
3227
    return result;
3228
}
3229

    
3230

    
3231
/*
3232
    When CloseStream() is called, the multi-api layer ensures that
3233
    the stream has already been stopped or aborted.
3234
*/
3235
static PaError CloseStream( PaStream* s )
3236
{
3237
    PaError result;
3238
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3239

    
3240
    result = CloseHandleWithPaError( stream->abortEvent );
3241
    if( result != paNoError ) goto error;
3242
    
3243
    TerminateWaveHeaders( &stream->output, 0 /* not isInput */ );
3244
    TerminateWaveHeaders( &stream->input, 1 /* isInput */ );
3245

    
3246
    TerminateWaveHandles( &stream->output, 0 /* not isInput */, 0 /* not currentlyProcessingAnError */ );
3247
    TerminateWaveHandles( &stream->input, 1 /* isInput */, 0 /* not currentlyProcessingAnError */ );
3248
    
3249
    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
3250
    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
3251
    PaUtil_FreeMemory( stream );
3252

    
3253
error:
3254
    /** @todo REVIEW: what is the best way to clean up a stream if an error is detected? */
3255
    return result;
3256
}
3257

    
3258

    
3259
static PaError StartStream( PaStream *s )
3260
{
3261
    PaError result;
3262
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3263
    MMRESULT mmresult;
3264
    unsigned int i, j;
3265
    int callbackResult;
3266
    unsigned int channel;
3267
    unsigned long framesProcessed;
3268
    PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement this for stream priming */
3269
    
3270
    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
3271
    
3272
    if( PA_IS_INPUT_STREAM_(stream) )
3273
    {
3274
        for( i=0; i<stream->input.bufferCount; ++i )
3275
        {
3276
            for( j=0; j<stream->input.deviceCount; ++j )
3277
            {
3278
                stream->input.waveHeaders[j][i].dwFlags &= ~WHDR_DONE;
3279
                mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
3280
                if( mmresult != MMSYSERR_NOERROR )
3281
                {
3282
                    result = paUnanticipatedHostError;
3283
                    PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
3284
                    goto error;
3285
                }
3286
            }
3287
        }
3288
        stream->input.currentBufferIndex = 0;
3289
        stream->input.framesUsedInCurrentBuffer = 0;
3290
    }
3291

    
3292
    if( PA_IS_OUTPUT_STREAM_(stream) )
3293
    {
3294
        for( i=0; i<stream->output.deviceCount; ++i )
3295
        {
3296
            if( (mmresult = waveOutPause( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
3297
            {
3298
                result = paUnanticipatedHostError;
3299
                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3300
                goto error;
3301
            }
3302
        }
3303

    
3304
        for( i=0; i<stream->output.bufferCount; ++i )
3305
        {
3306
            if( stream->primeStreamUsingCallback )
3307
            {
3308

    
3309
                stream->output.framesUsedInCurrentBuffer = 0;
3310
                do{
3311

    
3312
                    PaUtil_BeginBufferProcessing( &stream->bufferProcessor,
3313
                            &timeInfo,
3314
                            paPrimingOutput | ((stream->input.bufferCount > 0 ) ? paInputUnderflow : 0));
3315

    
3316
                    if( stream->input.bufferCount > 0 )
3317
                        PaUtil_SetNoInput( &stream->bufferProcessor );
3318

    
3319
                    PaUtil_SetOutputFrameCount( &stream->bufferProcessor, 0 /* default to host buffer size */ );
3320

    
3321
                    channel = 0;
3322
                    for( j=0; j<stream->output.deviceCount; ++j )
3323
                    {
3324
                        /* we have stored the number of channels in the buffer in dwUser */
3325
                        int channelCount = (int)stream->output.waveHeaders[j][i].dwUser;
3326

    
3327
                        PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
3328
                                stream->output.waveHeaders[j][i].lpData +
3329
                                stream->output.framesUsedInCurrentBuffer * channelCount *
3330
                                stream->bufferProcessor.bytesPerHostOutputSample,
3331
                                channelCount );
3332

    
3333
                        /* we have stored the number of channels in the buffer in dwUser */
3334
                        channel += channelCount;
3335
                    }
3336

    
3337
                    callbackResult = paContinue;
3338
                    framesProcessed = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &callbackResult );
3339
                    stream->output.framesUsedInCurrentBuffer += framesProcessed;
3340

    
3341
                    if( callbackResult != paContinue )
3342
                    {
3343
                        /** @todo fix this, what do we do if callback result is non-zero during stream
3344
                            priming?
3345

3346
                            for complete: play out primed waveHeaders as usual
3347
                            for abort: clean up immediately.
3348
                       */
3349
                    }
3350

    
3351
                }while( stream->output.framesUsedInCurrentBuffer != stream->output.framesPerBuffer );
3352

    
3353
            }
3354
            else
3355
            {
3356
                for( j=0; j<stream->output.deviceCount; ++j )
3357
                {
3358
                    ZeroMemory( stream->output.waveHeaders[j][i].lpData, stream->output.waveHeaders[j][i].dwBufferLength );
3359
                }
3360
            }   
3361

    
3362
            /* we queue all channels of a single buffer frame (accross all
3363
                devices, because some multidevice multichannel drivers work
3364
                better this way */
3365
            for( j=0; j<stream->output.deviceCount; ++j )
3366
            {
3367
                mmresult = waveOutWrite( ((HWAVEOUT*)stream->output.waveHandles)[j], &stream->output.waveHeaders[j][i], sizeof(WAVEHDR) );
3368
                if( mmresult != MMSYSERR_NOERROR )
3369
                {
3370
                    result = paUnanticipatedHostError;
3371
                    PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3372
                    goto error;
3373
                }
3374
            }
3375
        }
3376
        stream->output.currentBufferIndex = 0;
3377
        stream->output.framesUsedInCurrentBuffer = 0;
3378
    }
3379

    
3380

    
3381
    stream->isStopped = 0;
3382
    stream->isActive = 1;
3383
    stream->stopProcessing = 0;
3384
    stream->abortProcessing = 0;
3385

    
3386
    result = ResetEventWithPaError( stream->input.bufferEvent );
3387
    if( result != paNoError ) goto error;
3388

    
3389
    result = ResetEventWithPaError( stream->output.bufferEvent );
3390
    if( result != paNoError ) goto error;
3391
    
3392
    
3393
    if( stream->streamRepresentation.streamCallback )
3394
    {
3395
        /* callback stream */
3396

    
3397
        result = ResetEventWithPaError( stream->abortEvent );
3398
        if( result != paNoError ) goto error;
3399

    
3400
        /* Create thread that waits for audio buffers to be ready for processing. */
3401
        stream->processingThread = CREATE_THREAD;
3402
        if( !stream->processingThread )
3403
        {
3404
            result = paUnanticipatedHostError;
3405
            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
3406
            goto error;
3407
        }
3408

    
3409
        /** @todo could have mme specific stream parameters to allow the user
3410
            to set the callback thread priorities */
3411
        stream->highThreadPriority = THREAD_PRIORITY_TIME_CRITICAL;
3412
        stream->throttledThreadPriority = THREAD_PRIORITY_NORMAL;
3413

    
3414
        if( !SetThreadPriority( stream->processingThread, stream->highThreadPriority ) )
3415
        {
3416
            result = paUnanticipatedHostError;
3417
            PA_MME_SET_LAST_SYSTEM_ERROR( GetLastError() );
3418
            goto error;
3419
        }
3420
        stream->processingThreadPriority = stream->highThreadPriority;
3421
    }
3422
    else
3423
    {
3424
        /* blocking read/write stream */
3425

    
3426
    }
3427

    
3428
    if( PA_IS_INPUT_STREAM_(stream) )
3429
    {
3430
        for( i=0; i < stream->input.deviceCount; ++i )
3431
        {
3432
            mmresult = waveInStart( ((HWAVEIN*)stream->input.waveHandles)[i] );
3433
            PA_DEBUG(("Pa_StartStream: waveInStart returned = 0x%X.\n", mmresult));
3434
            if( mmresult != MMSYSERR_NOERROR )
3435
            {
3436
                result = paUnanticipatedHostError;
3437
                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
3438
                goto error;
3439
            }
3440
        }
3441
    }
3442

    
3443
    if( PA_IS_OUTPUT_STREAM_(stream) )
3444
    {
3445
        for( i=0; i < stream->output.deviceCount; ++i )
3446
        {
3447
            if( (mmresult = waveOutRestart( ((HWAVEOUT*)stream->output.waveHandles)[i] )) != MMSYSERR_NOERROR )
3448
            {
3449
                result = paUnanticipatedHostError;
3450
                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3451
                goto error;
3452
            }
3453
        }
3454
    }
3455

    
3456
    return result;
3457

    
3458
error:
3459
    /** @todo FIXME: implement recovery as best we can
3460
    This should involve rolling back to a state as-if this function had never been called
3461
    */
3462
    return result;
3463
}
3464

    
3465

    
3466
static PaError StopStream( PaStream *s )
3467
{
3468
    PaError result = paNoError;
3469
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3470
    int timeout;
3471
    DWORD waitResult;
3472
    MMRESULT mmresult;
3473
    signed int hostOutputBufferIndex;
3474
    unsigned int channel, waitCount, i;                  
3475
    
3476
    /** @todo
3477
        REVIEW: the error checking in this function needs review. the basic
3478
        idea is to return from this function in a known state - for example
3479
        there is no point avoiding calling waveInReset just because
3480
        the thread times out.
3481
    */
3482

    
3483
    if( stream->processingThread )
3484
    {
3485
        /* callback stream */
3486

    
3487
        /* Tell processing thread to stop generating more data and to let current data play out. */
3488
        stream->stopProcessing = 1;
3489

    
3490
        /* Calculate timeOut longer than longest time it could take to return all buffers. */
3491
        timeout = (int)(stream->allBuffersDurationMs * 1.5);
3492
        if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
3493
            timeout = PA_MME_MIN_TIMEOUT_MSEC_;
3494

    
3495
        PA_DEBUG(("WinMME StopStream: waiting for background thread.\n"));
3496

    
3497
        waitResult = WaitForSingleObject( stream->processingThread, timeout );
3498
        if( waitResult == WAIT_TIMEOUT )
3499
        {
3500
            /* try to abort */
3501
            stream->abortProcessing = 1;
3502
            SetEvent( stream->abortEvent );
3503
            waitResult = WaitForSingleObject( stream->processingThread, timeout );
3504
            if( waitResult == WAIT_TIMEOUT )
3505
            {
3506
                PA_DEBUG(("WinMME StopStream: timed out while waiting for background thread to finish.\n"));
3507
                result = paTimedOut;
3508
            }
3509
        }
3510

    
3511
        CloseHandle( stream->processingThread );
3512
        stream->processingThread = NULL;
3513
    }
3514
    else
3515
    {
3516
        /* blocking read / write stream */
3517

    
3518
        if( PA_IS_OUTPUT_STREAM_(stream) )
3519
        {
3520
            if( stream->output.framesUsedInCurrentBuffer > 0 )
3521
            {
3522
                /* there are still unqueued frames in the current buffer, so flush them */
3523

    
3524
                hostOutputBufferIndex = stream->output.currentBufferIndex;
3525

    
3526
                PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
3527
                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
3528
                
3529
                channel = 0;
3530
                for( i=0; i<stream->output.deviceCount; ++i )
3531
                {
3532
                    /* we have stored the number of channels in the buffer in dwUser */
3533
                    int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
3534

    
3535
                    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
3536
                            stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
3537
                                stream->output.framesUsedInCurrentBuffer * channelCount *
3538
                                stream->bufferProcessor.bytesPerHostOutputSample,
3539
                            channelCount );
3540

    
3541
                    channel += channelCount;
3542
                }
3543

    
3544
                PaUtil_ZeroOutput( &stream->bufferProcessor,
3545
                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
3546

    
3547
                /* we send the entire buffer to the output devices, but we could
3548
                    just send a partial buffer, rather than zeroing the unused
3549
                    samples.
3550
                */
3551
                AdvanceToNextOutputBuffer( stream );
3552
            }
3553
            
3554

    
3555
            timeout = (stream->allBuffersDurationMs / stream->output.bufferCount) + 1;
3556
            if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
3557
                timeout = PA_MME_MIN_TIMEOUT_MSEC_;
3558

    
3559
            waitCount = 0;
3560
            while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
3561
            {
3562
                /* wait for MME to signal that a buffer is available */
3563
                waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
3564
                if( waitResult == WAIT_FAILED )
3565
                {
3566
                    break;
3567
                }
3568
                else if( waitResult == WAIT_TIMEOUT )
3569
                {
3570
                    /* keep waiting */
3571
                }
3572

    
3573
                ++waitCount;
3574
            }
3575
        }
3576
    }
3577

    
3578
    if( PA_IS_OUTPUT_STREAM_(stream) )
3579
    {
3580
        for( i =0; i < stream->output.deviceCount; ++i )
3581
        {
3582
            mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
3583
            if( mmresult != MMSYSERR_NOERROR )
3584
            {
3585
                result = paUnanticipatedHostError;
3586
                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3587
            }
3588
        }
3589
    }
3590

    
3591
    if( PA_IS_INPUT_STREAM_(stream) )
3592
    {
3593
        for( i=0; i < stream->input.deviceCount; ++i )
3594
        {
3595
            mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
3596
            if( mmresult != MMSYSERR_NOERROR )
3597
            {
3598
                result = paUnanticipatedHostError;
3599
                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
3600
            }
3601
        }
3602
    }
3603

    
3604
    stream->isStopped = 1;
3605
    stream->isActive = 0;
3606

    
3607
    return result;
3608
}
3609

    
3610

    
3611
static PaError AbortStream( PaStream *s )
3612
{
3613
    PaError result = paNoError;
3614
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3615
    int timeout;
3616
    DWORD waitResult;
3617
    MMRESULT mmresult;
3618
    unsigned int i;
3619
    
3620
    /** @todo
3621
        REVIEW: the error checking in this function needs review. the basic
3622
        idea is to return from this function in a known state - for example
3623
        there is no point avoiding calling waveInReset just because
3624
        the thread times out.
3625
    */
3626

    
3627
    if( stream->processingThread )
3628
    {
3629
        /* callback stream */
3630
        
3631
        /* Tell processing thread to abort immediately */
3632
        stream->abortProcessing = 1;
3633
        SetEvent( stream->abortEvent );
3634
    }
3635

    
3636

    
3637
    if( PA_IS_OUTPUT_STREAM_(stream) )
3638
    {
3639
        for( i =0; i < stream->output.deviceCount; ++i )
3640
        {
3641
            mmresult = waveOutReset( ((HWAVEOUT*)stream->output.waveHandles)[i] );
3642
            if( mmresult != MMSYSERR_NOERROR )
3643
            {
3644
                PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
3645
                return paUnanticipatedHostError;
3646
            }
3647
        }
3648
    }
3649

    
3650
    if( PA_IS_INPUT_STREAM_(stream) )
3651
    {
3652
        for( i=0; i < stream->input.deviceCount; ++i )
3653
        {
3654
            mmresult = waveInReset( ((HWAVEIN*)stream->input.waveHandles)[i] );
3655
            if( mmresult != MMSYSERR_NOERROR )
3656
            {
3657
                PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
3658
                return paUnanticipatedHostError;
3659
            }
3660
        }
3661
    }
3662

    
3663

    
3664
    if( stream->processingThread )
3665
    {
3666
        /* callback stream */
3667
        
3668
        PA_DEBUG(("WinMME AbortStream: waiting for background thread.\n"));
3669

    
3670
        /* Calculate timeOut longer than longest time it could take to return all buffers. */
3671
        timeout = (int)(stream->allBuffersDurationMs * 1.5);
3672
        if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
3673
            timeout = PA_MME_MIN_TIMEOUT_MSEC_;
3674
            
3675
        waitResult = WaitForSingleObject( stream->processingThread, timeout );
3676
        if( waitResult == WAIT_TIMEOUT )
3677
        {
3678
            PA_DEBUG(("WinMME AbortStream: timed out while waiting for background thread to finish.\n"));
3679
            return paTimedOut;
3680
        }
3681

    
3682
        CloseHandle( stream->processingThread );
3683
        stream->processingThread = NULL;
3684
    }
3685

    
3686
    stream->isStopped = 1;
3687
    stream->isActive = 0;
3688

    
3689
    return result;
3690
}
3691

    
3692

    
3693
static PaError IsStreamStopped( PaStream *s )
3694
{
3695
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3696

    
3697
    return stream->isStopped;
3698
}
3699

    
3700

    
3701
static PaError IsStreamActive( PaStream *s )
3702
{
3703
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3704

    
3705
    return stream->isActive;
3706
}
3707

    
3708

    
3709
static PaTime GetStreamTime( PaStream *s )
3710
{
3711
    (void) s; /* unused parameter */
3712
    
3713
    return PaUtil_GetTime();
3714
}
3715

    
3716

    
3717
static double GetStreamCpuLoad( PaStream* s )
3718
{
3719
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3720

    
3721
    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
3722
}
3723

    
3724

    
3725
/*
3726
    As separate stream interfaces are used for blocking and callback
3727
    streams, the following functions can be guaranteed to only be called
3728
    for blocking streams.
3729
*/
3730

    
3731
static PaError ReadStream( PaStream* s,
3732
                           void *buffer,
3733
                           unsigned long frames )
3734
{
3735
    PaError result = paNoError;
3736
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3737
    void *userBuffer;
3738
    unsigned long framesRead = 0;
3739
    unsigned long framesProcessed;
3740
    signed int hostInputBufferIndex;
3741
    DWORD waitResult;
3742
    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
3743
    unsigned int channel, i;
3744
    
3745
    if( PA_IS_INPUT_STREAM_(stream) )
3746
    {
3747
        /* make a local copy of the user buffer pointer(s). this is necessary
3748
            because PaUtil_CopyInput() advances these pointers every time
3749
            it is called.
3750
        */
3751
        if( stream->bufferProcessor.userInputIsInterleaved )
3752
        {
3753
            userBuffer = buffer;
3754
        }
3755
        else
3756
        {
3757
            userBuffer = (void*)alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
3758
            if( !userBuffer )
3759
                return paInsufficientMemory;
3760
            for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
3761
                ((void**)userBuffer)[i] = ((void**)buffer)[i];
3762
        }
3763
        
3764
        do{
3765
            if( CurrentInputBuffersAreDone( stream ) )
3766
            {
3767
                if( NoBuffersAreQueued( &stream->input ) )
3768
                {
3769
                    /** @todo REVIEW: consider what to do if the input overflows.
3770
                        do we requeue all of the buffers? should we be running
3771
                        a thread to make sure they are always queued? 
3772
                        see: http://www.portaudio.com/trac/ticket/117
3773
                        */
3774

    
3775
                    result = paInputOverflowed;
3776
                }
3777

    
3778
                hostInputBufferIndex = stream->input.currentBufferIndex;
3779

    
3780
                PaUtil_SetInputFrameCount( &stream->bufferProcessor,
3781
                        stream->input.framesPerBuffer - stream->input.framesUsedInCurrentBuffer );
3782
                
3783
                channel = 0;
3784
                for( i=0; i<stream->input.deviceCount; ++i )
3785
                {
3786
                    /* we have stored the number of channels in the buffer in dwUser */
3787
                    int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
3788

    
3789
                    PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
3790
                            stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
3791
                                stream->input.framesUsedInCurrentBuffer * channelCount *
3792
                                stream->bufferProcessor.bytesPerHostInputSample,
3793
                            channelCount );
3794

    
3795
                    channel += channelCount;
3796
                }
3797
                
3798
                framesProcessed = PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, frames - framesRead );
3799

    
3800
                stream->input.framesUsedInCurrentBuffer += framesProcessed;
3801
                if( stream->input.framesUsedInCurrentBuffer == stream->input.framesPerBuffer )
3802
                {
3803
                    result = AdvanceToNextInputBuffer( stream );
3804
                    if( result != paNoError )
3805
                        break;
3806
                }
3807

    
3808
                framesRead += framesProcessed;      
3809

    
3810
            }else{
3811
                /* wait for MME to signal that a buffer is available */
3812
                waitResult = WaitForSingleObject( stream->input.bufferEvent, timeout );
3813
                if( waitResult == WAIT_FAILED )
3814
                {
3815
                    result = paUnanticipatedHostError;
3816
                    break;
3817
                }
3818
                else if( waitResult == WAIT_TIMEOUT )
3819
                {
3820
                    /* if a timeout is encountered, continue,
3821
                        perhaps we should give up eventually
3822
                    */
3823
                }         
3824
            }
3825
        }while( framesRead < frames );
3826
    }
3827
    else
3828
    {
3829
        result = paCanNotReadFromAnOutputOnlyStream;
3830
    }
3831

    
3832
    return result;
3833
}
3834

    
3835

    
3836
static PaError WriteStream( PaStream* s,
3837
                            const void *buffer,
3838
                            unsigned long frames )
3839
{
3840
    PaError result = paNoError;
3841
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3842
    const void *userBuffer;
3843
    unsigned long framesWritten = 0;
3844
    unsigned long framesProcessed;
3845
    signed int hostOutputBufferIndex;
3846
    DWORD waitResult;
3847
    DWORD timeout = (unsigned long)(stream->allBuffersDurationMs * 0.5);
3848
    unsigned int channel, i;
3849

    
3850
        
3851
    if( PA_IS_OUTPUT_STREAM_(stream) )
3852
    {
3853
        /* make a local copy of the user buffer pointer(s). this is necessary
3854
            because PaUtil_CopyOutput() advances these pointers every time
3855
            it is called.
3856
        */
3857
        if( stream->bufferProcessor.userOutputIsInterleaved )
3858
        {
3859
            userBuffer = buffer;
3860
        }
3861
        else
3862
        {
3863
            userBuffer = (const void*)alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
3864
            if( !userBuffer )
3865
                return paInsufficientMemory;
3866
            for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
3867
                ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
3868
        }
3869

    
3870
        do{
3871
            if( CurrentOutputBuffersAreDone( stream ) )
3872
            {
3873
                if( NoBuffersAreQueued( &stream->output ) )
3874
                {
3875
                    /** @todo REVIEW: consider what to do if the output
3876
                    underflows. do we requeue all the existing buffers with
3877
                    zeros? should we run a separate thread to keep the buffers
3878
                    enqueued at all times? 
3879
                    see: http://www.portaudio.com/trac/ticket/117
3880
                    */
3881

    
3882
                    result = paOutputUnderflowed;
3883
                }
3884

    
3885
                hostOutputBufferIndex = stream->output.currentBufferIndex;
3886

    
3887
                PaUtil_SetOutputFrameCount( &stream->bufferProcessor,
3888
                        stream->output.framesPerBuffer - stream->output.framesUsedInCurrentBuffer );
3889
                
3890
                channel = 0;
3891
                for( i=0; i<stream->output.deviceCount; ++i )
3892
                {
3893
                    /* we have stored the number of channels in the buffer in dwUser */
3894
                    int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
3895

    
3896
                    PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
3897
                            stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
3898
                                stream->output.framesUsedInCurrentBuffer * channelCount *
3899
                                stream->bufferProcessor.bytesPerHostOutputSample,
3900
                            channelCount );
3901

    
3902
                    channel += channelCount;
3903
                }
3904
                
3905
                framesProcessed = PaUtil_CopyOutput( &stream->bufferProcessor, &userBuffer, frames - framesWritten );
3906

    
3907
                stream->output.framesUsedInCurrentBuffer += framesProcessed;
3908
                if( stream->output.framesUsedInCurrentBuffer == stream->output.framesPerBuffer )
3909
                {
3910
                    result = AdvanceToNextOutputBuffer( stream );
3911
                    if( result != paNoError )
3912
                        break;
3913
                }
3914

    
3915
                framesWritten += framesProcessed;
3916
            }
3917
            else
3918
            {
3919
                /* wait for MME to signal that a buffer is available */
3920
                waitResult = WaitForSingleObject( stream->output.bufferEvent, timeout );
3921
                if( waitResult == WAIT_FAILED )
3922
                {
3923
                    result = paUnanticipatedHostError;
3924
                    break;
3925
                }
3926
                else if( waitResult == WAIT_TIMEOUT )
3927
                {
3928
                    /* if a timeout is encountered, continue,
3929
                        perhaps we should give up eventually
3930
                    */
3931
                }             
3932
            }        
3933
        }while( framesWritten < frames );
3934
    }
3935
    else
3936
    {
3937
        result = paCanNotWriteToAnInputOnlyStream;
3938
    }
3939
    
3940
    return result;
3941
}
3942

    
3943

    
3944
static signed long GetStreamReadAvailable( PaStream* s )
3945
{
3946
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3947
    
3948
    if( PA_IS_INPUT_STREAM_(stream) )
3949
        return GetAvailableFrames( &stream->input );
3950
    else
3951
        return paCanNotReadFromAnOutputOnlyStream;
3952
}
3953

    
3954

    
3955
static signed long GetStreamWriteAvailable( PaStream* s )
3956
{
3957
    PaWinMmeStream *stream = (PaWinMmeStream*)s;
3958
    
3959
    if( PA_IS_OUTPUT_STREAM_(stream) )
3960
        return GetAvailableFrames( &stream->output );
3961
    else
3962
        return paCanNotWriteToAnInputOnlyStream;
3963
}
3964

    
3965

    
3966
/* NOTE: the following functions are MME-stream specific, and are called directly
3967
    by client code. We need to check for many more error conditions here because
3968
    we don't have the benefit of pa_front.c's parameter checking.
3969
*/
3970

    
3971
static PaError GetWinMMEStreamPointer( PaWinMmeStream **stream, PaStream *s )
3972
{
3973
    PaError result;
3974
    PaUtilHostApiRepresentation *hostApi;
3975
    PaWinMmeHostApiRepresentation *winMmeHostApi;
3976
    
3977
    result = PaUtil_ValidateStreamPointer( s );
3978
    if( result != paNoError )
3979
        return result;
3980

    
3981
    result = PaUtil_GetHostApiRepresentation( &hostApi, paMME );
3982
    if( result != paNoError )
3983
        return result;
3984

    
3985
    winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
3986
    
3987
    /* note, the following would be easier if there was a generic way of testing
3988
        that a stream belongs to a specific host API */
3989
    
3990
    if( PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->callbackStreamInterface
3991
            || PA_STREAM_REP( s )->streamInterface == &winMmeHostApi->blockingStreamInterface )
3992
    {
3993
        /* s is a WinMME stream */
3994
        *stream = (PaWinMmeStream *)s;
3995
        return paNoError;
3996
    }
3997
    else
3998
    {
3999
        return paIncompatibleStreamHostApi;
4000
    }
4001
}
4002

    
4003

    
4004
int PaWinMME_GetStreamInputHandleCount( PaStream* s )
4005
{
4006
    PaWinMmeStream *stream;
4007
    PaError result = GetWinMMEStreamPointer( &stream, s );
4008

    
4009
    if( result == paNoError )
4010
        return (PA_IS_INPUT_STREAM_(stream)) ? stream->input.deviceCount : 0;
4011
    else
4012
        return result;
4013
}
4014

    
4015

    
4016
HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* s, int handleIndex )
4017
{
4018
    PaWinMmeStream *stream;
4019
    PaError result = GetWinMMEStreamPointer( &stream, s );
4020

    
4021
    if( result == paNoError
4022
            && PA_IS_INPUT_STREAM_(stream)
4023
            && handleIndex >= 0
4024
            && (unsigned int)handleIndex < stream->input.deviceCount )
4025
        return ((HWAVEIN*)stream->input.waveHandles)[handleIndex];
4026
    else
4027
        return 0;
4028
}
4029

    
4030

    
4031
int PaWinMME_GetStreamOutputHandleCount( PaStream* s)
4032
{
4033
    PaWinMmeStream *stream;
4034
    PaError result = GetWinMMEStreamPointer( &stream, s );
4035

    
4036
    if( result == paNoError )
4037
        return (PA_IS_OUTPUT_STREAM_(stream)) ? stream->output.deviceCount : 0;
4038
    else
4039
        return result;
4040
}
4041

    
4042

    
4043
HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* s, int handleIndex )
4044
{
4045
    PaWinMmeStream *stream;
4046
    PaError result = GetWinMMEStreamPointer( &stream, s );
4047

    
4048
    if( result == paNoError
4049
            && PA_IS_OUTPUT_STREAM_(stream)
4050
            && handleIndex >= 0
4051
            && (unsigned int)handleIndex < stream->output.deviceCount )
4052
        return ((HWAVEOUT*)stream->output.waveHandles)[handleIndex];
4053
    else
4054
        return 0;
4055
}