/[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 31 - (hide annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (9 years, 11 months ago) by william
File MIME type: text/plain
File size: 109164 byte(s)
committing r3113 initial commit again...
1 william 31 /*
2     * $Id: pa_win_ds.c 1450 2010-02-03 00:28:29Z rossb $
3     * Portable Audio I/O Library DirectSound implementation
4     *
5     * Authors: Phil Burk, Robert Marsanyi & Ross Bencina
6     * Based on the Open Source API proposed by Ross Bencina
7     * Copyright (c) 1999-2007 Ross Bencina, Phil Burk, Robert Marsanyi
8     *
9     * Permission is hereby granted, free of charge, to any person obtaining
10     * a copy of this software and associated documentation files
11     * (the "Software"), to deal in the Software without restriction,
12     * including without limitation the rights to use, copy, modify, merge,
13     * publish, distribute, sublicense, and/or sell copies of the Software,
14     * and to permit persons to whom the Software is furnished to do so,
15     * subject to the following conditions:
16     *
17     * The above copyright notice and this permission notice shall be
18     * included in all copies or substantial portions of the Software.
19     *
20     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21     * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23     * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
24     * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
25     * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26     * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27     */
28    
29     /*
30     * The text above constitutes the entire PortAudio license; however,
31     * the PortAudio community also makes the following non-binding requests:
32     *
33     * Any person wishing to distribute modifications to the Software is
34     * requested to send the modifications to the original developer so that
35     * they can be incorporated into the canonical version. It is also
36     * requested that these non-binding requests be included along with the
37     * license above.
38     */
39    
40     /** @file
41     @ingroup hostapi_src
42    
43     @todo implement paInputOverflow callback status flag
44    
45     @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     UINT previousPlayCursor;
248     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    
260     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    
271     /* 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    
288     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    
345     return result;
346     }
347    
348     static PaError ExpandDSDeviceNameAndGUIDVector( DSDeviceNameAndGUIDVector *guidVector )
349     {
350     PaError result = paNoError;
351     DSDeviceNameAndGUID *newItems;
352     int i;
353    
354     /* 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     }
383    
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     ** Collect preliminary device information during DirectSound enumeration
407     */
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    
428     /* 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    
438     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    
454     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     deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
483     (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     deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
496     (char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
497     break;
498     }
499     }
500     }
501    
502     return TRUE;
503     }
504    
505    
506     static GUID pawin_CLSID_DirectSoundPrivate =
507     { 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
508    
509     static GUID pawin_DSPROPSETID_DirectSoundDevice =
510     { 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
511    
512     static GUID pawin_IID_IKsPropertySet =
513     { 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     the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
521     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     net from people who had the same problem with the first method, so I think the method used here is
524     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     The mechanism we use works by registering an enumeration callback which is called for
528     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     Note that we could have used this enumeration callback to perform the original
531     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    
542     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    
546     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    
562     IKsPropertySet_Release( pPropertySet );
563     }
564     pClassFactory->lpVtbl->Release( pClassFactory );
565     }
566    
567     /*
568     The following code fragment, which I chose not to use, queries for the
569     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     Property.DeviceId = *lpGUID;
577    
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     /*
598     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    
642     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    
673     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     memset( &caps, 0, sizeof(caps) );
699     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     returning an error.
730     */
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     /* Guess channels count from speaker configuration. We do it only when
743     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     case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
763     #ifndef DSSPEAKER_5POINT1_SURROUND
764     #define DSSPEAKER_5POINT1_SURROUND 0x00000009
765     #endif
766     case DSSPEAKER_5POINT1_SURROUND: count = 6; break;
767     }
768     if( count )
769     {
770     deviceInfo->maxOutputChannels = count;
771     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
772     }
773     }
774     }
775    
776     #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
777     if( pnpInterface )
778     {
779     int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 0 );
780     if( count > 0 )
781     {
782     deviceInfo->maxOutputChannels = count;
783     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
784     }
785     }
786     #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
787    
788     deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
789     deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
790     deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
791     deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
792    
793     /* initialize defaultSampleRate */
794    
795     if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
796     {
797     /* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
798     deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
799    
800     for( i = 0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
801     {
802     if( defaultSampleRateSearchOrder_[i] >= caps.dwMinSecondarySampleRate
803     && defaultSampleRateSearchOrder_[i] <= caps.dwMaxSecondarySampleRate )
804     {
805     deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[i];
806     break;
807     }
808     }
809     }
810     else if( caps.dwMinSecondarySampleRate == caps.dwMaxSecondarySampleRate )
811     {
812     if( caps.dwMinSecondarySampleRate == 0 )
813     {
814     /*
815     ** On my Thinkpad 380Z, DirectSoundV6 returns min-max=0 !!
816     ** But it supports continuous sampling.
817     ** So fake range of rates, and hope it really supports it.
818     */
819     deviceInfo->defaultSampleRate = 44100.0f;
820    
821     DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
822     }
823     else
824     {
825     deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
826     }
827     }
828     else if( (caps.dwMinSecondarySampleRate < 1000.0) && (caps.dwMaxSecondarySampleRate > 50000.0) )
829     {
830     /* The EWS88MT drivers lie, lie, lie. The say they only support two rates, 100 & 100000.
831     ** But we know that they really support a range of rates!
832     ** So when we see a ridiculous set of rates, assume it is a range.
833     */
834     deviceInfo->defaultSampleRate = 44100.0f;
835     DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
836     }
837     else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
838    
839    
840     //printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
841     // dwFlags | DSCAPS_CONTINUOUSRATE
842     }
843     }
844    
845     IDirectSound_Release( lpDirectSound );
846     }
847    
848     if( deviceOK )
849     {
850     deviceInfo->name = name;
851    
852     if( lpGUID == NULL )
853     hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
854    
855     hostApi->info.deviceCount++;
856     }
857    
858     return result;
859     }
860    
861    
862     /************************************************************************************
863     ** Extract capabilities from an input device, and add it to the device info list
864     ** if successful. This function assumes that there is enough room in the
865     ** device info list to accomodate all entries.
866     **
867     ** The device will not be added to the device list if any errors are encountered.
868     */
869     static PaError AddInputDeviceInfoFromDirectSoundCapture(
870     PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
871     {
872     PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
873     PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
874     PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
875     HRESULT hr;
876     LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
877     DSCCAPS caps;
878     int deviceOK = TRUE;
879     PaError result = paNoError;
880    
881     /* Copy GUID to the device info structure. Set pointer. */
882     if( lpGUID == NULL )
883     {
884     winDsDeviceInfo->lpGUID = NULL;
885     }
886     else
887     {
888     winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
889     memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
890     }
891    
892     hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
893    
894     /** try using CoCreateInstance because DirectSoundCreate was hanging under
895     some circumstances - note this was probably related to the
896     #define BOOL short bug which has now been fixed
897     @todo delete this comment and the following code once we've ensured
898     there is no bug.
899     */
900     /*
901     hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
902     &IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
903     */
904     if( hr != DS_OK )
905     {
906     DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
907     deviceOK = FALSE;
908     }
909     else
910     {
911     /* Query device characteristics. */
912     memset( &caps, 0, sizeof(caps) );
913     caps.dwSize = sizeof(caps);
914     hr = IDirectSoundCapture_GetCaps( lpDirectSoundCapture, &caps );
915     if( hr != DS_OK )
916     {
917     DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
918     deviceOK = FALSE;
919     }
920     else
921     {
922     #ifndef PA_NO_WMME
923     if( caps.dwFlags & DSCAPS_EMULDRIVER )
924     {
925     /* If WMME supported, then reject Emulated drivers because they are lousy. */
926     deviceOK = FALSE;
927     }
928     #endif
929    
930     if( deviceOK )
931     {
932     deviceInfo->maxInputChannels = caps.dwChannels;
933     winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
934    
935     deviceInfo->maxOutputChannels = 0;
936     winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
937    
938     #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
939     if( pnpInterface )
940     {
941     int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( pnpInterface, /* isInput= */ 1 );
942     if( count > 0 )
943     {
944     deviceInfo->maxInputChannels = count;
945     winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
946     }
947     }
948     #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
949    
950     deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
951     deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
952     deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
953     deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
954    
955     /* constants from a WINE patch by Francois Gouget, see:
956     http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
957    
958     ---
959     Date: Fri, 14 May 2004 10:38:12 +0200 (CEST)
960     From: Francois Gouget <fgouget@ ... .fr>
961     To: Ross Bencina <rbencina@ ... .au>
962     Subject: Re: Permission to use wine 48/96 wave patch in BSD licensed library
963    
964     [snip]
965    
966     I give you permission to use the patch below under the BSD license.
967     http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
968    
969     [snip]
970     */
971     #ifndef WAVE_FORMAT_48M08
972     #define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
973     #define WAVE_FORMAT_48S08 0x00002000 /* 48 kHz, Stereo, 8-bit */
974     #define WAVE_FORMAT_48M16 0x00004000 /* 48 kHz, Mono, 16-bit */
975     #define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
976     #define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
977     #define WAVE_FORMAT_96S08 0x00020000 /* 96 kHz, Stereo, 8-bit */
978     #define WAVE_FORMAT_96M16 0x00040000 /* 96 kHz, Mono, 16-bit */
979     #define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
980     #endif
981    
982     /* defaultSampleRate */
983     if( caps.dwChannels == 2 )
984     {
985     if( caps.dwFormats & WAVE_FORMAT_4S16 )
986     deviceInfo->defaultSampleRate = 44100.0;
987     else if( caps.dwFormats & WAVE_FORMAT_48S16 )
988     deviceInfo->defaultSampleRate = 48000.0;
989     else if( caps.dwFormats & WAVE_FORMAT_2S16 )
990     deviceInfo->defaultSampleRate = 22050.0;
991     else if( caps.dwFormats & WAVE_FORMAT_1S16 )
992     deviceInfo->defaultSampleRate = 11025.0;
993     else if( caps.dwFormats & WAVE_FORMAT_96S16 )
994     deviceInfo->defaultSampleRate = 96000.0;
995     else
996     deviceInfo->defaultSampleRate = 0.;
997     }
998     else if( caps.dwChannels == 1 )
999     {
1000     if( caps.dwFormats & WAVE_FORMAT_4M16 )
1001     deviceInfo->defaultSampleRate = 44100.0;
1002     else if( caps.dwFormats & WAVE_FORMAT_48M16 )
1003     deviceInfo->defaultSampleRate = 48000.0;
1004     else if( caps.dwFormats & WAVE_FORMAT_2M16 )
1005     deviceInfo->defaultSampleRate = 22050.0;
1006     else if( caps.dwFormats & WAVE_FORMAT_1M16 )
1007     deviceInfo->defaultSampleRate = 11025.0;
1008     else if( caps.dwFormats & WAVE_FORMAT_96M16 )
1009     deviceInfo->defaultSampleRate = 96000.0;
1010     else
1011     deviceInfo->defaultSampleRate = 0.;
1012     }
1013     else deviceInfo->defaultSampleRate = 0.;
1014     }
1015     }
1016    
1017     IDirectSoundCapture_Release( lpDirectSoundCapture );
1018     }
1019    
1020     if( deviceOK )
1021     {
1022     deviceInfo->name = name;
1023    
1024     if( lpGUID == NULL )
1025     hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
1026    
1027     hostApi->info.deviceCount++;
1028     }
1029    
1030     return result;
1031     }
1032    
1033    
1034     /***********************************************************************************/
1035     PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1036     {
1037     PaError result = paNoError;
1038     int i, deviceCount;
1039     PaWinDsHostApiRepresentation *winDsHostApi;
1040     DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
1041    
1042     PaWinDsDeviceInfo *deviceInfoArray;
1043     char comWasInitialized = 0;
1044    
1045     /*
1046     If COM is already initialized CoInitialize will either return
1047     FALSE, or RPC_E_CHANGED_MODE if it was initialised in a different
1048     threading mode. In either case we shouldn't consider it an error
1049     but we need to be careful to not call CoUninitialize() if
1050     RPC_E_CHANGED_MODE was returned.
1051     */
1052    
1053     HRESULT hr = CoInitialize(NULL);
1054     if( FAILED(hr) && hr != RPC_E_CHANGED_MODE )
1055     return paUnanticipatedHostError;
1056    
1057     if( hr != RPC_E_CHANGED_MODE )
1058     comWasInitialized = 1;
1059    
1060     /* initialise guid vectors so they can be safely deleted on error */
1061     deviceNamesAndGUIDs.winDsHostApi = NULL;
1062     deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
1063     deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
1064    
1065     PaWinDs_InitializeDSoundEntryPoints();
1066    
1067     winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
1068     if( !winDsHostApi )
1069     {
1070     result = paInsufficientMemory;
1071     goto error;
1072     }
1073    
1074     winDsHostApi->comWasInitialized = comWasInitialized;
1075    
1076     winDsHostApi->allocations = PaUtil_CreateAllocationGroup();
1077     if( !winDsHostApi->allocations )
1078     {
1079     result = paInsufficientMemory;
1080     goto error;
1081     }
1082    
1083     *hostApi = &winDsHostApi->inheritedHostApiRep;
1084     (*hostApi)->info.structVersion = 1;
1085     (*hostApi)->info.type = paDirectSound;
1086     (*hostApi)->info.name = "Windows DirectSound";
1087    
1088     (*hostApi)->info.deviceCount = 0;
1089     (*hostApi)->info.defaultInputDevice = paNoDevice;
1090     (*hostApi)->info.defaultOutputDevice = paNoDevice;
1091    
1092    
1093     /* DSound - enumerate devices to count them and to gather their GUIDs */
1094    
1095     result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
1096     if( result != paNoError )
1097     goto error;
1098    
1099     result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
1100     if( result != paNoError )
1101     goto error;
1102    
1103     paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
1104    
1105     paWinDsDSoundEntryPoints.DirectSoundEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
1106    
1107     if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
1108     {
1109     result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
1110     goto error;
1111     }
1112    
1113     if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
1114     {
1115     result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
1116     goto error;
1117     }
1118    
1119     deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
1120    
1121     #ifdef PAWIN_USE_WDMKS_DEVICE_INFO
1122     if( deviceCount > 0 )
1123     {
1124     deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
1125     FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
1126     }
1127     #endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
1128    
1129     if( deviceCount > 0 )
1130     {
1131     /* allocate array for pointers to PaDeviceInfo structs */
1132     (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1133     winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
1134     if( !(*hostApi)->deviceInfos )
1135     {
1136     result = paInsufficientMemory;
1137     goto error;
1138     }
1139    
1140     /* allocate all PaDeviceInfo structs in a contiguous block */
1141     deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
1142     winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
1143     if( !deviceInfoArray )
1144     {
1145     result = paInsufficientMemory;
1146     goto error;
1147     }
1148    
1149     for( i=0; i < deviceCount; ++i )
1150     {
1151     PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
1152     deviceInfo->structVersion = 2;
1153     deviceInfo->hostApi = hostApiIndex;
1154     deviceInfo->name = 0;
1155     (*hostApi)->deviceInfos[i] = deviceInfo;
1156     }
1157    
1158     for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
1159     {
1160     result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
1161     deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
1162     deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
1163     deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
1164     if( result != paNoError )
1165     goto error;
1166     }
1167    
1168     for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
1169     {
1170     result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
1171     deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
1172     deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
1173     deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
1174     if( result != paNoError )
1175     goto error;
1176     }
1177     }
1178    
1179     result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
1180     if( result != paNoError )
1181     goto error;
1182    
1183     result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
1184     if( result != paNoError )
1185     goto error;
1186    
1187    
1188     (*hostApi)->Terminate = Terminate;
1189     (*hostApi)->OpenStream = OpenStream;
1190     (*hostApi)->IsFormatSupported = IsFormatSupported;
1191    
1192     PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
1193     StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1194     GetStreamTime, GetStreamCpuLoad,
1195     PaUtil_DummyRead, PaUtil_DummyWrite,
1196     PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1197    
1198     PaUtil_InitializeStreamInterface( &winDsHostApi->blockingStreamInterface, CloseStream, StartStream,
1199     StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1200     GetStreamTime, PaUtil_DummyGetCpuLoad,
1201     ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1202    
1203     return result;
1204    
1205     error:
1206     if( winDsHostApi )
1207     {
1208     if( winDsHostApi->allocations )
1209     {
1210     PaUtil_FreeAllAllocations( winDsHostApi->allocations );
1211     PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
1212     }
1213    
1214     PaUtil_FreeMemory( winDsHostApi );
1215     }
1216    
1217     TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
1218     TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
1219    
1220     if( comWasInitialized )
1221     CoUninitialize();
1222    
1223     return result;
1224     }
1225    
1226    
1227     /***********************************************************************************/
1228     static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1229     {
1230     PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
1231     char comWasInitialized = winDsHostApi->comWasInitialized;
1232    
1233     /*
1234     IMPLEMENT ME:
1235     - clean up any resources not handled by the allocation group
1236     */
1237    
1238     if( winDsHostApi->allocations )
1239     {
1240     PaUtil_FreeAllAllocations( winDsHostApi->allocations );
1241     PaUtil_DestroyAllocationGroup( winDsHostApi->allocations );
1242     }
1243    
1244     PaUtil_FreeMemory( winDsHostApi );
1245    
1246     PaWinDs_TerminateDSoundEntryPoints();
1247    
1248     if( comWasInitialized )
1249     CoUninitialize();
1250     }
1251    
1252    
1253     /* Set minimal latency based on whether NT or Win95.
1254     * NT has higher latency.
1255     */
1256     static int PaWinDS_GetMinSystemLatency( void )
1257     {
1258     int minLatencyMsec;
1259     /* Set minimal latency based on whether NT or other OS.
1260     * NT has higher latency.
1261     */
1262     OSVERSIONINFO osvi;
1263     osvi.dwOSVersionInfoSize = sizeof( osvi );
1264     GetVersionEx( &osvi );
1265     DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
1266     DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
1267     DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
1268     /* Check for NT */
1269     if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
1270     {
1271     minLatencyMsec = PA_WIN_NT_LATENCY;
1272     }
1273     else if(osvi.dwMajorVersion >= 5)
1274     {
1275     minLatencyMsec = PA_WIN_WDM_LATENCY;
1276     }
1277     else
1278     {
1279     minLatencyMsec = PA_WIN_9X_LATENCY;
1280     }
1281     return minLatencyMsec;
1282     }
1283    
1284     static PaError ValidateWinDirectSoundSpecificStreamInfo(
1285     const PaStreamParameters *streamParameters,
1286     const PaWinDirectSoundStreamInfo *streamInfo )
1287     {
1288     if( streamInfo )
1289     {
1290     if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
1291     || streamInfo->version != 1 )
1292     {
1293     return paIncompatibleHostApiSpecificStreamInfo;
1294     }
1295     }
1296    
1297     return paNoError;
1298     }
1299    
1300     /***********************************************************************************/
1301     static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1302     const PaStreamParameters *inputParameters,
1303     const PaStreamParameters *outputParameters,
1304     double sampleRate )
1305     {
1306     PaError result;
1307     PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
1308     PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1309     int inputChannelCount, outputChannelCount;
1310     PaSampleFormat inputSampleFormat, outputSampleFormat;
1311     PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
1312    
1313     if( inputParameters )
1314     {
1315     inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
1316     inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
1317    
1318     inputChannelCount = inputParameters->channelCount;
1319     inputSampleFormat = inputParameters->sampleFormat;
1320    
1321     /* unless alternate device specification is supported, reject the use of
1322     paUseHostApiSpecificDeviceSpecification */
1323    
1324     if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1325     return paInvalidDevice;
1326    
1327     /* check that input device can support inputChannelCount */
1328     if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
1329     && inputChannelCount > inputDeviceInfo->maxInputChannels )
1330     return paInvalidChannelCount;
1331    
1332     /* validate inputStreamInfo */
1333     inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1334     result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
1335     if( result != paNoError ) return result;
1336     }
1337     else
1338     {
1339     inputChannelCount = 0;
1340     }
1341    
1342     if( outputParameters )
1343     {
1344     outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
1345     outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
1346    
1347     outputChannelCount = outputParameters->channelCount;
1348     outputSampleFormat = outputParameters->sampleFormat;
1349    
1350     /* unless alternate device specification is supported, reject the use of
1351     paUseHostApiSpecificDeviceSpecification */
1352    
1353     if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1354     return paInvalidDevice;
1355    
1356     /* check that output device can support inputChannelCount */
1357     if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
1358     && outputChannelCount > outputDeviceInfo->maxOutputChannels )
1359     return paInvalidChannelCount;
1360    
1361     /* validate outputStreamInfo */
1362     outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1363     result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
1364     if( result != paNoError ) return result;
1365     }
1366     else
1367     {
1368     outputChannelCount = 0;
1369     }
1370    
1371     /*
1372     IMPLEMENT ME:
1373    
1374     - if a full duplex stream is requested, check that the combination
1375     of input and output parameters is supported if necessary
1376    
1377     - check that the device supports sampleRate
1378    
1379     Because the buffer adapter handles conversion between all standard
1380     sample formats, the following checks are only required if paCustomFormat
1381     is implemented, or under some other unusual conditions.
1382    
1383     - check that input device can support inputSampleFormat, or that
1384     we have the capability to convert from outputSampleFormat to
1385     a native format
1386    
1387     - check that output device can support outputSampleFormat, or that
1388     we have the capability to convert from outputSampleFormat to
1389     a native format
1390     */
1391    
1392     return paFormatIsSupported;
1393     }
1394    
1395    
1396     /*************************************************************************
1397     ** Determine minimum number of buffers required for this host based
1398     ** on minimum latency. Latency can be optionally set by user by setting
1399     ** an environment variable. For example, to set latency to 200 msec, put:
1400     **
1401     ** set PA_MIN_LATENCY_MSEC=200
1402     **
1403     ** in the AUTOEXEC.BAT file and reboot.
1404     ** If the environment variable is not set, then the latency will be determined
1405     ** based on the OS. Windows NT has higher latency than Win95.
1406     */
1407     #define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
1408     #define PA_ENV_BUF_SIZE (32)
1409    
1410     static int PaWinDs_GetMinLatencyFrames( double sampleRate )
1411     {
1412     char envbuf[PA_ENV_BUF_SIZE];
1413     DWORD hresult;
1414     int minLatencyMsec = 0;
1415    
1416     /* Let user determine minimal latency by setting environment variable. */
1417     hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
1418     if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
1419     {
1420     minLatencyMsec = atoi( envbuf );
1421     }
1422     else
1423     {
1424     minLatencyMsec = PaWinDS_GetMinSystemLatency();
1425     #if PA_USE_HIGH_LATENCY
1426     PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
1427     #endif
1428    
1429     }
1430    
1431     return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
1432     }
1433    
1434    
1435     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
1436     static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
1437     PaWinDsDeviceInfo *inputDevice,
1438     PaSampleFormat hostInputSampleFormat,
1439     WORD inputChannelCount,
1440     int bytesPerInputBuffer,
1441     PaWinWaveFormatChannelMask inputChannelMask,
1442     PaWinDsDeviceInfo *outputDevice,
1443     PaSampleFormat hostOutputSampleFormat,
1444     WORD outputChannelCount,
1445     int bytesPerOutputBuffer,
1446     PaWinWaveFormatChannelMask outputChannelMask,
1447     unsigned long nFrameRate
1448     )
1449     {
1450     HRESULT hr;
1451     DSCBUFFERDESC captureDesc;
1452     PaWinWaveFormat captureWaveFormat;
1453     DSBUFFERDESC secondaryRenderDesc;
1454     PaWinWaveFormat renderWaveFormat;
1455     LPDIRECTSOUNDBUFFER8 pRenderBuffer8;
1456     LPDIRECTSOUNDCAPTUREBUFFER8 pCaptureBuffer8;
1457    
1458     // capture buffer description
1459    
1460     // only try wave format extensible. assume it's available on all ds 8 systems
1461     PaWin_InitializeWaveFormatExtensible( &captureWaveFormat, inputChannelCount,
1462     hostInputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostInputSampleFormat ),
1463     nFrameRate, inputChannelMask );
1464    
1465     ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
1466     captureDesc.dwSize = sizeof(DSCBUFFERDESC);
1467     captureDesc.dwFlags = 0;
1468     captureDesc.dwBufferBytes = bytesPerInputBuffer;
1469     captureDesc.lpwfxFormat = (WAVEFORMATEX*)&captureWaveFormat;
1470    
1471     // render buffer description
1472    
1473     PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount,
1474     hostOutputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostOutputSampleFormat ),
1475     nFrameRate, outputChannelMask );
1476    
1477     ZeroMemory(&secondaryRenderDesc, sizeof(DSBUFFERDESC));
1478     secondaryRenderDesc.dwSize = sizeof(DSBUFFERDESC);
1479     secondaryRenderDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
1480     secondaryRenderDesc.dwBufferBytes = bytesPerOutputBuffer;
1481     secondaryRenderDesc.lpwfxFormat = (WAVEFORMATEX*)&renderWaveFormat;
1482    
1483     /* note that we don't create a primary buffer here at all */
1484    
1485     hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8(
1486     inputDevice->lpGUID, outputDevice->lpGUID,
1487     &captureDesc, &secondaryRenderDesc,
1488     GetDesktopWindow(), /* see InitOutputBuffer() for a discussion of whether this is a good idea */
1489     DSSCL_EXCLUSIVE,
1490     &stream->pDirectSoundFullDuplex8,
1491     &pCaptureBuffer8,
1492     &pRenderBuffer8,
1493     NULL /* pUnkOuter must be NULL */
1494     );
1495    
1496     if( hr == DS_OK )
1497     {
1498     PA_DEBUG(("DirectSoundFullDuplexCreate succeeded!\n"));
1499    
1500     /* retrieve the pre ds 8 buffer interfaces which are used by the rest of the code */
1501    
1502     hr = IUnknown_QueryInterface( pCaptureBuffer8, &IID_IDirectSoundCaptureBuffer, &stream->pDirectSoundInputBuffer );
1503    
1504     if( hr == DS_OK )
1505     hr = IUnknown_QueryInterface( pRenderBuffer8, &IID_IDirectSoundBuffer, &stream->pDirectSoundOutputBuffer );
1506    
1507     /* release the ds 8 interfaces, we don't need them */
1508     IUnknown_Release( pCaptureBuffer8 );
1509     IUnknown_Release( pRenderBuffer8 );
1510    
1511     if( !stream->pDirectSoundInputBuffer || !stream->pDirectSoundOutputBuffer ){
1512     /* couldn't get pre ds 8 interfaces for some reason. clean up. */
1513     if( stream->pDirectSoundInputBuffer )
1514     {
1515     IUnknown_Release( stream->pDirectSoundInputBuffer );
1516     stream->pDirectSoundInputBuffer = NULL;
1517     }
1518    
1519     if( stream->pDirectSoundOutputBuffer )
1520     {
1521     IUnknown_Release( stream->pDirectSoundOutputBuffer );
1522     stream->pDirectSoundOutputBuffer = NULL;
1523     }
1524    
1525     IUnknown_Release( stream->pDirectSoundFullDuplex8 );
1526     stream->pDirectSoundFullDuplex8 = NULL;
1527     }
1528     }
1529     else
1530     {
1531     PA_DEBUG(("DirectSoundFullDuplexCreate failed. hr=%d\n", hr));
1532     }
1533    
1534     return hr;
1535     }
1536     #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
1537    
1538    
1539     static HRESULT InitInputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
1540     {
1541     DSCBUFFERDESC captureDesc;
1542     PaWinWaveFormat waveFormat;
1543     HRESULT result;
1544    
1545     if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
1546     device->lpGUID, &stream->pDirectSoundCapture, NULL) ) != DS_OK ){
1547     ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
1548     return result;
1549     }
1550    
1551     // Setup the secondary buffer description
1552     ZeroMemory(&captureDesc, sizeof(DSCBUFFERDESC));
1553     captureDesc.dwSize = sizeof(DSCBUFFERDESC);
1554     captureDesc.dwFlags = 0;
1555     captureDesc.dwBufferBytes = bytesPerBuffer;
1556     captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
1557    
1558     // Create the capture buffer
1559    
1560     // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
1561     PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
1562     sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
1563     nFrameRate, channelMask );
1564    
1565     if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1566     &captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
1567     {
1568     PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
1569     PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
1570    
1571     if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
1572     &captureDesc, &stream->pDirectSoundInputBuffer, NULL)) != DS_OK) return result;
1573     }
1574    
1575     stream->readOffset = 0; // reset last read position to start of buffer
1576     return DS_OK;
1577     }
1578    
1579    
1580     static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
1581     {
1582     HRESULT result;
1583     HWND hWnd;
1584     HRESULT hr;
1585     PaWinWaveFormat waveFormat;
1586     DSBUFFERDESC primaryDesc;
1587     DSBUFFERDESC secondaryDesc;
1588    
1589     if( (hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
1590     device->lpGUID, &stream->pDirectSound, NULL )) != DS_OK ){
1591     ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
1592     return hr;
1593     }
1594    
1595     // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the
1596     // applications's window. Also if that window is closed before the Buffer is closed
1597     // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.)
1598     // So we will use GetDesktopWindow() which was suggested by Miller Puckette.
1599     // hWnd = GetForegroundWindow();
1600     //
1601     // FIXME: The example code I have on the net creates a hidden window that
1602     // is managed by our code - I think we should do that - one hidden
1603     // window for the whole of Pa_DS
1604     //
1605     hWnd = GetDesktopWindow();
1606    
1607     // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz.
1608     // exclusive also prevents unexpected sounds from other apps during a performance.
1609     if ((hr = IDirectSound_SetCooperativeLevel( stream->pDirectSound,
1610     hWnd, DSSCL_EXCLUSIVE)) != DS_OK)
1611     {
1612     return hr;
1613     }
1614    
1615     // -----------------------------------------------------------------------
1616     // Create primary buffer and set format just so we can specify our custom format.
1617     // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz.
1618     // Setup the primary buffer description
1619     ZeroMemory(&primaryDesc, sizeof(DSBUFFERDESC));
1620     primaryDesc.dwSize = sizeof(DSBUFFERDESC);
1621     primaryDesc.dwFlags = DSBCAPS_PRIMARYBUFFER; // all panning, mixing, etc done by synth
1622     primaryDesc.dwBufferBytes = 0;
1623     primaryDesc.lpwfxFormat = NULL;
1624     // Create the buffer
1625     if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
1626     &primaryDesc, &stream->pDirectSoundPrimaryBuffer, NULL)) != DS_OK)
1627     goto error;
1628    
1629     // Set the primary buffer's format
1630    
1631     // first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
1632     PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
1633     sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
1634     nFrameRate, channelMask );
1635    
1636     if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
1637     {
1638     PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
1639     PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
1640    
1641     if((result = IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK)
1642     goto error;
1643     }
1644    
1645     // ----------------------------------------------------------------------
1646     // Setup the secondary buffer description
1647     ZeroMemory(&secondaryDesc, sizeof(DSBUFFERDESC));
1648     secondaryDesc.dwSize = sizeof(DSBUFFERDESC);
1649     secondaryDesc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
1650     secondaryDesc.dwBufferBytes = bytesPerBuffer;
1651     secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat; /* waveFormat contains whatever format was negotiated for the primary buffer above */
1652     // Create the secondary buffer
1653     if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
1654     &secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK)
1655     goto error;
1656    
1657     return DS_OK;
1658    
1659     error:
1660    
1661     if( stream->pDirectSoundPrimaryBuffer )
1662     {
1663     IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
1664     stream->pDirectSoundPrimaryBuffer = NULL;
1665     }
1666    
1667     return result;
1668     }
1669    
1670     /***********************************************************************************/
1671     /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1672    
1673     static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1674     PaStream** s,
1675     const PaStreamParameters *inputParameters,
1676     const PaStreamParameters *outputParameters,
1677     double sampleRate,
1678     unsigned long framesPerBuffer,
1679     PaStreamFlags streamFlags,
1680     PaStreamCallback *streamCallback,
1681     void *userData )
1682     {
1683     PaError result = paNoError;
1684     PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
1685     PaWinDsStream *stream = 0;
1686     int bufferProcessorIsInitialized = 0;
1687     int streamRepresentationIsInitialized = 0;
1688     PaWinDsDeviceInfo *inputWinDsDeviceInfo, *outputWinDsDeviceInfo;
1689     PaDeviceInfo *inputDeviceInfo, *outputDeviceInfo;
1690     int inputChannelCount, outputChannelCount;
1691     PaSampleFormat inputSampleFormat, outputSampleFormat;
1692     PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
1693     unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
1694     PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
1695     PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
1696    
1697     if( inputParameters )
1698     {
1699     inputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ inputParameters->device ];
1700     inputDeviceInfo = &inputWinDsDeviceInfo->inheritedDeviceInfo;
1701    
1702     inputChannelCount = inputParameters->channelCount;
1703     inputSampleFormat = inputParameters->sampleFormat;
1704     suggestedInputLatencyFrames = (unsigned long)(inputParameters->suggestedLatency * sampleRate);
1705    
1706     /* IDEA: the following 3 checks could be performed by default by pa_front
1707     unless some flag indicated otherwise */
1708    
1709     /* unless alternate device specification is supported, reject the use of
1710     paUseHostApiSpecificDeviceSpecification */
1711     if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1712     return paInvalidDevice;
1713    
1714     /* check that input device can support inputChannelCount */
1715     if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
1716     && inputChannelCount > inputDeviceInfo->maxInputChannels )
1717     return paInvalidChannelCount;
1718    
1719     /* validate hostApiSpecificStreamInfo */
1720     inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1721     result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
1722     if( result != paNoError ) return result;
1723    
1724     if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
1725     inputChannelMask = inputStreamInfo->channelMask;
1726     else
1727     inputChannelMask = PaWin_DefaultChannelMask( inputChannelCount );
1728     }
1729     else
1730     {
1731     inputChannelCount = 0;
1732     inputSampleFormat = 0;
1733     suggestedInputLatencyFrames = 0;
1734     }
1735    
1736    
1737     if( outputParameters )
1738     {
1739     outputWinDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[ outputParameters->device ];
1740     outputDeviceInfo = &outputWinDsDeviceInfo->inheritedDeviceInfo;
1741    
1742     outputChannelCount = outputParameters->channelCount;
1743     outputSampleFormat = outputParameters->sampleFormat;
1744     suggestedOutputLatencyFrames = (unsigned long)(outputParameters->suggestedLatency * sampleRate);
1745    
1746     /* unless alternate device specification is supported, reject the use of
1747     paUseHostApiSpecificDeviceSpecification */
1748     if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1749     return paInvalidDevice;
1750    
1751     /* check that output device can support outputChannelCount */
1752     if( outputWinDsDeviceInfo->deviceOutputChannelCountIsKnown
1753     && outputChannelCount > outputDeviceInfo->maxOutputChannels )
1754     return paInvalidChannelCount;
1755    
1756     /* validate hostApiSpecificStreamInfo */
1757     outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1758     result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
1759     if( result != paNoError ) return result;
1760    
1761     if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
1762     outputChannelMask = outputStreamInfo->channelMask;
1763     else
1764     outputChannelMask = PaWin_DefaultChannelMask( outputChannelCount );
1765     }
1766     else
1767     {
1768     outputChannelCount = 0;
1769     outputSampleFormat = 0;
1770     suggestedOutputLatencyFrames = 0;
1771     }
1772    
1773    
1774     /*
1775     IMPLEMENT ME:
1776    
1777     ( the following two checks are taken care of by PaUtil_InitializeBufferProcessor() )
1778    
1779     - check that input device can support inputSampleFormat, or that
1780     we have the capability to convert from outputSampleFormat to
1781     a native format
1782    
1783     - check that output device can support outputSampleFormat, or that
1784     we have the capability to convert from outputSampleFormat to
1785     a native format
1786    
1787     - if a full duplex stream is requested, check that the combination
1788     of input and output parameters is supported
1789    
1790     - check that the device supports sampleRate
1791    
1792     - alter sampleRate to a close allowable rate if possible / necessary
1793    
1794     - validate suggestedInputLatency and suggestedOutputLatency parameters,
1795     use default values where necessary
1796     */
1797    
1798    
1799     /* validate platform specific flags */
1800     if( (streamFlags & paPlatformSpecificFlags) != 0 )
1801     return paInvalidFlag; /* unexpected platform specific flag */
1802    
1803    
1804     stream = (PaWinDsStream*)PaUtil_AllocateMemory( sizeof(PaWinDsStream) );
1805     if( !stream )
1806     {
1807     result = paInsufficientMemory;
1808     goto error;
1809     }
1810    
1811     memset( stream, 0, sizeof(PaWinDsStream) ); /* initialize all stream variables to 0 */
1812    
1813     if( streamCallback )
1814     {
1815     PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1816     &winDsHostApi->callbackStreamInterface, streamCallback, userData );
1817     }
1818     else
1819     {
1820     PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
1821     &winDsHostApi->blockingStreamInterface, streamCallback, userData );
1822     }
1823    
1824     streamRepresentationIsInitialized = 1;
1825    
1826     stream->streamFlags = streamFlags;
1827    
1828     PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
1829    
1830    
1831     if( inputParameters )
1832     {
1833     /* IMPLEMENT ME - establish which host formats are available */
1834     PaSampleFormat nativeInputFormats = paInt16;
1835     //PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
1836    
1837     hostInputSampleFormat =
1838     PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
1839     }
1840     else
1841     {
1842     hostInputSampleFormat = 0;
1843     }
1844    
1845     if( outputParameters )
1846     {
1847     /* IMPLEMENT ME - establish which host formats are available */
1848     PaSampleFormat nativeOutputFormats = paInt16;
1849     //PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
1850    
1851     hostOutputSampleFormat =
1852     PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
1853     }
1854     else
1855     {
1856     hostOutputSampleFormat = 0;
1857     }
1858    
1859     result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
1860     inputChannelCount, inputSampleFormat, hostInputSampleFormat,
1861     outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
1862     sampleRate, streamFlags, framesPerBuffer,
1863     framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
1864     /* This next mode is required because DS can split the host buffer when it wraps around. */
1865     paUtilVariableHostBufferSizePartialUsageAllowed,
1866     streamCallback, userData );
1867     if( result != paNoError )
1868     goto error;
1869    
1870     bufferProcessorIsInitialized = 1;
1871    
1872     stream->streamRepresentation.streamInfo.inputLatency =
1873     PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
1874     stream->streamRepresentation.streamInfo.outputLatency =
1875     PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor); /* FIXME: not initialised anywhere else */
1876     stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
1877    
1878    
1879     /* DirectSound specific initialization */
1880     {
1881     HRESULT hr;
1882     int bytesPerDirectSoundInputBuffer, bytesPerDirectSoundOutputBuffer;
1883     int userLatencyFrames;
1884     int minLatencyFrames;
1885     unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
1886    
1887     stream->timerID = 0;
1888    
1889     stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
1890     if( stream->processingCompleted == NULL )
1891     {
1892     result = paInsufficientMemory;
1893     goto error;
1894     }
1895    
1896     /* Get system minimum latency. */
1897     minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
1898    
1899     /* Let user override latency by passing latency parameter. */
1900     userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames)
1901     ? suggestedInputLatencyFrames
1902     : suggestedOutputLatencyFrames;
1903     if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames;
1904    
1905     /* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */
1906     if( framesPerBuffer == paFramesPerBufferUnspecified )
1907     {
1908     /* App support variable framesPerBuffer */
1909     stream->framesPerDSBuffer = minLatencyFrames;
1910    
1911     stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate;
1912     }
1913     else
1914     {
1915     /* Round up to number of buffers needed to guarantee that latency. */
1916     int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer;
1917     if( numUserBuffers < 1 ) numUserBuffers = 1;
1918     numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */
1919     stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers;
1920    
1921     stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate;
1922     }
1923    
1924     {
1925     /** @todo REVIEW: this calculation seems incorrect to me - rossb. */
1926     int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate);
1927     PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
1928     }
1929    
1930     /* set up i/o parameters */
1931    
1932     /* ------------------ OUTPUT */
1933     if( outputParameters )
1934     {
1935     LARGE_INTEGER counterFrequency;
1936    
1937     /*
1938     PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
1939     DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
1940     */
1941    
1942     int bytesPerSample = Pa_GetSampleSize(hostOutputSampleFormat);
1943     bytesPerDirectSoundOutputBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * bytesPerSample;
1944     if( bytesPerDirectSoundOutputBuffer < DSBSIZE_MIN )
1945     {
1946     result = paBufferTooSmall;
1947     goto error;
1948     }
1949     else if( bytesPerDirectSoundOutputBuffer > DSBSIZE_MAX )
1950     {
1951     result = paBufferTooBig;
1952     goto error;
1953     }
1954    
1955     /* Calculate value used in latency calculation to avoid real-time divides. */
1956     stream->secondsPerHostByte = 1.0 /
1957     (stream->bufferProcessor.bytesPerHostOutputSample *
1958     outputChannelCount * sampleRate);
1959    
1960     stream->outputBufferSizeBytes = bytesPerDirectSoundOutputBuffer;
1961     stream->outputIsRunning = FALSE;
1962     stream->outputUnderflowCount = 0;
1963     stream->bytesPerOutputFrame = outputParameters->channelCount * bytesPerSample;
1964    
1965     /* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
1966     if( QueryPerformanceFrequency( &counterFrequency ) )
1967     {
1968     stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->framesPerDSBuffer) / integerSampleRate;
1969     }
1970     else
1971     {
1972     stream->perfCounterTicksPerBuffer.QuadPart = 0;
1973     }
1974     }
1975    
1976     /* ------------------ INPUT */
1977     if( inputParameters )
1978     {
1979     /*
1980     PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
1981     DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
1982     */
1983    
1984     int bytesPerSample = Pa_GetSampleSize(hostInputSampleFormat);
1985     bytesPerDirectSoundInputBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * bytesPerSample;
1986     if( bytesPerDirectSoundInputBuffer < DSBSIZE_MIN )
1987     {
1988     result = paBufferTooSmall;
1989     goto error;
1990     }
1991     else if( bytesPerDirectSoundInputBuffer > DSBSIZE_MAX )
1992     {
1993     result = paBufferTooBig;
1994     goto error;
1995     }
1996    
1997     stream->bytesPerInputFrame = inputParameters->channelCount * bytesPerSample;
1998    
1999     stream->inputSize = bytesPerDirectSoundInputBuffer;
2000     }
2001    
2002     /* open/create the DirectSound buffers */
2003    
2004     /* interface ptrs should be zeroed when stream is zeroed. */
2005     assert( stream->pDirectSoundCapture == NULL );
2006     assert( stream->pDirectSoundInputBuffer == NULL );
2007     assert( stream->pDirectSound == NULL );
2008     assert( stream->pDirectSoundPrimaryBuffer == NULL );
2009     assert( stream->pDirectSoundOutputBuffer == NULL );
2010    
2011    
2012     if( inputParameters && outputParameters )
2013     {
2014     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2015     /* try to use the full-duplex DX8 API to create the buffers.
2016     if that fails we fall back to the half-duplex API below */
2017    
2018     hr = InitFullDuplexInputOutputBuffers( stream,
2019     (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
2020     hostInputSampleFormat,
2021     (WORD)inputParameters->channelCount, bytesPerDirectSoundInputBuffer,
2022     inputChannelMask,
2023     (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
2024     hostOutputSampleFormat,
2025     (WORD)outputParameters->channelCount, bytesPerDirectSoundOutputBuffer,
2026     outputChannelMask,
2027     integerSampleRate
2028     );
2029     DBUG(("InitFullDuplexInputOutputBuffers() returns %x\n", hr));
2030     /* ignore any error returned by InitFullDuplexInputOutputBuffers.
2031     we retry opening the buffers below */
2032     #endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
2033     }
2034    
2035     /* create half duplex buffers. also used for full-duplex streams which didn't
2036     succeed when using the full duplex API. that could happen because
2037     DX8 or greater isnt installed, the i/o devices aren't the same
2038     physical device. etc.
2039     */
2040    
2041     if( outputParameters && !stream->pDirectSoundOutputBuffer )
2042     {
2043     hr = InitOutputBuffer( stream,
2044     (PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
2045     hostOutputSampleFormat,
2046     integerSampleRate,
2047     (WORD)outputParameters->channelCount, bytesPerDirectSoundOutputBuffer,
2048     outputChannelMask );
2049     DBUG(("InitOutputBuffer() returns %x\n", hr));
2050     if( hr != DS_OK )
2051     {
2052     result = paUnanticipatedHostError;
2053     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2054     goto error;
2055     }
2056     }
2057    
2058     if( inputParameters && !stream->pDirectSoundInputBuffer )
2059     {
2060     hr = InitInputBuffer( stream,
2061     (PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
2062     hostInputSampleFormat,
2063     integerSampleRate,
2064     (WORD)inputParameters->channelCount, bytesPerDirectSoundInputBuffer,
2065     inputChannelMask );
2066     DBUG(("InitInputBuffer() returns %x\n", hr));
2067     if( hr != DS_OK )
2068     {
2069     ERR_RPT(("PortAudio: DSW_InitInputBuffer() returns %x\n", hr));
2070     result = paUnanticipatedHostError;
2071     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2072     goto error;
2073     }
2074     }
2075     }
2076    
2077     *s = (PaStream*)stream;
2078    
2079     return result;
2080    
2081     error:
2082     if( stream )
2083     {
2084     if( stream->processingCompleted != NULL )
2085     CloseHandle( stream->processingCompleted );
2086    
2087     if( stream->pDirectSoundOutputBuffer )
2088     {
2089     IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2090     IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
2091     stream->pDirectSoundOutputBuffer = NULL;
2092     }
2093    
2094     if( stream->pDirectSoundPrimaryBuffer )
2095     {
2096     IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
2097     stream->pDirectSoundPrimaryBuffer = NULL;
2098     }
2099    
2100     if( stream->pDirectSoundInputBuffer )
2101     {
2102     IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2103     IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
2104     stream->pDirectSoundInputBuffer = NULL;
2105     }
2106    
2107     if( stream->pDirectSoundCapture )
2108     {
2109     IDirectSoundCapture_Release( stream->pDirectSoundCapture );
2110     stream->pDirectSoundCapture = NULL;
2111     }
2112    
2113     if( stream->pDirectSound )
2114     {
2115     IDirectSound_Release( stream->pDirectSound );
2116     stream->pDirectSound = NULL;
2117     }
2118    
2119     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2120     if( stream->pDirectSoundFullDuplex8 )
2121     {
2122     IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
2123     stream->pDirectSoundFullDuplex8 = NULL;
2124     }
2125     #endif
2126     if( bufferProcessorIsInitialized )
2127     PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2128    
2129     if( streamRepresentationIsInitialized )
2130     PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2131    
2132     PaUtil_FreeMemory( stream );
2133     }
2134    
2135     return result;
2136     }
2137    
2138    
2139     /************************************************************************************
2140     * Determine how much space can be safely written to in DS buffer.
2141     * Detect underflows and overflows.
2142     * Does not allow writing into safety gap maintained by DirectSound.
2143     */
2144     static HRESULT QueryOutputSpace( PaWinDsStream *stream, long *bytesEmpty )
2145     {
2146     HRESULT hr;
2147     DWORD playCursor;
2148     DWORD writeCursor;
2149     long numBytesEmpty;
2150     long playWriteGap;
2151     // Query to see how much room is in buffer.
2152     hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
2153     &playCursor, &writeCursor );
2154     if( hr != DS_OK )
2155     {
2156     return hr;
2157     }
2158    
2159     // Determine size of gap between playIndex and WriteIndex that we cannot write into.
2160     playWriteGap = writeCursor - playCursor;
2161     if( playWriteGap < 0 ) playWriteGap += stream->outputBufferSizeBytes; // unwrap
2162    
2163     /* DirectSound doesn't have a large enough playCursor so we cannot detect wrap-around. */
2164     /* Attempt to detect playCursor wrap-around and correct it. */
2165     if( stream->outputIsRunning && (stream->perfCounterTicksPerBuffer.QuadPart != 0) )
2166     {
2167     /* How much time has elapsed since last check. */
2168     LARGE_INTEGER currentTime;
2169     LARGE_INTEGER elapsedTime;
2170     long bytesPlayed;
2171     long bytesExpected;
2172     long buffersWrapped;
2173    
2174     QueryPerformanceCounter( &currentTime );
2175     elapsedTime.QuadPart = currentTime.QuadPart - stream->previousPlayTime.QuadPart;
2176     stream->previousPlayTime = currentTime;
2177    
2178     /* How many bytes does DirectSound say have been played. */
2179     bytesPlayed = playCursor - stream->previousPlayCursor;
2180     if( bytesPlayed < 0 ) bytesPlayed += stream->outputBufferSizeBytes; // unwrap
2181     stream->previousPlayCursor = playCursor;
2182    
2183     /* Calculate how many bytes we would have expected to been played by now. */
2184     bytesExpected = (long) ((elapsedTime.QuadPart * stream->outputBufferSizeBytes) / stream->perfCounterTicksPerBuffer.QuadPart);
2185     buffersWrapped = (bytesExpected - bytesPlayed) / stream->outputBufferSizeBytes;
2186     if( buffersWrapped > 0 )
2187     {
2188     playCursor += (buffersWrapped * stream->outputBufferSizeBytes);
2189     bytesPlayed += (buffersWrapped * stream->outputBufferSizeBytes);
2190     }
2191     }
2192     numBytesEmpty = playCursor - stream->outputBufferWriteOffsetBytes;
2193     if( numBytesEmpty < 0 ) numBytesEmpty += stream->outputBufferSizeBytes; // unwrap offset
2194    
2195     /* Have we underflowed? */
2196     if( numBytesEmpty > (stream->outputBufferSizeBytes - playWriteGap) )
2197     {
2198     if( stream->outputIsRunning )
2199     {
2200     stream->outputUnderflowCount += 1;
2201     }
2202    
2203     /*
2204     From MSDN:
2205     The write cursor indicates the position at which it is safe
2206     to write new data to the buffer. The write cursor always leads the
2207     play cursor, typically by about 15 milliseconds' worth of audio
2208     data.
2209     It is always safe to change data that is behind the position
2210     indicated by the lpdwCurrentPlayCursor parameter.
2211     */
2212    
2213     stream->outputBufferWriteOffsetBytes = writeCursor;
2214     numBytesEmpty = stream->outputBufferSizeBytes - playWriteGap;
2215     }
2216     *bytesEmpty = numBytesEmpty;
2217     return hr;
2218     }
2219    
2220     /***********************************************************************************/
2221     static int TimeSlice( PaWinDsStream *stream )
2222     {
2223     long numFrames = 0;
2224     long bytesEmpty = 0;
2225     long bytesFilled = 0;
2226     long bytesToXfer = 0;
2227     long framesToXfer = 0;
2228     long numInFramesReady = 0;
2229     long numOutFramesReady = 0;
2230     long bytesProcessed;
2231     HRESULT hresult;
2232     double outputLatency = 0;
2233     PaStreamCallbackTimeInfo timeInfo = {0,0,0}; /** @todo implement inputBufferAdcTime */
2234    
2235     /* Input */
2236     LPBYTE lpInBuf1 = NULL;
2237     LPBYTE lpInBuf2 = NULL;
2238     DWORD dwInSize1 = 0;
2239     DWORD dwInSize2 = 0;
2240     /* Output */
2241     LPBYTE lpOutBuf1 = NULL;
2242     LPBYTE lpOutBuf2 = NULL;
2243     DWORD dwOutSize1 = 0;
2244     DWORD dwOutSize2 = 0;
2245    
2246     /* How much input data is available? */
2247     if( stream->bufferProcessor.inputChannelCount > 0 )
2248     {
2249     HRESULT hr;
2250     DWORD capturePos;
2251     DWORD readPos;
2252     long filled = 0;
2253     // Query to see how much data is in buffer.
2254     // We don't need the capture position but sometimes DirectSound doesn't handle NULLS correctly
2255     // so let's pass a pointer just to be safe.
2256     hr = IDirectSoundCaptureBuffer_GetCurrentPosition( stream->pDirectSoundInputBuffer, &capturePos, &readPos );
2257     if( hr == DS_OK )
2258     {
2259     filled = readPos - stream->readOffset;
2260     if( filled < 0 ) filled += stream->inputSize; // unwrap offset
2261     bytesFilled = filled;
2262     }
2263     // FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
2264    
2265     framesToXfer = numInFramesReady = bytesFilled / stream->bytesPerInputFrame;
2266     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
2267    
2268     /** @todo Check for overflow */
2269     }
2270    
2271     /* How much output room is available? */
2272     if( stream->bufferProcessor.outputChannelCount > 0 )
2273     {
2274     UINT previousUnderflowCount = stream->outputUnderflowCount;
2275     QueryOutputSpace( stream, &bytesEmpty );
2276     framesToXfer = numOutFramesReady = bytesEmpty / stream->bytesPerOutputFrame;
2277    
2278     /* Check for underflow */
2279     if( stream->outputUnderflowCount != previousUnderflowCount )
2280     stream->callbackFlags |= paOutputUnderflow;
2281     }
2282    
2283     if( (numInFramesReady > 0) && (numOutFramesReady > 0) )
2284     {
2285     framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
2286     }
2287    
2288     if( framesToXfer > 0 )
2289     {
2290    
2291     PaUtil_BeginCpuLoadMeasurement( &stream->cpuLoadMeasurer );
2292    
2293     /* The outputBufferDacTime parameter should indicates the time at which
2294     the first sample of the output buffer is heard at the DACs. */
2295     timeInfo.currentTime = PaUtil_GetTime();
2296     timeInfo.outputBufferDacTime = timeInfo.currentTime + outputLatency; // FIXME: QueryOutputSpace gets the playback position, we could use that (?)
2297    
2298    
2299     PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
2300     stream->callbackFlags = 0;
2301    
2302     /* Input */
2303     if( stream->bufferProcessor.inputChannelCount > 0 )
2304     {
2305     bytesToXfer = framesToXfer * stream->bytesPerInputFrame;
2306     hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer,
2307     stream->readOffset, bytesToXfer,
2308     (void **) &lpInBuf1, &dwInSize1,
2309     (void **) &lpInBuf2, &dwInSize2, 0);
2310     if (hresult != DS_OK)
2311     {
2312     ERR_RPT(("DirectSound IDirectSoundCaptureBuffer_Lock failed, hresult = 0x%x\n",hresult));
2313     /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
2314     PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
2315     stream->callbackResult = paComplete;
2316     goto error2;
2317     }
2318    
2319     numFrames = dwInSize1 / stream->bytesPerInputFrame;
2320     PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
2321     PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
2322     /* Is input split into two regions. */
2323     if( dwInSize2 > 0 )
2324     {
2325     numFrames = dwInSize2 / stream->bytesPerInputFrame;
2326     PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
2327     PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
2328     }
2329     }
2330    
2331     /* Output */
2332     if( stream->bufferProcessor.outputChannelCount > 0 )
2333     {
2334     bytesToXfer = framesToXfer * stream->bytesPerOutputFrame;
2335     hresult = IDirectSoundBuffer_Lock ( stream->pDirectSoundOutputBuffer,
2336     stream->outputBufferWriteOffsetBytes, bytesToXfer,
2337     (void **) &lpOutBuf1, &dwOutSize1,
2338     (void **) &lpOutBuf2, &dwOutSize2, 0);
2339     if (hresult != DS_OK)
2340     {
2341     ERR_RPT(("DirectSound IDirectSoundBuffer_Lock failed, hresult = 0x%x\n",hresult));
2342     /* PA_DS_SET_LAST_DIRECTSOUND_ERROR( hresult ); */
2343     PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); /* flush the buffer processor */
2344     stream->callbackResult = paComplete;
2345     goto error1;
2346     }
2347    
2348     numFrames = dwOutSize1 / stream->bytesPerOutputFrame;
2349     PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
2350     PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
2351    
2352     /* Is output split into two regions. */
2353     if( dwOutSize2 > 0 )
2354     {
2355     numFrames = dwOutSize2 / stream->bytesPerOutputFrame;
2356     PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
2357     PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
2358     }
2359     }
2360    
2361     numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &stream->callbackResult );
2362     stream->framesWritten += numFrames;
2363    
2364     if( stream->bufferProcessor.outputChannelCount > 0 )
2365     {
2366     /* FIXME: an underflow could happen here */
2367    
2368     /* Update our buffer offset and unlock sound buffer */
2369     bytesProcessed = numFrames * stream->bytesPerOutputFrame;
2370     stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes;
2371     IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
2372     }
2373    
2374     error1:
2375     if( stream->bufferProcessor.inputChannelCount > 0 )
2376     {
2377     /* FIXME: an overflow could happen here */
2378    
2379     /* Update our buffer offset and unlock sound buffer */
2380     bytesProcessed = numFrames * stream->bytesPerInputFrame;
2381     stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputSize;
2382     IDirectSoundCaptureBuffer_Unlock( stream->pDirectSoundInputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
2383     }
2384     error2:
2385    
2386     PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
2387     }
2388    
2389     if( stream->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
2390     {
2391     /* don't return completed until the buffer processor has been drained */
2392     return paContinue;
2393     }
2394     else
2395     {
2396     return stream->callbackResult;
2397     }
2398     }
2399     /*******************************************************************/
2400    
2401     static HRESULT ZeroAvailableOutputSpace( PaWinDsStream *stream )
2402     {
2403     HRESULT hr;
2404     LPBYTE lpbuf1 = NULL;
2405     LPBYTE lpbuf2 = NULL;
2406     DWORD dwsize1 = 0;
2407     DWORD dwsize2 = 0;
2408     long bytesEmpty;
2409     hr = QueryOutputSpace( stream, &bytesEmpty );
2410     if (hr != DS_OK) return hr;
2411     if( bytesEmpty == 0 ) return DS_OK;
2412     // Lock free space in the DS
2413     hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, stream->outputBufferWriteOffsetBytes,
2414     bytesEmpty, (void **) &lpbuf1, &dwsize1,
2415     (void **) &lpbuf2, &dwsize2, 0);
2416     if (hr == DS_OK)
2417     {
2418     // Copy the buffer into the DS
2419     ZeroMemory(lpbuf1, dwsize1);
2420     if(lpbuf2 != NULL)
2421     {
2422     ZeroMemory(lpbuf2, dwsize2);
2423     }
2424     // Update our buffer offset and unlock sound buffer
2425     stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + dwsize1 + dwsize2) % stream->outputBufferSizeBytes;
2426     IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpbuf1, dwsize1, lpbuf2, dwsize2);
2427    
2428     stream->finalZeroBytesWritten += dwsize1 + dwsize2;
2429     }
2430     return hr;
2431     }
2432    
2433    
2434     static void CALLBACK TimerCallback(UINT uID, UINT uMsg, DWORD_PTR dwUser, DWORD dw1, DWORD dw2)
2435     {
2436     PaWinDsStream *stream;
2437     int isFinished = 0;
2438    
2439     /* suppress unused variable warnings */
2440     (void) uID;
2441     (void) uMsg;
2442     (void) dw1;
2443     (void) dw2;
2444    
2445     stream = (PaWinDsStream *) dwUser;
2446     if( stream == NULL ) return;
2447    
2448     if( stream->isActive )
2449     {
2450     if( stream->abortProcessing )
2451     {
2452     isFinished = 1;
2453     }
2454     else if( stream->stopProcessing )
2455     {
2456     if( stream->bufferProcessor.outputChannelCount > 0 )
2457     {
2458     ZeroAvailableOutputSpace( stream );
2459     if( stream->finalZeroBytesWritten >= stream->outputBufferSizeBytes )
2460     {
2461     /* once we've flushed the whole output buffer with zeros we know all data has been played */
2462     isFinished = 1;
2463     }
2464     }
2465     else
2466     {
2467     isFinished = 1;
2468     }
2469     }
2470     else
2471     {
2472     int callbackResult = TimeSlice( stream );
2473     if( callbackResult != paContinue )
2474     {
2475     /* FIXME implement handling of paComplete and paAbort if possible
2476     At the moment this should behave as if paComplete was called and
2477     flush the buffer.
2478     */
2479    
2480     stream->stopProcessing = 1;
2481     }
2482     }
2483    
2484     if( isFinished )
2485     {
2486     if( stream->streamRepresentation.streamFinishedCallback != 0 )
2487     stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
2488    
2489     stream->isActive = 0; /* don't set this until the stream really is inactive */
2490     SetEvent( stream->processingCompleted );
2491     }
2492     }
2493     }
2494    
2495     /***********************************************************************************
2496     When CloseStream() is called, the multi-api layer ensures that
2497     the stream has already been stopped or aborted.
2498     */
2499     static PaError CloseStream( PaStream* s )
2500     {
2501     PaError result = paNoError;
2502     PaWinDsStream *stream = (PaWinDsStream*)s;
2503    
2504     CloseHandle( stream->processingCompleted );
2505    
2506     // Cleanup the sound buffers
2507     if( stream->pDirectSoundOutputBuffer )
2508     {
2509     IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2510     IDirectSoundBuffer_Release( stream->pDirectSoundOutputBuffer );
2511     stream->pDirectSoundOutputBuffer = NULL;
2512     }
2513    
2514     if( stream->pDirectSoundPrimaryBuffer )
2515     {
2516     IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
2517     stream->pDirectSoundPrimaryBuffer = NULL;
2518     }
2519    
2520     if( stream->pDirectSoundInputBuffer )
2521     {
2522     IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2523     IDirectSoundCaptureBuffer_Release( stream->pDirectSoundInputBuffer );
2524     stream->pDirectSoundInputBuffer = NULL;
2525     }
2526    
2527     if( stream->pDirectSoundCapture )
2528     {
2529     IDirectSoundCapture_Release( stream->pDirectSoundCapture );
2530     stream->pDirectSoundCapture = NULL;
2531     }
2532    
2533     if( stream->pDirectSound )
2534     {
2535     IDirectSound_Release( stream->pDirectSound );
2536     stream->pDirectSound = NULL;
2537     }
2538    
2539     #ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
2540     if( stream->pDirectSoundFullDuplex8 )
2541     {
2542     IDirectSoundFullDuplex_Release( stream->pDirectSoundFullDuplex8 );
2543     stream->pDirectSoundFullDuplex8 = NULL;
2544     }
2545     #endif
2546    
2547     PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2548     PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2549     PaUtil_FreeMemory( stream );
2550    
2551     return result;
2552     }
2553    
2554     /***********************************************************************************/
2555     static HRESULT ClearOutputBuffer( PaWinDsStream *stream )
2556     {
2557     PaError result = paNoError;
2558     unsigned char* pDSBuffData;
2559     DWORD dwDataLen;
2560     HRESULT hr;
2561    
2562     hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 );
2563     DBUG(("PaHost_ClearOutputBuffer: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr));
2564     if( hr != DS_OK )
2565     return hr;
2566    
2567     // Lock the DS buffer
2568     if ((hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData,
2569     &dwDataLen, NULL, 0, 0)) != DS_OK )
2570     return hr;
2571    
2572     // Zero the DS buffer
2573     ZeroMemory(pDSBuffData, dwDataLen);
2574     // Unlock the DS buffer
2575     if ((hr = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK)
2576     return hr;
2577    
2578     // Let DSound set the starting write position because if we set it to zero, it looks like the
2579     // buffer is full to begin with. This causes a long pause before sound starts when using large buffers.
2580     if ((hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer,
2581     &stream->previousPlayCursor, &stream->outputBufferWriteOffsetBytes )) != DS_OK)
2582     return hr;
2583    
2584     /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */
2585    
2586     return DS_OK;
2587     }
2588    
2589     static PaError StartStream( PaStream *s )
2590     {
2591     PaError result = paNoError;
2592     PaWinDsStream *stream = (PaWinDsStream*)s;
2593     HRESULT hr;
2594    
2595     stream->callbackResult = paContinue;
2596     PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
2597    
2598     ResetEvent( stream->processingCompleted );
2599    
2600     if( stream->bufferProcessor.inputChannelCount > 0 )
2601     {
2602     // Start the buffer capture
2603     if( stream->pDirectSoundInputBuffer != NULL ) // FIXME: not sure this check is necessary
2604     {
2605     hr = IDirectSoundCaptureBuffer_Start( stream->pDirectSoundInputBuffer, DSCBSTART_LOOPING );
2606     }
2607    
2608     DBUG(("StartStream: DSW_StartInput returned = 0x%X.\n", hr));
2609     if( hr != DS_OK )
2610     {
2611     result = paUnanticipatedHostError;
2612     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2613     goto error;
2614     }
2615     }
2616    
2617     stream->framesWritten = 0;
2618     stream->callbackFlags = 0;
2619    
2620     stream->abortProcessing = 0;
2621     stream->stopProcessing = 0;
2622     stream->isActive = 1;
2623    
2624     if( stream->bufferProcessor.outputChannelCount > 0 )
2625     {
2626     QueryPerformanceCounter( &stream->previousPlayTime );
2627     stream->finalZeroBytesWritten = 0;
2628    
2629     hr = ClearOutputBuffer( stream );
2630     if( hr != DS_OK )
2631     {
2632     result = paUnanticipatedHostError;
2633     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2634     goto error;
2635     }
2636    
2637     if( stream->streamRepresentation.streamCallback && (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback) )
2638     {
2639     stream->callbackFlags = paPrimingOutput;
2640    
2641     TimeSlice( stream );
2642     /* we ignore the return value from TimeSlice here and start the stream as usual.
2643     The first timer callback will detect if the callback has completed. */
2644    
2645     stream->callbackFlags = 0;
2646     }
2647    
2648     // Start the buffer playback in a loop.
2649     if( stream->pDirectSoundOutputBuffer != NULL ) // FIXME: not sure this needs to be checked here
2650     {
2651     hr = IDirectSoundBuffer_Play( stream->pDirectSoundOutputBuffer, 0, 0, DSBPLAY_LOOPING );
2652     DBUG(("PaHost_StartOutput: IDirectSoundBuffer_Play returned = 0x%X.\n", hr));
2653     if( hr != DS_OK )
2654     {
2655     result = paUnanticipatedHostError;
2656     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2657     goto error;
2658     }
2659     stream->outputIsRunning = TRUE;
2660     }
2661     }
2662    
2663     if( stream->streamRepresentation.streamCallback )
2664     {
2665     /* Create timer that will wake us up so we can fill the DSound buffer. */
2666     int resolution;
2667     int framesPerWakeup = stream->framesPerDSBuffer / 4;
2668     int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
2669     if( msecPerWakeup < 10 ) msecPerWakeup = 10;
2670     else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
2671     resolution = msecPerWakeup/4;
2672     stream->timerID = timeSetEvent( msecPerWakeup, resolution, (LPTIMECALLBACK) TimerCallback,
2673     (DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
2674    
2675     if( stream->timerID == 0 )
2676     {
2677     stream->isActive = 0;
2678     result = paUnanticipatedHostError;
2679     PA_DS_SET_LAST_DIRECTSOUND_ERROR( hr );
2680     goto error;
2681     }
2682     }
2683    
2684     stream->isStarted = TRUE;
2685    
2686     error:
2687     return result;
2688     }
2689    
2690    
2691     /***********************************************************************************/
2692     static PaError StopStream( PaStream *s )
2693     {
2694     PaError result = paNoError;
2695     PaWinDsStream *stream = (PaWinDsStream*)s;
2696     HRESULT hr;
2697     int timeoutMsec;
2698    
2699     if( stream->streamRepresentation.streamCallback )
2700     {
2701     stream->stopProcessing = 1;
2702    
2703     /* Set timeout at 4 times maximum time we might wait. */
2704     timeoutMsec = (int) (4 * MSEC_PER_SECOND * (stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate));
2705    
2706     WaitForSingleObject( stream->processingCompleted, timeoutMsec );
2707     }
2708    
2709     if( stream->timerID != 0 )
2710     {
2711     timeKillEvent(stream->timerID); /* Stop callback timer. */
2712     stream->timerID = 0;
2713     }
2714    
2715    
2716     if( stream->bufferProcessor.outputChannelCount > 0 )
2717     {
2718     // Stop the buffer playback
2719     if( stream->pDirectSoundOutputBuffer != NULL )
2720     {
2721     stream->outputIsRunning = FALSE;
2722     // FIXME: what happens if IDirectSoundBuffer_Stop returns an error?
2723     hr = IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer );
2724    
2725     if( stream->pDirectSoundPrimaryBuffer )
2726     IDirectSoundBuffer_Stop( stream->pDirectSoundPrimaryBuffer ); /* FIXME we never started the primary buffer so I'm not sure we need to stop it */
2727     }
2728     }
2729    
2730     if( stream->bufferProcessor.inputChannelCount > 0 )
2731     {
2732     // Stop the buffer capture
2733     if( stream->pDirectSoundInputBuffer != NULL )
2734     {
2735     // FIXME: what happens if IDirectSoundCaptureBuffer_Stop returns an error?
2736     hr = IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
2737     }
2738     }
2739    
2740     stream->isStarted = FALSE;
2741    
2742     return result;
2743     }
2744    
2745    
2746     /***********************************************************************************/
2747     static PaError AbortStream( PaStream *s )
2748     {
2749     PaWinDsStream *stream = (PaWinDsStream*)s;
2750    
2751     stream->abortProcessing = 1;
2752     return StopStream( s );
2753     }
2754    
2755    
2756     /***********************************************************************************/
2757     static PaError IsStreamStopped( PaStream *s )
2758     {
2759     PaWinDsStream *stream = (PaWinDsStream*)s;
2760    
2761     return !stream->isStarted;
2762     }
2763    
2764    
2765     /***********************************************************************************/
2766     static PaError IsStreamActive( PaStream *s )
2767     {
2768     PaWinDsStream *stream = (PaWinDsStream*)s;
2769    
2770     return stream->isActive;
2771     }
2772    
2773     /***********************************************************************************/
2774     static PaTime GetStreamTime( PaStream *s )
2775     {
2776     /* suppress unused variable warnings */
2777     (void) s;
2778    
2779     return PaUtil_GetTime();
2780     }
2781    
2782    
2783     /***********************************************************************************/
2784     static double GetStreamCpuLoad( PaStream* s )
2785     {
2786     PaWinDsStream *stream = (PaWinDsStream*)s;
2787    
2788     return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
2789     }
2790    
2791    
2792     /***********************************************************************************
2793     As separate stream interfaces are used for blocking and callback
2794     streams, the following functions can be guaranteed to only be called
2795     for blocking streams.
2796     */
2797    
2798     static PaError ReadStream( PaStream* s,
2799     void *buffer,
2800     unsigned long frames )
2801     {
2802     PaWinDsStream *stream = (PaWinDsStream*)s;
2803    
2804     /* suppress unused variable warnings */
2805     (void) buffer;
2806     (void) frames;
2807     (void) stream;
2808    
2809     /* IMPLEMENT ME, see portaudio.h for required behavior*/
2810    
2811     return paNoError;
2812     }
2813    
2814    
2815     /***********************************************************************************/
2816     static PaError WriteStream( PaStream* s,
2817     const void *buffer,
2818     unsigned long frames )
2819     {
2820     PaWinDsStream *stream = (PaWinDsStream*)s;
2821    
2822     /* suppress unused variable warnings */
2823     (void) buffer;
2824     (void) frames;
2825     (void) stream;
2826    
2827     /* IMPLEMENT ME, see portaudio.h for required behavior*/
2828    
2829     return paNoError;
2830     }
2831    
2832    
2833     /***********************************************************************************/
2834     static signed long GetStreamReadAvailable( PaStream* s )
2835     {
2836     PaWinDsStream *stream = (PaWinDsStream*)s;
2837    
2838     /* suppress unused variable warnings */
2839     (void) stream;
2840    
2841     /* IMPLEMENT ME, see portaudio.h for required behavior*/
2842    
2843     return 0;
2844     }
2845    
2846    
2847     /***********************************************************************************/
2848     static signed long GetStreamWriteAvailable( PaStream* s )
2849     {
2850     PaWinDsStream *stream = (PaWinDsStream*)s;
2851    
2852     /* suppress unused variable warnings */
2853     (void) stream;
2854    
2855     /* IMPLEMENT ME, see portaudio.h for required behavior*/
2856    
2857     return 0;
2858     }
2859    
2860    
2861    

  ViewVC Help
Powered by ViewVC 1.1.22