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

  ViewVC Help
Powered by ViewVC 1.1.22