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

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

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.22