/[pcsx2_0.9.7]/trunk/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c
ViewVC logotype

Annotation of /trunk/3rdparty/portaudio/src/hostapi/dsound/pa_win_ds.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 273 - (hide annotations) (download)
Fri Nov 12 01:10:22 2010 UTC (9 years, 8 months ago) by william
File MIME type: text/plain
File size: 109716 byte(s)
Auto Commited Import of: pcsx2-0.9.7-DEBUG (upstream: v0.9.7.4013 local: v0.9.7.197-latest) in ./trunk
1 william 31 /*
2 william 273 * $Id: pa_win_ds.c 1534 2010-08-03 21:02:52Z dmitrykos $
3 william 31 * 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 william 273 * The text above constitutes the entire PortAudio license; however,
31 william 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 william 273 * they can be incorporated into the canonical version. It is also
36     * requested that these non-binding requests be included along with the
37 william 31 * license above.
38     */
39    
40     /** @file
41     @ingroup hostapi_src
42    
43     @todo implement paInputOverflow callback status flag
44 william 273
45 william 31 @todo implement paNeverDropInput.
46    
47     @todo implement host api specific extension to set i/o buffer sizes in frames
48    
49     @todo implement initialisation of PaDeviceInfo default*Latency fields (currently set to 0.)
50    
51     @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
52    
53     @todo audit handling of DirectSound result codes - in many cases we could convert a HRESULT into
54     a native portaudio error code. Standard DirectSound result codes are documented at msdn.
55    
56     @todo implement IsFormatSupported
57    
58     @todo call PaUtil_SetLastHostErrorInfo with a specific error string (currently just "DSound error").
59    
60     @todo make sure all buffers have been played before stopping the stream
61     when the stream callback returns paComplete
62    
63     @todo retrieve default devices using the DRVM_MAPPER_PREFERRED_GET functions used in the wmme api
64     these wave device ids can be aligned with the directsound devices either by retrieving
65     the system interface device name using DRV_QUERYDEVICEINTERFACE or by using the wave device
66     id retrieved in KsPropertySetEnumerateCallback.
67    
68     old TODOs from phil, need to work out if these have been done:
69     O- fix "patest_stop.c"
70     */
71    
72    
73     #include <assert.h>
74     #include <stdio.h>
75     #include <string.h> /* strlen() */
76    
77     #include <initguid.h> /* make sure ds guids get defined */
78     #include <windows.h>
79     #include <objbase.h>
80    
81     /*
82     Use the earliest version of DX required, no need to polute the namespace
83     */
84     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
85     #define DIRECTSOUND_VERSION 0x0800
86     #else
87     #define DIRECTSOUND_VERSION 0x0300
88     #endif
89     #include <dsound.h>
90     #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
91     #include <dsconf.h>
92     #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
93    
94     #include "pa_util.h"
95     #include "pa_allocation.h"
96     #include "pa_hostapi.h"
97     #include "pa_stream.h"
98     #include "pa_cpuload.h"
99     #include "pa_process.h"
100     #include "pa_debugprint.h"
101    
102     #include "pa_win_ds.h"
103     #include "pa_win_ds_dynlink.h"
104     #include "pa_win_waveformat.h"
105     #include "pa_win_wdmks_utils.h"
106    
107    
108     #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
109     #pragma comment( lib, "dsound.lib" )
110     #pragma comment( lib, "winmm.lib" )
111     #endif
112    
113     /*
114     provided in newer platform sdks and x64
115     */
116     #ifndef DWORD_PTR
117     #if defined(_WIN64)
118     #define DWORD_PTR unsigned __int64
119     #else
120     #define DWORD_PTR unsigned long
121     #endif
122     #endif
123    
124     #define PRINT(x) PA_DEBUG(x);
125     #define ERR_RPT(x) PRINT(x)
126     #define DBUG(x) PRINT(x)
127     #define DBUGX(x) PRINT(x)
128    
129     #define PA_USE_HIGH_LATENCY (0)
130     #if PA_USE_HIGH_LATENCY
131     #define PA_WIN_9X_LATENCY (500)
132     #define PA_WIN_NT_LATENCY (600)
133     #else
134     #define PA_WIN_9X_LATENCY (140)
135     #define PA_WIN_NT_LATENCY (280)
136     #endif
137    
138     #define PA_WIN_WDM_LATENCY (120)
139    
140     #define SECONDS_PER_MSEC (0.001)
141     #define MSEC_PER_SECOND (1000)
142    
143     /* prototypes for functions declared in this file */
144    
145     #ifdef __cplusplus
146     extern "C"
147     {
148     #endif /* __cplusplus */
149    
150     PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index );
151    
152     #ifdef __cplusplus
153     }
154     #endif /* __cplusplus */
155    
156     static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
157     static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
158     PaStream** s,
159     const PaStreamParameters *inputParameters,
160     const PaStreamParameters *outputParameters,
161     double sampleRate,
162     unsigned long framesPerBuffer,
163     PaStreamFlags streamFlags,
164     PaStreamCallback *streamCallback,
165     void *userData );
166     static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
167     const PaStreamParameters *inputParameters,
168     const PaStreamParameters *outputParameters,
169     double sampleRate );
170     static PaError CloseStream( PaStream* stream );
171     static PaError StartStream( PaStream *stream );
172     static PaError StopStream( PaStream *stream );
173     static PaError AbortStream( PaStream *stream );
174     static PaError IsStreamStopped( PaStream *s );
175     static PaError IsStreamActive( PaStream *stream );
176     static PaTime GetStreamTime( PaStream *stream );
177     static double GetStreamCpuLoad( PaStream* stream );
178     static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
179     static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
180     static signed long GetStreamReadAvailable( PaStream* stream );
181     static signed long GetStreamWriteAvailable( PaStream* stream );
182    
183    
184     /* FIXME: should convert hr to a string */
185     #define PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr ) \
186     PaUtil_SetLastHostErrorInfo( paDirectSound, hr, "DirectSound error" )
187    
188     /************************************************* DX Prototypes **********/
189     static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
190     LPCTSTR lpszDesc,
191     LPCTSTR lpszDrvName,
192     LPVOID lpContext );
193    
194     /************************************************************************************/
195     /********************** Structures **************************************************/
196     /************************************************************************************/
197     /* PaWinDsHostApiRepresentation - host api datastructure specific to this implementation */
198    
199     typedef struct PaWinDsDeviceInfo
200     {
201     PaDeviceInfo inheritedDeviceInfo;
202     GUID guid;
203     GUID *lpGUID;
204     double sampleRates[3];
205     char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
206     char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
207     } PaWinDsDeviceInfo;
208    
209     typedef struct
210     {
211     PaUtilHostApiRepresentation inheritedHostApiRep;
212     PaUtilStreamInterface callbackStreamInterface;
213     PaUtilStreamInterface blockingStreamInterface;
214    
215     PaUtilAllocationGroup *allocations;
216    
217     /* implementation specific data goes here */
218    
219     char comWasInitialized;
220    
221     } PaWinDsHostApiRepresentation;
222    
223    
224     /* PaWinDsStream - a stream data structure specifically for this implementation */
225    
226     typedef struct PaWinDsStream
227     {
228     PaUtilStreamRepresentation streamRepresentation;
229     PaUtilCpuLoadMeasurer cpuLoadMeasurer;
230     PaUtilBufferProcessor bufferProcessor;
231    
232     /* DirectSound specific data. */
233     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
234     LPDIRECTSOUNDFULLDUPLEX8 pDirectSoundFullDuplex8;
235     #endif
236    
237     /* Output */
238     LPDIRECTSOUND pDirectSound;
239     LPDIRECTSOUNDBUFFER pDirectSoundPrimaryBuffer;
240     LPDIRECTSOUNDBUFFER pDirectSoundOutputBuffer;
241     DWORD outputBufferWriteOffsetBytes; /* last write position */
242     INT outputBufferSizeBytes;
243     INT bytesPerOutputFrame;
244     /* Try to detect play buffer underflows. */
245     LARGE_INTEGER perfCounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
246     LARGE_INTEGER previousPlayTime;
247 william 273 DWORD previousPlayCursor;
248 william 31 UINT outputUnderflowCount;
249     BOOL outputIsRunning;
250     INT finalZeroBytesWritten; /* used to determine when we've flushed the whole buffer */
251    
252     /* Input */
253     LPDIRECTSOUNDCAPTURE pDirectSoundCapture;
254     LPDIRECTSOUNDCAPTUREBUFFER pDirectSoundInputBuffer;
255     INT bytesPerInputFrame;
256     UINT readOffset; /* last read position */
257     UINT inputSize;
258    
259 william 273
260 william 31 MMRESULT timerID;
261     int framesPerDSBuffer;
262     double framesWritten;
263     double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
264    
265     PaStreamCallbackFlags callbackFlags;
266    
267     PaStreamFlags streamFlags;
268     int callbackResult;
269     HANDLE processingCompleted;
270 william 273
271 william 31 /* FIXME - move all below to PaUtilStreamRepresentation */
272     volatile int isStarted;
273     volatile int isActive;
274     volatile int stopProcessing; /* stop thread once existing buffers have been returned */
275     volatile int abortProcessing; /* stop thread immediately */
276     } PaWinDsStream;
277    
278    
279     /************************************************************************************
280     ** Duplicate the input string using the allocations allocator.
281     ** A NULL string is converted to a zero length string.
282     ** If memory cannot be allocated, NULL is returned.
283     **/
284     static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const char* src )
285     {
286     char *result = 0;
287 william 273
288 william 31 if( src != NULL )
289     {
290     size_t len = strlen(src);
291     result = (char*)PaUtil_GroupAllocateMemory( allocations, (long)(len + 1) );
292     if( result )
293     memcpy( (void *) result, src, len+1 );
294     }
295     else
296     {
297     result = (char*)PaUtil_GroupAllocateMemory( allocations, 1 );
298     if( result )
299     result[0] = '\0';
300     }
301    
302     return result;
303     }
304    
305     /************************************************************************************
306     ** DSDeviceNameAndGUID, DSDeviceNameAndGUIDVector used for collecting preliminary
307     ** information during device enumeration.
308     */
309     typedef struct DSDeviceNameAndGUID{
310     char *name; // allocated from parent's allocations, never deleted by this structure
311     GUID guid;
312     LPGUID lpGUID;
313     void *pnpInterface; // wchar_t* interface path, allocated using the DS host api's allocation group
314     } DSDeviceNameAndGUID;
315    
316     typedef struct DSDeviceNameAndGUIDVector{
317     PaUtilAllocationGroup *allocations;
318     PaError enumerationError;
319    
320     int count;
321     int free;
322     DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
323     } DSDeviceNameAndGUIDVector;
324    
325     typedef struct DSDeviceNamesAndGUIDs{
326     PaWinDsHostApiRepresentation *winDsHostApi;
327     DSDeviceNameAndGUIDVector inputNamesAndGUIDs;
328     DSDeviceNameAndGUIDVector outputNamesAndGUIDs;
329     } DSDeviceNamesAndGUIDs;
330    
331     static PaError InitializeDSDeviceNameAndGUIDVector(
332     DSDeviceNameAndGUIDVector *guidVector, PaUtilAllocationGroup *allocations )
333     {
334     PaError result = paNoError;
335    
336     guidVector->allocations = allocations;
337     guidVector->enumerationError = paNoError;
338    
339     guidVector->count = 0;
340     guidVector->free = 8;
341     guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
342     if( guidVector->items == NULL )
343     result = paInsufficientMemory;
344 william 273
345 william 31 return result;
346     }
347    
348     static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
349     {
350     PaError result = paNoError;
351     DSDeviceNameAndGUID *newItems;
352     int i;
353 william 273
354 william 31 /* double size of vector */
355     int size = guidVector->count + guidVector->free;
356     guidVector->free += size;
357    
358     newItems = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * size * 2 );
359     if( newItems == NULL )
360     {
361     result = paInsufficientMemory;
362     }
363     else
364     {
365     for( i=0; i < guidVector->count; ++i )
366     {
367     newItems[i].name = guidVector->items[i].name;
368     if( guidVector->items[i].lpGUID == NULL )
369     {
370     newItems[i].lpGUID = NULL;
371     }
372     else
373     {
374     newItems[i].lpGUID = &newItems[i].guid;
375     memcpy( &newItems[i].guid, guidVector->items[i].lpGUID, sizeof(GUID) );;
376     }
377     newItems[i].pnpInterface = guidVector->items[i].pnpInterface;
378     }
379    
380     LocalFree( guidVector->items );
381     guidVector->items = newItems;
382 william 273 }
383 william 31
384     return result;
385     }
386    
387     /*
388     it's safe to call DSDeviceNameAndGUIDVector multiple times
389     */
390     static PaError TerminateDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
391     {
392     PaError result = paNoError;
393    
394     if( guidVector->items != NULL )
395     {
396     if( LocalFree( guidVector->items ) != NULL )
397     result = paInsufficientMemory; /** @todo this isn't the correct error to return from a deallocation failure */
398    
399     guidVector->items = NULL;
400     }
401    
402     return result;
403     }
404    
405     /************************************************************************************
406 william 273 ** Collect preliminary device information during DirectSound enumeration
407 william 31 */
408     static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
409     LPCTSTR lpszDesc,
410     LPCTSTR lpszDrvName,
411     LPVOID lpContext )
412     {
413     DSDeviceNameAndGUIDVector *namesAndGUIDs = (DSDeviceNameAndGUIDVector*)lpContext;
414     PaError error;
415    
416     (void) lpszDrvName; /* unused variable */
417    
418     if( namesAndGUIDs->free == 0 )
419     {
420     error = ExpandDSDeviceNameAndGUIDVector( namesAndGUIDs );
421     if( error != paNoError )
422     {
423     namesAndGUIDs->enumerationError = error;
424     return FALSE;
425     }
426     }
427 william 273
428 william 31 /* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
429     if( lpGUID == NULL )
430     {
431     namesAndGUIDs->items[namesAndGUIDs->count].lpGUID = NULL;
432     }
433     else
434     {
435     namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
436     &namesAndGUIDs->items[namesAndGUIDs->count].guid;
437 william 273
438 william 31 memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
439     }
440    
441     namesAndGUIDs->items[namesAndGUIDs->count].name =
442     DuplicateDeviceNameString( namesAndGUIDs->allocations, lpszDesc );
443     if( namesAndGUIDs->items[namesAndGUIDs->count].name == NULL )
444     {
445     namesAndGUIDs->enumerationError = paInsufficientMemory;
446     return FALSE;
447     }
448    
449     namesAndGUIDs->items[namesAndGUIDs->count].pnpInterface = 0;
450    
451     ++namesAndGUIDs->count;
452     --namesAndGUIDs->free;
453 william 273
454 william 31 return TRUE;
455     }
456    
457     #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
458    
459     static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source )
460     {
461     size_t len;
462     wchar_t *result;
463    
464     len = wcslen( source );
465     result = (wchar_t*)PaUtil_GroupAllocateMemory( allocations, (long) ((len+1) * sizeof(wchar_t)) );
466     wcscpy( result, source );
467     return result;
468     }
469    
470     static BOOL CALLBACK KsPropertySetEnumerateCallback( PDSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA data, LPVOID context )
471     {
472     int i;
473     DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs = (DSDeviceNamesAndGUIDs*)context;
474    
475     if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_RENDER )
476     {
477     for( i=0; i < deviceNamesAndGUIDs->outputNamesAndGUIDs.count; ++i )
478     {
479     if( deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID
480     && memcmp( &data->DeviceId, deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
481     {
482 william 273 deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
483 william 31 (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
484     break;
485     }
486     }
487     }
488     else if( data->DataFlow == DIRECTSOUNDDEVICE_DATAFLOW_CAPTURE )
489     {
490     for( i=0; i < deviceNamesAndGUIDs->inputNamesAndGUIDs.count; ++i )
491     {
492     if( deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID
493     && memcmp( &data->DeviceId, deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
494     {
495 william 273 deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
496 william 31 (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
497     break;
498     }
499     }
500     }
501    
502     return TRUE;
503     }
504    
505    
506 william 273 static GUID pawin_CLSID_DirectSoundPrivate =
507 william 31 { 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
508    
509 william 273 static GUID pawin_DSPROPSETID_DirectSoundDevice =
510 william 31 { 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
511    
512 william 273 static GUID pawin_IID_IKsPropertySet =
513 william 31 { 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 };
514    
515    
516     /*
517     FindDevicePnpInterfaces fills in the pnpInterface fields in deviceNamesAndGUIDs
518     with UNICODE file paths to the devices. The DS documentation mentions
519     at least two techniques by which these Interface paths can be found using IKsPropertySet on
520 william 273 the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
521 william 31 property, and the other is using DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE.
522     I tried both methods and only the second worked. I found two postings on the
523 william 273 net from people who had the same problem with the first method, so I think the method used here is
524 william 31 more common/likely to work. The probem is that IKsPropertySet_Get returns S_OK
525     but the fields of the device description are not filled in.
526    
527 william 273 The mechanism we use works by registering an enumeration callback which is called for
528 william 31 every DSound device. Our callback searches for a device in our deviceNamesAndGUIDs list
529     with the matching GUID and copies the pointer to the Interface path.
530 william 273 Note that we could have used this enumeration callback to perform the original
531 william 31 device enumeration, however we choose not to so we can disable this step easily.
532    
533     Apparently the IKsPropertySet mechanism was added in DirectSound 9c 2004
534     http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2004-12/0099.html
535    
536     -- rossb
537     */
538     static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
539     {
540     IClassFactory *pClassFactory;
541 william 273
542 william 31 if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
543     IKsPropertySet *pPropertySet;
544     if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
545 william 273
546 william 31 DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W_DATA data;
547     ULONG bytesReturned;
548    
549     data.Callback = KsPropertySetEnumerateCallback;
550     data.Context = deviceNamesAndGUIDs;
551    
552     IKsPropertySet_Get( pPropertySet,
553     &pawin_DSPROPSETID_DirectSoundDevice,
554     DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W,
555     NULL,
556     0,
557     &data,
558     sizeof(data),
559     &bytesReturned
560     );
561 william 273
562 william 31 IKsPropertySet_Release( pPropertySet );
563     }
564     pClassFactory->lpVtbl->Release( pClassFactory );
565     }
566    
567     /*
568 william 273 The following code fragment, which I chose not to use, queries for the
569 william 31 device interface for a device with a specific GUID:
570    
571     ULONG BytesReturned;
572     DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W_DATA Property;
573    
574     memset (&Property, 0, sizeof(Property));
575     Property.DataFlow = DIRECTSOUNDDEVICE_DATAFLOW_RENDER;
576 william 273 Property.DeviceId = *lpGUID;
577 william 31
578     hr = IKsPropertySet_Get( pPropertySet,
579     &pawin_DSPROPSETID_DirectSoundDevice,
580     DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION_W,
581     NULL,
582     0,
583     &Property,
584     sizeof(Property),
585     &BytesReturned
586     );
587    
588     if( hr == S_OK )
589     {
590     //pnpInterface = Property.Interface;
591     }
592     */
593     }
594     #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
595    
596    
597 william 273 /*
598 william 31 GUIDs for emulated devices which we blacklist below.
599     are there more than two of them??
600     */
601    
602     GUID IID_IRolandVSCEmulated1 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x01};
603     GUID IID_IRolandVSCEmulated2 = {0xc2ad1800, 0xb243, 0x11ce, 0xa8, 0xa4, 0x00, 0xaa, 0x00, 0x6c, 0x45, 0x02};
604    
605    
606     #define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ (13) /* must match array length below */
607     static double defaultSampleRateSearchOrder_[] =
608     { 44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, 192000.0,
609     16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
610    
611     /************************************************************************************
612     ** Extract capabilities from an output device, and add it to the device info list
613     ** if successful. This function assumes that there is enough room in the
614     ** device info list to accomodate all entries.
615     **
616     ** The device will not be added to the device list if any errors are encountered.
617     */
618     static PaError AddOutputDeviceInfoFromDirectSound(
619     PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
620     {
621     PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
622     PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
623     PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
624     HRESULT hr;
625     LPDIRECTSOUND lpDirectSound;
626     DSCAPS caps;
627     int deviceOK = TRUE;
628     PaError result = paNoError;
629     int i;
630    
631     /* Copy GUID to the device info structure. Set pointer. */
632     if( lpGUID == NULL )
633     {
634     winDsDeviceInfo->lpGUID = NULL;
635     }
636     else
637     {
638     memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
639     winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
640     }
641 william 273
642 william 31 if( lpGUID )
643     {
644     if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
645     IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
646     {
647     PA_DEBUG(("BLACKLISTED: %s \n",name));
648     return paNoError;
649     }
650     }
651    
652     /* Create a DirectSound object for the specified GUID
653     Note that using CoCreateInstance doesn't work on windows CE.
654     */
655     hr = paWinDsDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
656    
657     /** try using CoCreateInstance because DirectSoundCreate was hanging under
658     some circumstances - note this was probably related to the
659     #define BOOL short bug which has now been fixed
660     @todo delete this comment and the following code once we've ensured
661     there is no bug.
662     */
663     /*
664     hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
665     &IID_IDirectSound, (void**)&lpDirectSound );
666    
667     if( hr == S_OK )
668     {
669     hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
670     }
671     */
672 william 273
673 william 31 if( hr != DS_OK )
674     {
675     if (hr == DSERR_ALLOCATED)
676     PA_DEBUG(("AddOutputDeviceInfoFromDirectSound %s DSERR_ALLOCATED\n",name));
677     DBUG(("Cannot create DirectSound for %s. Result = 0x%x\n", name, hr ));
678     if (lpGUID)
679     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",
680     name,
681     lpGUID->Data1,
682     lpGUID->Data2,
683     lpGUID->Data3,
684     lpGUID->Data4[0],
685     lpGUID->Data4[1],
686     lpGUID->Data4[2],
687     lpGUID->Data4[3],
688     lpGUID->Data4[4],
689     lpGUID->Data4[5],
690     lpGUID->Data4[6],
691     lpGUID->Data4[7]));
692    
693     deviceOK = FALSE;
694     }
695     else
696     {
697     /* Query device characteristics. */
698 william 273 memset( &caps, 0, sizeof(caps) );
699 william 31 caps.dwSize = sizeof(caps);
700     hr = IDirectSound_GetCaps( lpDirectSound, &caps );
701     if( hr != DS_OK )
702     {
703     DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
704     deviceOK = FALSE;
705     }
706     else
707     {
708    
709     #ifndef PA_NO_WMME
710     if( caps.dwFlags & DSCAPS_EMULDRIVER )
711     {
712     /* If WMME supported, then reject Emulated drivers because they are lousy. */
713     deviceOK = FALSE;
714     }
715     #endif
716    
717     if( deviceOK )
718     {
719     deviceInfo->maxInputChannels = 0;
720     winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
721    
722     /* DS output capabilities only indicate supported number of channels
723     using two flags which indicate mono and/or stereo.
724     We assume that stereo devices may support more than 2 channels
725     (as is the case with 5.1 devices for example) and so
726     set deviceOutputChannelCountIsKnown to 0 (unknown).
727     In this case OpenStream will try to open the device
728     when the user requests more than 2 channels, rather than
729 william 273 returning an error.
730 william 31 */
731     if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
732     {
733     deviceInfo->maxOutputChannels = 2;
734     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 0;
735     }
736     else
737     {
738     deviceInfo->maxOutputChannels = 1;
739     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
740     }
741    
742 william 273 /* Guess channels count from speaker configuration. We do it only when
743 william 31 pnpInterface is NULL or when PAWIN_USE_WDMKS_DEVICE_INFO is undefined.
744     */
745     #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
746     if( !pnpInterface )
747     #endif
748     {
749     DWORD spkrcfg;
750     if( SUCCEEDED(IDirectSound_GetSpeakerConfig( lpDirectSound, &spkrcfg )) )
751     {
752     int count = 0;
753     switch (DSSPEAKER_CONFIG(spkrcfg))
754     {
755     case DSSPEAKER_HEADPHONE: count = 2; break;
756     case DSSPEAKER_MONO: count = 1; break;
757     case DSSPEAKER_QUAD: count = 4; break;
758     case DSSPEAKER_STEREO: count = 2; break;
759     case DSSPEAKER_SURROUND: count = 4; break;
760     case DSSPEAKER_5POINT1: count = 6; break;
761     case DSSPEAKER_7POINT1: count = 8; break;
762 william 62 #ifndef DSSPEAKER_7POINT1_SURROUND
763     #define DSSPEAKER_7POINT1_SURROUND 0x00000008
764     #endif
765 william 31 case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
766     #ifndef DSSPEAKER_5POINT1_SURROUND
767     #define DSSPEAKER_5POINT1_SURROUND 0x00000009
768     #endif
769     case DSSPEAKER_5POINT1_SURROUND: count = 6; break;
770     }
771     if( count )
772     {
773     deviceInfo->maxOutputChannels = count;
774     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
775     }
776     }
777     }
778    
779     #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
780     if( pnpInterface )
781     {
782     int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0 );
783     if( count > 0 )
784     {
785     deviceInfo->maxOutputChannels = count;
786     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
787     }
788     }
789     #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
790    
791     deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
792     deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
793     deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
794     deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
795 william 273
796 william 31 /* initialize defaultSampleRate */
797 william 273
798 william 31 if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
799     {
800     /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
801     deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
802    
803     for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
804     {
805     if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
806     && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
807     {
808     deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
809     break;
810     }
811     }
812     }
813     else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
814     {
815     if( caps.dwMinSecondarySampleRate == 0 )
816     {
817     /*
818     ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
819     ** But it supports continuous sampling.
820     ** So fake range of rates, and hope it really supports it.
821     */
822     deviceInfo->defaultSampleRate = 44100.0f;
823    
824     DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
825     }
826     else
827     {
828     deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
829     }
830     }
831     else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
832     {
833     /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
834     ** But we know that they really support a range of rates!
835     ** So when we see a ridiculous set of rates, assume it is a range.
836     */
837     deviceInfo->defaultSampleRate = 44100.0f;
838     DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
839     }
840     else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
841    
842    
843     //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
844 william 273 // dwFlags | DSCAPS_CONTINUOUSRATE
845 william 31 }
846     }
847    
848     IDirectSound_Release( lpDirectSound );
849     }
850    
851     if( deviceOK )
852     {
853     deviceInfo->name = name;
854    
855     if( lpGUID == NULL )
856     hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
857 william 273
858 william 31 hostApi->info.deviceCount++;
859     }
860    
861     return result;
862     }
863    
864    
865     /************************************************************************************
866     ** Extract capabilities from an input device, and add it to the device info list
867     ** if successful. This function assumes that there is enough room in the
868     ** device info list to accomodate all entries.
869     **
870     ** The device will not be added to the device list if any errors are encountered.
871     */
872     static PaError AddInputDeviceInfoFromDirectSoundCapture(
873     PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
874     {
875     PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
876     PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
877     PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
878     HRESULT hr;
879     LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
880     DSCCAPS caps;
881     int deviceOK = TRUE;
882     PaError result = paNoError;
883 william 273
884 william 31 /* Copy GUID to the device info structure. Set pointer. */
885     if( lpGUID == NULL )
886     {
887     winDsDeviceInfo->lpGUID = NULL;
888     }
889     else
890     {
891     winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
892     memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
893     }
894    
895     hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
896    
897     /** try using CoCreateInstance because DirectSoundCreate was hanging under
898     some circumstances - note this was probably related to the
899     #define BOOL short bug which has now been fixed
900     @todo delete this comment and the following code once we've ensured
901     there is no bug.
902     */
903     /*
904     hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
905     &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
906     */
907     if( hr != DS_OK )
908     {
909     DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
910     deviceOK = FALSE;
911     }
912     else
913     {
914     /* Query device characteristics. */
915     memset( &caps, 0, sizeof(caps) );
916     caps.dwSize = sizeof(caps);
917     hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
918     if( hr != DS_OK )
919     {
920     DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
921     deviceOK = FALSE;
922     }
923     else
924     {
925     #ifndef PA_NO_WMME
926     if( caps.dwFlags & DSCAPS_EMULDRIVER )
927     {
928     /* If WMME supported, then reject Emulated drivers because they are lousy. */
929     deviceOK = FALSE;
930     }
931     #endif
932    
933     if( deviceOK )
934     {
935     deviceInfo->maxInputChannels = caps.dwChannels;
936     winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
937    
938     deviceInfo->maxOutputChannels = 0;
939     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
940    
941     #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
942     if( pnpInterface )
943     {
944     int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1 );
945     if( count > 0 )
946     {
947     deviceInfo->maxInputChannels = count;
948     winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
949     }
950     }
951     #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
952    
953     deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
954     deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
955     deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
956     deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
957    
958     /* constants from a WINE patch by Francois Gouget, see:
959     http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
960    
961     ---
962     Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
963     From: Francois Gouget <fgouget@ ... .fr>
964     To: Ross Bencina <rbencina@ ... .au>
965     Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
966    
967     [snip]
968    
969     I give you permission to use the patch below under the BSD license.
970     http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
971    
972     [snip]
973     */
974     #ifndef WAVE_FORMAT_48M08
975     #define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
976     #define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */
977     #define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */
978     #define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
979     #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
980     #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
981     #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
982     #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
983     #endif
984    
985     /* defaultSampleRate */
986     if( caps.dwChannels == 2 )
987     {
988     if( caps.dwFormats & WAVE_FORMAT_4S16 )
989     deviceInfo->defaultSampleRate = 44100.0;
990     else if( caps.dwFormats & WAVE_FORMAT_48S16 )
991     deviceInfo->defaultSampleRate = 48000.0;
992     else if( caps.dwFormats & WAVE_FORMAT_2S16 )
993     deviceInfo->defaultSampleRate = 22050.0;
994     else if( caps.dwFormats & WAVE_FORMAT_1S16 )
995     deviceInfo->defaultSampleRate = 11025.0;
996     else if( caps.dwFormats & WAVE_FORMAT_96S16 )
997     deviceInfo->defaultSampleRate = 96000.0;
998     else
999     deviceInfo->defaultSampleRate = 0.;
1000     }
1001     else if( caps.dwChannels == 1 )
1002     {
1003     if( caps.dwFormats & WAVE_FORMAT_4M16 )
1004     deviceInfo->defaultSampleRate = 44100.0;
1005     else if( caps.dwFormats & WAVE_FORMAT_48M16 )
1006     deviceInfo->defaultSampleRate = 48000.0;
1007     else if( caps.dwFormats & WAVE_FORMAT_2M16 )
1008     deviceInfo->defaultSampleRate = 22050.0;
1009     else if( caps.dwFormats & WAVE_FORMAT_1M16 )
1010     deviceInfo->defaultSampleRate = 11025.0;
1011     else if( caps.dwFormats & WAVE_FORMAT_96M16 )
1012     deviceInfo->defaultSampleRate = 96000.0;
1013     else
1014     deviceInfo->defaultSampleRate = 0.;
1015     }
1016     else deviceInfo->defaultSampleRate = 0.;
1017     }
1018     }
1019 william 273
1020 william 31 IDirectSoundCapture_Release( lpDirectSoundCapture );
1021     }
1022    
1023     if( deviceOK )
1024     {
1025     deviceInfo->name = name;
1026    
1027     if( lpGUID == NULL )
1028     hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
1029    
1030     hostApi->info.deviceCount++;
1031     }
1032    
1033     return result;
1034     }
1035    
1036    
1037     /***********************************************************************************/
1038     PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1039     {
1040     PaError result = paNoError;
1041     int i, deviceCount;
1042     PaWinDsHostApiRepresentation *winDsHostApi;
1043     DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
1044    
1045     PaWinDsDeviceInfo *deviceInfoArray;
1046     char comWasInitialized = 0;
1047    
1048     /*
1049     If COM is already initialized CoInitialize will either return
1050     FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
1051     threading mode. In either case we shouldn't consider it an error
1052 william 273 but we need to be careful to not call CoUninitialize() if
1053 william 31 RPC_E_CHANGED_MODE was returned.
1054     */
1055    
1056     HRESULT hr = CoInitialize(NULL);
1057     if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )
1058     return paUnanticipatedHostError;
1059    
1060     if( hr != RPC_E_CHANGED_MODE )
1061     comWasInitialized = 1;
1062    
1063     /* initialise guid vectors so they can be safely deleted on error */
1064     deviceNamesAndGUIDs.winDsHostApi = NULL;
1065     deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
1066     deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
1067    
1068     PaWinDs_InitializeDSoundEntryPoints();
1069    
1070     winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
1071     if( !winDsHostApi )
1072     {
1073     result = paInsufficientMemory;
1074     goto error;
1075     }
1076    
1077     winDsHostApi->comWasInitialized = comWasInitialized;
1078    
1079     winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
1080     if( !winDsHostApi->allocations )
1081     {
1082     result = paInsufficientMemory;
1083     goto error;
1084     }
1085    
1086     *hostApi = &winDsHostApi->inheritedHostApiRep;
1087     (*hostApi)->info.structVersion = 1;
1088     (*hostApi)->info.type = paDirectSound;
1089     (*hostApi)->info.name = "Windows DirectSound";
1090 william 273
1091 william 31 (*hostApi)->info.deviceCount = 0;
1092     (*hostApi)->info.defaultInputDevice = paNoDevice;
1093     (*hostApi)->info.defaultOutputDevice = paNoDevice;
1094    
1095 william 273
1096 william 31 /* DSound - enumerate devices to count them and to gather their GUIDs */
1097    
1098     result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
1099     if( result != paNoError )
1100     goto error;
1101    
1102     result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
1103     if( result != paNoError )
1104     goto error;
1105    
1106     paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
1107    
1108     paWinDsDSoundEntryPoints.DirectSoundEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
1109    
1110     if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
1111     {
1112     result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
1113     goto error;
1114     }
1115    
1116     if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
1117     {
1118     result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
1119     goto error;
1120     }
1121    
1122     deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
1123    
1124     #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
1125     if( deviceCount > 0 )
1126     {
1127     deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
1128     FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
1129     }
1130     #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
1131    
1132     if( deviceCount > 0 )
1133     {
1134     /* allocate array for pointers to PaDeviceInfo structs */
1135     (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1136     winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
1137     if( !(*hostApi)->deviceInfos )
1138     {
1139     result = paInsufficientMemory;
1140     goto error;
1141     }
1142    
1143     /* allocate all PaDeviceInfo structs in a contiguous block */
1144     deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
1145     winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
1146     if( !deviceInfoArray )
1147     {
1148     result = paInsufficientMemory;
1149     goto error;
1150     }
1151    
1152     for( i=0; i < deviceCount; ++i )
1153     {
1154     PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
1155     deviceInfo->structVersion = 2;
1156     deviceInfo->hostApi = hostApiIndex;
1157     deviceInfo->name = 0;
1158     (*hostApi)->deviceInfos[i] = deviceInfo;
1159     }
1160    
1161     for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
1162     {
1163     result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
1164     deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
1165     deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
1166     deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
1167     if( result != paNoError )
1168     goto error;
1169     }
1170    
1171     for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
1172     {
1173     result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
1174     deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
1175     deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
1176     deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
1177     if( result != paNoError )
1178     goto error;
1179     }
1180 william 273 }
1181 william 31
1182     result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
1183     if( result != paNoError )
1184     goto error;
1185    
1186     result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
1187     if( result != paNoError )
1188     goto error;
1189    
1190 william 273
1191 william 31 (*hostApi)->Terminate = Terminate;
1192     (*hostApi)->OpenStream = OpenStream;
1193     (*hostApi)->IsFormatSupported = IsFormatSupported;
1194    
1195     PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
1196     StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1197     GetStreamTime, GetStreamCpuLoad,
1198     PaUtil_DummyRead, PaUtil_DummyWrite,
1199     PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1200    
1201     PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
1202     StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1203     GetStreamTime, PaUtil_DummyGetCpuLoad,
1204     ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1205    
1206     return result;
1207    
1208     error:
1209     if( winDsHostApi )
1210     {
1211     if( winDsHostApi->allocations )
1212     {
1213     PaUtil_FreeAllAllocations( winDsHostApi->allocations );
1214     PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
1215     }
1216 william 273
1217 william 31 PaUtil_FreeMemory( winDsHostApi );
1218     }
1219    
1220     TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
1221     TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
1222    
1223     if( comWasInitialized )
1224     CoUninitialize();
1225    
1226     return result;
1227     }
1228    
1229    
1230     /***********************************************************************************/
1231     static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1232     {
1233     PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
1234     char comWasInitialized = winDsHostApi->comWasInitialized;
1235    
1236     /*
1237     IMPLEMENT ME:
1238     - clean up any resources not handled by the allocation group
1239     */
1240    
1241     if( winDsHostApi->allocations )
1242     {
1243     PaUtil_FreeAllAllocations( winDsHostApi->allocations );
1244     PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
1245     }
1246    
1247     PaUtil_FreeMemory( winDsHostApi );
1248    
1249     PaWinDs_TerminateDSoundEntryPoints();
1250    
1251     if( comWasInitialized )
1252     CoUninitialize();
1253     }
1254    
1255    
1256     /* Set minimal latency based on whether NT or Win95.
1257     * NT has higher latency.
1258     */
1259     static int PaWinDS_GetMinSystemLatency( void )
1260     {
1261     int minLatencyMsec;
1262     /* Set minimal latency based on whether NT or other OS.
1263     * NT has higher latency.
1264     */
1265     OSVERSIONINFO osvi;
1266     osvi.dwOSVersionInfoSize = sizeof( osvi );
1267     GetVersionEx( &osvi );
1268     DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
1269     DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
1270     DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
1271     /* Check for NT */
1272     if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
1273     {
1274     minLatencyMsec = PA_WIN_NT_LATENCY;
1275     }
1276     else if(osvi.dwMajorVersion >= 5)
1277     {
1278     minLatencyMsec = PA_WIN_WDM_LATENCY;
1279     }
1280     else
1281     {
1282     minLatencyMsec = PA_WIN_9X_LATENCY;
1283     }
1284     return minLatencyMsec;
1285     }
1286    
1287     static PaError ValidateWinDirectSoundSpecificStreamInfo(
1288     const PaStreamParameters *streamParameters,
1289     const PaWinDirectSoundStreamInfo *streamInfo )
1290     {
1291     if( streamInfo )
1292     {
1293     if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
1294     || streamInfo->version != 1 )
1295     {
1296     return paIncompatibleHostApiSpecificStreamInfo;
1297     }
1298     }
1299    
1300     return paNoError;
1301     }
1302    
1303     /***********************************************************************************/
1304     static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1305     const PaStreamParameters *inputParameters,
1306     const PaStreamParameters *outputParameters,
1307     double sampleRate )
1308     {
1309     PaError result;
1310     PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
1311     PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1312     int inputChannelCount, outputChannelCount;
1313     PaSampleFormat inputSampleFormat, outputSampleFormat;
1314     PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
1315    
1316     if( inputParameters )
1317     {
1318     inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
1319     inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
1320    
1321     inputChannelCount = inputParameters->channelCount;
1322     inputSampleFormat = inputParameters->sampleFormat;
1323    
1324     /* unless alternate device specification is supported, reject the use of
1325     paUseHostApiSpecificDeviceSpecification */
1326    
1327     if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1328     return paInvalidDevice;
1329    
1330     /* check that input device can support inputChannelCount */
1331     if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
1332     && inputChannelCount > inputDeviceInfo->maxInputChannels )
1333     return paInvalidChannelCount;
1334    
1335     /* validate inputStreamInfo */
1336     inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1337     result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
1338     if( result != paNoError ) return result;
1339     }
1340     else
1341     {
1342     inputChannelCount = 0;
1343     }
1344    
1345     if( outputParameters )
1346     {
1347     outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
1348     outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
1349    
1350     outputChannelCount = outputParameters->channelCount;
1351     outputSampleFormat = outputParameters->sampleFormat;
1352 william 273
1353 william 31 /* unless alternate device specification is supported, reject the use of
1354     paUseHostApiSpecificDeviceSpecification */
1355    
1356     if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1357     return paInvalidDevice;
1358    
1359     /* check that output device can support inputChannelCount */
1360     if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
1361     && outputChannelCount > outputDeviceInfo->maxOutputChannels )
1362     return paInvalidChannelCount;
1363    
1364     /* validate outputStreamInfo */
1365     outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1366     result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
1367     if( result != paNoError ) return result;
1368     }
1369     else
1370     {
1371     outputChannelCount = 0;
1372     }
1373 william 273
1374 william 31 /*
1375     IMPLEMENT ME:
1376    
1377     - if a full duplex stream is requested, check that the combination
1378     of input and output parameters is supported if necessary
1379    
1380     - check that the device supports sampleRate
1381    
1382     Because the buffer adapter handles conversion between all standard
1383     sample formats, the following checks are only required if paCustomFormat
1384     is implemented, or under some other unusual conditions.
1385    
1386     - check that input device can support inputSampleFormat, or that
1387     we have the capability to convert from outputSampleFormat to
1388     a native format
1389    
1390     - check that output device can support outputSampleFormat, or that
1391     we have the capability to convert from outputSampleFormat to
1392     a native format
1393     */
1394    
1395     return paFormatIsSupported;
1396     }
1397    
1398    
1399     /*************************************************************************
1400     ** Determine minimum number of buffers required for this host based
1401     ** on minimum latency. Latency can be optionally set by user by setting
1402     ** an environment variable. For example, to set latency to 200 msec, put:
1403     **
1404     ** set PA_MIN_LATENCY_MSEC=200
1405     **
1406     ** in the AUTOEXEC.BAT file and reboot.
1407     ** If the environment variable is not set, then the latency will be determined
1408     ** based on the OS. Windows NT has higher latency than Win95.
1409     */
1410     #define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
1411     #define PA_ENV_BUF_SIZE (32)
1412    
1413     static int PaWinDs_GetMinLatencyFrames( double sampleRate )
1414     {
1415     char envbuf[PA_ENV_BUF_SIZE];
1416     DWORD hresult;
1417     int minLatencyMsec = 0;
1418    
1419     /* Let user determine minimal latency by setting environment variable. */
1420     hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
1421     if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
1422     {
1423     minLatencyMsec = atoi( envbuf );
1424     }
1425     else
1426     {
1427     minLatencyMsec = PaWinDS_GetMinSystemLatency();
1428     #if PA_USE_HIGH_LATENCY
1429     PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
1430     #endif
1431    
1432     }
1433    
1434     return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
1435     }
1436    
1437    
1438     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
1439     static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
1440     PaWinDsDeviceInfo *inputDevice,
1441     PaSampleFormat hostInputSampleFormat,
1442 william 273 WORD inputChannelCount,
1443 william 31 int bytesPerInputBuffer,
1444     PaWinWaveFormatChannelMask inputChannelMask,
1445     PaWinDsDeviceInfo *outputDevice,
1446     PaSampleFormat hostOutputSampleFormat,
1447 william 273 WORD outputChannelCount,
1448 william 31 int bytesPerOutputBuffer,
1449     PaWinWaveFormatChannelMask outputChannelMask,
1450     unsigned long nFrameRate
1451     )
1452     {
1453     HRESULT hr;
1454     DSCBUFFERDESC captureDesc;
1455     PaWinWaveFormat captureWaveFormat;
1456     DSBUFFERDESC secondaryRenderDesc;
1457     PaWinWaveFormat renderWaveFormat;
1458     LPDIRECTSOUNDBUFFER8 pRenderBuffer8;
1459     LPDIRECTSOUNDCAPTUREBUFFER8 pCaptureBuffer8;
1460    
1461     // capture buffer description
1462    
1463     // only try wave format extensible. assume it's available on all ds 8 systems
1464 william 273 PaWin_InitializeWaveFormatExtensible( &captureWaveFormat, inputChannelCount,
1465 william 31 hostInputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostInputSampleFormat ),
1466     nFrameRate, inputChannelMask );
1467    
1468     ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
1469     captureDesc.dwSize = sizeof(DSCBUFFERDESC);
1470     captureDesc.dwFlags = 0;
1471     captureDesc.dwBufferBytes = bytesPerInputBuffer;
1472     captureDesc.lpwfxFormat = (WAVEFORMATEX*)&captureWaveFormat;
1473    
1474     // render buffer description
1475    
1476 william 273 PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount,
1477 william 31 hostOutputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostOutputSampleFormat ),
1478     nFrameRate, outputChannelMask );
1479    
1480     ZeroMemory(&secondaryRenderDesc, sizeof(DSBUFFERDESC));
1481     secondaryRenderDesc.dwSize = sizeof(DSBUFFERDESC);
1482     secondaryRenderDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
1483     secondaryRenderDesc.dwBufferBytes = bytesPerOutputBuffer;
1484     secondaryRenderDesc.lpwfxFormat = (WAVEFORMATEX*)&renderWaveFormat;
1485    
1486     /* note that we don't create a primary buffer here at all */
1487    
1488 william 273 hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8(
1489 william 31 inputDevice->lpGUID, outputDevice->lpGUID,
1490     &captureDesc, &secondaryRenderDesc,
1491     GetDesktopWindow(), /* see InitOutputBuffer() for a discussion of whether this is a good idea */
1492     DSSCL_EXCLUSIVE,
1493     &stream->pDirectSoundFullDuplex8,
1494     &pCaptureBuffer8,
1495     &pRenderBuffer8,
1496 william 273 NULL /* pUnkOuter must be NULL */
1497 william 31 );
1498    
1499     if( hr == DS_OK )
1500     {
1501     PA_DEBUG(("DirectSoundFullDuplexCreate succeeded!\n"));
1502    
1503     /* retrieve the pre ds 8 buffer interfaces which are used by the rest of the code */
1504    
1505 william 273 hr = IUnknown_QueryInterface( pCaptureBuffer8, &IID_IDirectSoundCaptureBuffer, (LPVOID *)&stream->pDirectSoundInputBuffer );
1506    
1507 william 31 if( hr == DS_OK )
1508 william 273 hr = IUnknown_QueryInterface( pRenderBuffer8, &IID_IDirectSoundBuffer, (LPVOID *)&stream->pDirectSoundOutputBuffer );
1509 william 31
1510     /* release the ds 8 interfaces, we don't need them */
1511     IUnknown_Release( pCaptureBuffer8 );
1512     IUnknown_Release( pRenderBuffer8 );
1513    
1514     if( !stream->pDirectSoundInputBuffer || !stream->pDirectSoundOutputBuffer ){
1515     /* couldn't get pre ds 8 interfaces for some reason. clean up. */
1516     if( stream->pDirectSoundInputBuffer )
1517     {
1518     IUnknown_Release( stream->pDirectSoundInputBuffer );
1519     stream->pDirectSoundInputBuffer = NULL;
1520     }
1521    
1522     if( stream->pDirectSoundOutputBuffer )
1523     {
1524     IUnknown_Release( stream->pDirectSoundOutputBuffer );
1525     stream->pDirectSoundOutputBuffer = NULL;
1526     }
1527 william 273
1528 william 31 IUnknown_Release( stream->pDirectSoundFullDuplex8 );
1529     stream->pDirectSoundFullDuplex8 = NULL;
1530     }
1531     }
1532     else
1533     {
1534     PA_DEBUG(("DirectSoundFullDuplexCreate failed. hr=%d\n", hr));
1535     }
1536    
1537     return hr;
1538     }
1539     #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
1540    
1541    
1542     static HRESULT InitInputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
1543     {
1544     DSCBUFFERDESC captureDesc;
1545     PaWinWaveFormat waveFormat;
1546     HRESULT result;
1547 william 273
1548     if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
1549 william 31 device->lpGUID, &stream->pDirectSoundCapture, NULL) ) != DS_OK ){
1550     ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
1551     return result;
1552     }
1553    
1554     // Setup the secondary buffer description
1555     ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
1556     captureDesc.dwSize = sizeof(DSCBUFFERDESC);
1557     captureDesc.dwFlags = 0;
1558     captureDesc.dwBufferBytes = bytesPerBuffer;
1559     captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
1560 william 273
1561 william 31 // Create the capture buffer
1562    
1563     // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
1564 william 273 PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
1565 william 31 sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
1566     nFrameRate, channelMask );
1567    
1568     if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1569     &captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
1570     {
1571 william 273 PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
1572 william 31 PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
1573    
1574     if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1575     &captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
1576     }
1577    
1578     stream->readOffset = 0; // reset last read position to start of buffer
1579     return DS_OK;
1580     }
1581    
1582    
1583     static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
1584     {
1585     HRESULT result;
1586     HWND hWnd;
1587     HRESULT hr;
1588     PaWinWaveFormat waveFormat;
1589     DSBUFFERDESC primaryDesc;
1590     DSBUFFERDESC secondaryDesc;
1591 william 273
1592     if( (hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
1593 william 31 device->lpGUID, &stream->pDirectSound, NULL )) != DS_OK ){
1594     ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
1595     return hr;
1596     }
1597    
1598     // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
1599     // applications's window. Also if that window is closed before the Buffer is closed
1600     // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
1601     // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
1602     // hWnd = GetForegroundWindow();
1603     //
1604     // FIXME: The example code I have on the net creates a hidden window that
1605     // is managed by our code - I think we should do that - one hidden
1606     // window for the whole of Pa_DS
1607     //
1608     hWnd = GetDesktopWindow();
1609    
1610     // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
1611     // exclusive also prevents unexpected sounds from other apps during a performance.
1612     if ((hr = IDirectSound_SetCooperativeLevel( stream->pDirectSound,
1613     hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
1614     {
1615     return hr;
1616     }
1617    
1618     // -----------------------------------------------------------------------
1619     // Create primary buffer and set format just so we can specify our custom format.
1620     // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
1621     // Setup the primary buffer description
1622     ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
1623     primaryDesc.dwSize = sizeof(DSBUFFERDESC);
1624     primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
1625     primaryDesc.dwBufferBytes = 0;
1626     primaryDesc.lpwfxFormat = NULL;
1627     // Create the buffer
1628     if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
1629     &primaryDesc, &stream->pDirectSoundPrimaryBuffer, NULL)) != DS_OK)
1630     goto error;
1631    
1632     // Set the primary buffer's format
1633    
1634     // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
1635 william 273 PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
1636 william 31 sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
1637     nFrameRate, channelMask );
1638    
1639     if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
1640     {
1641 william 273 PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
1642 william 31 PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
1643    
1644     if((result = IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK)
1645     goto error;
1646     }
1647    
1648     // ----------------------------------------------------------------------
1649     // Setup the secondary buffer description
1650     ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
1651     secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
1652     secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
1653     secondaryDesc.dwBufferBytes = bytesPerBuffer;
1654     secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat; /* waveFormat contains whatever format was negotiated for the primary buffer above */
1655     // Create the secondary buffer
1656     if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
1657     &secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK)
1658     goto error;
1659 william 273
1660 william 31 return DS_OK;
1661    
1662     error:
1663    
1664     if( stream->pDirectSoundPrimaryBuffer )
1665     {
1666     IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
1667     stream->pDirectSoundPrimaryBuffer = NULL;
1668     }
1669    
1670     return result;
1671     }
1672    
1673     /***********************************************************************************/
1674     /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1675    
1676     static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1677     PaStream** s,
1678     const PaStreamParameters *inputParameters,
1679     const PaStreamParameters *outputParameters,
1680     double sampleRate,
1681     unsigned long framesPerBuffer,
1682     PaStreamFlags streamFlags,
1683     PaStreamCallback *streamCallback,
1684     void *userData )
1685     {
1686     PaError result = paNoError;
1687     PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
1688     PaWinDsStream *stream = 0;
1689     int bufferProcessorIsInitialized = 0;
1690     int streamRepresentationIsInitialized = 0;
1691     PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
1692     PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1693     int inputChannelCount, outputChannelCount;
1694     PaSampleFormat inputSampleFormat, outputSampleFormat;
1695     PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
1696     unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
1697     PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
1698     PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
1699    
1700     if( inputParameters )
1701     {
1702     inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
1703     inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
1704    
1705     inputChannelCount = inputParameters->channelCount;
1706     inputSampleFormat = inputParameters->sampleFormat;
1707     suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
1708    
1709     /* IDEA: the following 3 checks could be performed by default by pa_front
1710     unless some flag indicated otherwise */
1711 william 273
1712 william 31 /* unless alternate device specification is supported, reject the use of
1713     paUseHostApiSpecificDeviceSpecification */
1714     if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1715     return paInvalidDevice;
1716    
1717     /* check that input device can support inputChannelCount */
1718     if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
1719     && inputChannelCount > inputDeviceInfo->maxInputChannels )
1720     return paInvalidChannelCount;
1721 william 273
1722 william 31 /* validate hostApiSpecificStreamInfo */
1723     inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1724     result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
1725     if( result != paNoError ) return result;
1726    
1727     if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
1728     inputChannelMask = inputStreamInfo->channelMask;
1729     else
1730     inputChannelMask = PaWin_DefaultChannelMask( inputChannelCount );
1731     }
1732     else
1733     {
1734     inputChannelCount = 0;
1735     inputSampleFormat = 0;
1736     suggestedInputLatencyFrames = 0;
1737     }
1738    
1739    
1740     if( outputParameters )
1741     {
1742     outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
1743     outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
1744    
1745     outputChannelCount = outputParameters->channelCount;
1746     outputSampleFormat = outputParameters->sampleFormat;
1747     suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
1748    
1749     /* unless alternate device specification is supported, reject the use of
1750     paUseHostApiSpecificDeviceSpecification */
1751     if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1752     return paInvalidDevice;
1753    
1754     /* check that output device can support outputChannelCount */
1755     if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
1756     && outputChannelCount > outputDeviceInfo->maxOutputChannels )
1757     return paInvalidChannelCount;
1758    
1759     /* validate hostApiSpecificStreamInfo */
1760     outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1761     result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
1762 william 273 if( result != paNoError ) return result;
1763 william 31
1764     if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
1765     outputChannelMask = outputStreamInfo->channelMask;
1766     else
1767     outputChannelMask = PaWin_DefaultChannelMask( outputChannelCount );
1768     }
1769     else
1770     {
1771     outputChannelCount = 0;
1772     outputSampleFormat = 0;
1773     suggestedOutputLatencyFrames = 0;
1774     }
1775    
1776    
1777     /*
1778     IMPLEMENT ME:
1779    
1780     ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
1781    
1782     - check that input device can support inputSampleFormat, or that
1783     we have the capability to convert from outputSampleFormat to
1784     a native format
1785    
1786     - check that output device can support outputSampleFormat, or that
1787     we have the capability to convert from outputSampleFormat to
1788     a native format
1789    
1790     - if a full duplex stream is requested, check that the combination
1791     of input and output parameters is supported
1792    
1793     - check that the device supports sampleRate
1794    
1795     - alter sampleRate to a close allowable rate if possible / necessary
1796    
1797     - validate suggestedInputLatency and suggestedOutputLatency parameters,
1798     use default values where necessary
1799     */
1800    
1801    
1802     /* validate platform specific flags */
1803     if( (streamFlags & paPlatformSpecificFlags) != 0 )
1804     return paInvalidFlag; /* unexpected platform specific flag */
1805    
1806    
1807     stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
1808     if( !stream )
1809     {
1810     result = paInsufficientMemory;
1811     goto error;
1812     }
1813    
1814     memset( stream, 0, sizeof(PaWinDsStream) ); /* initialize all stream variables to 0 */
1815    
1816     if( streamCallback )
1817     {
1818     PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1819     &winDsHostApi->callbackStreamInterface, streamCallback, userData );
1820     }
1821     else
1822     {
1823     PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1824     &winDsHostApi->blockingStreamInterface, streamCallback, userData );
1825     }
1826 william 273
1827 william 31 streamRepresentationIsInitialized = 1;
1828    
1829     stream->streamFlags = streamFlags;
1830    
1831     PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
1832    
1833    
1834     if( inputParameters )
1835     {
1836     /* IMPLEMENT ME - establish which host formats are available */
1837     PaSampleFormat nativeInputFormats = paInt16;
1838     //PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
1839    
1840     hostInputSampleFormat =
1841     PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
1842     }
1843     else
1844     {
1845     hostInputSampleFormat = 0;
1846     }
1847    
1848     if( outputParameters )
1849     {
1850     /* IMPLEMENT ME - establish which host formats are available */
1851     PaSampleFormat nativeOutputFormats = paInt16;
1852     //PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
1853    
1854     hostOutputSampleFormat =
1855     PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
1856     }
1857     else
1858     {
1859     hostOutputSampleFormat = 0;
1860     }
1861    
1862     result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
1863     inputChannelCount, inputSampleFormat, hostInputSampleFormat,
1864     outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
1865     sampleRate, streamFlags, framesPerBuffer,
1866     framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
1867     /* This next mode is required because DS can split the host buffer when it wraps around. */
1868     paUtilVariableHostBufferSizePartialUsageAllowed,
1869     streamCallback, userData );
1870     if( result != paNoError )
1871     goto error;
1872    
1873     bufferProcessorIsInitialized = 1;
1874    
1875     stream->streamRepresentation.streamInfo.inputLatency =
1876     PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
1877     stream->streamRepresentation.streamInfo.outputLatency =
1878     PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
1879     stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
1880    
1881 william 273
1882     /* DirectSound specific initialization */
1883 william 31 {
1884     HRESULT hr;
1885     int bytesPerDirectSoundInputBuffer, bytesPerDirectSoundOutputBuffer;
1886     int userLatencyFrames;
1887     int minLatencyFrames;
1888     unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
1889 william 273
1890 william 31 stream->timerID = 0;
1891    
1892     stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
1893     if( stream->processingCompleted == NULL )
1894     {
1895     result = paInsufficientMemory;
1896     goto error;
1897     }
1898    
1899     /* Get system minimum latency. */
1900     minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
1901    
1902     /* Let user override latency by passing latency parameter. */
1903     userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames)
1904     ? suggestedInputLatencyFrames
1905     : suggestedOutputLatencyFrames;
1906     if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames;
1907    
1908     /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */
1909     if( framesPerBuffer == paFramesPerBufferUnspecified )
1910     {
1911     /* App support variable framesPerBuffer */
1912     stream->framesPerDSBuffer = minLatencyFrames;
1913    
1914     stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate;
1915     }
1916     else
1917     {
1918     /* Round up to number of buffers needed to guarantee that latency. */
1919     int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer;
1920     if( numUserBuffers < 1 ) numUserBuffers = 1;
1921     numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */
1922     stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers;
1923    
1924     stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate;
1925     }
1926    
1927     {
1928     /** @todo REVIEW: this calculation seems incorrect to me - rossb. */
1929     int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate);
1930     PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
1931     }
1932    
1933     /* set up i/o parameters */
1934    
1935     /* ------------------ OUTPUT */
1936     if( outputParameters )
1937     {
1938     LARGE_INTEGER counterFrequency;
1939    
1940     /*
1941     PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
1942     DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
1943     */
1944 william 273
1945 william 31 int bytesPerSample = Pa_GetSampleSize(hostOutputSampleFormat);
1946     bytesPerDirectSoundOutputBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * bytesPerSample;
1947     if( bytesPerDirectSoundOutputBuffer < DSBSIZE_MIN )
1948     {
1949     result = paBufferTooSmall;
1950     goto error;
1951     }
1952     else if( bytesPerDirectSoundOutputBuffer > DSBSIZE_MAX )
1953     {
1954     result = paBufferTooBig;
1955     goto error;
1956     }
1957    
1958     /* Calculate value used in latency calculation to avoid real-time divides. */
1959     stream->secondsPerHostByte = 1.0 /
1960     (stream->bufferProcessor.bytesPerHostOutputSample *
1961     outputChannelCount * sampleRate);
1962    
1963     stream->outputBufferSizeBytes = bytesPerDirectSoundOutputBuffer;
1964     stream->outputIsRunning = FALSE;
1965     stream->outputUnderflowCount = 0;
1966     stream->bytesPerOutputFrame = outputParameters->channelCount * bytesPerSample;
1967    
1968     /* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
1969     if( QueryPerformanceFrequency( &counterFrequency ) )
1970     {
1971     stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->framesPerDSBuffer) / integerSampleRate;
1972     }
1973     else
1974     {
1975     stream->perfCounterTicksPerBuffer.QuadPart = 0;
1976     }
1977     }
1978    
1979     /* ------------------ INPUT */
1980     if( inputParameters )
1981     {
1982     /*
1983     PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
1984     DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
1985     */
1986 william 273
1987 william 31 int bytesPerSample = Pa_GetSampleSize(hostInputSampleFormat);
1988     bytesPerDirectSoundInputBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * bytesPerSample;
1989     if( bytesPerDirectSoundInputBuffer < DSBSIZE_MIN )
1990     {
1991     result = paBufferTooSmall;
1992     goto error;
1993     }
1994     else if( bytesPerDirectSoundInputBuffer > DSBSIZE_MAX )
1995     {
1996     result = paBufferTooBig;
1997     goto error;
1998     }
1999    
2000     stream->bytesPerInputFrame = inputParameters->channelCount * bytesPerSample;
2001    
2002     stream->inputSize = bytesPerDirectSoundInputBuffer;
2003     }
2004    
2005     /* open/create the DirectSound buffers */
2006    
2007     /* interface ptrs should be zeroed when stream is zeroed. */
2008     assert( stream->pDirectSoundCapture == NULL );
2009     assert( stream->pDirectSoundInputBuffer == NULL );
2010     assert( stream->pDirectSound == NULL );
2011     assert( stream->pDirectSoundPrimaryBuffer == NULL );
2012     assert( stream->pDirectSoundOutputBuffer == NULL );
2013 william 273
2014 william 31
2015     if( inputParameters && outputParameters )
2016     {
2017     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2018     /* try to use the full-duplex DX8 API to create the buffers.
2019     if that fails we fall back to the half-duplex API below */
2020    
2021     hr = InitFullDuplexInputOutputBuffers( stream,
2022     (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
2023     hostInputSampleFormat,
2024     (WORD)inputParameters->channelCount, bytesPerDirectSoundInputBuffer,
2025     inputChannelMask,
2026     (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
2027     hostOutputSampleFormat,
2028     (WORD)outputParameters->channelCount, bytesPerDirectSoundOutputBuffer,
2029     outputChannelMask,
2030     integerSampleRate
2031     );
2032     DBUG(("InitFullDuplexInputOutputBuffers() returns %x\n", hr));
2033 william 273 /* ignore any error returned by InitFullDuplexInputOutputBuffers.
2034 william 31 we retry opening the buffers below */
2035     #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
2036     }
2037    
2038 william 273 /* create half duplex buffers. also used for full-duplex streams which didn't
2039 william 31 succeed when using the full duplex API. that could happen because
2040 william 273 DX8 or greater isnt installed, the i/o devices aren't the same
2041 william 31 physical device. etc.
2042     */
2043    
2044     if( outputParameters && !stream->pDirectSoundOutputBuffer )
2045     {
2046     hr = InitOutputBuffer( stream,
2047     (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
2048     hostOutputSampleFormat,
2049     integerSampleRate,
2050     (WORD)outputParameters->channelCount, bytesPerDirectSoundOutputBuffer,
2051     outputChannelMask );
2052     DBUG(("InitOutputBuffer() returns %x\n", hr));
2053     if( hr != DS_OK )
2054     {
2055     result = paUnanticipatedHostError;
2056     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2057     goto error;
2058     }
2059     }
2060    
2061     if( inputParameters && !stream->pDirectSoundInputBuffer )
2062     {
2063     hr = InitInputBuffer( stream,
2064     (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
2065     hostInputSampleFormat,
2066     integerSampleRate,
2067     (WORD)inputParameters->channelCount, bytesPerDirectSoundInputBuffer,
2068     inputChannelMask );
2069     DBUG(("InitInputBuffer() returns %x\n", hr));
2070     if( hr != DS_OK )
2071     {
2072     ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
2073     result = paUnanticipatedHostError;
2074     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2075     goto error;
2076     }
2077     }
2078     }
2079    
2080     *s = (PaStream*)stream;
2081    
2082     return result;
2083    
2084     error:
2085     if( stream )
2086     {
2087     if( stream->processingCompleted != NULL )
2088     CloseHandle( stream->processingCompleted );
2089    
2090     if( stream->pDirectSoundOutputBuffer )
2091     {
2092     IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2093     IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
2094     stream->pDirectSoundOutputBuffer = NULL;
2095     }
2096    
2097     if( stream->pDirectSoundPrimaryBuffer )
2098     {
2099     IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
2100     stream->pDirectSoundPrimaryBuffer = NULL;
2101     }
2102    
2103     if( stream->pDirectSoundInputBuffer )
2104     {
2105     IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2106     IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
2107     stream->pDirectSoundInputBuffer = NULL;
2108     }
2109    
2110     if( stream->pDirectSoundCapture )
2111     {
2112     IDirectSoundCapture_Release( stream->pDirectSoundCapture );
2113     stream->pDirectSoundCapture = NULL;
2114     }
2115    
2116     if( stream->pDirectSound )
2117     {
2118     IDirectSound_Release( stream->pDirectSound );
2119     stream->pDirectSound = NULL;
2120     }
2121    
2122     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2123     if( stream->pDirectSoundFullDuplex8 )
2124     {
2125     IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
2126     stream->pDirectSoundFullDuplex8 = NULL;
2127     }
2128     #endif
2129     if( bufferProcessorIsInitialized )
2130     PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2131    
2132     if( streamRepresentationIsInitialized )
2133     PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2134    
2135     PaUtil_FreeMemory( stream );
2136     }
2137    
2138     return result;
2139     }
2140    
2141    
2142     /************************************************************************************
2143     * Determine how much space can be safely written to in DS buffer.
2144     * Detect underflows and overflows.
2145     * Does not allow writing into safety gap maintained by DirectSound.
2146     */
2147     static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
2148     {
2149     HRESULT hr;
2150     DWORD playCursor;
2151     DWORD writeCursor;
2152     long numBytesEmpty;
2153     long playWriteGap;
2154     // Query to see how much room is in buffer.
2155     hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
2156     &playCursor, &writeCursor );
2157     if( hr != DS_OK )
2158     {
2159     return hr;
2160     }
2161    
2162     // Determine size of gap between playIndex and WriteIndex that we cannot write into.
2163     playWriteGap = writeCursor - playCursor;
2164     if( playWriteGap < 0 ) playWriteGap += stream->outputBufferSizeBytes; // unwrap
2165    
2166     /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
2167     /* Attempt to detect playCursor wrap-around and correct it. */
2168     if( stream->outputIsRunning && (stream->perfCounterTicksPerBuffer.QuadPart != 0) )
2169     {
2170     /* How much time has elapsed since last check. */
2171     LARGE_INTEGER currentTime;
2172     LARGE_INTEGER elapsedTime;
2173     long bytesPlayed;
2174     long bytesExpected;
2175     long buffersWrapped;
2176    
2177     QueryPerformanceCounter( &currentTime );
2178     elapsedTime.QuadPart = currentTime.QuadPart - stream->previousPlayTime.QuadPart;
2179     stream->previousPlayTime = currentTime;
2180    
2181     /* How many bytes does DirectSound say have been played. */
2182     bytesPlayed = playCursor - stream->previousPlayCursor;
2183     if( bytesPlayed < 0 ) bytesPlayed += stream->outputBufferSizeBytes; // unwrap
2184     stream->previousPlayCursor = playCursor;
2185    
2186     /* Calculate how many bytes we would have expected to been played by now. */
2187     bytesExpected = (long) ((elapsedTime.QuadPart * stream->outputBufferSizeBytes) / stream->perfCounterTicksPerBuffer.QuadPart);
2188     buffersWrapped = (bytesExpected - bytesPlayed) / stream->outputBufferSizeBytes;
2189     if( buffersWrapped > 0 )
2190     {
2191     playCursor += (buffersWrapped * stream->outputBufferSizeBytes);
2192     bytesPlayed += (buffersWrapped * stream->outputBufferSizeBytes);
2193     }
2194     }
2195     numBytesEmpty = playCursor - stream->outputBufferWriteOffsetBytes;
2196     if( numBytesEmpty < 0 ) numBytesEmpty += stream->outputBufferSizeBytes; // unwrap offset
2197    
2198     /* Have we underflowed? */
2199     if( numBytesEmpty > (stream->outputBufferSizeBytes - playWriteGap) )
2200     {
2201     if( stream->outputIsRunning )
2202     {
2203     stream->outputUnderflowCount += 1;
2204     }
2205    
2206     /*
2207     From MSDN:
2208 william 273 The write cursor indicates the position at which it is safe
2209 william 31 to write new data to the buffer. The write cursor always leads the
2210     play cursor, typically by about 15 milliseconds' worth of audio
2211     data.
2212 william 273 It is always safe to change data that is behind the position
2213 william 31 indicated by the lpdwCurrentPlayCursor parameter.
2214     */
2215    
2216     stream->outputBufferWriteOffsetBytes = writeCursor;
2217     numBytesEmpty = stream->outputBufferSizeBytes - playWriteGap;
2218     }
2219     *bytesEmpty = numBytesEmpty;
2220     return hr;
2221     }
2222    
2223     /***********************************************************************************/
2224     static int TimeSlice( PaWinDsStream *stream )
2225     {
2226     long numFrames = 0;
2227     long bytesEmpty = 0;
2228     long bytesFilled = 0;
2229     long bytesToXfer = 0;
2230     long framesToXfer = 0;
2231     long numInFramesReady = 0;
2232     long numOutFramesReady = 0;
2233     long bytesProcessed;
2234     HRESULT hresult;
2235     double outputLatency = 0;
2236     PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
2237 william 273
2238 william 31 /* Input */
2239     LPBYTE lpInBuf1 = NULL;
2240     LPBYTE lpInBuf2 = NULL;
2241     DWORD dwInSize1 = 0;
2242     DWORD dwInSize2 = 0;
2243     /* Output */
2244     LPBYTE lpOutBuf1 = NULL;
2245     LPBYTE lpOutBuf2 = NULL;
2246     DWORD dwOutSize1 = 0;
2247     DWORD dwOutSize2 = 0;
2248    
2249     /* How much input data is available? */
2250     if( stream->bufferProcessor.inputChannelCount > 0 )
2251     {
2252     HRESULT hr;
2253     DWORD capturePos;
2254     DWORD readPos;
2255     long filled = 0;
2256     // Query to see how much data is in buffer.
2257     // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
2258     // so let's pass a pointer just to be safe.
2259     hr = IDirectSoundCaptureBuffer_GetCurrentPosition( stream->pDirectSoundInputBuffer, &capturePos, &readPos );
2260     if( hr == DS_OK )
2261     {
2262     filled = readPos - stream->readOffset;
2263     if( filled < 0 ) filled += stream->inputSize; // unwrap offset
2264     bytesFilled = filled;
2265     }
2266     // FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
2267    
2268 william 273 framesToXfer = numInFramesReady = bytesFilled / stream->bytesPerInputFrame;
2269 william 31 outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte; // FIXME: this doesn't look right. we're calculating output latency in input branch. also secondsPerHostByte is only initialized for the output stream
2270    
2271     /** @todo Check for overflow */
2272     }
2273    
2274     /* How much output room is available? */
2275     if( stream->bufferProcessor.outputChannelCount > 0 )
2276     {
2277     UINT previousUnderflowCount = stream->outputUnderflowCount;
2278     QueryOutputSpace( stream, &bytesEmpty );
2279     framesToXfer = numOutFramesReady = bytesEmpty / stream->bytesPerOutputFrame;
2280    
2281     /* Check for underflow */
2282     if( stream->outputUnderflowCount != previousUnderflowCount )
2283     stream->callbackFlags |= paOutputUnderflow;
2284     }
2285    
2286     if( (numInFramesReady > 0) && (numOutFramesReady > 0) )
2287     {
2288     framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
2289     }
2290    
2291     if( framesToXfer > 0 )
2292     {
2293    
2294     PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
2295    
2296     /* The outputBufferDacTime parameter should indicates the time at which
2297     the first sample of the output buffer is heard at the DACs. */
2298     timeInfo.currentTime = PaUtil_GetTime();
2299     timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; // FIXME: QueryOutputSpace gets the playback position, we could use that (?)
2300    
2301    
2302     PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
2303     stream->callbackFlags = 0;
2304 william 273
2305 william 31 /* Input */
2306     if( stream->bufferProcessor.inputChannelCount > 0 )
2307     {
2308     bytesToXfer = framesToXfer * stream->bytesPerInputFrame;
2309     hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer,
2310     stream->readOffset, bytesToXfer,
2311     (void **) &lpInBuf1, &dwInSize1,
2312     (void **) &lpInBuf2, &dwInSize2, 0);
2313     if (hresult != DS_OK)
2314     {
2315     ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
2316     /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
2317     PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
2318     stream->callbackResult = paComplete;
2319     goto error2;
2320     }
2321    
2322     numFrames = dwInSize1 / stream->bytesPerInputFrame;
2323     PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
2324     PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
2325     /* Is input split into two regions. */
2326     if( dwInSize2 > 0 )
2327     {
2328     numFrames = dwInSize2 / stream->bytesPerInputFrame;
2329     PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
2330     PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
2331     }
2332     }
2333    
2334     /* Output */
2335     if( stream->bufferProcessor.outputChannelCount > 0 )
2336     {
2337     bytesToXfer = framesToXfer * stream->bytesPerOutputFrame;
2338     hresult = IDirectSoundBuffer_Lock ( stream->pDirectSoundOutputBuffer,
2339     stream->outputBufferWriteOffsetBytes, bytesToXfer,
2340     (void **) &lpOutBuf1, &dwOutSize1,
2341     (void **) &lpOutBuf2, &dwOutSize2, 0);
2342     if (hresult != DS_OK)
2343     {
2344     ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
2345     /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
2346     PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
2347     stream->callbackResult = paComplete;
2348     goto error1;
2349     }
2350    
2351     numFrames = dwOutSize1 / stream->bytesPerOutputFrame;
2352     PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
2353     PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
2354    
2355     /* Is output split into two regions. */
2356     if( dwOutSize2 > 0 )
2357     {
2358     numFrames = dwOutSize2 / stream->bytesPerOutputFrame;
2359     PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
2360     PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
2361     }
2362     }
2363    
2364     numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &stream->callbackResult );
2365     stream->framesWritten += numFrames;
2366 william 273
2367 william 31 if( stream->bufferProcessor.outputChannelCount > 0 )
2368     {
2369     /* FIXME: an underflow could happen here */
2370    
2371     /* Update our buffer offset and unlock sound buffer */
2372     bytesProcessed = numFrames * stream->bytesPerOutputFrame;
2373     stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes;
2374     IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
2375     }
2376    
2377     error1:
2378     if( stream->bufferProcessor.inputChannelCount > 0 )
2379     {
2380     /* FIXME: an overflow could happen here */
2381    
2382     /* Update our buffer offset and unlock sound buffer */
2383     bytesProcessed = numFrames * stream->bytesPerInputFrame;
2384     stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputSize;
2385     IDirectSoundCaptureBuffer_Unlock( stream->pDirectSoundInputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
2386     }
2387     error2:
2388    
2389 william 273 PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
2390 william 31 }
2391    
2392     if( stream->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
2393     {
2394     /* don't return completed until the buffer processor has been drained */
2395     return paContinue;
2396     }
2397     else
2398     {
2399     return stream->callbackResult;
2400     }
2401     }
2402     /*******************************************************************/
2403    
2404     static HRESULT ZeroAvailableOutputSpace( PaWinDsStream *stream )
2405     {
2406     HRESULT hr;
2407     LPBYTE lpbuf1 = NULL;
2408     LPBYTE lpbuf2 = NULL;
2409     DWORD dwsize1 = 0;
2410     DWORD dwsize2 = 0;
2411     long bytesEmpty;
2412     hr = QueryOutputSpace( stream, &bytesEmpty );
2413     if (hr != DS_OK) return hr;
2414     if( bytesEmpty == 0 ) return DS_OK;
2415     // Lock free space in the DS
2416     hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, stream->outputBufferWriteOffsetBytes,
2417     bytesEmpty, (void **) &lpbuf1, &dwsize1,
2418     (void **) &lpbuf2, &dwsize2, 0);
2419     if (hr == DS_OK)
2420     {
2421     // Copy the buffer into the DS
2422     ZeroMemory(lpbuf1, dwsize1);
2423     if(lpbuf2 != NULL)
2424     {
2425     ZeroMemory(lpbuf2, dwsize2);
2426     }
2427     // Update our buffer offset and unlock sound buffer
2428     stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + dwsize1 + dwsize2) % stream->outputBufferSizeBytes;
2429     IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
2430    
2431     stream->finalZeroBytesWritten += dwsize1 + dwsize2;
2432     }
2433     return hr;
2434     }
2435    
2436    
2437     static void CALLBACK TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
2438     {
2439     PaWinDsStream *stream;
2440     int isFinished = 0;
2441    
2442     /* suppress unused variable warnings */
2443     (void) uID;
2444     (void) uMsg;
2445     (void) dw1;
2446     (void) dw2;
2447 william 273
2448 william 31 stream = (PaWinDsStream *) dwUser;
2449     if( stream == NULL ) return;
2450    
2451     if( stream->isActive )
2452     {
2453     if( stream->abortProcessing )
2454     {
2455     isFinished = 1;
2456     }
2457     else if( stream->stopProcessing )
2458     {
2459     if( stream->bufferProcessor.outputChannelCount > 0 )
2460     {
2461     ZeroAvailableOutputSpace( stream );
2462     if( stream->finalZeroBytesWritten >= stream->outputBufferSizeBytes )
2463     {
2464     /* once we've flushed the whole output buffer with zeros we know all data has been played */
2465     isFinished = 1;
2466     }
2467     }
2468     else
2469     {
2470     isFinished = 1;
2471     }
2472     }
2473     else
2474     {
2475     int callbackResult = TimeSlice( stream );
2476     if( callbackResult != paContinue )
2477     {
2478 william 273 /* FIXME implement handling of paComplete and paAbort if possible
2479     At the moment this should behave as if paComplete was called and
2480 william 31 flush the buffer.
2481     */
2482    
2483     stream->stopProcessing = 1;
2484     }
2485     }
2486    
2487     if( isFinished )
2488     {
2489     if( stream->streamRepresentation.streamFinishedCallback != 0 )
2490     stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
2491    
2492     stream->isActive = 0; /* don't set this until the stream really is inactive */
2493     SetEvent( stream->processingCompleted );
2494     }
2495     }
2496     }
2497    
2498     /***********************************************************************************
2499     When CloseStream() is called, the multi-api layer ensures that
2500     the stream has already been stopped or aborted.
2501     */
2502     static PaError CloseStream( PaStream* s )
2503     {
2504     PaError result = paNoError;
2505     PaWinDsStream *stream = (PaWinDsStream*)s;
2506    
2507     CloseHandle( stream->processingCompleted );
2508    
2509     // Cleanup the sound buffers
2510     if( stream->pDirectSoundOutputBuffer )
2511     {
2512     IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2513     IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
2514     stream->pDirectSoundOutputBuffer = NULL;
2515     }
2516    
2517     if( stream->pDirectSoundPrimaryBuffer )
2518     {
2519     IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
2520     stream->pDirectSoundPrimaryBuffer = NULL;
2521     }
2522    
2523     if( stream->pDirectSoundInputBuffer )
2524     {
2525     IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2526     IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
2527     stream->pDirectSoundInputBuffer = NULL;
2528     }
2529    
2530     if( stream->pDirectSoundCapture )
2531     {
2532     IDirectSoundCapture_Release( stream->pDirectSoundCapture );
2533     stream->pDirectSoundCapture = NULL;
2534     }
2535    
2536     if( stream->pDirectSound )
2537     {
2538     IDirectSound_Release( stream->pDirectSound );
2539     stream->pDirectSound = NULL;
2540     }
2541    
2542     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2543     if( stream->pDirectSoundFullDuplex8 )
2544     {
2545     IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
2546     stream->pDirectSoundFullDuplex8 = NULL;
2547     }
2548     #endif
2549    
2550     PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2551     PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2552     PaUtil_FreeMemory( stream );
2553    
2554     return result;
2555     }
2556    
2557     /***********************************************************************************/
2558     static HRESULT ClearOutputBuffer( PaWinDsStream *stream )
2559     {
2560     PaError result = paNoError;
2561     unsigned char* pDSBuffData;
2562     DWORD dwDataLen;
2563     HRESULT hr;
2564    
2565     hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 );
2566     DBUG(("PaHost_ClearOutputBuffer: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr));
2567     if( hr != DS_OK )
2568     return hr;
2569    
2570     // Lock the DS buffer
2571     if ((hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData,
2572     &dwDataLen, NULL, 0, 0)) != DS_OK )
2573     return hr;
2574    
2575     // Zero the DS buffer
2576     ZeroMemory(pDSBuffData, dwDataLen);
2577     // Unlock the DS buffer
2578     if ((hr = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK)
2579     return hr;
2580 william 273
2581 william 31 // Let DSound set the starting write position because if we set it to zero, it looks like the
2582     // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
2583     if ((hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
2584     &stream->previousPlayCursor, &stream->outputBufferWriteOffsetBytes )) != DS_OK)
2585     return hr;
2586    
2587     /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
2588    
2589     return DS_OK;
2590     }
2591    
2592     static PaError StartStream( PaStream *s )
2593     {
2594     PaError result = paNoError;
2595     PaWinDsStream *stream = (PaWinDsStream*)s;
2596     HRESULT hr;
2597 william 273
2598 william 31 stream->callbackResult = paContinue;
2599     PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
2600 william 273
2601 william 31 ResetEvent( stream->processingCompleted );
2602    
2603     if( stream->bufferProcessor.inputChannelCount > 0 )
2604     {
2605     // Start the buffer capture
2606     if( stream->pDirectSoundInputBuffer != NULL ) // FIXME: not sure this check is necessary
2607     {
2608     hr = IDirectSoundCaptureBuffer_Start( stream->pDirectSoundInputBuffer, DSCBSTART_LOOPING );
2609     }
2610    
2611     DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
2612     if( hr != DS_OK )
2613     {
2614     result = paUnanticipatedHostError;
2615     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2616     goto error;
2617     }
2618     }
2619    
2620     stream->framesWritten = 0;
2621     stream->callbackFlags = 0;
2622    
2623     stream->abortProcessing = 0;
2624     stream->stopProcessing = 0;
2625     stream->isActive = 1;
2626    
2627     if( stream->bufferProcessor.outputChannelCount > 0 )
2628     {
2629     QueryPerformanceCounter( &stream->previousPlayTime );
2630     stream->finalZeroBytesWritten = 0;
2631    
2632     hr = ClearOutputBuffer( stream );
2633     if( hr != DS_OK )
2634     {
2635     result = paUnanticipatedHostError;
2636     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2637     goto error;
2638     }
2639    
2640     if( stream->streamRepresentation.streamCallback && (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback) )
2641     {
2642     stream->callbackFlags = paPrimingOutput;
2643    
2644     TimeSlice( stream );
2645     /* we ignore the return value from TimeSlice here and start the stream as usual.
2646     The first timer callback will detect if the callback has completed. */
2647    
2648     stream->callbackFlags = 0;
2649     }
2650    
2651     // Start the buffer playback in a loop.
2652     if( stream->pDirectSoundOutputBuffer != NULL ) // FIXME: not sure this needs to be checked here
2653     {
2654     hr = IDirectSoundBuffer_Play( stream->pDirectSoundOutputBuffer, 0, 0, DSBPLAY_LOOPING );
2655     DBUG(("PaHost_StartOutput: IDirectSoundBuffer_Play returned = 0x%X.\n", hr));
2656     if( hr != DS_OK )
2657     {
2658     result = paUnanticipatedHostError;
2659     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2660     goto error;
2661     }
2662     stream->outputIsRunning = TRUE;
2663     }
2664     }
2665    
2666     if( stream->streamRepresentation.streamCallback )
2667     {
2668     /* Create timer that will wake us up so we can fill the DSound buffer. */
2669     int resolution;
2670     int framesPerWakeup = stream->framesPerDSBuffer / 4;
2671     int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
2672     if( msecPerWakeup < 10 ) msecPerWakeup = 10;
2673     else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
2674     resolution = msecPerWakeup/4;
2675     stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) TimerCallback,
2676     (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
2677 william 273
2678 william 31 if( stream->timerID == 0 )
2679     {
2680     stream->isActive = 0;
2681     result = paUnanticipatedHostError;
2682     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2683     goto error;
2684     }
2685     }
2686    
2687     stream->isStarted = TRUE;
2688    
2689     error:
2690     return result;
2691     }
2692    
2693    
2694     /***********************************************************************************/
2695     static PaError StopStream( PaStream *s )
2696     {
2697     PaError result = paNoError;
2698     PaWinDsStream *stream = (PaWinDsStream*)s;
2699     HRESULT hr;
2700     int timeoutMsec;
2701    
2702     if( stream->streamRepresentation.streamCallback )
2703     {
2704     stream->stopProcessing = 1;
2705    
2706     /* Set timeout at 4 times maximum time we might wait. */
2707     timeoutMsec = (int) (4 * MSEC_PER_SECOND * (stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate));
2708    
2709     WaitForSingleObject( stream->processingCompleted, timeoutMsec );
2710     }
2711    
2712     if( stream->timerID != 0 )
2713     {
2714     timeKillEvent(stream->timerID); /* Stop callback timer. */
2715     stream->timerID = 0;
2716     }
2717    
2718    
2719     if( stream->bufferProcessor.outputChannelCount > 0 )
2720     {
2721     // Stop the buffer playback
2722     if( stream->pDirectSoundOutputBuffer != NULL )
2723     {
2724     stream->outputIsRunning = FALSE;
2725     // FIXME: what happens if IDirectSoundBuffer_Stop returns an error?
2726     hr = IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2727    
2728     if( stream->pDirectSoundPrimaryBuffer )
2729     IDirectSoundBuffer_Stop( stream->pDirectSoundPrimaryBuffer ); /* FIXME we never started the primary buffer so I'm not sure we need to stop it */
2730     }
2731     }
2732    
2733     if( stream->bufferProcessor.inputChannelCount > 0 )
2734     {
2735     // Stop the buffer capture
2736     if( stream->pDirectSoundInputBuffer != NULL )
2737     {
2738     // FIXME: what happens if IDirectSoundCaptureBuffer_Stop returns an error?
2739     hr = IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2740     }
2741     }
2742    
2743     stream->isStarted = FALSE;
2744    
2745     return result;
2746     }
2747    
2748    
2749     /***********************************************************************************/
2750     static PaError AbortStream( PaStream *s )
2751     {
2752     PaWinDsStream *stream = (PaWinDsStream*)s;
2753    
2754     stream->abortProcessing = 1;
2755     return StopStream( s );
2756     }
2757    
2758    
2759     /***********************************************************************************/
2760     static PaError IsStreamStopped( PaStream *s )
2761     {
2762     PaWinDsStream *stream = (PaWinDsStream*)s;
2763    
2764     return !stream->isStarted;
2765     }
2766    
2767    
2768     /***********************************************************************************/
2769     static PaError IsStreamActive( PaStream *s )
2770     {
2771     PaWinDsStream *stream = (PaWinDsStream*)s;
2772    
2773     return stream->isActive;
2774     }
2775    
2776     /***********************************************************************************/
2777     static PaTime GetStreamTime( PaStream *s )
2778     {
2779     /* suppress unused variable warnings */
2780     (void) s;
2781    
2782     return PaUtil_GetTime();
2783     }
2784    
2785    
2786     /***********************************************************************************/
2787     static double GetStreamCpuLoad( PaStream* s )
2788     {
2789     PaWinDsStream *stream = (PaWinDsStream*)s;
2790    
2791     return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
2792     }
2793    
2794    
2795     /***********************************************************************************
2796     As separate stream interfaces are used for blocking and callback
2797     streams, the following functions can be guaranteed to only be called
2798     for blocking streams.
2799     */
2800    
2801     static PaError ReadStream( PaStream* s,
2802     void *buffer,
2803     unsigned long frames )
2804     {
2805     PaWinDsStream *stream = (PaWinDsStream*)s;
2806    
2807     /* suppress unused variable warnings */
2808     (void) buffer;
2809     (void) frames;
2810     (void) stream;
2811    
2812     /* IMPLEMENT ME, see portaudio.h for required behavior*/
2813    
2814     return paNoError;
2815     }
2816    
2817    
2818     /***********************************************************************************/
2819     static PaError WriteStream( PaStream* s,
2820     const void *buffer,
2821     unsigned long frames )
2822     {
2823     PaWinDsStream *stream = (PaWinDsStream*)s;
2824    
2825     /* suppress unused variable warnings */
2826     (void) buffer;
2827     (void) frames;
2828     (void) stream;
2829    
2830     /* IMPLEMENT ME, see portaudio.h for required behavior*/
2831    
2832     return paNoError;
2833     }
2834    
2835    
2836     /***********************************************************************************/
2837     static signed long GetStreamReadAvailable( PaStream* s )
2838     {
2839     PaWinDsStream *stream = (PaWinDsStream*)s;
2840    
2841     /* suppress unused variable warnings */
2842     (void) stream;
2843    
2844     /* IMPLEMENT ME, see portaudio.h for required behavior*/
2845    
2846     return 0;
2847     }
2848    
2849    
2850     /***********************************************************************************/
2851     static signed long GetStreamWriteAvailable( PaStream* s )
2852     {
2853     PaWinDsStream *stream = (PaWinDsStream*)s;
2854    
2855     /* suppress unused variable warnings */
2856     (void) stream;
2857 william 273
2858 william 31 /* IMPLEMENT ME, see portaudio.h for required behavior*/
2859    
2860     return 0;
2861     }
2862    
2863    
2864    

  ViewVC Help
Powered by ViewVC 1.1.22