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 / dsound / pa_win_ds.c @ 164:9fa11135915a

History | View | Annotate | Download (123 KB)

1
/*
2
 * $Id$
3
 * Portable Audio I/O Library DirectSound implementation
4
 *
5
 * Authors: Phil Burk, Robert Marsanyi & Ross Bencina
6
 * Based on the Open Source API proposed by Ross Bencina
7
 * Copyright (c) 1999-2007 Ross Bencina, Phil Burk, Robert Marsanyi
8
 *
9
 * Permission is hereby granted, free of charge, to any person obtaining
10
 * a copy of this software and associated documentation files
11
 * (the "Software"), to deal in the Software without restriction,
12
 * including without limitation the rights to use, copy, modify, merge,
13
 * publish, distribute, sublicense, and/or sell copies of the Software,
14
 * and to permit persons to whom the Software is furnished to do so,
15
 * subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be
18
 * included in all copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
24
 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
 */
28

    
29
/*
30
 * The text above constitutes the entire PortAudio license; however, 
31
 * the PortAudio community also makes the following non-binding requests:
32
 *
33
 * Any person wishing to distribute modifications to the Software is
34
 * requested to send the modifications to the original developer so that
35
 * they can be incorporated into the canonical version. It is also 
36
 * requested that these non-binding requests be included along with the 
37
 * license above.
38
 */
39

    
40
/** @file
41
 @ingroup hostapi_src
42
*/
43

    
44
/* Until May 2011 PA/DS has used a multimedia timer to perform the callback.
45
   We're replacing this with a new implementation using a thread and a different timer mechanim.
46
   Defining PA_WIN_DS_USE_WMME_TIMER uses the old (pre-May 2011) behavior.
47
*/
48
//#define PA_WIN_DS_USE_WMME_TIMER
49

    
50
#include <assert.h>
51
#include <stdio.h>
52
#include <string.h> /* strlen() */
53

    
54
#define _WIN32_WINNT 0x0400 /* required to get waitable timer APIs */
55
#include <initguid.h> /* make sure ds guids get defined */
56
#include <windows.h>
57
#include <objbase.h>
58

    
59

    
60
/*
61
  Use the earliest version of DX required, no need to polute the namespace
62
*/
63
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
64
#define DIRECTSOUND_VERSION 0x0800
65
#else
66
#define DIRECTSOUND_VERSION 0x0300
67
#endif
68
#include <dsound.h>
69
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
70
#include <dsconf.h>
71
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
72
#ifndef PA_WIN_DS_USE_WMME_TIMER
73
#ifndef UNDER_CE
74
#include <process.h>
75
#endif
76
#endif
77

    
78
#include "pa_util.h"
79
#include "pa_allocation.h"
80
#include "pa_hostapi.h"
81
#include "pa_stream.h"
82
#include "pa_cpuload.h"
83
#include "pa_process.h"
84
#include "pa_debugprint.h"
85

    
86
#include "pa_win_ds.h"
87
#include "pa_win_ds_dynlink.h"
88
#include "pa_win_waveformat.h"
89
#include "pa_win_wdmks_utils.h"
90
#include "pa_win_coinitialize.h"
91

    
92
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
93
#pragma comment( lib, "dsound.lib" )
94
#pragma comment( lib, "winmm.lib" )
95
#pragma comment( lib, "kernel32.lib" )
96
#endif
97

    
98
/* use CreateThread for CYGWIN, _beginthreadex for all others */
99
#ifndef PA_WIN_DS_USE_WMME_TIMER
100

    
101
#if !defined(__CYGWIN__) && !defined(UNDER_CE)
102
#define CREATE_THREAD (HANDLE)_beginthreadex
103
#undef CLOSE_THREAD_HANDLE /* as per documentation we don't call CloseHandle on a thread created with _beginthreadex */
104
#define PA_THREAD_FUNC static unsigned WINAPI
105
#define PA_THREAD_ID unsigned
106
#else
107
#define CREATE_THREAD CreateThread
108
#define CLOSE_THREAD_HANDLE CloseHandle
109
#define PA_THREAD_FUNC static DWORD WINAPI
110
#define PA_THREAD_ID DWORD
111
#endif
112

    
113
#if (defined(UNDER_CE))
114
#pragma comment(lib, "Coredll.lib")
115
#elif (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
116
#pragma comment(lib, "winmm.lib")
117
#endif
118

    
119
PA_THREAD_FUNC ProcessingThreadProc( void *pArg );
120

    
121
#if !defined(UNDER_CE)
122
#define PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT /* use waitable timer where possible, otherwise we use a WaitForSingleObject timeout */
123
#endif
124

    
125
#endif /* !PA_WIN_DS_USE_WMME_TIMER */
126

    
127

    
128
/*
129
 provided in newer platform sdks and x64
130
 */
131
#ifndef DWORD_PTR
132
    #if defined(_WIN64)
133
        #define DWORD_PTR unsigned __int64
134
    #else
135
        #define DWORD_PTR unsigned long
136
    #endif
137
#endif
138

    
139
#define PRINT(x) PA_DEBUG(x);
140
#define ERR_RPT(x) PRINT(x)
141
#define DBUG(x)   PRINT(x)
142
#define DBUGX(x)  PRINT(x)
143

    
144
#define PA_USE_HIGH_LATENCY   (0)
145
#if PA_USE_HIGH_LATENCY
146
#define PA_DS_WIN_9X_DEFAULT_LATENCY_     (.500)
147
#define PA_DS_WIN_NT_DEFAULT_LATENCY_     (.600)
148
#else
149
#define PA_DS_WIN_9X_DEFAULT_LATENCY_     (.140)
150
#define PA_DS_WIN_NT_DEFAULT_LATENCY_     (.280)
151
#endif
152

    
153
#define PA_DS_WIN_WDM_DEFAULT_LATENCY_    (.120)
154

    
155
/* we allow the polling period to range between 1 and 100ms.
156
   prior to August 2011 we limited the minimum polling period to 10ms.
157
*/
158
#define PA_DS_MINIMUM_POLLING_PERIOD_SECONDS    (0.001) /* 1ms */
159
#define PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS    (0.100) /* 100ms */
160
#define PA_DS_POLLING_JITTER_SECONDS            (0.001) /* 1ms */
161

    
162
#define SECONDS_PER_MSEC      (0.001)
163
#define MSECS_PER_SECOND       (1000)
164

    
165
/* prototypes for functions declared in this file */
166

    
167
#ifdef __cplusplus
168
extern "C"
169
{
170
#endif /* __cplusplus */
171

    
172
PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
173

    
174
#ifdef __cplusplus
175
}
176
#endif /* __cplusplus */
177

    
178
static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
179
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
180
                           PaStream** s,
181
                           const PaStreamParameters *inputParameters,
182
                           const PaStreamParameters *outputParameters,
183
                           double sampleRate,
184
                           unsigned long framesPerBuffer,
185
                           PaStreamFlags streamFlags,
186
                           PaStreamCallback *streamCallback,
187
                           void *userData );
188
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
189
                                  const PaStreamParameters *inputParameters,
190
                                  const PaStreamParameters *outputParameters,
191
                                  double sampleRate );
192
static PaError CloseStream( PaStream* stream );
193
static PaError StartStream( PaStream *stream );
194
static PaError StopStream( PaStream *stream );
195
static PaError AbortStream( PaStream *stream );
196
static PaError IsStreamStopped( PaStream *s );
197
static PaError IsStreamActive( PaStream *stream );
198
static PaTime GetStreamTime( PaStream *stream );
199
static double GetStreamCpuLoad( PaStream* stream );
200
static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
201
static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
202
static signed long GetStreamReadAvailable( PaStream* stream );
203
static signed long GetStreamWriteAvailable( PaStream* stream );
204

    
205

    
206
/* FIXME: should convert hr to a string */
207
#define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
208
    PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
209

    
210
/************************************************* DX Prototypes **********/
211
static BOOL CALLBACK CollectGUIDsProcW(LPGUID lpGUID,
212
                                     LPCWSTR lpszDesc,
213
                                     LPCWSTR lpszDrvName,
214
                                     LPVOID lpContext );
215

    
216
/************************************************************************************/
217
/********************** Structures **************************************************/
218
/************************************************************************************/
219
/* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
220

    
221
typedef struct PaWinDsDeviceInfo
222
{
223
    PaDeviceInfo        inheritedDeviceInfo;
224
    GUID                guid;
225
    GUID                *lpGUID;
226
    double              sampleRates[3];
227
    char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
228
    char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
229
} PaWinDsDeviceInfo;
230

    
231
typedef struct
232
{
233
    PaUtilHostApiRepresentation inheritedHostApiRep;
234
    PaUtilStreamInterface    callbackStreamInterface;
235
    PaUtilStreamInterface    blockingStreamInterface;
236

    
237
    PaUtilAllocationGroup   *allocations;
238

    
239
    /* implementation specific data goes here */
240

    
241
    PaWinUtilComInitializationResult comInitializationResult;
242

    
243
} PaWinDsHostApiRepresentation;
244

    
245

    
246
/* PaWinDsStream - a stream data structure specifically for this implementation */
247

    
248
typedef struct PaWinDsStream
249
{
250
    PaUtilStreamRepresentation streamRepresentation;
251
    PaUtilCpuLoadMeasurer cpuLoadMeasurer;
252
    PaUtilBufferProcessor bufferProcessor;
253

    
254
/* DirectSound specific data. */
255
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
256
    LPDIRECTSOUNDFULLDUPLEX8 pDirectSoundFullDuplex8;
257
#endif
258

    
259
/* Output */
260
    LPDIRECTSOUND        pDirectSound;
261
    LPDIRECTSOUNDBUFFER  pDirectSoundPrimaryBuffer;
262
    LPDIRECTSOUNDBUFFER  pDirectSoundOutputBuffer;
263
    DWORD                outputBufferWriteOffsetBytes;     /* last write position */
264
    INT                  outputBufferSizeBytes;
265
    INT                  outputFrameSizeBytes;
266
    /* Try to detect play buffer underflows. */
267
    LARGE_INTEGER        perfCounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
268
    LARGE_INTEGER        previousPlayTime;
269
    DWORD                previousPlayCursor;
270
    UINT                 outputUnderflowCount;
271
    BOOL                 outputIsRunning;
272
    INT                  finalZeroBytesWritten; /* used to determine when we've flushed the whole buffer */
273

    
274
/* Input */
275
    LPDIRECTSOUNDCAPTURE pDirectSoundCapture;
276
    LPDIRECTSOUNDCAPTUREBUFFER   pDirectSoundInputBuffer;
277
    INT                  inputFrameSizeBytes;
278
    UINT                 readOffset;      /* last read position */
279
    UINT                 inputBufferSizeBytes;
280

    
281
    
282
    int              hostBufferSizeFrames; /* input and output host ringbuffers have the same number of frames */
283
    double           framesWritten;
284
    double           secondsPerHostByte; /* Used to optimize latency calculation for outTime */
285
    double           pollingPeriodSeconds;
286

    
287
    PaStreamCallbackFlags callbackFlags;
288

    
289
    PaStreamFlags    streamFlags;
290
    int              callbackResult;
291
    HANDLE           processingCompleted;
292
    
293
/* FIXME - move all below to PaUtilStreamRepresentation */
294
    volatile int     isStarted;
295
    volatile int     isActive;
296
    volatile int     stopProcessing; /* stop thread once existing buffers have been returned */
297
    volatile int     abortProcessing; /* stop thread immediately */
298

    
299
    UINT             systemTimerResolutionPeriodMs; /* set to 0 if we were unable to set the timer period */ 
300

    
301
#ifdef PA_WIN_DS_USE_WMME_TIMER
302
    MMRESULT         timerID;
303
#else
304

    
305
#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
306
    HANDLE           waitableTimer;
307
#endif
308
    HANDLE           processingThread;
309
    PA_THREAD_ID     processingThreadId;
310
    HANDLE           processingThreadCompleted;
311
#endif
312

    
313
} PaWinDsStream;
314

    
315

    
316
/* Set minimal latency based on the current OS version.
317
 * NT has higher latency.
318
 */
319
static double PaWinDS_GetMinSystemLatencySeconds( void )
320
{
321
/*
322
NOTE: GetVersionEx() is deprecated as of Windows 8.1 and can not be used to reliably detect
323
versions of Windows higher than Windows 8 (due to manifest requirements for reporting higher versions).
324
Microsoft recommends switching to VerifyVersionInfo (available on Win 2k and later), however GetVersionEx
325
is is faster, for now we just disable the deprecation warning.
326
See: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
327
See: http://www.codeproject.com/Articles/678606/Part-Overcoming-Windows-s-deprecation-of-GetVe
328
*/
329
#pragma warning (disable : 4996) /* use of GetVersionEx */
330

    
331
    double minLatencySeconds;
332
    /* Set minimal latency based on whether NT or other OS.
333
     * NT has higher latency.
334
     */
335

    
336
    OSVERSIONINFO osvi;
337
    osvi.dwOSVersionInfoSize = sizeof( osvi );
338
    GetVersionEx( &osvi );
339
    DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
340
    DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
341
    DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
342
    /* Check for NT */
343
    if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
344
    {
345
        minLatencySeconds = PA_DS_WIN_NT_DEFAULT_LATENCY_;
346
    }
347
    else if(osvi.dwMajorVersion >= 5)
348
    {
349
        minLatencySeconds = PA_DS_WIN_WDM_DEFAULT_LATENCY_;
350
    }
351
    else
352
    {
353
        minLatencySeconds = PA_DS_WIN_9X_DEFAULT_LATENCY_;
354
    }
355
    return minLatencySeconds;
356

    
357
#pragma warning (default : 4996)
358
}
359

    
360

    
361
/*************************************************************************
362
** Return minimum workable latency required for this host. This is returned
363
** As the default stream latency in PaDeviceInfo.
364
** Latency can be optionally set by user by setting an environment variable. 
365
** For example, to set latency to 200 msec, put:
366
**
367
**    set PA_MIN_LATENCY_MSEC=200
368
**
369
** in the AUTOEXEC.BAT file and reboot.
370
** If the environment variable is not set, then the latency will be determined
371
** based on the OS. Windows NT has higher latency than Win95.
372
*/
373
#define PA_LATENCY_ENV_NAME  ("PA_MIN_LATENCY_MSEC")
374
#define PA_ENV_BUF_SIZE  (32)
375

    
376
static double PaWinDs_GetMinLatencySeconds( double sampleRate )
377
{
378
    char      envbuf[PA_ENV_BUF_SIZE];
379
    DWORD     hresult;
380
    double    minLatencySeconds = 0;
381

    
382
    /* Let user determine minimal latency by setting environment variable. */
383
    hresult = GetEnvironmentVariableA( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
384
    if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
385
    {
386
        minLatencySeconds = atoi( envbuf ) * SECONDS_PER_MSEC;
387
    }
388
    else
389
    {
390
        minLatencySeconds = PaWinDS_GetMinSystemLatencySeconds();
391
#if PA_USE_HIGH_LATENCY
392
        PRINT(("PA - Minimum Latency set to %f msec!\n", minLatencySeconds * MSECS_PER_SECOND ));
393
#endif
394
    }
395

    
396
    return minLatencySeconds;
397
}
398

    
399

    
400
/************************************************************************************
401
** Duplicate and convert the input string using the group allocations allocator.
402
** A NULL string is converted to a zero length string.
403
** If memory cannot be allocated, NULL is returned.
404
**/
405
static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const wchar_t* src )
406
{
407
    char *result = 0;
408
    
409
    if( src != NULL )
410
    {
411
#if !defined(_UNICODE) && !defined(UNICODE)
412
        size_t len = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
413

    
414
        result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
415
        if( result ) {
416
            if (WideCharToMultiByte(CP_ACP, 0, src, -1, result, (int)len, NULL, NULL) == 0) {
417
                result = 0;
418
            }
419
        }
420
#else
421
        size_t len = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
422

    
423
        result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
424
        if( result ) {
425
            if (WideCharToMultiByte(CP_UTF8, 0, src, -1, result, (int)len, NULL, NULL) == 0) {
426
                result = 0;
427
            }
428
        }
429
#endif
430
    }
431
    else
432
    {
433
        result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
434
        if( result )
435
            result[0] = '\0';
436
    }
437

    
438
    return result;
439
}
440

    
441
/************************************************************************************
442
** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
443
** information during device enumeration.
444
*/
445
typedef struct DSDeviceNameAndGUID{
446
    char *name; // allocated from parent's allocations, never deleted by this structure
447
    GUID guid;
448
    LPGUID lpGUID;
449
    void *pnpInterface;  // wchar_t* interface path, allocated using the DS host api's allocation group
450
} DSDeviceNameAndGUID;
451

    
452
typedef struct DSDeviceNameAndGUIDVector{
453
    PaUtilAllocationGroup *allocations;
454
    PaError enumerationError;
455

    
456
    int count;
457
    int free;
458
    DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
459
} DSDeviceNameAndGUIDVector;
460

    
461
typedef struct DSDeviceNamesAndGUIDs{
462
    PaWinDsHostApiRepresentation *winDsHostApi;
463
    DSDeviceNameAndGUIDVector inputNamesAndGUIDs;
464
    DSDeviceNameAndGUIDVector outputNamesAndGUIDs;
465
} DSDeviceNamesAndGUIDs;
466

    
467
static PaError InitializeDSDeviceNameAndGUIDVector(
468
        DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
469
{
470
    PaError result = paNoError;
471

    
472
    guidVector->allocations = allocations;
473
    guidVector->enumerationError = paNoError;
474

    
475
    guidVector->count = 0;
476
    guidVector->free = 8;
477
    guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
478
    if( guidVector->items == NULL )
479
        result = paInsufficientMemory;
480
    
481
    return result;
482
}
483

    
484
static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
485
{
486
    PaError result = paNoError;
487
    DSDeviceNameAndGUID *newItems;
488
    int i;
489
    
490
    /* double size of vector */
491
    int size = guidVector->count + guidVector->free;
492
    guidVector->free += size;
493

    
494
    newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
495
    if( newItems == NULL )
496
    {
497
        result = paInsufficientMemory;
498
    }
499
    else
500
    {
501
        for( i=0; i < guidVector->count; ++i )
502
        {
503
            newItems[i].name = guidVector->items[i].name;
504
            if( guidVector->items[i].lpGUID == NULL )
505
            {
506
                newItems[i].lpGUID = NULL;
507
            }
508
            else
509
            {
510
                newItems[i].lpGUID = &newItems[i].guid;
511
                memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );
512
            }
513
            newItems[i].pnpInterface = guidVector->items[i].pnpInterface;
514
        }
515

    
516
        LocalFree( guidVector->items );
517
        guidVector->items = newItems;
518
    }                                
519

    
520
    return result;
521
}
522

    
523
/*
524
    it's safe to call DSDeviceNameAndGUIDVector multiple times
525
*/
526
static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
527
{
528
    PaError result = paNoError;
529

    
530
    if( guidVector->items != NULL )
531
    {
532
        if( LocalFree( guidVector->items ) != NULL )
533
            result = paInsufficientMemory;              /** @todo this isn't the correct error to return from a deallocation failure */
534

    
535
        guidVector->items = NULL;
536
    }
537

    
538
    return result;
539
}
540

    
541
/************************************************************************************
542
** Collect preliminary device information during DirectSound enumeration 
543
*/
544
static BOOL CALLBACK CollectGUIDsProcW(LPGUID lpGUID,
545
                                     LPCWSTR lpszDesc,
546
                                     LPCWSTR lpszDrvName,
547
                                     LPVOID lpContext )
548
{
549
    DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
550
    PaError error;
551

    
552
    (void) lpszDrvName; /* unused variable */
553

    
554
    if( namesAndGUIDs->free == 0 )
555
    {
556
        error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
557
        if( error != paNoError )
558
        {
559
            namesAndGUIDs->enumerationError = error;
560
            return FALSE;
561
        }
562
    }
563
    
564
    /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
565
    if( lpGUID == NULL )
566
    {
567
        namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
568
    }
569
    else
570
    {
571
        namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
572
                &namesAndGUIDs->items[namesAndGUIDs->count].guid;
573
      
574
        memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
575
    }
576

    
577
    namesAndGUIDs->items[namesAndGUIDs->count].name =
578
            DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
579
    if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
580
    {
581
        namesAndGUIDs->enumerationError = paInsufficientMemory;
582
        return FALSE;
583
    }
584

    
585
    namesAndGUIDs->items[namesAndGUIDs->count].pnpInterface = 0;
586

    
587
    ++namesAndGUIDs->count;
588
    --namesAndGUIDs->free;
589
    
590
    return TRUE;
591
}
592

    
593

    
594
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
595

    
596
static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source )
597
{
598
    size_t len;
599
    wchar_t *result;
600

    
601
    len = wcslen( source );
602
    result = (wchar_t*)PaUtil_GroupAllocateMemory( allocations, (long) ((len+1) * sizeof(wchar_t)) );
603
    wcscpy( result, source );
604
    return result;
605
}
606

    
607
static BOOL CALLBACK KsPropertySetEnumerateCallback( PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA data, LPVOID context )
608
{
609
    int i;
610
    DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs = (DSDeviceNamesAndGUIDs*)context;
611

    
612
    /*
613
        Apparently data->Interface can be NULL in some cases. 
614
        Possibly virtual devices without hardware.
615
        So we check for NULLs now. See mailing list message November 10, 2012:
616
        "[Portaudio] portaudio initialization crash in KsPropertySetEnumerateCallback(pa_win_ds.c)"
617
    */
618
    if( data->Interface )
619
    {
620
        if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER )
621
        {
622
            for( i=0; i < deviceNamesAndGUIDs->outputNamesAndGUIDs.count; ++i )
623
            {
624
                if( deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID
625
                    && memcmp( &data->DeviceId, deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
626
                {
627
                    deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface = 
628
                        (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
629
                    break;
630
                }
631
            }
632
        }
633
        else if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE )
634
        {
635
            for( i=0; i < deviceNamesAndGUIDs->inputNamesAndGUIDs.count; ++i )
636
            {
637
                if( deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID
638
                    && memcmp( &data->DeviceId, deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
639
                {
640
                    deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface = 
641
                        (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
642
                    break;
643
                }
644
            }
645
        }
646
    }
647

    
648
    return TRUE;
649
}
650

    
651

    
652
static GUID pawin_CLSID_DirectSoundPrivate = 
653
{ 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
654

    
655
static GUID pawin_DSPROPSETID_DirectSoundDevice = 
656
{ 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
657

    
658
static GUID pawin_IID_IKsPropertySet = 
659
{ 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 };
660

    
661

    
662
/*
663
    FindDevicePnpInterfaces fills in the pnpInterface fields in deviceNamesAndGUIDs
664
    with UNICODE file paths to the devices. The DS documentation mentions
665
    at least two techniques by which these Interface paths can be found using IKsPropertySet on
666
    the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION 
667
    property, and the other is using DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE.
668
    I tried both methods and only the second worked. I found two postings on the
669
    net from people who had the same problem with the first method, so I think the method used here is 
670
    more common/likely to work. The probem is that IKsPropertySet_Get returns S_OK
671
    but the fields of the device description are not filled in.
672

673
    The mechanism we use works by registering an enumeration callback which is called for 
674
    every DSound device. Our callback searches for a device in our deviceNamesAndGUIDs list
675
    with the matching GUID and copies the pointer to the Interface path.
676
    Note that we could have used this enumeration callback to perform the original 
677
    device enumeration, however we choose not to so we can disable this step easily.
678

679
    Apparently the IKsPropertySet mechanism was added in DirectSound 9c 2004
680
    http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2004-12/0099.html
681

682
        -- rossb
683
*/
684
static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
685
{
686
    IClassFactory *pClassFactory;
687
   
688
    if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
689
        IKsPropertySet *pPropertySet;
690
        if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
691
            
692
            DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W_DATA data;
693
            ULONG bytesReturned;
694

    
695
            data.Callback = KsPropertySetEnumerateCallback;
696
            data.Context = deviceNamesAndGUIDs;
697

    
698
            IKsPropertySet_Get( pPropertySet,
699
                &pawin_DSPROPSETID_DirectSoundDevice,
700
                DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W,
701
                NULL,
702
                0,
703
                &data,
704
                sizeof(data),
705
                &bytesReturned
706
            );
707
            
708
            IKsPropertySet_Release( pPropertySet );
709
        }
710
        pClassFactory->lpVtbl->Release( pClassFactory );
711
    }
712

    
713
    /*
714
        The following code fragment, which I chose not to use, queries for the 
715
        device interface for a device with a specific GUID:
716

717
        ULONG BytesReturned;
718
        DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA Property;
719

720
        memset (&Property, 0, sizeof(Property));
721
        Property.DataFlow = DIRECTSOUNDDEVICE_DATAFLOW_RENDER;
722
        Property.DeviceId = *lpGUID;  
723

724
        hr = IKsPropertySet_Get( pPropertySet,
725
            &pawin_DSPROPSETID_DirectSoundDevice,
726
            DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W,
727
            NULL,
728
            0,
729
            &Property,
730
            sizeof(Property),
731
            &BytesReturned
732
        );
733

734
        if( hr == S_OK )
735
        {
736
            //pnpInterface = Property.Interface;
737
        }
738
    */
739
}
740
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
741

    
742

    
743
/* 
744
    GUIDs for emulated devices which we blacklist below.
745
    are there more than two of them??
746
*/
747

    
748
GUID IID_IRolandVSCEmulated1 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x01};
749
GUID IID_IRolandVSCEmulated2 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x02};
750

    
751

    
752
#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_  (13) /* must match array length below */
753
static double defaultSampleRateSearchOrder_[] =
754
    { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
755
        16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
756

    
757
/************************************************************************************
758
** Extract capabilities from an output device, and add it to the device info list
759
** if successful. This function assumes that there is enough room in the
760
** device info list to accomodate all entries.
761
**
762
** The device will not be added to the device list if any errors are encountered.
763
*/
764
static PaError AddOutputDeviceInfoFromDirectSound(
765
        PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
766
{
767
    PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
768
    PaWinDsDeviceInfo            *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
769
    PaDeviceInfo                 *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
770
    HRESULT                       hr;
771
    LPDIRECTSOUND                 lpDirectSound;
772
    DSCAPS                        caps;
773
    int                           deviceOK = TRUE;
774
    PaError                       result = paNoError;
775
    int                           i;
776

    
777
    /* Copy GUID to the device info structure. Set pointer. */
778
    if( lpGUID == NULL )
779
    {
780
        winDsDeviceInfo->lpGUID = NULL;
781
    }
782
    else
783
    {
784
        memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
785
        winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
786
    }
787
    
788
    if( lpGUID )
789
    {
790
        if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
791
            IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
792
        {
793
            PA_DEBUG(("BLACKLISTED: %s \n",name));
794
            return paNoError;
795
        }
796
    }
797

    
798
    /* Create a DirectSound object for the specified GUID
799
        Note that using CoCreateInstance doesn't work on windows CE.
800
    */
801
    hr = paWinDsDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
802

    
803
    /** try using CoCreateInstance because DirectSoundCreate was hanging under
804
        some circumstances - note this was probably related to the
805
        #define BOOL short bug which has now been fixed
806
        @todo delete this comment and the following code once we've ensured
807
        there is no bug.
808
    */
809
    /*
810
    hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
811
            &IID_IDirectSound, (void**)&lpDirectSound );
812

813
    if( hr == S_OK )
814
    {
815
        hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
816
    }
817
    */
818
    
819
    if( hr != DS_OK )
820
    {
821
        if (hr == DSERR_ALLOCATED)
822
            PA_DEBUG(("AddOutputDeviceInfoFromDirectSound %s DSERR_ALLOCATED\n",name));
823
        DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
824
        if (lpGUID)
825
            DBUG(("%s's GUID: {0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x, 0x%x} \n",
826
                 name,
827
                 lpGUID->Data1,
828
                 lpGUID->Data2,
829
                 lpGUID->Data3,
830
                 lpGUID->Data4[0],
831
                 lpGUID->Data4[1],
832
                 lpGUID->Data4[2],
833
                 lpGUID->Data4[3],
834
                 lpGUID->Data4[4],
835
                 lpGUID->Data4[5],
836
                 lpGUID->Data4[6],
837
                 lpGUID->Data4[7]));
838

    
839
        deviceOK = FALSE;
840
    }
841
    else
842
    {
843
        /* Query device characteristics. */
844
        memset( &caps, 0, sizeof(caps) ); 
845
        caps.dwSize = sizeof(caps);
846
        hr = IDirectSound_GetCaps( lpDirectSound, &caps );
847
        if( hr != DS_OK )
848
        {
849
            DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
850
            deviceOK = FALSE;
851
        }
852
        else
853
        {
854

    
855
#if PA_USE_WMME
856
            if( caps.dwFlags & DSCAPS_EMULDRIVER )
857
            {
858
                /* If WMME supported, then reject Emulated drivers because they are lousy. */
859
                deviceOK = FALSE;
860
            }
861
#endif
862

    
863
            if( deviceOK )
864
            {
865
                deviceInfo->maxInputChannels = 0;
866
                winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
867

    
868
                /* DS output capabilities only indicate supported number of channels
869
                   using two flags which indicate mono and/or stereo.
870
                   We assume that stereo devices may support more than 2 channels
871
                   (as is the case with 5.1 devices for example) and so
872
                   set deviceOutputChannelCountIsKnown to 0 (unknown).
873
                   In this case OpenStream will try to open the device
874
                   when the user requests more than 2 channels, rather than
875
                   returning an error. 
876
                */
877
                if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
878
                {
879
                    deviceInfo->maxOutputChannels = 2;
880
                    winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
881
                }
882
                else
883
                {
884
                    deviceInfo->maxOutputChannels = 1;
885
                    winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
886
                }
887

    
888
                /* Guess channels count from speaker configuration. We do it only when 
889
                   pnpInterface is NULL or when PAWIN_USE_WDMKS_DEVICE_INFO is undefined.
890
                */
891
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
892
                if( !pnpInterface )
893
#endif
894
                {
895
                    DWORD spkrcfg;
896
                    if( SUCCEEDED(IDirectSound_GetSpeakerConfig( lpDirectSound, &spkrcfg )) )
897
                    {
898
                        int count = 0;
899
                        switch (DSSPEAKER_CONFIG(spkrcfg))
900
                        {
901
                            case DSSPEAKER_HEADPHONE:        count = 2; break;
902
                            case DSSPEAKER_MONO:             count = 1; break;
903
                            case DSSPEAKER_QUAD:             count = 4; break;
904
                            case DSSPEAKER_STEREO:           count = 2; break;
905
                            case DSSPEAKER_SURROUND:         count = 4; break;
906
                            case DSSPEAKER_5POINT1:          count = 6; break;
907
                            case DSSPEAKER_7POINT1:          count = 8; break;
908
#ifndef DSSPEAKER_7POINT1_SURROUND
909
#define DSSPEAKER_7POINT1_SURROUND 0x00000008
910
#endif                            
911
                            case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
912
#ifndef DSSPEAKER_5POINT1_SURROUND
913
#define DSSPEAKER_5POINT1_SURROUND 0x00000009
914
#endif
915
                            case DSSPEAKER_5POINT1_SURROUND: count = 6; break;
916
                        }
917
                        if( count )
918
                        {
919
                            deviceInfo->maxOutputChannels = count;
920
                            winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
921
                        }
922
                    }
923
                }
924

    
925
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
926
                if( pnpInterface )
927
                {
928
                    int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0  );
929
                    if( count > 0 )
930
                    {
931
                        deviceInfo->maxOutputChannels = count;
932
                        winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
933
                    }
934
                }
935
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
936

    
937
                /* initialize defaultSampleRate */
938
                
939
                if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
940
                {
941
                    /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
942
                    deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
943

    
944
                    for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
945
                    {
946
                        if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
947
                                && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
948
                        {
949
                            deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
950
                            break;
951
                        }
952
                    }
953
                }
954
                else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
955
                {
956
                    if( caps.dwMinSecondarySampleRate == 0 )
957
                    {
958
                        /*
959
                        ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
960
                        ** But it supports continuous sampling.
961
                        ** So fake range of rates, and hope it really supports it.
962
                        */
963
                        deviceInfo->defaultSampleRate = 48000.0f;  /* assume 48000 as the default */
964

    
965
                        DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
966
                    }
967
                    else
968
                    {
969
                        deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
970
                    }
971
                }
972
                else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
973
                {
974
                    /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
975
                    ** But we know that they really support a range of rates!
976
                    ** So when we see a ridiculous set of rates, assume it is a range.
977
                    */
978
                  deviceInfo->defaultSampleRate = 48000.0f;  /* assume 48000 as the default */
979
                  DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
980
                }
981
                else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
982

    
983
                //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
984
                // dwFlags | DSCAPS_CONTINUOUSRATE 
985

    
986
                deviceInfo->defaultLowInputLatency = 0.;
987
                deviceInfo->defaultHighInputLatency = 0.;
988

    
989
                deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
990
                deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2;
991
            }
992
        }
993

    
994
        IDirectSound_Release( lpDirectSound );
995
    }
996

    
997
    if( deviceOK )
998
    {
999
        deviceInfo->name = name;
1000

    
1001
        if( lpGUID == NULL )
1002
            hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
1003
            
1004
        hostApi->info.deviceCount++;
1005
    }
1006

    
1007
    return result;
1008
}
1009

    
1010

    
1011
/************************************************************************************
1012
** Extract capabilities from an input device, and add it to the device info list
1013
** if successful. This function assumes that there is enough room in the
1014
** device info list to accomodate all entries.
1015
**
1016
** The device will not be added to the device list if any errors are encountered.
1017
*/
1018
static PaError AddInputDeviceInfoFromDirectSoundCapture(
1019
        PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
1020
{
1021
    PaUtilHostApiRepresentation  *hostApi = &winDsHostApi->inheritedHostApiRep;
1022
    PaWinDsDeviceInfo            *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
1023
    PaDeviceInfo                 *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
1024
    HRESULT                       hr;
1025
    LPDIRECTSOUNDCAPTURE          lpDirectSoundCapture;
1026
    DSCCAPS                       caps;
1027
    int                           deviceOK = TRUE;
1028
    PaError                       result = paNoError;
1029
    
1030
    /* Copy GUID to the device info structure. Set pointer. */
1031
    if( lpGUID == NULL )
1032
    {
1033
        winDsDeviceInfo->lpGUID = NULL;
1034
    }
1035
    else
1036
    {
1037
        winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
1038
        memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
1039
    }
1040

    
1041
    hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
1042

    
1043
    /** try using CoCreateInstance because DirectSoundCreate was hanging under
1044
        some circumstances - note this was probably related to the
1045
        #define BOOL short bug which has now been fixed
1046
        @todo delete this comment and the following code once we've ensured
1047
        there is no bug.
1048
    */
1049
    /*
1050
    hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
1051
            &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
1052
    */
1053
    if( hr != DS_OK )
1054
    {
1055
        DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
1056
        deviceOK = FALSE;
1057
    }
1058
    else
1059
    {
1060
        /* Query device characteristics. */
1061
        memset( &caps, 0, sizeof(caps) );
1062
        caps.dwSize = sizeof(caps);
1063
        hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
1064
        if( hr != DS_OK )
1065
        {
1066
            DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
1067
            deviceOK = FALSE;
1068
        }
1069
        else
1070
        {
1071
#if PA_USE_WMME
1072
            if( caps.dwFlags & DSCAPS_EMULDRIVER )
1073
            {
1074
                /* If WMME supported, then reject Emulated drivers because they are lousy. */
1075
                deviceOK = FALSE;
1076
            }
1077
#endif
1078

    
1079
            if( deviceOK )
1080
            {
1081
                deviceInfo->maxInputChannels = caps.dwChannels;
1082
                winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
1083

    
1084
                deviceInfo->maxOutputChannels = 0;
1085
                winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
1086

    
1087
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
1088
                if( pnpInterface )
1089
                {
1090
                    int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1  );
1091
                    if( count > 0 )
1092
                    {
1093
                        deviceInfo->maxInputChannels = count;
1094
                        winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
1095
                    }
1096
                }
1097
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
1098

    
1099
/*  constants from a WINE patch by Francois Gouget, see:
1100
    http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
1101

1102
    ---
1103
    Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
1104
    From: Francois Gouget <fgouget@ ... .fr>
1105
    To: Ross Bencina <rbencina@ ... .au>
1106
    Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
1107

1108
    [snip]
1109

1110
    I give you permission to use the patch below under the BSD license.
1111
    http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
1112

1113
    [snip]
1114
*/
1115
#ifndef WAVE_FORMAT_48M08
1116
#define WAVE_FORMAT_48M08      0x00001000    /* 48     kHz, Mono,   8-bit  */
1117
#define WAVE_FORMAT_48S08      0x00002000    /* 48     kHz, Stereo, 8-bit  */
1118
#define WAVE_FORMAT_48M16      0x00004000    /* 48     kHz, Mono,   16-bit */
1119
#define WAVE_FORMAT_48S16      0x00008000    /* 48     kHz, Stereo, 16-bit */
1120
#define WAVE_FORMAT_96M08      0x00010000    /* 96     kHz, Mono,   8-bit  */
1121
#define WAVE_FORMAT_96S08      0x00020000    /* 96     kHz, Stereo, 8-bit  */
1122
#define WAVE_FORMAT_96M16      0x00040000    /* 96     kHz, Mono,   16-bit */
1123
#define WAVE_FORMAT_96S16      0x00080000    /* 96     kHz, Stereo, 16-bit */
1124
#endif
1125

    
1126
                /* defaultSampleRate */
1127
                if( caps.dwChannels == 2 )
1128
                {
1129
                    if( caps.dwFormats & WAVE_FORMAT_4S16 )
1130
                        deviceInfo->defaultSampleRate = 44100.0;
1131
                    else if( caps.dwFormats & WAVE_FORMAT_48S16 )
1132
                        deviceInfo->defaultSampleRate = 48000.0;
1133
                    else if( caps.dwFormats & WAVE_FORMAT_2S16 )
1134
                        deviceInfo->defaultSampleRate = 22050.0;
1135
                    else if( caps.dwFormats & WAVE_FORMAT_1S16 )
1136
                        deviceInfo->defaultSampleRate = 11025.0;
1137
                    else if( caps.dwFormats & WAVE_FORMAT_96S16 )
1138
                        deviceInfo->defaultSampleRate = 96000.0;
1139
                    else
1140
                        deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
1141
                }
1142
                else if( caps.dwChannels == 1 )
1143
                {
1144
                    if( caps.dwFormats & WAVE_FORMAT_4M16 )
1145
                        deviceInfo->defaultSampleRate = 44100.0;
1146
                    else if( caps.dwFormats & WAVE_FORMAT_48M16 )
1147
                        deviceInfo->defaultSampleRate = 48000.0;
1148
                    else if( caps.dwFormats & WAVE_FORMAT_2M16 )
1149
                        deviceInfo->defaultSampleRate = 22050.0;
1150
                    else if( caps.dwFormats & WAVE_FORMAT_1M16 )
1151
                        deviceInfo->defaultSampleRate = 11025.0;
1152
                    else if( caps.dwFormats & WAVE_FORMAT_96M16 )
1153
                        deviceInfo->defaultSampleRate = 96000.0;
1154
                    else
1155
                        deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
1156
                }
1157
                else deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
1158

    
1159
                deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
1160
                deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
1161
        
1162
                deviceInfo->defaultLowOutputLatency = 0.;
1163
                deviceInfo->defaultHighOutputLatency = 0.;
1164
            }
1165
        }
1166
        
1167
        IDirectSoundCapture_Release( lpDirectSoundCapture );
1168
    }
1169

    
1170
    if( deviceOK )
1171
    {
1172
        deviceInfo->name = name;
1173

    
1174
        if( lpGUID == NULL )
1175
            hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
1176

    
1177
        hostApi->info.deviceCount++;
1178
    }
1179

    
1180
    return result;
1181
}
1182

    
1183

    
1184
/***********************************************************************************/
1185
PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1186
{
1187
    PaError result = paNoError;
1188
    int i, deviceCount;
1189
    PaWinDsHostApiRepresentation *winDsHostApi;
1190
    DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
1191
    PaWinDsDeviceInfo *deviceInfoArray;
1192

    
1193
    PaWinDs_InitializeDSoundEntryPoints();
1194

    
1195
    /* initialise guid vectors so they can be safely deleted on error */
1196
    deviceNamesAndGUIDs.winDsHostApi = NULL;
1197
    deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
1198
    deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
1199

    
1200
    winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
1201
    if( !winDsHostApi )
1202
    {
1203
        result = paInsufficientMemory;
1204
        goto error;
1205
    }
1206

    
1207
    memset( winDsHostApi, 0, sizeof(PaWinDsHostApiRepresentation) ); /* ensure all fields are zeroed. especially winDsHostApi->allocations */
1208

    
1209
    result = PaWinUtil_CoInitialize( paDirectSound, &winDsHostApi->comInitializationResult );
1210
    if( result != paNoError )
1211
    {
1212
        goto error;
1213
    }
1214

    
1215
    winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
1216
    if( !winDsHostApi->allocations )
1217
    {
1218
        result = paInsufficientMemory;
1219
        goto error;
1220
    }
1221

    
1222
    *hostApi = &winDsHostApi->inheritedHostApiRep;
1223
    (*hostApi)->info.structVersion = 1;
1224
    (*hostApi)->info.type = paDirectSound;
1225
    (*hostApi)->info.name = "Windows DirectSound";
1226
    
1227
    (*hostApi)->info.deviceCount = 0;
1228
    (*hostApi)->info.defaultInputDevice = paNoDevice;
1229
    (*hostApi)->info.defaultOutputDevice = paNoDevice;
1230

    
1231
    
1232
/* DSound - enumerate devices to count them and to gather their GUIDs */
1233

    
1234
    result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
1235
    if( result != paNoError )
1236
        goto error;
1237

    
1238
    result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
1239
    if( result != paNoError )
1240
        goto error;
1241

    
1242
    paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
1243

    
1244
    paWinDsDSoundEntryPoints.DirectSoundEnumerateW( (LPDSENUMCALLBACKW)CollectGUIDsProcW, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
1245

    
1246
    if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
1247
    {
1248
        result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
1249
        goto error;
1250
    }
1251

    
1252
    if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
1253
    {
1254
        result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
1255
        goto error;
1256
    }
1257

    
1258
    deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
1259

    
1260
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
1261
    if( deviceCount > 0 )
1262
    {
1263
        deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
1264
        FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
1265
    }
1266
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
1267

    
1268
    if( deviceCount > 0 )
1269
    {
1270
        /* allocate array for pointers to PaDeviceInfo structs */
1271
        (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1272
                winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
1273
        if( !(*hostApi)->deviceInfos )
1274
        {
1275
            result = paInsufficientMemory;
1276
            goto error;
1277
        }
1278

    
1279
        /* allocate all PaDeviceInfo structs in a contiguous block */
1280
        deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
1281
                winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
1282
        if( !deviceInfoArray )
1283
        {
1284
            result = paInsufficientMemory;
1285
            goto error;
1286
        }
1287

    
1288
        for( i=0; i < deviceCount; ++i )
1289
        {
1290
            PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
1291
            deviceInfo->structVersion = 2;
1292
            deviceInfo->hostApi = hostApiIndex;
1293
            deviceInfo->name = 0;
1294
            (*hostApi)->deviceInfos[i] = deviceInfo;
1295
        }
1296

    
1297
        for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
1298
        {
1299
            result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
1300
                    deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
1301
                    deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
1302
                    deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
1303
            if( result != paNoError )
1304
                goto error;
1305
        }
1306

    
1307
        for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
1308
        {
1309
            result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
1310
                    deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
1311
                    deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
1312
                    deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
1313
            if( result != paNoError )
1314
                goto error;
1315
        }
1316
    }    
1317

    
1318
    result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
1319
    if( result != paNoError )
1320
        goto error;
1321

    
1322
    result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
1323
    if( result != paNoError )
1324
        goto error;
1325

    
1326
    
1327
    (*hostApi)->Terminate = Terminate;
1328
    (*hostApi)->OpenStream = OpenStream;
1329
    (*hostApi)->IsFormatSupported = IsFormatSupported;
1330

    
1331
    PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
1332
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1333
                                      GetStreamTime, GetStreamCpuLoad,
1334
                                      PaUtil_DummyRead, PaUtil_DummyWrite,
1335
                                      PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1336

    
1337
    PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
1338
                                      StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1339
                                      GetStreamTime, PaUtil_DummyGetCpuLoad,
1340
                                      ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1341

    
1342
    return result;
1343

    
1344
error:
1345
    TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
1346
    TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
1347

    
1348
    Terminate( (struct PaUtilHostApiRepresentation *)winDsHostApi );
1349

    
1350
    return result;
1351
}
1352

    
1353

    
1354
/***********************************************************************************/
1355
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1356
{
1357
    PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
1358

    
1359
    if( winDsHostApi ){
1360
        if( winDsHostApi->allocations )
1361
        {
1362
            PaUtil_FreeAllAllocations( winDsHostApi->allocations );
1363
            PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
1364
        }
1365

    
1366
        PaWinUtil_CoUninitialize( paDirectSound, &winDsHostApi->comInitializationResult );
1367

    
1368
        PaUtil_FreeMemory( winDsHostApi );
1369
    }
1370

    
1371
    PaWinDs_TerminateDSoundEntryPoints();
1372
}
1373

    
1374
static PaError ValidateWinDirectSoundSpecificStreamInfo(
1375
        const PaStreamParameters *streamParameters,
1376
        const PaWinDirectSoundStreamInfo *streamInfo )
1377
{
1378
    if( streamInfo )
1379
    {
1380
        if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
1381
                || streamInfo->version != 2 )
1382
        {
1383
            return paIncompatibleHostApiSpecificStreamInfo;
1384
        }
1385

    
1386
        if( streamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
1387
        {
1388
            if( streamInfo->framesPerBuffer <= 0 )
1389
                return paIncompatibleHostApiSpecificStreamInfo;
1390

    
1391
        }
1392
    }
1393

    
1394
    return paNoError;
1395
}
1396

    
1397
/***********************************************************************************/
1398
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1399
                                  const PaStreamParameters *inputParameters,
1400
                                  const PaStreamParameters *outputParameters,
1401
                                  double sampleRate )
1402
{
1403
    PaError result;
1404
    PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
1405
    PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1406
    int inputChannelCount, outputChannelCount;
1407
    PaSampleFormat inputSampleFormat, outputSampleFormat;
1408
    PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
1409

    
1410
    if( inputParameters )
1411
    {
1412
        inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
1413
        inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
1414

    
1415
        inputChannelCount = inputParameters->channelCount;
1416
        inputSampleFormat = inputParameters->sampleFormat;
1417

    
1418
        /* unless alternate device specification is supported, reject the use of
1419
            paUseHostApiSpecificDeviceSpecification */
1420

    
1421
        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1422
            return paInvalidDevice;
1423

    
1424
        /* check that input device can support inputChannelCount */
1425
        if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
1426
                && inputChannelCount > inputDeviceInfo->maxInputChannels )
1427
            return paInvalidChannelCount;
1428

    
1429
        /* validate inputStreamInfo */
1430
        inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1431
        result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
1432
        if( result != paNoError ) return result;
1433
    }
1434
    else
1435
    {
1436
        inputChannelCount = 0;
1437
    }
1438

    
1439
    if( outputParameters )
1440
    {
1441
        outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
1442
        outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
1443

    
1444
        outputChannelCount = outputParameters->channelCount;
1445
        outputSampleFormat = outputParameters->sampleFormat;
1446
        
1447
        /* unless alternate device specification is supported, reject the use of
1448
            paUseHostApiSpecificDeviceSpecification */
1449

    
1450
        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1451
            return paInvalidDevice;
1452

    
1453
        /* check that output device can support inputChannelCount */
1454
        if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
1455
                && outputChannelCount > outputDeviceInfo->maxOutputChannels )
1456
            return paInvalidChannelCount;
1457

    
1458
        /* validate outputStreamInfo */
1459
        outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1460
        result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
1461
        if( result != paNoError ) return result;
1462
    }
1463
    else
1464
    {
1465
        outputChannelCount = 0;
1466
    }
1467
    
1468
    /*
1469
        IMPLEMENT ME:
1470

1471
            - if a full duplex stream is requested, check that the combination
1472
                of input and output parameters is supported if necessary
1473

1474
            - check that the device supports sampleRate
1475

1476
        Because the buffer adapter handles conversion between all standard
1477
        sample formats, the following checks are only required if paCustomFormat
1478
        is implemented, or under some other unusual conditions.
1479

1480
            - check that input device can support inputSampleFormat, or that
1481
                we have the capability to convert from outputSampleFormat to
1482
                a native format
1483

1484
            - check that output device can support outputSampleFormat, or that
1485
                we have the capability to convert from outputSampleFormat to
1486
                a native format
1487
    */
1488

    
1489
    return paFormatIsSupported;
1490
}
1491

    
1492

    
1493
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
1494
static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
1495
                                       PaWinDsDeviceInfo *inputDevice,
1496
                                       PaSampleFormat hostInputSampleFormat,
1497
                                       WORD inputChannelCount, 
1498
                                       int bytesPerInputBuffer,
1499
                                       PaWinWaveFormatChannelMask inputChannelMask,
1500
                                       PaWinDsDeviceInfo *outputDevice,
1501
                                       PaSampleFormat hostOutputSampleFormat,
1502
                                       WORD outputChannelCount, 
1503
                                       int bytesPerOutputBuffer,
1504
                                       PaWinWaveFormatChannelMask outputChannelMask,
1505
                                       unsigned long nFrameRate
1506
                                        )
1507
{
1508
    HRESULT hr;
1509
    DSCBUFFERDESC  captureDesc;
1510
    PaWinWaveFormat captureWaveFormat;
1511
    DSBUFFERDESC   secondaryRenderDesc;
1512
    PaWinWaveFormat renderWaveFormat;
1513
    LPDIRECTSOUNDBUFFER8 pRenderBuffer8;
1514
    LPDIRECTSOUNDCAPTUREBUFFER8 pCaptureBuffer8;
1515

    
1516
    // capture buffer description
1517

    
1518
    // only try wave format extensible. assume it's available on all ds 8 systems
1519
    PaWin_InitializeWaveFormatExtensible( &captureWaveFormat, inputChannelCount, 
1520
                hostInputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostInputSampleFormat ),
1521
                nFrameRate, inputChannelMask );
1522

    
1523
    ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
1524
    captureDesc.dwSize = sizeof(DSCBUFFERDESC);
1525
    captureDesc.dwFlags = 0;
1526
    captureDesc.dwBufferBytes = bytesPerInputBuffer;
1527
    captureDesc.lpwfxFormat = (WAVEFORMATEX*)&captureWaveFormat;
1528

    
1529
    // render buffer description
1530

    
1531
    PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount, 
1532
                hostOutputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostOutputSampleFormat ),
1533
                nFrameRate, outputChannelMask );
1534

    
1535
    ZeroMemory(&secondaryRenderDesc, sizeof(DSBUFFERDESC));
1536
    secondaryRenderDesc.dwSize = sizeof(DSBUFFERDESC);
1537
    secondaryRenderDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
1538
    secondaryRenderDesc.dwBufferBytes = bytesPerOutputBuffer;
1539
    secondaryRenderDesc.lpwfxFormat = (WAVEFORMATEX*)&renderWaveFormat;
1540

    
1541
    /* note that we don't create a primary buffer here at all */
1542

    
1543
    hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8( 
1544
            inputDevice->lpGUID, outputDevice->lpGUID,
1545
            &captureDesc, &secondaryRenderDesc,
1546
            GetDesktopWindow(), /* see InitOutputBuffer() for a discussion of whether this is a good idea */
1547
            DSSCL_EXCLUSIVE,
1548
            &stream->pDirectSoundFullDuplex8,
1549
            &pCaptureBuffer8,
1550
            &pRenderBuffer8,
1551
            NULL /* pUnkOuter must be NULL */ 
1552
        );
1553

    
1554
    if( hr == DS_OK )
1555
    {
1556
        PA_DEBUG(("DirectSoundFullDuplexCreate succeeded!\n"));
1557

    
1558
        /* retrieve the pre ds 8 buffer interfaces which are used by the rest of the code */
1559

    
1560
        hr = IUnknown_QueryInterface( pCaptureBuffer8, &IID_IDirectSoundCaptureBuffer, (LPVOID *)&stream->pDirectSoundInputBuffer );
1561
        
1562
        if( hr == DS_OK )
1563
            hr = IUnknown_QueryInterface( pRenderBuffer8, &IID_IDirectSoundBuffer, (LPVOID *)&stream->pDirectSoundOutputBuffer );
1564

    
1565
        /* release the ds 8 interfaces, we don't need them */
1566
        IUnknown_Release( pCaptureBuffer8 );
1567
        IUnknown_Release( pRenderBuffer8 );
1568

    
1569
        if( !stream->pDirectSoundInputBuffer || !stream->pDirectSoundOutputBuffer ){
1570
            /* couldn't get pre ds 8 interfaces for some reason. clean up. */
1571
            if( stream->pDirectSoundInputBuffer )
1572
            {
1573
                IUnknown_Release( stream->pDirectSoundInputBuffer );
1574
                stream->pDirectSoundInputBuffer = NULL;
1575
            }
1576

    
1577
            if( stream->pDirectSoundOutputBuffer )
1578
            {
1579
                IUnknown_Release( stream->pDirectSoundOutputBuffer );
1580
                stream->pDirectSoundOutputBuffer = NULL;
1581
            }
1582
            
1583
            IUnknown_Release( stream->pDirectSoundFullDuplex8 );
1584
            stream->pDirectSoundFullDuplex8 = NULL;
1585
        }
1586
    }
1587
    else
1588
    {
1589
        PA_DEBUG(("DirectSoundFullDuplexCreate failed. hr=%d\n", hr));
1590
    }
1591

    
1592
    return hr;
1593
}
1594
#endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
1595

    
1596

    
1597
static HRESULT InitInputBuffer( PaWinDsStream *stream, 
1598
                               PaWinDsDeviceInfo *device, 
1599
                               PaSampleFormat sampleFormat, 
1600
                               unsigned long nFrameRate, 
1601
                               WORD nChannels, 
1602
                               int bytesPerBuffer, 
1603
                               PaWinWaveFormatChannelMask channelMask )
1604
{
1605
    DSCBUFFERDESC  captureDesc;
1606
    PaWinWaveFormat waveFormat;
1607
    HRESULT        result;
1608
    
1609
    if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( 
1610
            device->lpGUID, &stream->pDirectSoundCapture, NULL) ) != DS_OK ){
1611
         ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
1612
         return result;
1613
    }
1614

    
1615
    // Setup the secondary buffer description
1616
    ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
1617
    captureDesc.dwSize = sizeof(DSCBUFFERDESC);
1618
    captureDesc.dwFlags = 0;
1619
    captureDesc.dwBufferBytes = bytesPerBuffer;
1620
    captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
1621
    
1622
    // Create the capture buffer
1623

    
1624
    // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
1625
    PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels, 
1626
                sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
1627
                nFrameRate, channelMask );
1628

    
1629
    if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1630
                  &captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
1631
    {
1632
        PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat, 
1633
                PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
1634

    
1635
        if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1636
                    &captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
1637
    }
1638

    
1639
    stream->readOffset = 0;  // reset last read position to start of buffer
1640
    return DS_OK;
1641
}
1642

    
1643

    
1644
static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device, 
1645
                                PaSampleFormat sampleFormat, unsigned long nFrameRate, 
1646
                                WORD nChannels, int bytesPerBuffer, 
1647
                                PaWinWaveFormatChannelMask channelMask )
1648
{
1649
    HRESULT        result;
1650
    HWND           hWnd;
1651
    HRESULT        hr;
1652
    PaWinWaveFormat waveFormat;
1653
    DSBUFFERDESC   primaryDesc;
1654
    DSBUFFERDESC   secondaryDesc;
1655
    
1656
    if( (hr = paWinDsDSoundEntryPoints.DirectSoundCreate( 
1657
                device->lpGUID, &stream->pDirectSound, NULL )) != DS_OK ){
1658
        ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
1659
        return hr;
1660
    }
1661

    
1662
    // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
1663
    // applications's window. Also if that window is closed before the Buffer is closed
1664
    // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
1665
    // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
1666
    // hWnd = GetForegroundWindow();
1667
    //
1668
    //  FIXME: The example code I have on the net creates a hidden window that
1669
    //      is managed by our code - I think we should do that - one hidden
1670
    //      window for the whole of Pa_DS
1671
    //
1672
    hWnd = GetDesktopWindow();
1673

    
1674
    // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
1675
    // exclusive also prevents unexpected sounds from other apps during a performance.
1676
    if ((hr = IDirectSound_SetCooperativeLevel( stream->pDirectSound,
1677
              hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
1678
    {
1679
        return hr;
1680
    }
1681

    
1682
    // -----------------------------------------------------------------------
1683
    // Create primary buffer and set format just so we can specify our custom format.
1684
    // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
1685
    // Setup the primary buffer description
1686
    ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
1687
    primaryDesc.dwSize        = sizeof(DSBUFFERDESC);
1688
    primaryDesc.dwFlags       = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
1689
    primaryDesc.dwBufferBytes = 0;
1690
    primaryDesc.lpwfxFormat   = NULL;
1691
    // Create the buffer
1692
    if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
1693
                  &primaryDesc, &stream->pDirectSoundPrimaryBuffer, NULL)) != DS_OK)
1694
        goto error;
1695

    
1696
    // Set the primary buffer's format
1697

    
1698
    // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
1699
    PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels, 
1700
                sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
1701
                nFrameRate, channelMask );
1702

    
1703
    if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
1704
    {
1705
        PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat, 
1706
                PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
1707

    
1708
        if((result = IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK)
1709
            goto error;
1710
    }
1711

    
1712
    // ----------------------------------------------------------------------
1713
    // Setup the secondary buffer description
1714
    ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
1715
    secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
1716
    secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
1717
    secondaryDesc.dwBufferBytes = bytesPerBuffer;
1718
    secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat; /* waveFormat contains whatever format was negotiated for the primary buffer above */
1719
    // Create the secondary buffer
1720
    if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
1721
                  &secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK)
1722
      goto error;
1723
    
1724
    return DS_OK;
1725

    
1726
error:
1727

    
1728
    if( stream->pDirectSoundPrimaryBuffer )
1729
    {
1730
        IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
1731
        stream->pDirectSoundPrimaryBuffer = NULL;
1732
    }
1733

    
1734
    return result;
1735
}
1736

    
1737

    
1738
static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames, 
1739
                                    unsigned long *pollingPeriodFrames,
1740
                                    int isFullDuplex,
1741
                                    unsigned long suggestedInputLatencyFrames,
1742
                                    unsigned long suggestedOutputLatencyFrames,
1743
                                    double sampleRate, unsigned long userFramesPerBuffer )
1744
{
1745
    unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
1746
    unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
1747
    unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
1748
    
1749
    if( userFramesPerBuffer == paFramesPerBufferUnspecified )
1750
    {
1751
        unsigned long targetBufferingLatencyFrames = max( suggestedInputLatencyFrames, suggestedOutputLatencyFrames );
1752

    
1753
        *pollingPeriodFrames = targetBufferingLatencyFrames / 4;
1754
        if( *pollingPeriodFrames < minimumPollingPeriodFrames )
1755
        {
1756
            *pollingPeriodFrames = minimumPollingPeriodFrames;
1757
        }
1758
        else if( *pollingPeriodFrames > maximumPollingPeriodFrames )
1759
        {
1760
            *pollingPeriodFrames = maximumPollingPeriodFrames;
1761
        }
1762

    
1763
        *hostBufferSizeFrames = *pollingPeriodFrames 
1764
                + max( *pollingPeriodFrames + pollingJitterFrames, targetBufferingLatencyFrames);
1765
    }
1766
    else
1767
    {
1768
        unsigned long targetBufferingLatencyFrames = suggestedInputLatencyFrames;
1769
        if( isFullDuplex )
1770
        {
1771
            /* In full duplex streams we know that the buffer adapter adds userFramesPerBuffer
1772
               extra fixed latency. so we subtract it here as a fixed latency before computing
1773
               the buffer size. being careful not to produce an unrepresentable negative result.
1774
               
1775
               Note: this only works as expected if output latency is greater than input latency.
1776
               Otherwise we use input latency anyway since we do max(in,out).
1777
            */
1778

    
1779
            if( userFramesPerBuffer < suggestedOutputLatencyFrames )
1780
            {
1781
                unsigned long adjustedSuggestedOutputLatencyFrames = 
1782
                        suggestedOutputLatencyFrames - userFramesPerBuffer;
1783

    
1784
                /* maximum of input and adjusted output suggested latency */
1785
                if( adjustedSuggestedOutputLatencyFrames > targetBufferingLatencyFrames )
1786
                    targetBufferingLatencyFrames = adjustedSuggestedOutputLatencyFrames;
1787
            }
1788
        }
1789
        else
1790
        {
1791
            /* maximum of input and output suggested latency */
1792
            if( suggestedOutputLatencyFrames > suggestedInputLatencyFrames )
1793
                targetBufferingLatencyFrames = suggestedOutputLatencyFrames;
1794
        }   
1795

    
1796
        *hostBufferSizeFrames = userFramesPerBuffer 
1797
                + max( userFramesPerBuffer + pollingJitterFrames, targetBufferingLatencyFrames);
1798

    
1799
        *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), targetBufferingLatencyFrames / 16 );
1800

    
1801
        if( *pollingPeriodFrames > maximumPollingPeriodFrames )
1802
        {
1803
            *pollingPeriodFrames = maximumPollingPeriodFrames;
1804
        }
1805
    } 
1806
}
1807

    
1808

    
1809
static void CalculatePollingPeriodFrames( unsigned long hostBufferSizeFrames, 
1810
                                    unsigned long *pollingPeriodFrames,
1811
                                    double sampleRate, unsigned long userFramesPerBuffer )
1812
{
1813
    unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
1814
    unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
1815
    unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
1816

    
1817
    *pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), hostBufferSizeFrames / 16 );
1818

    
1819
    if( *pollingPeriodFrames > maximumPollingPeriodFrames )
1820
    {
1821
        *pollingPeriodFrames = maximumPollingPeriodFrames;
1822
    }
1823
}
1824

    
1825

    
1826
static void SetStreamInfoLatencies( PaWinDsStream *stream, 
1827
                                   unsigned long userFramesPerBuffer, 
1828
                                   unsigned long pollingPeriodFrames,
1829
                                   double sampleRate )
1830
{
1831
    /* compute the stream info actual latencies based on framesPerBuffer, polling period, hostBufferSizeFrames, 
1832
    and the configuration of the buffer processor */
1833

    
1834
    unsigned long effectiveFramesPerBuffer = (userFramesPerBuffer == paFramesPerBufferUnspecified)
1835
                                             ? pollingPeriodFrames
1836
                                             : userFramesPerBuffer;
1837

    
1838
    if( stream->bufferProcessor.inputChannelCount > 0 )
1839
    {
1840
        /* stream info input latency is the minimum buffering latency 
1841
           (unlike suggested and default which are *maximums*) */
1842
        stream->streamRepresentation.streamInfo.inputLatency =
1843
                (double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
1844
                    + effectiveFramesPerBuffer) / sampleRate;
1845
    }
1846
    else
1847
    {
1848
        stream->streamRepresentation.streamInfo.inputLatency = 0;
1849
    }
1850

    
1851
    if( stream->bufferProcessor.outputChannelCount > 0 )
1852
    {
1853
        stream->streamRepresentation.streamInfo.outputLatency =
1854
                (double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
1855
                    + (stream->hostBufferSizeFrames - effectiveFramesPerBuffer)) / sampleRate;
1856
    }
1857
    else
1858
    {
1859
        stream->streamRepresentation.streamInfo.outputLatency = 0;
1860
    }
1861
}
1862

    
1863

    
1864
/***********************************************************************************/
1865
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1866

    
1867
static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1868
                           PaStream** s,
1869
                           const PaStreamParameters *inputParameters,
1870
                           const PaStreamParameters *outputParameters,
1871
                           double sampleRate,
1872
                           unsigned long framesPerBuffer,
1873
                           PaStreamFlags streamFlags,
1874
                           PaStreamCallback *streamCallback,
1875
                           void *userData )
1876
{
1877
    PaError result = paNoError;
1878
    PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
1879
    PaWinDsStream *stream = 0;
1880
    int bufferProcessorIsInitialized = 0;
1881
    int streamRepresentationIsInitialized = 0;
1882
    PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
1883
    PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1884
    int inputChannelCount, outputChannelCount;
1885
    PaSampleFormat inputSampleFormat, outputSampleFormat;
1886
    PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
1887
    int userRequestedHostInputBufferSizeFrames = 0;
1888
    int userRequestedHostOutputBufferSizeFrames = 0;
1889
    unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
1890
    PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
1891
    PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
1892
    unsigned long pollingPeriodFrames = 0;
1893

    
1894
    if( inputParameters )
1895
    {
1896
        inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
1897
        inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
1898

    
1899
        inputChannelCount = inputParameters->channelCount;
1900
        inputSampleFormat = inputParameters->sampleFormat;
1901
        suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
1902

    
1903
        /* IDEA: the following 3 checks could be performed by default by pa_front
1904
            unless some flag indicated otherwise */
1905
            
1906
        /* unless alternate device specification is supported, reject the use of
1907
            paUseHostApiSpecificDeviceSpecification */
1908
        if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1909
            return paInvalidDevice;
1910

    
1911
        /* check that input device can support inputChannelCount */
1912
        if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
1913
                && inputChannelCount > inputDeviceInfo->maxInputChannels )
1914
            return paInvalidChannelCount;
1915
            
1916
        /* validate hostApiSpecificStreamInfo */
1917
        inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1918
        result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
1919
        if( result != paNoError ) return result;
1920

    
1921
        if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
1922
            userRequestedHostInputBufferSizeFrames = inputStreamInfo->framesPerBuffer;
1923

    
1924
        if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
1925
            inputChannelMask = inputStreamInfo->channelMask;
1926
        else
1927
            inputChannelMask = PaWin_DefaultChannelMask( inputChannelCount );
1928
    }
1929
    else
1930
    {
1931
        inputChannelCount = 0;
1932
        inputSampleFormat = 0;
1933
        suggestedInputLatencyFrames = 0;
1934
    }
1935

    
1936

    
1937
    if( outputParameters )
1938
    {
1939
        outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
1940
        outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
1941

    
1942
        outputChannelCount = outputParameters->channelCount;
1943
        outputSampleFormat = outputParameters->sampleFormat;
1944
        suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
1945

    
1946
        /* unless alternate device specification is supported, reject the use of
1947
            paUseHostApiSpecificDeviceSpecification */
1948
        if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1949
            return paInvalidDevice;
1950

    
1951
        /* check that output device can support outputChannelCount */
1952
        if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
1953
                && outputChannelCount > outputDeviceInfo->maxOutputChannels )
1954
            return paInvalidChannelCount;
1955

    
1956
        /* validate hostApiSpecificStreamInfo */
1957
        outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1958
        result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
1959
        if( result != paNoError ) return result;   
1960

    
1961
        if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
1962
            userRequestedHostOutputBufferSizeFrames = outputStreamInfo->framesPerBuffer;
1963

    
1964
        if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
1965
            outputChannelMask = outputStreamInfo->channelMask;
1966
        else
1967
            outputChannelMask = PaWin_DefaultChannelMask( outputChannelCount );
1968
    }
1969
    else
1970
    {
1971
        outputChannelCount = 0;
1972
        outputSampleFormat = 0;
1973
        suggestedOutputLatencyFrames = 0;
1974
    }
1975

    
1976
    /*
1977
        If low level host buffer size is specified for both input and output
1978
        the current code requires the sizes to match.
1979
    */
1980

    
1981
    if( (userRequestedHostInputBufferSizeFrames > 0 && userRequestedHostOutputBufferSizeFrames > 0)
1982
            && userRequestedHostInputBufferSizeFrames != userRequestedHostOutputBufferSizeFrames )
1983
        return paIncompatibleHostApiSpecificStreamInfo;
1984

    
1985

    
1986

    
1987
    /*
1988
        IMPLEMENT ME:
1989

1990
        ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
1991

1992
            - check that input device can support inputSampleFormat, or that
1993
                we have the capability to convert from outputSampleFormat to
1994
                a native format
1995

1996
            - check that output device can support outputSampleFormat, or that
1997
                we have the capability to convert from outputSampleFormat to
1998
                a native format
1999

2000
            - if a full duplex stream is requested, check that the combination
2001
                of input and output parameters is supported
2002

2003
            - check that the device supports sampleRate
2004

2005
            - alter sampleRate to a close allowable rate if possible / necessary
2006

2007
            - validate suggestedInputLatency and suggestedOutputLatency parameters,
2008
                use default values where necessary
2009
    */
2010

    
2011

    
2012
    /* validate platform specific flags */
2013
    if( (streamFlags & paPlatformSpecificFlags) != 0 )
2014
        return paInvalidFlag; /* unexpected platform specific flag */
2015

    
2016

    
2017
    stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
2018
    if( !stream )
2019
    {
2020
        result = paInsufficientMemory;
2021
        goto error;
2022
    }
2023

    
2024
    memset( stream, 0, sizeof(PaWinDsStream) ); /* initialize all stream variables to 0 */
2025

    
2026
    if( streamCallback )
2027
    {
2028
        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2029
                                               &winDsHostApi->callbackStreamInterface, streamCallback, userData );
2030
    }
2031
    else
2032
    {
2033
        PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2034
                                               &winDsHostApi->blockingStreamInterface, streamCallback, userData );
2035
    }
2036
    
2037
    streamRepresentationIsInitialized = 1;
2038

    
2039
    stream->streamFlags = streamFlags;
2040

    
2041
    PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
2042

    
2043

    
2044
    if( inputParameters )
2045
    {
2046
        /* IMPLEMENT ME - establish which  host formats are available */
2047
        PaSampleFormat nativeInputFormats = paInt16;
2048
        /* PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
2049

    
2050
        hostInputSampleFormat =
2051
            PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
2052
    }
2053
    else
2054
    {
2055
        hostInputSampleFormat = 0;
2056
    }
2057

    
2058
    if( outputParameters )
2059
    {
2060
        /* IMPLEMENT ME - establish which  host formats are available */
2061
        PaSampleFormat nativeOutputFormats = paInt16;
2062
        /* PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
2063

    
2064
        hostOutputSampleFormat =
2065
            PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
2066
    }
2067
    else
2068
    {
2069
        hostOutputSampleFormat = 0;
2070
    }
2071

    
2072
    result =  PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2073
                    inputChannelCount, inputSampleFormat, hostInputSampleFormat,
2074
                    outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
2075
                    sampleRate, streamFlags, framesPerBuffer,
2076
                    0, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
2077
                /* This next mode is required because DS can split the host buffer when it wraps around. */
2078
                    paUtilVariableHostBufferSizePartialUsageAllowed,
2079
                    streamCallback, userData );
2080
    if( result != paNoError )
2081
        goto error;
2082

    
2083
    bufferProcessorIsInitialized = 1;
2084

    
2085
   
2086
/* DirectSound specific initialization */ 
2087
    {
2088
        HRESULT          hr;
2089
        unsigned long    integerSampleRate = (unsigned long) (sampleRate + 0.5);
2090
        
2091
        stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
2092
        if( stream->processingCompleted == NULL )
2093
        {
2094
            result = paInsufficientMemory;
2095
            goto error;
2096
        }
2097

    
2098
#ifdef PA_WIN_DS_USE_WMME_TIMER
2099
        stream->timerID = 0;
2100
#endif
2101

    
2102
#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2103
        stream->waitableTimer = (HANDLE)CreateWaitableTimer( 0, FALSE, NULL );
2104
        if( stream->waitableTimer == NULL )
2105
        {
2106
            result = paUnanticipatedHostError;
2107
            PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
2108
            goto error;
2109
        }
2110
#endif
2111

    
2112
#ifndef PA_WIN_DS_USE_WMME_TIMER
2113
        stream->processingThreadCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
2114
        if( stream->processingThreadCompleted == NULL )
2115
        {
2116
            result = paUnanticipatedHostError;
2117
            PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
2118
            goto error;
2119
        }
2120
#endif
2121

    
2122
        /* set up i/o parameters */
2123

    
2124
        if( userRequestedHostInputBufferSizeFrames > 0 || userRequestedHostOutputBufferSizeFrames > 0 )
2125
        {
2126
            /* use low level parameters */
2127

    
2128
            /* since we use the same host buffer size for input and output
2129
               we choose the highest user specified value.
2130
            */
2131
            stream->hostBufferSizeFrames = max( userRequestedHostInputBufferSizeFrames, userRequestedHostOutputBufferSizeFrames );
2132

    
2133
            CalculatePollingPeriodFrames( 
2134
                    stream->hostBufferSizeFrames, &pollingPeriodFrames,
2135
                    sampleRate, framesPerBuffer );
2136
        }
2137
        else
2138
        {
2139
            CalculateBufferSettings( &stream->hostBufferSizeFrames, &pollingPeriodFrames,
2140
                    /* isFullDuplex = */ (inputParameters && outputParameters),
2141
                    suggestedInputLatencyFrames,
2142
                    suggestedOutputLatencyFrames, 
2143
                    sampleRate, framesPerBuffer );
2144
        }
2145

    
2146
        stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate;
2147

    
2148
        DBUG(("DirectSound host buffer size frames: %d, polling period seconds: %f, @ sr: %f\n", 
2149
                stream->hostBufferSizeFrames, stream->pollingPeriodSeconds, sampleRate ));
2150

    
2151

    
2152
        /* ------------------ OUTPUT */
2153
        if( outputParameters )
2154
        {
2155
            LARGE_INTEGER  counterFrequency;
2156

    
2157
            /*
2158
            PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
2159
            DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
2160
            */
2161
            
2162
            int sampleSizeBytes = Pa_GetSampleSize(hostOutputSampleFormat);
2163
            stream->outputFrameSizeBytes = outputParameters->channelCount * sampleSizeBytes;
2164

    
2165
            stream->outputBufferSizeBytes = stream->hostBufferSizeFrames * stream->outputFrameSizeBytes;
2166
            if( stream->outputBufferSizeBytes < DSBSIZE_MIN )
2167
            {
2168
                result = paBufferTooSmall;
2169
                goto error;
2170
            }
2171
            else if( stream->outputBufferSizeBytes > DSBSIZE_MAX )
2172
            {
2173
                result = paBufferTooBig;
2174
                goto error;
2175
            }
2176

    
2177
            /* Calculate value used in latency calculation to avoid real-time divides. */
2178
            stream->secondsPerHostByte = 1.0 /
2179
                (stream->bufferProcessor.bytesPerHostOutputSample *
2180
                outputChannelCount * sampleRate);
2181

    
2182
            stream->outputIsRunning = FALSE;
2183
            stream->outputUnderflowCount = 0;
2184
            
2185
            /* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
2186
            if( QueryPerformanceFrequency( &counterFrequency ) )
2187
            {
2188
                stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->hostBufferSizeFrames) / integerSampleRate;
2189
            }
2190
            else
2191
            {
2192
                stream->perfCounterTicksPerBuffer.QuadPart = 0;
2193
            }
2194
        }
2195

    
2196
        /* ------------------ INPUT */
2197
        if( inputParameters )
2198
        {
2199
            /*
2200
            PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
2201
            DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
2202
            */
2203
            
2204
            int sampleSizeBytes = Pa_GetSampleSize(hostInputSampleFormat);
2205
            stream->inputFrameSizeBytes = inputParameters->channelCount * sampleSizeBytes;
2206

    
2207
            stream->inputBufferSizeBytes = stream->hostBufferSizeFrames * stream->inputFrameSizeBytes;
2208
            if( stream->inputBufferSizeBytes < DSBSIZE_MIN )
2209
            {
2210
                result = paBufferTooSmall;
2211
                goto error;
2212
            }
2213
            else if( stream->inputBufferSizeBytes > DSBSIZE_MAX )
2214
            {
2215
                result = paBufferTooBig;
2216
                goto error;
2217
            }
2218
        }
2219

    
2220
        /* open/create the DirectSound buffers */
2221

    
2222
        /* interface ptrs should be zeroed when stream is zeroed. */
2223
        assert( stream->pDirectSoundCapture == NULL );
2224
        assert( stream->pDirectSoundInputBuffer == NULL );
2225
        assert( stream->pDirectSound == NULL );
2226
        assert( stream->pDirectSoundPrimaryBuffer == NULL );
2227
        assert( stream->pDirectSoundOutputBuffer == NULL );
2228
        
2229

    
2230
        if( inputParameters && outputParameters )
2231
        {
2232
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2233
            /* try to use the full-duplex DX8 API to create the buffers.
2234
                if that fails we fall back to the half-duplex API below */
2235

    
2236
            hr = InitFullDuplexInputOutputBuffers( stream,
2237
                                       (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
2238
                                       hostInputSampleFormat,
2239
                                       (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
2240
                                       inputChannelMask,
2241
                                       (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
2242
                                       hostOutputSampleFormat,
2243
                                       (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
2244
                                       outputChannelMask,
2245
                                       integerSampleRate
2246
                                        );
2247
            DBUG(("InitFullDuplexInputOutputBuffers() returns %x\n", hr));
2248
            /* ignore any error returned by InitFullDuplexInputOutputBuffers. 
2249
                we retry opening the buffers below */
2250
#endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
2251
        }
2252

    
2253
        /*  create half duplex buffers. also used for full-duplex streams which didn't 
2254
            succeed when using the full duplex API. that could happen because
2255
            DX8 or greater isnt installed, the i/o devices aren't the same 
2256
            physical device. etc.
2257
        */
2258

    
2259
        if( outputParameters && !stream->pDirectSoundOutputBuffer )
2260
        {
2261
            hr = InitOutputBuffer( stream,
2262
                                       (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
2263
                                       hostOutputSampleFormat,
2264
                                       integerSampleRate,
2265
                                       (WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
2266
                                       outputChannelMask );
2267
            DBUG(("InitOutputBuffer() returns %x\n", hr));
2268
            if( hr != DS_OK )
2269
            {
2270
                result = paUnanticipatedHostError;
2271
                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2272
                goto error;
2273
            }
2274
        }
2275

    
2276
        if( inputParameters && !stream->pDirectSoundInputBuffer )
2277
        {
2278
            hr = InitInputBuffer( stream,
2279
                                      (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
2280
                                      hostInputSampleFormat,
2281
                                      integerSampleRate,
2282
                                      (WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
2283
                                      inputChannelMask );
2284
            DBUG(("InitInputBuffer() returns %x\n", hr));
2285
            if( hr != DS_OK )
2286
            {
2287
                ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
2288
                result = paUnanticipatedHostError;
2289
                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2290
                goto error;
2291
            }
2292
        }
2293
    }
2294

    
2295
    SetStreamInfoLatencies( stream, framesPerBuffer, pollingPeriodFrames, sampleRate );
2296

    
2297
    stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2298

    
2299
    *s = (PaStream*)stream;
2300

    
2301
    return result;
2302

    
2303
error:
2304
    if( stream )
2305
    {
2306
        if( stream->processingCompleted != NULL )
2307
            CloseHandle( stream->processingCompleted );
2308

    
2309
#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2310
        if( stream->waitableTimer != NULL )
2311
            CloseHandle( stream->waitableTimer );
2312
#endif
2313

    
2314
#ifndef PA_WIN_DS_USE_WMME_TIMER
2315
        if( stream->processingThreadCompleted != NULL )
2316
            CloseHandle( stream->processingThreadCompleted );
2317
#endif
2318

    
2319
        if( stream->pDirectSoundOutputBuffer )
2320
        {
2321
            IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2322
            IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
2323
            stream->pDirectSoundOutputBuffer = NULL;
2324
        }
2325

    
2326
        if( stream->pDirectSoundPrimaryBuffer )
2327
        {
2328
            IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
2329
            stream->pDirectSoundPrimaryBuffer = NULL;
2330
        }
2331

    
2332
        if( stream->pDirectSoundInputBuffer )
2333
        {
2334
            IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2335
            IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
2336
            stream->pDirectSoundInputBuffer = NULL;
2337
        }
2338

    
2339
        if( stream->pDirectSoundCapture )
2340
        {
2341
            IDirectSoundCapture_Release( stream->pDirectSoundCapture );
2342
            stream->pDirectSoundCapture = NULL;
2343
        }
2344

    
2345
        if( stream->pDirectSound )
2346
        {
2347
            IDirectSound_Release( stream->pDirectSound );
2348
            stream->pDirectSound = NULL;
2349
        }
2350

    
2351
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2352
        if( stream->pDirectSoundFullDuplex8 )
2353
        {
2354
            IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
2355
            stream->pDirectSoundFullDuplex8 = NULL;
2356
        }
2357
#endif
2358
        if( bufferProcessorIsInitialized )
2359
            PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2360

    
2361
        if( streamRepresentationIsInitialized )
2362
            PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2363

    
2364
        PaUtil_FreeMemory( stream );
2365
    }
2366

    
2367
    return result;
2368
}
2369

    
2370

    
2371
/************************************************************************************
2372
 * Determine how much space can be safely written to in DS buffer.
2373
 * Detect underflows and overflows.
2374
 * Does not allow writing into safety gap maintained by DirectSound.
2375
 */
2376
static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
2377
{
2378
    HRESULT hr;
2379
    DWORD   playCursor;
2380
    DWORD   writeCursor;
2381
    long    numBytesEmpty;
2382
    long    playWriteGap;
2383
    // Query to see how much room is in buffer.
2384
    hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
2385
            &playCursor, &writeCursor );
2386
    if( hr != DS_OK )
2387
    {
2388
        return hr;
2389
    }
2390

    
2391
    // Determine size of gap between playIndex and WriteIndex that we cannot write into.
2392
    playWriteGap = writeCursor - playCursor;
2393
    if( playWriteGap < 0 ) playWriteGap += stream->outputBufferSizeBytes; // unwrap
2394

    
2395
    /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
2396
    /* Attempt to detect playCursor wrap-around and correct it. */
2397
    if( stream->outputIsRunning && (stream->perfCounterTicksPerBuffer.QuadPart != 0) )
2398
    {
2399
        /* How much time has elapsed since last check. */
2400
        LARGE_INTEGER   currentTime;
2401
        LARGE_INTEGER   elapsedTime;
2402
        long            bytesPlayed;
2403
        long            bytesExpected;
2404
        long            buffersWrapped;
2405

    
2406
        QueryPerformanceCounter( &currentTime );
2407
        elapsedTime.QuadPart = currentTime.QuadPart - stream->previousPlayTime.QuadPart;
2408
        stream->previousPlayTime = currentTime;
2409

    
2410
        /* How many bytes does DirectSound say have been played. */
2411
        bytesPlayed = playCursor - stream->previousPlayCursor;
2412
        if( bytesPlayed < 0 ) bytesPlayed += stream->outputBufferSizeBytes; // unwrap
2413
        stream->previousPlayCursor = playCursor;
2414

    
2415
        /* Calculate how many bytes we would have expected to been played by now. */
2416
        bytesExpected = (long) ((elapsedTime.QuadPart * stream->outputBufferSizeBytes) / stream->perfCounterTicksPerBuffer.QuadPart);
2417
        buffersWrapped = (bytesExpected - bytesPlayed) / stream->outputBufferSizeBytes;
2418
        if( buffersWrapped > 0 )
2419
        {
2420
            playCursor += (buffersWrapped * stream->outputBufferSizeBytes);
2421
            bytesPlayed += (buffersWrapped * stream->outputBufferSizeBytes);
2422
        }
2423
    }
2424
    numBytesEmpty = playCursor - stream->outputBufferWriteOffsetBytes;
2425
    if( numBytesEmpty < 0 ) numBytesEmpty += stream->outputBufferSizeBytes; // unwrap offset
2426

    
2427
    /* Have we underflowed? */
2428
    if( numBytesEmpty > (stream->outputBufferSizeBytes - playWriteGap) )
2429
    {
2430
        if( stream->outputIsRunning )
2431
        {
2432
            stream->outputUnderflowCount += 1;
2433
        }
2434

    
2435
        /*
2436
            From MSDN:
2437
                The write cursor indicates the position at which it is safe  
2438
            to write new data to the buffer. The write cursor always leads the
2439
            play cursor, typically by about 15 milliseconds' worth of audio
2440
            data.
2441
                It is always safe to change data that is behind the position 
2442
            indicated by the lpdwCurrentPlayCursor parameter.
2443
        */
2444

    
2445
        stream->outputBufferWriteOffsetBytes = writeCursor;
2446
        numBytesEmpty = stream->outputBufferSizeBytes - playWriteGap;
2447
    }
2448
    *bytesEmpty = numBytesEmpty;
2449
    return hr;
2450
}
2451

    
2452
/***********************************************************************************/
2453
static int TimeSlice( PaWinDsStream *stream )
2454
{
2455
    long              numFrames = 0;
2456
    long              bytesEmpty = 0;
2457
    long              bytesFilled = 0;
2458
    long              bytesToXfer = 0;
2459
    long              framesToXfer = 0; /* the number of frames we'll process this tick */
2460
    long              numInFramesReady = 0;
2461
    long              numOutFramesReady = 0;
2462
    long              bytesProcessed;
2463
    HRESULT           hresult;
2464
    double            outputLatency = 0;
2465
    double            inputLatency = 0;
2466
    PaStreamCallbackTimeInfo timeInfo = {0,0,0};
2467
    
2468
/* Input */
2469
    LPBYTE            lpInBuf1 = NULL;
2470
    LPBYTE            lpInBuf2 = NULL;
2471
    DWORD             dwInSize1 = 0;
2472
    DWORD             dwInSize2 = 0;
2473
/* Output */
2474
    LPBYTE            lpOutBuf1 = NULL;
2475
    LPBYTE            lpOutBuf2 = NULL;
2476
    DWORD             dwOutSize1 = 0;
2477
    DWORD             dwOutSize2 = 0;
2478

    
2479
    /* How much input data is available? */
2480
    if( stream->bufferProcessor.inputChannelCount > 0 )
2481
    {
2482
        HRESULT hr;
2483
        DWORD capturePos;
2484
        DWORD readPos;
2485
        long  filled = 0;
2486
        // Query to see how much data is in buffer.
2487
        // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
2488
        // so let's pass a pointer just to be safe.
2489
        hr = IDirectSoundCaptureBuffer_GetCurrentPosition( stream->pDirectSoundInputBuffer, &capturePos, &readPos );
2490
        if( hr == DS_OK )
2491
        {
2492
            filled = readPos - stream->readOffset;
2493
            if( filled < 0 ) filled += stream->inputBufferSizeBytes; // unwrap offset
2494
            bytesFilled = filled;
2495

    
2496
            inputLatency = ((double)bytesFilled) * stream->secondsPerHostByte;
2497
        }
2498
            // FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
2499

    
2500
        framesToXfer = numInFramesReady = bytesFilled / stream->inputFrameSizeBytes; 
2501

    
2502
        /** @todo Check for overflow */
2503
    }
2504

    
2505
    /* How much output room is available? */
2506
    if( stream->bufferProcessor.outputChannelCount > 0 )
2507
    {
2508
        UINT previousUnderflowCount = stream->outputUnderflowCount;
2509
        QueryOutputSpace( stream, &bytesEmpty );
2510
        framesToXfer = numOutFramesReady = bytesEmpty / stream->outputFrameSizeBytes;
2511

    
2512
        /* Check for underflow */
2513
        /* FIXME QueryOutputSpace should not adjust underflow count as a side effect. 
2514
            A query function should be a const operator on the stream and return a flag on underflow. */
2515
        if( stream->outputUnderflowCount != previousUnderflowCount )
2516
            stream->callbackFlags |= paOutputUnderflow;
2517

    
2518
        /* We are about to compute audio into the first byte of empty space in the output buffer.
2519
           This audio will reach the DAC after all of the current (non-empty) audio
2520
           in the buffer has played. Therefore the output time is the current time
2521
           plus the time it takes to play the non-empty bytes in the buffer,
2522
           computed here:
2523
        */
2524
        outputLatency = ((double)(stream->outputBufferSizeBytes - bytesEmpty)) * stream->secondsPerHostByte;
2525
    }
2526

    
2527
    /* if it's a full duplex stream, set framesToXfer to the minimum of input and output frames ready */
2528
    if( stream->bufferProcessor.inputChannelCount > 0 && stream->bufferProcessor.outputChannelCount > 0 )
2529
    {
2530
        framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
2531
    }
2532

    
2533
    if( framesToXfer > 0 )
2534
    {
2535
        PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
2536

    
2537
    /* The outputBufferDacTime parameter should indicates the time at which
2538
        the first sample of the output buffer is heard at the DACs. */
2539
        timeInfo.currentTime = PaUtil_GetTime();
2540

    
2541
        PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
2542
        stream->callbackFlags = 0;
2543
        
2544
    /* Input */
2545
        if( stream->bufferProcessor.inputChannelCount > 0 )
2546
        {
2547
            timeInfo.inputBufferAdcTime = timeInfo.currentTime - inputLatency; 
2548

    
2549
            bytesToXfer = framesToXfer * stream->inputFrameSizeBytes;
2550
            hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer,
2551
                stream->readOffset, bytesToXfer,
2552
                (void **) &lpInBuf1, &dwInSize1,
2553
                (void **) &lpInBuf2, &dwInSize2, 0);
2554
            if (hresult != DS_OK)
2555
            {
2556
                ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
2557
                /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
2558
                PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
2559
                stream->callbackResult = paComplete;
2560
                goto error2;
2561
            }
2562

    
2563
            numFrames = dwInSize1 / stream->inputFrameSizeBytes;
2564
            PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
2565
            PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
2566
        /* Is input split into two regions. */
2567
            if( dwInSize2 > 0 )
2568
            {
2569
                numFrames = dwInSize2 / stream->inputFrameSizeBytes;
2570
                PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
2571
                PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
2572
            }
2573
        }
2574

    
2575
    /* Output */
2576
        if( stream->bufferProcessor.outputChannelCount > 0 )
2577
        {
2578
            /*
2579
            We don't currently add outputLatency here because it appears to produce worse
2580
            results than not adding it. Need to do more testing to verify this.
2581
            */
2582
            /* timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; */
2583
            timeInfo.outputBufferDacTime = timeInfo.currentTime;
2584

    
2585
            bytesToXfer = framesToXfer * stream->outputFrameSizeBytes;
2586
            hresult = IDirectSoundBuffer_Lock ( stream->pDirectSoundOutputBuffer,
2587
                stream->outputBufferWriteOffsetBytes, bytesToXfer,
2588
                (void **) &lpOutBuf1, &dwOutSize1,
2589
                (void **) &lpOutBuf2, &dwOutSize2, 0);
2590
            if (hresult != DS_OK)
2591
            {
2592
                ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
2593
                /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
2594
                PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
2595
                stream->callbackResult = paComplete;
2596
                goto error1;
2597
            }
2598

    
2599
            numFrames = dwOutSize1 / stream->outputFrameSizeBytes;
2600
            PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
2601
            PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
2602

    
2603
        /* Is output split into two regions. */
2604
            if( dwOutSize2 > 0 )
2605
            {
2606
                numFrames = dwOutSize2 / stream->outputFrameSizeBytes;
2607
                PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
2608
                PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
2609
            }
2610
        }
2611

    
2612
        numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &stream->callbackResult );
2613
        stream->framesWritten += numFrames;
2614
        
2615
        if( stream->bufferProcessor.outputChannelCount > 0 )
2616
        {
2617
        /* FIXME: an underflow could happen here */
2618

    
2619
        /* Update our buffer offset and unlock sound buffer */
2620
            bytesProcessed = numFrames * stream->outputFrameSizeBytes;
2621
            stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes;
2622
            IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
2623
        }
2624

    
2625
error1:
2626
        if( stream->bufferProcessor.inputChannelCount > 0 )
2627
        {
2628
        /* FIXME: an overflow could happen here */
2629

    
2630
        /* Update our buffer offset and unlock sound buffer */
2631
            bytesProcessed = numFrames * stream->inputFrameSizeBytes;
2632
            stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputBufferSizeBytes;
2633
            IDirectSoundCaptureBuffer_Unlock( stream->pDirectSoundInputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
2634
        }
2635
error2:
2636

    
2637
        PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );        
2638
    }
2639

    
2640
    if( stream->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
2641
    {
2642
        /* don't return completed until the buffer processor has been drained */
2643
        return paContinue;
2644
    }
2645
    else
2646
    {
2647
        return stream->callbackResult;
2648
    }
2649
}
2650
/*******************************************************************/
2651

    
2652
static HRESULT ZeroAvailableOutputSpace( PaWinDsStream *stream )
2653
{
2654
    HRESULT hr;
2655
    LPBYTE lpbuf1 = NULL;
2656
    LPBYTE lpbuf2 = NULL;
2657
    DWORD dwsize1 = 0;
2658
    DWORD dwsize2 = 0;
2659
    long  bytesEmpty;
2660
    hr = QueryOutputSpace( stream, &bytesEmpty );
2661
    if (hr != DS_OK) return hr;
2662
    if( bytesEmpty == 0 ) return DS_OK;
2663
    // Lock free space in the DS
2664
    hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, stream->outputBufferWriteOffsetBytes,
2665
                                    bytesEmpty, (void **) &lpbuf1, &dwsize1,
2666
                                    (void **) &lpbuf2, &dwsize2, 0);
2667
    if (hr == DS_OK)
2668
    {
2669
        // Copy the buffer into the DS
2670
        ZeroMemory(lpbuf1, dwsize1);
2671
        if(lpbuf2 != NULL)
2672
        {
2673
            ZeroMemory(lpbuf2, dwsize2);
2674
        }
2675
        // Update our buffer offset and unlock sound buffer
2676
        stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + dwsize1 + dwsize2) % stream->outputBufferSizeBytes;
2677
        IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
2678

    
2679
        stream->finalZeroBytesWritten += dwsize1 + dwsize2;
2680
    }
2681
    return hr;
2682
}
2683

    
2684

    
2685
static void CALLBACK TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
2686
{
2687
    PaWinDsStream *stream;
2688
    int isFinished = 0;
2689

    
2690
    /* suppress unused variable warnings */
2691
    (void) uID;
2692
    (void) uMsg;
2693
    (void) dw1;
2694
    (void) dw2;
2695
    
2696
    stream = (PaWinDsStream *) dwUser;
2697
    if( stream == NULL ) return;
2698

    
2699
    if( stream->isActive )
2700
    {
2701
        if( stream->abortProcessing )
2702
        {
2703
            isFinished = 1;
2704
        }
2705
        else if( stream->stopProcessing )
2706
        {
2707
            if( stream->bufferProcessor.outputChannelCount > 0 )
2708
            {
2709
                ZeroAvailableOutputSpace( stream );
2710
                if( stream->finalZeroBytesWritten >= stream->outputBufferSizeBytes )
2711
                {
2712
                    /* once we've flushed the whole output buffer with zeros we know all data has been played */
2713
                    isFinished = 1;
2714
                }
2715
            }
2716
            else
2717
            {
2718
                isFinished = 1;
2719
            }
2720
        }
2721
        else
2722
        {
2723
            int callbackResult = TimeSlice( stream );
2724
            if( callbackResult != paContinue )
2725
            {
2726
                /* FIXME implement handling of paComplete and paAbort if possible 
2727
                   At the moment this should behave as if paComplete was called and 
2728
                   flush the buffer.
2729
                */
2730

    
2731
                stream->stopProcessing = 1;
2732
            }
2733
        }
2734

    
2735
        if( isFinished )
2736
        {
2737
            if( stream->streamRepresentation.streamFinishedCallback != 0 )
2738
                stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
2739

    
2740
            stream->isActive = 0; /* don't set this until the stream really is inactive */
2741
            SetEvent( stream->processingCompleted );
2742
        }
2743
    }
2744
}
2745

    
2746
#ifndef PA_WIN_DS_USE_WMME_TIMER
2747

    
2748
#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2749

    
2750
static void CALLBACK WaitableTimerAPCProc(
2751
   LPVOID lpArg,               // Data value
2752
   DWORD dwTimerLowValue,      // Timer low value
2753
   DWORD dwTimerHighValue )    // Timer high value
2754

    
2755
{
2756
    (void)dwTimerLowValue;
2757
    (void)dwTimerHighValue;
2758

    
2759
    TimerCallback( 0, 0, (DWORD_PTR)lpArg, 0, 0 );
2760
}
2761

    
2762
#endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
2763

    
2764

    
2765
PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
2766
{
2767
    PaWinDsStream *stream = (PaWinDsStream *)pArg;
2768
    LARGE_INTEGER dueTime;
2769
    int timerPeriodMs;
2770

    
2771
    timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
2772
    if( timerPeriodMs < 1 )
2773
        timerPeriodMs = 1;
2774

    
2775
#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2776
    assert( stream->waitableTimer != NULL );
2777

    
2778
    /* invoke first timeout immediately */
2779
    dueTime.LowPart = timerPeriodMs * 1000 * 10;
2780
    dueTime.HighPart = 0;
2781

    
2782
    /* tick using waitable timer */
2783
    if( SetWaitableTimer( stream->waitableTimer, &dueTime, timerPeriodMs, WaitableTimerAPCProc, pArg, FALSE ) != 0 )
2784
    {
2785
        DWORD wfsoResult = 0;
2786
        do
2787
        {
2788
            /* wait for processingCompleted to be signaled or our timer APC to be called */
2789
            wfsoResult = WaitForSingleObjectEx( stream->processingCompleted, timerPeriodMs * 10, /* alertable = */ TRUE );
2790

    
2791
        }while( wfsoResult == WAIT_TIMEOUT || wfsoResult == WAIT_IO_COMPLETION );
2792
    }
2793

    
2794
    CancelWaitableTimer( stream->waitableTimer );
2795

    
2796
#else
2797

    
2798
    /* tick using WaitForSingleObject timout */
2799
    while ( WaitForSingleObject( stream->processingCompleted, timerPeriodMs ) == WAIT_TIMEOUT )
2800
    {
2801
        TimerCallback( 0, 0, (DWORD_PTR)pArg, 0, 0 );
2802
    }
2803
#endif /* PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT */
2804

    
2805
    SetEvent( stream->processingThreadCompleted );
2806

    
2807
    return 0;
2808
}
2809

    
2810
#endif /* !PA_WIN_DS_USE_WMME_TIMER */
2811

    
2812
/***********************************************************************************
2813
    When CloseStream() is called, the multi-api layer ensures that
2814
    the stream has already been stopped or aborted.
2815
*/
2816
static PaError CloseStream( PaStream* s )
2817
{
2818
    PaError result = paNoError;
2819
    PaWinDsStream *stream = (PaWinDsStream*)s;
2820

    
2821
    CloseHandle( stream->processingCompleted );
2822

    
2823
#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
2824
    if( stream->waitableTimer != NULL )
2825
        CloseHandle( stream->waitableTimer );
2826
#endif
2827

    
2828
#ifndef PA_WIN_DS_USE_WMME_TIMER
2829
    CloseHandle( stream->processingThreadCompleted );
2830
#endif
2831

    
2832
    // Cleanup the sound buffers
2833
    if( stream->pDirectSoundOutputBuffer )
2834
    {
2835
        IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2836
        IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
2837
        stream->pDirectSoundOutputBuffer = NULL;
2838
    }
2839

    
2840
    if( stream->pDirectSoundPrimaryBuffer )
2841
    {
2842
        IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
2843
        stream->pDirectSoundPrimaryBuffer = NULL;
2844
    }
2845

    
2846
    if( stream->pDirectSoundInputBuffer )
2847
    {
2848
        IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2849
        IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
2850
        stream->pDirectSoundInputBuffer = NULL;
2851
    }
2852

    
2853
    if( stream->pDirectSoundCapture )
2854
    {
2855
        IDirectSoundCapture_Release( stream->pDirectSoundCapture );
2856
        stream->pDirectSoundCapture = NULL;
2857
    }
2858

    
2859
    if( stream->pDirectSound )
2860
    {
2861
        IDirectSound_Release( stream->pDirectSound );
2862
        stream->pDirectSound = NULL;
2863
    }
2864

    
2865
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2866
    if( stream->pDirectSoundFullDuplex8 )
2867
    {
2868
        IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
2869
        stream->pDirectSoundFullDuplex8 = NULL;
2870
    }
2871
#endif
2872

    
2873
    PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2874
    PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2875
    PaUtil_FreeMemory( stream );
2876

    
2877
    return result;
2878
}
2879

    
2880
/***********************************************************************************/
2881
static HRESULT ClearOutputBuffer( PaWinDsStream *stream )
2882
{
2883
    PaError          result = paNoError;
2884
    unsigned char*   pDSBuffData;
2885
    DWORD            dwDataLen;
2886
    HRESULT          hr;
2887

    
2888
    hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 );
2889
    DBUG(("PaHost_ClearOutputBuffer: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr));
2890
    if( hr != DS_OK )
2891
        return hr;
2892

    
2893
    // Lock the DS buffer
2894
    if ((hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData,
2895
                                           &dwDataLen, NULL, 0, 0)) != DS_OK )
2896
        return hr;
2897

    
2898
    // Zero the DS buffer
2899
    ZeroMemory(pDSBuffData, dwDataLen);
2900
    // Unlock the DS buffer
2901
    if ((hr = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK)
2902
        return hr;
2903
    
2904
    // Let DSound set the starting write position because if we set it to zero, it looks like the
2905
    // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
2906
    if ((hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
2907
            &stream->previousPlayCursor, &stream->outputBufferWriteOffsetBytes )) != DS_OK)
2908
        return hr;
2909

    
2910
    /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
2911

    
2912
    return DS_OK;
2913
}
2914

    
2915
static PaError StartStream( PaStream *s )
2916
{
2917
    PaError          result = paNoError;
2918
    PaWinDsStream   *stream = (PaWinDsStream*)s;
2919
    HRESULT          hr;
2920
        
2921
    stream->callbackResult = paContinue;
2922
    PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
2923
    
2924
    ResetEvent( stream->processingCompleted );
2925

    
2926
#ifndef PA_WIN_DS_USE_WMME_TIMER
2927
    ResetEvent( stream->processingThreadCompleted );
2928
#endif
2929

    
2930
    if( stream->bufferProcessor.inputChannelCount > 0 )
2931
    {
2932
        // Start the buffer capture
2933
        if( stream->pDirectSoundInputBuffer != NULL ) // FIXME: not sure this check is necessary
2934
        {
2935
            hr = IDirectSoundCaptureBuffer_Start( stream->pDirectSoundInputBuffer, DSCBSTART_LOOPING );
2936
        }
2937

    
2938
        DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
2939
        if( hr != DS_OK )
2940
        {
2941
            result = paUnanticipatedHostError;
2942
            PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2943
            goto error;
2944
        }
2945
    }
2946

    
2947
    stream->framesWritten = 0;
2948
    stream->callbackFlags = 0;
2949

    
2950
    stream->abortProcessing = 0;
2951
    stream->stopProcessing = 0;
2952

    
2953
    if( stream->bufferProcessor.outputChannelCount > 0 )
2954
    {
2955
        QueryPerformanceCounter( &stream->previousPlayTime );
2956
        stream->finalZeroBytesWritten = 0;
2957

    
2958
        hr = ClearOutputBuffer( stream );
2959
        if( hr != DS_OK )
2960
        {
2961
            result = paUnanticipatedHostError;
2962
            PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2963
            goto error;
2964
        }
2965

    
2966
        if( stream->streamRepresentation.streamCallback && (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback) )
2967
        {
2968
            stream->callbackFlags = paPrimingOutput;
2969

    
2970
            TimeSlice( stream );
2971
            /* we ignore the return value from TimeSlice here and start the stream as usual.
2972
                The first timer callback will detect if the callback has completed. */
2973

    
2974
            stream->callbackFlags = 0;
2975
        }
2976

    
2977
        // Start the buffer playback in a loop.
2978
        if( stream->pDirectSoundOutputBuffer != NULL ) // FIXME: not sure this needs to be checked here
2979
        {
2980
            hr = IDirectSoundBuffer_Play( stream->pDirectSoundOutputBuffer, 0, 0, DSBPLAY_LOOPING );
2981
            DBUG(("PaHost_StartOutput: IDirectSoundBuffer_Play returned = 0x%X.\n", hr));
2982
            if( hr != DS_OK )
2983
            {
2984
                result = paUnanticipatedHostError;
2985
                PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2986
                goto error;
2987
            }
2988
            stream->outputIsRunning = TRUE;
2989
        }
2990
    }
2991

    
2992
    if( stream->streamRepresentation.streamCallback )
2993
    {
2994
        TIMECAPS timecaps;
2995
        int timerPeriodMs = (int)(stream->pollingPeriodSeconds * MSECS_PER_SECOND);
2996
        if( timerPeriodMs < 1 )
2997
            timerPeriodMs = 1;
2998

    
2999
        /* set windows scheduler granularity only as fine as needed, no finer */
3000
        /* Although this is not fully documented by MS, it appears that
3001
           timeBeginPeriod() affects the scheduling granulatity of all timers
3002
           including Waitable Timer Objects. So we always call timeBeginPeriod, whether
3003
           we're using an MM timer callback via timeSetEvent or not.
3004
        */
3005
        assert( stream->systemTimerResolutionPeriodMs == 0 );
3006
        if( timeGetDevCaps( &timecaps, sizeof(TIMECAPS) ) == MMSYSERR_NOERROR && timecaps.wPeriodMin > 0 )
3007
        {
3008
            /* aim for resolution 4 times higher than polling rate */
3009
            stream->systemTimerResolutionPeriodMs = (UINT)((stream->pollingPeriodSeconds * MSECS_PER_SECOND) * .25);
3010
            if( stream->systemTimerResolutionPeriodMs < timecaps.wPeriodMin )
3011
                stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMin;
3012
            if( stream->systemTimerResolutionPeriodMs > timecaps.wPeriodMax )
3013
                stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMax;
3014

    
3015
            if( timeBeginPeriod( stream->systemTimerResolutionPeriodMs ) != MMSYSERR_NOERROR )
3016
                stream->systemTimerResolutionPeriodMs = 0; /* timeBeginPeriod failed, so we don't need to call timeEndPeriod() later */
3017
        }
3018

    
3019

    
3020
#ifdef PA_WIN_DS_USE_WMME_TIMER
3021
        /* Create timer that will wake us up so we can fill the DSound buffer. */
3022
        /* We have deprecated timeSetEvent because all MM timer callbacks
3023
           are serialised onto a single thread. Which creates problems with multiple
3024
           PA streams, or when also using timers for other time critical tasks
3025
        */
3026
        stream->timerID = timeSetEvent( timerPeriodMs, stream->systemTimerResolutionPeriodMs, (LPTIMECALLBACK) TimerCallback,
3027
                                             (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
3028
    
3029
        if( stream->timerID == 0 )
3030
        {
3031
            stream->isActive = 0;
3032
            result = paUnanticipatedHostError;
3033
            PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
3034
            goto error;
3035
        }
3036
#else
3037
        /* Create processing thread which calls TimerCallback */
3038

    
3039
        stream->processingThread = CREATE_THREAD( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
3040
        if( !stream->processingThread )
3041
        {
3042
            result = paUnanticipatedHostError;
3043
            PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
3044
            goto error;
3045
        }
3046

    
3047
        if( !SetThreadPriority( stream->processingThread, THREAD_PRIORITY_TIME_CRITICAL ) )
3048
        {
3049
            result = paUnanticipatedHostError;
3050
            PA_DS_SET_LAST_DIRECTSOUND_ERROR( GetLastError() );
3051
            goto error;
3052
        }
3053
#endif
3054
    }
3055

    
3056
    stream->isActive = 1;
3057
    stream->isStarted = 1;
3058

    
3059
    assert( result == paNoError );
3060
    return result;
3061

    
3062
error:
3063

    
3064
    if( stream->pDirectSoundOutputBuffer != NULL && stream->outputIsRunning )
3065
        IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
3066
    stream->outputIsRunning = FALSE;
3067

    
3068
#ifndef PA_WIN_DS_USE_WMME_TIMER
3069
    if( stream->processingThread )
3070
    {
3071
#ifdef CLOSE_THREAD_HANDLE
3072
        CLOSE_THREAD_HANDLE( stream->processingThread ); /* Delete thread. */
3073
#endif
3074
        stream->processingThread = NULL;
3075
    }
3076
#endif
3077

    
3078
    return result;
3079
}
3080

    
3081

    
3082
/***********************************************************************************/
3083
static PaError StopStream( PaStream *s )
3084
{
3085
    PaError result = paNoError;
3086
    PaWinDsStream *stream = (PaWinDsStream*)s;
3087
    HRESULT          hr;
3088
    int timeoutMsec;
3089

    
3090
    if( stream->streamRepresentation.streamCallback )
3091
    {
3092
        stream->stopProcessing = 1;
3093

    
3094
        /* Set timeout at 4 times maximum time we might wait. */
3095
        timeoutMsec = (int) (4 * MSECS_PER_SECOND * (stream->hostBufferSizeFrames / stream->streamRepresentation.streamInfo.sampleRate));
3096

    
3097
        WaitForSingleObject( stream->processingCompleted, timeoutMsec );
3098
    }
3099

    
3100
#ifdef PA_WIN_DS_USE_WMME_TIMER
3101
    if( stream->timerID != 0 )
3102
    {
3103
        timeKillEvent(stream->timerID);  /* Stop callback timer. */
3104
        stream->timerID = 0;
3105
    }
3106
#else
3107
    if( stream->processingThread )
3108
    {
3109
        if( WaitForSingleObject( stream->processingThreadCompleted, 30*100 ) == WAIT_TIMEOUT )
3110
            return paUnanticipatedHostError;
3111

    
3112
#ifdef CLOSE_THREAD_HANDLE
3113
        CloseHandle( stream->processingThread ); /* Delete thread. */
3114
        stream->processingThread = NULL;
3115
#endif
3116

    
3117
    }
3118
#endif
3119

    
3120
    if( stream->systemTimerResolutionPeriodMs > 0 ){
3121
        timeEndPeriod( stream->systemTimerResolutionPeriodMs );
3122
        stream->systemTimerResolutionPeriodMs = 0;
3123
    }  
3124

    
3125
    if( stream->bufferProcessor.outputChannelCount > 0 )
3126
    {
3127
        // Stop the buffer playback
3128
        if( stream->pDirectSoundOutputBuffer != NULL )
3129
        {
3130
            stream->outputIsRunning = FALSE;
3131
            // FIXME: what happens if IDirectSoundBuffer_Stop returns an error?
3132
            hr = IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
3133

    
3134
            if( stream->pDirectSoundPrimaryBuffer )
3135
                IDirectSoundBuffer_Stop( stream->pDirectSoundPrimaryBuffer ); /* FIXME we never started the primary buffer so I'm not sure we need to stop it */
3136
        }
3137
    }
3138

    
3139
    if( stream->bufferProcessor.inputChannelCount > 0 )
3140
    {
3141
        // Stop the buffer capture
3142
        if( stream->pDirectSoundInputBuffer != NULL )
3143
        {
3144
            // FIXME: what happens if IDirectSoundCaptureBuffer_Stop returns an error?
3145
            hr = IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
3146
        }
3147
    }
3148

    
3149
    stream->isStarted = 0;
3150

    
3151
    return result;
3152
}
3153

    
3154

    
3155
/***********************************************************************************/
3156
static PaError AbortStream( PaStream *s )
3157
{
3158
    PaWinDsStream *stream = (PaWinDsStream*)s;
3159

    
3160
    stream->abortProcessing = 1;
3161
    return StopStream( s );
3162
}
3163

    
3164

    
3165
/***********************************************************************************/
3166
static PaError IsStreamStopped( PaStream *s )
3167
{
3168
    PaWinDsStream *stream = (PaWinDsStream*)s;
3169

    
3170
    return !stream->isStarted;
3171
}
3172

    
3173

    
3174
/***********************************************************************************/
3175
static PaError IsStreamActive( PaStream *s )
3176
{
3177
    PaWinDsStream *stream = (PaWinDsStream*)s;
3178

    
3179
    return stream->isActive;
3180
}
3181

    
3182
/***********************************************************************************/
3183
static PaTime GetStreamTime( PaStream *s )
3184
{
3185
    /* suppress unused variable warnings */
3186
    (void) s;
3187

    
3188
    return PaUtil_GetTime();
3189
}
3190

    
3191

    
3192
/***********************************************************************************/
3193
static double GetStreamCpuLoad( PaStream* s )
3194
{
3195
    PaWinDsStream *stream = (PaWinDsStream*)s;
3196

    
3197
    return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
3198
}
3199

    
3200

    
3201
/***********************************************************************************
3202
    As separate stream interfaces are used for blocking and callback
3203
    streams, the following functions can be guaranteed to only be called
3204
    for blocking streams.
3205
*/
3206

    
3207
static PaError ReadStream( PaStream* s,
3208
                           void *buffer,
3209
                           unsigned long frames )
3210
{
3211
    PaWinDsStream *stream = (PaWinDsStream*)s;
3212

    
3213
    /* suppress unused variable warnings */
3214
    (void) buffer;
3215
    (void) frames;
3216
    (void) stream;
3217

    
3218
    /* IMPLEMENT ME, see portaudio.h for required behavior*/
3219

    
3220
    return paNoError;
3221
}
3222

    
3223

    
3224
/***********************************************************************************/
3225
static PaError WriteStream( PaStream* s,
3226
                            const void *buffer,
3227
                            unsigned long frames )
3228
{
3229
    PaWinDsStream *stream = (PaWinDsStream*)s;
3230

    
3231
    /* suppress unused variable warnings */
3232
    (void) buffer;
3233
    (void) frames;
3234
    (void) stream;
3235

    
3236
    /* IMPLEMENT ME, see portaudio.h for required behavior*/
3237

    
3238
    return paNoError;
3239
}
3240

    
3241

    
3242
/***********************************************************************************/
3243
static signed long GetStreamReadAvailable( PaStream* s )
3244
{
3245
    PaWinDsStream *stream = (PaWinDsStream*)s;
3246

    
3247
    /* suppress unused variable warnings */
3248
    (void) stream;
3249

    
3250
    /* IMPLEMENT ME, see portaudio.h for required behavior*/
3251

    
3252
    return 0;
3253
}
3254

    
3255

    
3256
/***********************************************************************************/
3257
static signed long GetStreamWriteAvailable( PaStream* s )
3258
{
3259
    PaWinDsStream *stream = (PaWinDsStream*)s;
3260

    
3261
    /* suppress unused variable warnings */
3262
    (void) stream;
3263
    
3264
    /* IMPLEMENT ME, see portaudio.h for required behavior*/
3265

    
3266
    return 0;
3267
}
3268