/[pcsx2_0.9.7]/trunk/3rdparty/portaudio/src/hostapi/asio/pa_asio.cpp
ViewVC logotype

Contents of /trunk/3rdparty/portaudio/src/hostapi/asio/pa_asio.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 273 - (show annotations) (download)
Fri Nov 12 01:10:22 2010 UTC (9 years, 2 months ago) by william
File size: 156083 byte(s)
Auto Commited Import of: pcsx2-0.9.7-DEBUG (upstream: v0.9.7.4013 local: v0.9.7.197-latest) in ./trunk
1 /*
2 * $Id: pa_asio.cpp 1525 2010-07-14 06:45:25Z rossb $
3 * Portable Audio I/O Library for ASIO Drivers
4 *
5 * Author: Stephane Letz
6 * Based on the Open Source API proposed by Ross Bencina
7 * Copyright (c) 2000-2002 Stephane Letz, Phil Burk, Ross Bencina
8 * Blocking i/o implementation by Sven Fischer, Institute of Hearing
9 * Technology and Audiology (www.hoertechnik-audiologie.de)
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining
12 * a copy of this software and associated documentation files
13 * (the "Software"), to deal in the Software without restriction,
14 * including without limitation the rights to use, copy, modify, merge,
15 * publish, distribute, sublicense, and/or sell copies of the Software,
16 * and to permit persons to whom the Software is furnished to do so,
17 * subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be
20 * included in all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
25 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
26 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
27 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 */
30
31 /*
32 * The text above constitutes the entire PortAudio license; however,
33 * the PortAudio community also makes the following non-binding requests:
34 *
35 * Any person wishing to distribute modifications to the Software is
36 * requested to send the modifications to the original developer so that
37 * they can be incorporated into the canonical version. It is also
38 * requested that these non-binding requests be included along with the
39 * license above.
40 */
41
42 /* Modification History
43
44 08-03-01 First version : Stephane Letz
45 08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk
46 08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz
47 08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz
48 08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil
49 08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the ouput buffer when
50 the stream is stopped : Stephane Letz
51 08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when
52 the stream is stopped : Stephane Letz
53 10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that
54 respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz
55 10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz
56 10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil
57 10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk
58 10-31-01 Clear the ouput buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz
59 11-06-01 Rename functions : Stephane Letz
60 11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz
61 11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk
62 01-03-02 Desallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly : Stephane Letz
63 02-01-02 Cleanup, test of multiple-stream opening : Stephane Letz
64 19-02-02 New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows : Stephane Letz
65 09-04-02 Correct error code management in PaHost_Term, removes various compiler warning : Stephane Letz
66 12-04-02 Add Mac includes for <Devices.h> and <Timer.h> : Phil Burk
67 13-04-02 Removes another compiler warning : Stephane Letz
68 30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz
69 12-06-02 Rehashed into new multi-api infrastructure, added support for all ASIO sample formats : Ross Bencina
70 18-06-02 Added pa_asio.h, PaAsio_GetAvailableLatencyValues() : Ross B.
71 21-06-02 Added SelectHostBufferSize() which selects host buffer size based on user latency parameters : Ross Bencina
72 ** NOTE maintanance history is now stored in CVS **
73 */
74
75 /** @file
76 @ingroup hostapi_src
77
78 Note that specific support for paInputUnderflow, paOutputOverflow and
79 paNeverDropInput is not necessary or possible with this driver due to the
80 synchronous full duplex double-buffered architecture of ASIO.
81
82 @todo implement host api specific extension to set i/o buffer sizes in frames
83
84 @todo review ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
85
86 @todo review Blocking i/o latency computations in OpenStream(), changing ring
87 buffer to a non-power-of-two structure could reduce blocking i/o latency.
88
89 @todo implement IsFormatSupported
90
91 @todo work out how to implement stream stoppage from callback and
92 implement IsStreamActive properly. Stream stoppage could be implemented
93 using a high-priority thread blocked on an Event which is signalled
94 by the callback. Or, we could just call ASIO stop from the callback
95 and see what happens.
96
97 @todo rigorously check asio return codes and convert to pa error codes
98
99 @todo Different channels of a multichannel stream can have different sample
100 formats, but we assume that all are the same as the first channel for now.
101 Fixing this will require the block processor to maintain per-channel
102 conversion functions - could get nasty.
103
104 @todo investigate whether the asio processNow flag needs to be honoured
105
106 @todo handle asioMessages() callbacks in a useful way, or at least document
107 what cases we don't handle.
108
109 @todo miscellaneous other FIXMEs
110
111 @todo provide an asio-specific method for setting the systems specific
112 value (aka main window handle) - check that this matches the value
113 passed to PaAsio_ShowControlPanel, or remove it entirely from
114 PaAsio_ShowControlPanel. - this would allow PaAsio_ShowControlPanel
115 to be called for the currently open stream (at present all streams
116 must be closed).
117 */
118
119
120
121 #include <stdio.h>
122 #include <assert.h>
123 #include <string.h>
124 //#include <values.h>
125 #include <new>
126
127 #include <windows.h>
128 #include <mmsystem.h>
129
130 #include "portaudio.h"
131 #include "pa_asio.h"
132 #include "pa_util.h"
133 #include "pa_allocation.h"
134 #include "pa_hostapi.h"
135 #include "pa_stream.h"
136 #include "pa_cpuload.h"
137 #include "pa_process.h"
138 #include "pa_debugprint.h"
139 #include "pa_ringbuffer.h"
140
141 /* This version of pa_asio.cpp is currently only targetted at Win32,
142 It would require a few tweaks to work with pre-OS X Macintosh.
143 To make configuration easier, we define WIN32 here to make sure
144 that the ASIO SDK knows this is Win32.
145 */
146 #ifndef WIN32
147 #define WIN32
148 #endif
149
150 #include "asiosys.h"
151 #include "asio.h"
152 #include "asiodrivers.h"
153 #include "iasiothiscallresolver.h"
154
155 /*
156 #if MAC
157 #include <Devices.h>
158 #include <Timer.h>
159 #include <Math64.h>
160 #else
161 */
162 /*
163 #include <math.h>
164 #include <windows.h>
165 #include <mmsystem.h>
166 */
167 /*
168 #endif
169 */
170
171
172 /* winmm.lib is needed for timeGetTime() (this is in winmm.a if you're using gcc) */
173 #if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
174 #pragma comment(lib, "winmm.lib")
175 #endif
176
177
178 /* external reference to ASIO SDK's asioDrivers.
179
180 This is a bit messy because we want to explicitly manage
181 allocation/deallocation of this structure, but some layers of the SDK
182 which we currently use (eg the implementation in asio.cpp) still
183 use this global version.
184
185 For now we keep it in sync with our local instance in the host
186 API representation structure, but later we should be able to remove
187 all dependence on it.
188 */
189 extern AsioDrivers* asioDrivers;
190
191
192 /* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
193 /* not tested at all since new V19 code was introduced. */
194 #define CARBON_COMPATIBLE (0)
195
196
197 /* prototypes for functions declared in this file */
198
199 extern "C" PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex );
200 static void Terminate( struct PaUtilHostApiRepresentation *hostApi );
201 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
202 PaStream** s,
203 const PaStreamParameters *inputParameters,
204 const PaStreamParameters *outputParameters,
205 double sampleRate,
206 unsigned long framesPerBuffer,
207 PaStreamFlags streamFlags,
208 PaStreamCallback *streamCallback,
209 void *userData );
210 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
211 const PaStreamParameters *inputParameters,
212 const PaStreamParameters *outputParameters,
213 double sampleRate );
214 static PaError CloseStream( PaStream* stream );
215 static PaError StartStream( PaStream *stream );
216 static PaError StopStream( PaStream *stream );
217 static PaError AbortStream( PaStream *stream );
218 static PaError IsStreamStopped( PaStream *s );
219 static PaError IsStreamActive( PaStream *stream );
220 static PaTime GetStreamTime( PaStream *stream );
221 static double GetStreamCpuLoad( PaStream* stream );
222 static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames );
223 static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames );
224 static signed long GetStreamReadAvailable( PaStream* stream );
225 static signed long GetStreamWriteAvailable( PaStream* stream );
226
227 /* Blocking i/o callback function. */
228 static int BlockingIoPaCallback(const void *inputBuffer ,
229 void *outputBuffer ,
230 unsigned long framesPerBuffer,
231 const PaStreamCallbackTimeInfo *timeInfo ,
232 PaStreamCallbackFlags statusFlags ,
233 void *userData );
234
235 /* our ASIO callback functions */
236
237 static void bufferSwitch(long index, ASIOBool processNow);
238 static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow);
239 static void sampleRateChanged(ASIOSampleRate sRate);
240 static long asioMessages(long selector, long value, void* message, double* opt);
241
242 static ASIOCallbacks asioCallbacks_ =
243 { bufferSwitch, sampleRateChanged, asioMessages, bufferSwitchTimeInfo };
244
245
246 #define PA_ASIO_SET_LAST_HOST_ERROR( errorCode, errorText ) \
247 PaUtil_SetLastHostErrorInfo( paASIO, errorCode, errorText )
248
249
250 static void PaAsio_SetLastSystemError( DWORD errorCode )
251 {
252 LPVOID lpMsgBuf;
253 FormatMessage(
254 FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
255 NULL,
256 errorCode,
257 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
258 (LPTSTR) &lpMsgBuf,
259 0,
260 NULL
261 );
262 PaUtil_SetLastHostErrorInfo( paASIO, errorCode, (const char*)lpMsgBuf );
263 LocalFree( lpMsgBuf );
264 }
265
266 #define PA_ASIO_SET_LAST_SYSTEM_ERROR( errorCode ) \
267 PaAsio_SetLastSystemError( errorCode )
268
269
270 static const char* PaAsio_GetAsioErrorText( ASIOError asioError )
271 {
272 const char *result;
273
274 switch( asioError ){
275 case ASE_OK:
276 case ASE_SUCCESS: result = "Success"; break;
277 case ASE_NotPresent: result = "Hardware input or output is not present or available"; break;
278 case ASE_HWMalfunction: result = "Hardware is malfunctioning"; break;
279 case ASE_InvalidParameter: result = "Input parameter invalid"; break;
280 case ASE_InvalidMode: result = "Hardware is in a bad mode or used in a bad mode"; break;
281 case ASE_SPNotAdvancing: result = "Hardware is not running when sample position is inquired"; break;
282 case ASE_NoClock: result = "Sample clock or rate cannot be determined or is not present"; break;
283 case ASE_NoMemory: result = "Not enough memory for completing the request"; break;
284 default: result = "Unknown ASIO error"; break;
285 }
286
287 return result;
288 }
289
290
291 #define PA_ASIO_SET_LAST_ASIO_ERROR( asioError ) \
292 PaUtil_SetLastHostErrorInfo( paASIO, asioError, PaAsio_GetAsioErrorText( asioError ) )
293
294
295
296
297 // Atomic increment and decrement operations
298 #if MAC
299 /* need to be implemented on Mac */
300 inline long PaAsio_AtomicIncrement(volatile long* v) {return ++(*const_cast<long*>(v));}
301 inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast<long*>(v));}
302 #elif WINDOWS
303 inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast<long*>(v));}
304 inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast<long*>(v));}
305 #endif
306
307
308
309 typedef struct PaAsioDriverInfo
310 {
311 ASIODriverInfo asioDriverInfo;
312 long inputChannelCount, outputChannelCount;
313 long bufferMinSize, bufferMaxSize, bufferPreferredSize, bufferGranularity;
314 bool postOutput;
315 }
316 PaAsioDriverInfo;
317
318
319 /* PaAsioHostApiRepresentation - host api datastructure specific to this implementation */
320
321 typedef struct
322 {
323 PaUtilHostApiRepresentation inheritedHostApiRep;
324 PaUtilStreamInterface callbackStreamInterface;
325 PaUtilStreamInterface blockingStreamInterface;
326
327 PaUtilAllocationGroup *allocations;
328
329 AsioDrivers *asioDrivers;
330 void *systemSpecific;
331
332 /* the ASIO C API only allows one ASIO driver to be open at a time,
333 so we keep track of whether we have the driver open here, and
334 use this information to return errors from OpenStream if the
335 driver is already open.
336
337 openAsioDeviceIndex will be PaNoDevice if there is no device open
338 and a valid pa_asio (not global) device index otherwise.
339
340 openAsioDriverInfo is populated with the driver info for the
341 currently open device (if any)
342 */
343 PaDeviceIndex openAsioDeviceIndex;
344 PaAsioDriverInfo openAsioDriverInfo;
345 }
346 PaAsioHostApiRepresentation;
347
348
349 /*
350 Retrieve <driverCount> driver names from ASIO, returned in a char**
351 allocated in <group>.
352 */
353 static char **GetAsioDriverNames( PaAsioHostApiRepresentation *asioHostApi, PaUtilAllocationGroup *group, long driverCount )
354 {
355 char **result = 0;
356 int i;
357
358 result =(char**)PaUtil_GroupAllocateMemory(
359 group, sizeof(char*) * driverCount );
360 if( !result )
361 goto error;
362
363 result[0] = (char*)PaUtil_GroupAllocateMemory(
364 group, 32 * driverCount );
365 if( !result[0] )
366 goto error;
367
368 for( i=0; i<driverCount; ++i )
369 result[i] = result[0] + (32 * i);
370
371 asioHostApi->asioDrivers->getDriverNames( result, driverCount );
372
373 error:
374 return result;
375 }
376
377
378 static PaSampleFormat AsioSampleTypeToPaNativeSampleFormat(ASIOSampleType type)
379 {
380 switch (type) {
381 case ASIOSTInt16MSB:
382 case ASIOSTInt16LSB:
383 return paInt16;
384
385 case ASIOSTFloat32MSB:
386 case ASIOSTFloat32LSB:
387 case ASIOSTFloat64MSB:
388 case ASIOSTFloat64LSB:
389 return paFloat32;
390
391 case ASIOSTInt32MSB:
392 case ASIOSTInt32LSB:
393 case ASIOSTInt32MSB16:
394 case ASIOSTInt32LSB16:
395 case ASIOSTInt32MSB18:
396 case ASIOSTInt32MSB20:
397 case ASIOSTInt32MSB24:
398 case ASIOSTInt32LSB18:
399 case ASIOSTInt32LSB20:
400 case ASIOSTInt32LSB24:
401 return paInt32;
402
403 case ASIOSTInt24MSB:
404 case ASIOSTInt24LSB:
405 return paInt24;
406
407 default:
408 return paCustomFormat;
409 }
410 }
411
412 void AsioSampleTypeLOG(ASIOSampleType type)
413 {
414 switch (type) {
415 case ASIOSTInt16MSB: PA_DEBUG(("ASIOSTInt16MSB\n")); break;
416 case ASIOSTInt16LSB: PA_DEBUG(("ASIOSTInt16LSB\n")); break;
417 case ASIOSTFloat32MSB:PA_DEBUG(("ASIOSTFloat32MSB\n"));break;
418 case ASIOSTFloat32LSB:PA_DEBUG(("ASIOSTFloat32LSB\n"));break;
419 case ASIOSTFloat64MSB:PA_DEBUG(("ASIOSTFloat64MSB\n"));break;
420 case ASIOSTFloat64LSB:PA_DEBUG(("ASIOSTFloat64LSB\n"));break;
421 case ASIOSTInt32MSB: PA_DEBUG(("ASIOSTInt32MSB\n")); break;
422 case ASIOSTInt32LSB: PA_DEBUG(("ASIOSTInt32LSB\n")); break;
423 case ASIOSTInt32MSB16:PA_DEBUG(("ASIOSTInt32MSB16\n"));break;
424 case ASIOSTInt32LSB16:PA_DEBUG(("ASIOSTInt32LSB16\n"));break;
425 case ASIOSTInt32MSB18:PA_DEBUG(("ASIOSTInt32MSB18\n"));break;
426 case ASIOSTInt32MSB20:PA_DEBUG(("ASIOSTInt32MSB20\n"));break;
427 case ASIOSTInt32MSB24:PA_DEBUG(("ASIOSTInt32MSB24\n"));break;
428 case ASIOSTInt32LSB18:PA_DEBUG(("ASIOSTInt32LSB18\n"));break;
429 case ASIOSTInt32LSB20:PA_DEBUG(("ASIOSTInt32LSB20\n"));break;
430 case ASIOSTInt32LSB24:PA_DEBUG(("ASIOSTInt32LSB24\n"));break;
431 case ASIOSTInt24MSB: PA_DEBUG(("ASIOSTInt24MSB\n")); break;
432 case ASIOSTInt24LSB: PA_DEBUG(("ASIOSTInt24LSB\n")); break;
433 default: PA_DEBUG(("Custom Format%d\n",type));break;
434
435 }
436 }
437
438 static int BytesPerAsioSample( ASIOSampleType sampleType )
439 {
440 switch (sampleType) {
441 case ASIOSTInt16MSB:
442 case ASIOSTInt16LSB:
443 return 2;
444
445 case ASIOSTFloat64MSB:
446 case ASIOSTFloat64LSB:
447 return 8;
448
449 case ASIOSTFloat32MSB:
450 case ASIOSTFloat32LSB:
451 case ASIOSTInt32MSB:
452 case ASIOSTInt32LSB:
453 case ASIOSTInt32MSB16:
454 case ASIOSTInt32LSB16:
455 case ASIOSTInt32MSB18:
456 case ASIOSTInt32MSB20:
457 case ASIOSTInt32MSB24:
458 case ASIOSTInt32LSB18:
459 case ASIOSTInt32LSB20:
460 case ASIOSTInt32LSB24:
461 return 4;
462
463 case ASIOSTInt24MSB:
464 case ASIOSTInt24LSB:
465 return 3;
466
467 default:
468 return 0;
469 }
470 }
471
472
473 static void Swap16( void *buffer, long shift, long count )
474 {
475 unsigned short *p = (unsigned short*)buffer;
476 unsigned short temp;
477 (void) shift; /* unused parameter */
478
479 while( count-- )
480 {
481 temp = *p;
482 *p++ = (unsigned short)((temp<<8) | (temp>>8));
483 }
484 }
485
486 static void Swap24( void *buffer, long shift, long count )
487 {
488 unsigned char *p = (unsigned char*)buffer;
489 unsigned char temp;
490 (void) shift; /* unused parameter */
491
492 while( count-- )
493 {
494 temp = *p;
495 *p = *(p+2);
496 *(p+2) = temp;
497 p += 3;
498 }
499 }
500
501 #define PA_SWAP32_( x ) ((x>>24) | ((x>>8)&0xFF00) | ((x<<8)&0xFF0000) | (x<<24));
502
503 static void Swap32( void *buffer, long shift, long count )
504 {
505 unsigned long *p = (unsigned long*)buffer;
506 unsigned long temp;
507 (void) shift; /* unused parameter */
508
509 while( count-- )
510 {
511 temp = *p;
512 *p++ = PA_SWAP32_( temp);
513 }
514 }
515
516 static void SwapShiftLeft32( void *buffer, long shift, long count )
517 {
518 unsigned long *p = (unsigned long*)buffer;
519 unsigned long temp;
520
521 while( count-- )
522 {
523 temp = *p;
524 temp = PA_SWAP32_( temp);
525 *p++ = temp << shift;
526 }
527 }
528
529 static void ShiftRightSwap32( void *buffer, long shift, long count )
530 {
531 unsigned long *p = (unsigned long*)buffer;
532 unsigned long temp;
533
534 while( count-- )
535 {
536 temp = *p >> shift;
537 *p++ = PA_SWAP32_( temp);
538 }
539 }
540
541 static void ShiftLeft32( void *buffer, long shift, long count )
542 {
543 unsigned long *p = (unsigned long*)buffer;
544 unsigned long temp;
545
546 while( count-- )
547 {
548 temp = *p;
549 *p++ = temp << shift;
550 }
551 }
552
553 static void ShiftRight32( void *buffer, long shift, long count )
554 {
555 unsigned long *p = (unsigned long*)buffer;
556 unsigned long temp;
557
558 while( count-- )
559 {
560 temp = *p;
561 *p++ = temp >> shift;
562 }
563 }
564
565 #define PA_SWAP_( x, y ) temp=x; x = y; y = temp;
566
567 static void Swap64ConvertFloat64ToFloat32( void *buffer, long shift, long count )
568 {
569 double *in = (double*)buffer;
570 float *out = (float*)buffer;
571 unsigned char *p;
572 unsigned char temp;
573 (void) shift; /* unused parameter */
574
575 while( count-- )
576 {
577 p = (unsigned char*)in;
578 PA_SWAP_( p[0], p[7] );
579 PA_SWAP_( p[1], p[6] );
580 PA_SWAP_( p[2], p[5] );
581 PA_SWAP_( p[3], p[4] );
582
583 *out++ = (float) (*in++);
584 }
585 }
586
587 static void ConvertFloat64ToFloat32( void *buffer, long shift, long count )
588 {
589 double *in = (double*)buffer;
590 float *out = (float*)buffer;
591 (void) shift; /* unused parameter */
592
593 while( count-- )
594 *out++ = (float) (*in++);
595 }
596
597 static void ConvertFloat32ToFloat64Swap64( void *buffer, long shift, long count )
598 {
599 float *in = ((float*)buffer) + (count-1);
600 double *out = ((double*)buffer) + (count-1);
601 unsigned char *p;
602 unsigned char temp;
603 (void) shift; /* unused parameter */
604
605 while( count-- )
606 {
607 *out = *in--;
608
609 p = (unsigned char*)out;
610 PA_SWAP_( p[0], p[7] );
611 PA_SWAP_( p[1], p[6] );
612 PA_SWAP_( p[2], p[5] );
613 PA_SWAP_( p[3], p[4] );
614
615 out--;
616 }
617 }
618
619 static void ConvertFloat32ToFloat64( void *buffer, long shift, long count )
620 {
621 float *in = ((float*)buffer) + (count-1);
622 double *out = ((double*)buffer) + (count-1);
623 (void) shift; /* unused parameter */
624
625 while( count-- )
626 *out-- = *in--;
627 }
628
629 #ifdef MAC
630 #define PA_MSB_IS_NATIVE_
631 #undef PA_LSB_IS_NATIVE_
632 #endif
633
634 #ifdef WINDOWS
635 #undef PA_MSB_IS_NATIVE_
636 #define PA_LSB_IS_NATIVE_
637 #endif
638
639 typedef void PaAsioBufferConverter( void *, long, long );
640
641 static void SelectAsioToPaConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
642 {
643 *shift = 0;
644 *converter = 0;
645
646 switch (type) {
647 case ASIOSTInt16MSB:
648 /* dest: paInt16, no conversion necessary, possible byte swap*/
649 #ifdef PA_LSB_IS_NATIVE_
650 *converter = Swap16;
651 #endif
652 break;
653 case ASIOSTInt16LSB:
654 /* dest: paInt16, no conversion necessary, possible byte swap*/
655 #ifdef PA_MSB_IS_NATIVE_
656 *converter = Swap16;
657 #endif
658 break;
659 case ASIOSTFloat32MSB:
660 /* dest: paFloat32, no conversion necessary, possible byte swap*/
661 #ifdef PA_LSB_IS_NATIVE_
662 *converter = Swap32;
663 #endif
664 break;
665 case ASIOSTFloat32LSB:
666 /* dest: paFloat32, no conversion necessary, possible byte swap*/
667 #ifdef PA_MSB_IS_NATIVE_
668 *converter = Swap32;
669 #endif
670 break;
671 case ASIOSTFloat64MSB:
672 /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
673 #ifdef PA_LSB_IS_NATIVE_
674 *converter = Swap64ConvertFloat64ToFloat32;
675 #else
676 *converter = ConvertFloat64ToFloat32;
677 #endif
678 break;
679 case ASIOSTFloat64LSB:
680 /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/
681 #ifdef PA_MSB_IS_NATIVE_
682 *converter = Swap64ConvertFloat64ToFloat32;
683 #else
684 *converter = ConvertFloat64ToFloat32;
685 #endif
686 break;
687 case ASIOSTInt32MSB:
688 /* dest: paInt32, no conversion necessary, possible byte swap */
689 #ifdef PA_LSB_IS_NATIVE_
690 *converter = Swap32;
691 #endif
692 break;
693 case ASIOSTInt32LSB:
694 /* dest: paInt32, no conversion necessary, possible byte swap */
695 #ifdef PA_MSB_IS_NATIVE_
696 *converter = Swap32;
697 #endif
698 break;
699 case ASIOSTInt32MSB16:
700 /* dest: paInt32, 16 bit shift, possible byte swap */
701 #ifdef PA_LSB_IS_NATIVE_
702 *converter = SwapShiftLeft32;
703 #else
704 *converter = ShiftLeft32;
705 #endif
706 *shift = 16;
707 break;
708 case ASIOSTInt32MSB18:
709 /* dest: paInt32, 14 bit shift, possible byte swap */
710 #ifdef PA_LSB_IS_NATIVE_
711 *converter = SwapShiftLeft32;
712 #else
713 *converter = ShiftLeft32;
714 #endif
715 *shift = 14;
716 break;
717 case ASIOSTInt32MSB20:
718 /* dest: paInt32, 12 bit shift, possible byte swap */
719 #ifdef PA_LSB_IS_NATIVE_
720 *converter = SwapShiftLeft32;
721 #else
722 *converter = ShiftLeft32;
723 #endif
724 *shift = 12;
725 break;
726 case ASIOSTInt32MSB24:
727 /* dest: paInt32, 8 bit shift, possible byte swap */
728 #ifdef PA_LSB_IS_NATIVE_
729 *converter = SwapShiftLeft32;
730 #else
731 *converter = ShiftLeft32;
732 #endif
733 *shift = 8;
734 break;
735 case ASIOSTInt32LSB16:
736 /* dest: paInt32, 16 bit shift, possible byte swap */
737 #ifdef PA_MSB_IS_NATIVE_
738 *converter = SwapShiftLeft32;
739 #else
740 *converter = ShiftLeft32;
741 #endif
742 *shift = 16;
743 break;
744 case ASIOSTInt32LSB18:
745 /* dest: paInt32, 14 bit shift, possible byte swap */
746 #ifdef PA_MSB_IS_NATIVE_
747 *converter = SwapShiftLeft32;
748 #else
749 *converter = ShiftLeft32;
750 #endif
751 *shift = 14;
752 break;
753 case ASIOSTInt32LSB20:
754 /* dest: paInt32, 12 bit shift, possible byte swap */
755 #ifdef PA_MSB_IS_NATIVE_
756 *converter = SwapShiftLeft32;
757 #else
758 *converter = ShiftLeft32;
759 #endif
760 *shift = 12;
761 break;
762 case ASIOSTInt32LSB24:
763 /* dest: paInt32, 8 bit shift, possible byte swap */
764 #ifdef PA_MSB_IS_NATIVE_
765 *converter = SwapShiftLeft32;
766 #else
767 *converter = ShiftLeft32;
768 #endif
769 *shift = 8;
770 break;
771 case ASIOSTInt24MSB:
772 /* dest: paInt24, no conversion necessary, possible byte swap */
773 #ifdef PA_LSB_IS_NATIVE_
774 *converter = Swap24;
775 #endif
776 break;
777 case ASIOSTInt24LSB:
778 /* dest: paInt24, no conversion necessary, possible byte swap */
779 #ifdef PA_MSB_IS_NATIVE_
780 *converter = Swap24;
781 #endif
782 break;
783 }
784 }
785
786
787 static void SelectPaToAsioConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift )
788 {
789 *shift = 0;
790 *converter = 0;
791
792 switch (type) {
793 case ASIOSTInt16MSB:
794 /* src: paInt16, no conversion necessary, possible byte swap*/
795 #ifdef PA_LSB_IS_NATIVE_
796 *converter = Swap16;
797 #endif
798 break;
799 case ASIOSTInt16LSB:
800 /* src: paInt16, no conversion necessary, possible byte swap*/
801 #ifdef PA_MSB_IS_NATIVE_
802 *converter = Swap16;
803 #endif
804 break;
805 case ASIOSTFloat32MSB:
806 /* src: paFloat32, no conversion necessary, possible byte swap*/
807 #ifdef PA_LSB_IS_NATIVE_
808 *converter = Swap32;
809 #endif
810 break;
811 case ASIOSTFloat32LSB:
812 /* src: paFloat32, no conversion necessary, possible byte swap*/
813 #ifdef PA_MSB_IS_NATIVE_
814 *converter = Swap32;
815 #endif
816 break;
817 case ASIOSTFloat64MSB:
818 /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
819 #ifdef PA_LSB_IS_NATIVE_
820 *converter = ConvertFloat32ToFloat64Swap64;
821 #else
822 *converter = ConvertFloat32ToFloat64;
823 #endif
824 break;
825 case ASIOSTFloat64LSB:
826 /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/
827 #ifdef PA_MSB_IS_NATIVE_
828 *converter = ConvertFloat32ToFloat64Swap64;
829 #else
830 *converter = ConvertFloat32ToFloat64;
831 #endif
832 break;
833 case ASIOSTInt32MSB:
834 /* src: paInt32, no conversion necessary, possible byte swap */
835 #ifdef PA_LSB_IS_NATIVE_
836 *converter = Swap32;
837 #endif
838 break;
839 case ASIOSTInt32LSB:
840 /* src: paInt32, no conversion necessary, possible byte swap */
841 #ifdef PA_MSB_IS_NATIVE_
842 *converter = Swap32;
843 #endif
844 break;
845 case ASIOSTInt32MSB16:
846 /* src: paInt32, 16 bit shift, possible byte swap */
847 #ifdef PA_LSB_IS_NATIVE_
848 *converter = ShiftRightSwap32;
849 #else
850 *converter = ShiftRight32;
851 #endif
852 *shift = 16;
853 break;
854 case ASIOSTInt32MSB18:
855 /* src: paInt32, 14 bit shift, possible byte swap */
856 #ifdef PA_LSB_IS_NATIVE_
857 *converter = ShiftRightSwap32;
858 #else
859 *converter = ShiftRight32;
860 #endif
861 *shift = 14;
862 break;
863 case ASIOSTInt32MSB20:
864 /* src: paInt32, 12 bit shift, possible byte swap */
865 #ifdef PA_LSB_IS_NATIVE_
866 *converter = ShiftRightSwap32;
867 #else
868 *converter = ShiftRight32;
869 #endif
870 *shift = 12;
871 break;
872 case ASIOSTInt32MSB24:
873 /* src: paInt32, 8 bit shift, possible byte swap */
874 #ifdef PA_LSB_IS_NATIVE_
875 *converter = ShiftRightSwap32;
876 #else
877 *converter = ShiftRight32;
878 #endif
879 *shift = 8;
880 break;
881 case ASIOSTInt32LSB16:
882 /* src: paInt32, 16 bit shift, possible byte swap */
883 #ifdef PA_MSB_IS_NATIVE_
884 *converter = ShiftRightSwap32;
885 #else
886 *converter = ShiftRight32;
887 #endif
888 *shift = 16;
889 break;
890 case ASIOSTInt32LSB18:
891 /* src: paInt32, 14 bit shift, possible byte swap */
892 #ifdef PA_MSB_IS_NATIVE_
893 *converter = ShiftRightSwap32;
894 #else
895 *converter = ShiftRight32;
896 #endif
897 *shift = 14;
898 break;
899 case ASIOSTInt32LSB20:
900 /* src: paInt32, 12 bit shift, possible byte swap */
901 #ifdef PA_MSB_IS_NATIVE_
902 *converter = ShiftRightSwap32;
903 #else
904 *converter = ShiftRight32;
905 #endif
906 *shift = 12;
907 break;
908 case ASIOSTInt32LSB24:
909 /* src: paInt32, 8 bit shift, possible byte swap */
910 #ifdef PA_MSB_IS_NATIVE_
911 *converter = ShiftRightSwap32;
912 #else
913 *converter = ShiftRight32;
914 #endif
915 *shift = 8;
916 break;
917 case ASIOSTInt24MSB:
918 /* src: paInt24, no conversion necessary, possible byte swap */
919 #ifdef PA_LSB_IS_NATIVE_
920 *converter = Swap24;
921 #endif
922 break;
923 case ASIOSTInt24LSB:
924 /* src: paInt24, no conversion necessary, possible byte swap */
925 #ifdef PA_MSB_IS_NATIVE_
926 *converter = Swap24;
927 #endif
928 break;
929 }
930 }
931
932
933 typedef struct PaAsioDeviceInfo
934 {
935 PaDeviceInfo commonDeviceInfo;
936 long minBufferSize;
937 long maxBufferSize;
938 long preferredBufferSize;
939 long bufferGranularity;
940
941 ASIOChannelInfo *asioChannelInfos;
942 }
943 PaAsioDeviceInfo;
944
945
946 PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
947 long *minLatency, long *maxLatency, long *preferredLatency, long *granularity )
948 {
949 PaError result;
950 PaUtilHostApiRepresentation *hostApi;
951 PaDeviceIndex hostApiDevice;
952
953 result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
954
955 if( result == paNoError )
956 {
957 result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi );
958
959 if( result == paNoError )
960 {
961 PaAsioDeviceInfo *asioDeviceInfo =
962 (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
963
964 *minLatency = asioDeviceInfo->minBufferSize;
965 *maxLatency = asioDeviceInfo->maxBufferSize;
966 *preferredLatency = asioDeviceInfo->preferredBufferSize;
967 *granularity = asioDeviceInfo->bufferGranularity;
968 }
969 }
970
971 return result;
972 }
973
974 /* Unload whatever we loaded in LoadAsioDriver().
975 Also balance the call to CoInitialize(0).
976 */
977 static void UnloadAsioDriver( void )
978 {
979 ASIOExit();
980 CoUninitialize();
981 }
982
983 /*
984 load the asio driver named by <driverName> and return statistics about
985 the driver in info. If no error occurred, the driver will remain open
986 and must be closed by the called by calling UnloadAsioDriver() - if an error
987 is returned the driver will already be unloaded.
988 */
989 static PaError LoadAsioDriver( PaAsioHostApiRepresentation *asioHostApi, const char *driverName,
990 PaAsioDriverInfo *driverInfo, void *systemSpecific )
991 {
992 PaError result = paNoError;
993 ASIOError asioError;
994 int asioIsInitialized = 0;
995
996 /*
997 ASIO uses CoCreateInstance() to load a driver. That requires that
998 CoInitialize(0) be called for every thread that loads a driver.
999 It is OK to call CoInitialize(0) multiple times form one thread as long
1000 as it is balanced by a call to CoUninitialize(). See UnloadAsioDriver().
1001
1002 The V18 version called CoInitialize() starting on 2/19/02.
1003 That was removed from PA V19 for unknown reasons.
1004 Phil Burk added it back on 6/27/08 so that JSyn would work.
1005 */
1006 CoInitialize( 0 );
1007
1008 if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(driverName) ) )
1009 {
1010 /* If this returns an error then it might be because CoInitialize(0) was removed.
1011 It should be called right before this.
1012 */
1013 result = paUnanticipatedHostError;
1014 PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
1015 goto error;
1016 }
1017
1018 memset( &driverInfo->asioDriverInfo, 0, sizeof(ASIODriverInfo) );
1019 driverInfo->asioDriverInfo.asioVersion = 2;
1020 driverInfo->asioDriverInfo.sysRef = systemSpecific;
1021 if( (asioError = ASIOInit( &driverInfo->asioDriverInfo )) != ASE_OK )
1022 {
1023 result = paUnanticipatedHostError;
1024 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1025 goto error;
1026 }
1027 else
1028 {
1029 asioIsInitialized = 1;
1030 }
1031
1032 if( (asioError = ASIOGetChannels(&driverInfo->inputChannelCount,
1033 &driverInfo->outputChannelCount)) != ASE_OK )
1034 {
1035 result = paUnanticipatedHostError;
1036 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1037 goto error;
1038 }
1039
1040 if( (asioError = ASIOGetBufferSize(&driverInfo->bufferMinSize,
1041 &driverInfo->bufferMaxSize, &driverInfo->bufferPreferredSize,
1042 &driverInfo->bufferGranularity)) != ASE_OK )
1043 {
1044 result = paUnanticipatedHostError;
1045 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1046 goto error;
1047 }
1048
1049 if( ASIOOutputReady() == ASE_OK )
1050 driverInfo->postOutput = true;
1051 else
1052 driverInfo->postOutput = false;
1053
1054 return result;
1055
1056 error:
1057 if( asioIsInitialized )
1058 {
1059 ASIOExit();
1060 }
1061 CoUninitialize();
1062 return result;
1063 }
1064
1065
1066 #define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ 13 /* must be the same number of elements as in the array below */
1067 static ASIOSampleRate defaultSampleRateSearchOrder_[]
1068 = {44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0,
1069 192000.0, 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 };
1070
1071
1072 /* we look up IsDebuggerPresent at runtime incase it isn't present (on Win95 for example) */
1073 typedef BOOL (WINAPI *IsDebuggerPresentPtr)(VOID);
1074 IsDebuggerPresentPtr IsDebuggerPresent_ = 0;
1075 //FARPROC IsDebuggerPresent_ = 0; // this is the current way to do it apparently according to davidv
1076
1077 PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
1078 {
1079 PaError result = paNoError;
1080 int i, driverCount;
1081 PaAsioHostApiRepresentation *asioHostApi;
1082 PaAsioDeviceInfo *deviceInfoArray;
1083 char **names;
1084 PaAsioDriverInfo paAsioDriverInfo;
1085
1086 asioHostApi = (PaAsioHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaAsioHostApiRepresentation) );
1087 if( !asioHostApi )
1088 {
1089 result = paInsufficientMemory;
1090 goto error;
1091 }
1092
1093 asioHostApi->asioDrivers = 0; /* avoid surprises in our error handler below */
1094
1095 asioHostApi->allocations = PaUtil_CreateAllocationGroup();
1096 if( !asioHostApi->allocations )
1097 {
1098 result = paInsufficientMemory;
1099 goto error;
1100 }
1101
1102 /* Allocate the AsioDrivers() driver list (class from ASIO SDK) */
1103 try
1104 {
1105 asioHostApi->asioDrivers = new AsioDrivers(); /* calls CoInitialize(0) */
1106 }
1107 catch (std::bad_alloc)
1108 {
1109 asioHostApi->asioDrivers = 0;
1110 }
1111 /* some implementations of new (ie MSVC, see http://support.microsoft.com/?kbid=167733)
1112 don't throw std::bad_alloc, so we also explicitly test for a null return. */
1113 if( asioHostApi->asioDrivers == 0 )
1114 {
1115 result = paInsufficientMemory;
1116 goto error;
1117 }
1118
1119 asioDrivers = asioHostApi->asioDrivers; /* keep SDK global in sync until we stop depending on it */
1120
1121 asioHostApi->systemSpecific = 0;
1122 asioHostApi->openAsioDeviceIndex = paNoDevice;
1123
1124 *hostApi = &asioHostApi->inheritedHostApiRep;
1125 (*hostApi)->info.structVersion = 1;
1126
1127 (*hostApi)->info.type = paASIO;
1128 (*hostApi)->info.name = "ASIO";
1129 (*hostApi)->info.deviceCount = 0;
1130
1131 #ifdef WINDOWS
1132 /* use desktop window as system specific ptr */
1133 asioHostApi->systemSpecific = GetDesktopWindow();
1134 #endif
1135
1136 /* driverCount is the number of installed drivers - not necessarily
1137 the number of installed physical devices. */
1138 #if MAC
1139 driverCount = asioHostApi->asioDrivers->getNumFragments();
1140 #elif WINDOWS
1141 driverCount = asioHostApi->asioDrivers->asioGetNumDev();
1142 #endif
1143
1144 if( driverCount > 0 )
1145 {
1146 names = GetAsioDriverNames( asioHostApi, asioHostApi->allocations, driverCount );
1147 if( !names )
1148 {
1149 result = paInsufficientMemory;
1150 goto error;
1151 }
1152
1153
1154 /* allocate enough space for all drivers, even if some aren't installed */
1155
1156 (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
1157 asioHostApi->allocations, sizeof(PaDeviceInfo*) * driverCount );
1158 if( !(*hostApi)->deviceInfos )
1159 {
1160 result = paInsufficientMemory;
1161 goto error;
1162 }
1163
1164 /* allocate all device info structs in a contiguous block */
1165 deviceInfoArray = (PaAsioDeviceInfo*)PaUtil_GroupAllocateMemory(
1166 asioHostApi->allocations, sizeof(PaAsioDeviceInfo) * driverCount );
1167 if( !deviceInfoArray )
1168 {
1169 result = paInsufficientMemory;
1170 goto error;
1171 }
1172
1173 IsDebuggerPresent_ = (IsDebuggerPresentPtr)GetProcAddress( LoadLibrary( "Kernel32.dll" ), "IsDebuggerPresent" );
1174
1175 for( i=0; i < driverCount; ++i )
1176 {
1177
1178 PA_DEBUG(("ASIO names[%d]:%s\n",i,names[i]));
1179
1180 // Since portaudio opens ALL ASIO drivers, and no one else does that,
1181 // we face fact that some drivers were not meant for it, drivers which act
1182 // like shells on top of REAL drivers, for instance.
1183 // so we get duplicate handles, locks and other problems.
1184 // so lets NOT try to load any such wrappers.
1185 // The ones i [davidv] know of so far are:
1186
1187 if ( strcmp (names[i],"ASIO DirectX Full Duplex Driver") == 0
1188 || strcmp (names[i],"ASIO Multimedia Driver") == 0
1189 || strncmp(names[i],"Premiere",8) == 0 //"Premiere Elements Windows Sound 1.0"
1190 || strncmp(names[i],"Adobe",5) == 0 //"Adobe Default Windows Sound 1.5"
1191 )
1192 {
1193 PA_DEBUG(("BLACKLISTED!!!\n"));
1194 continue;
1195 }
1196
1197
1198 if( IsDebuggerPresent_ && IsDebuggerPresent_() )
1199 {
1200 /* ASIO Digidesign Driver uses PACE copy protection which quits out
1201 if a debugger is running. So we don't load it if a debugger is running. */
1202 if( strcmp(names[i], "ASIO Digidesign Driver") == 0 )
1203 {
1204 PA_DEBUG(("BLACKLISTED!!! ASIO Digidesign Driver would quit the debugger\n"));
1205 continue;
1206 }
1207 }
1208
1209
1210 /* Attempt to load the asio driver... */
1211 if( LoadAsioDriver( asioHostApi, names[i], &paAsioDriverInfo, asioHostApi->systemSpecific ) == paNoError )
1212 {
1213 PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
1214 PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo;
1215
1216 deviceInfo->structVersion = 2;
1217 deviceInfo->hostApi = hostApiIndex;
1218
1219 deviceInfo->name = names[i];
1220 PA_DEBUG(("PaAsio_Initialize: drv:%d name = %s\n", i,deviceInfo->name));
1221 PA_DEBUG(("PaAsio_Initialize: drv:%d inputChannels = %d\n", i, paAsioDriverInfo.inputChannelCount));
1222 PA_DEBUG(("PaAsio_Initialize: drv:%d outputChannels = %d\n", i, paAsioDriverInfo.outputChannelCount));
1223 PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMinSize = %d\n", i, paAsioDriverInfo.bufferMinSize));
1224 PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMaxSize = %d\n", i, paAsioDriverInfo.bufferMaxSize));
1225 PA_DEBUG(("PaAsio_Initialize: drv:%d bufferPreferredSize = %d\n", i, paAsioDriverInfo.bufferPreferredSize));
1226 PA_DEBUG(("PaAsio_Initialize: drv:%d bufferGranularity = %d\n", i, paAsioDriverInfo.bufferGranularity));
1227
1228 deviceInfo->maxInputChannels = paAsioDriverInfo.inputChannelCount;
1229 deviceInfo->maxOutputChannels = paAsioDriverInfo.outputChannelCount;
1230
1231 deviceInfo->defaultSampleRate = 0.;
1232 bool foundDefaultSampleRate = false;
1233 for( int j=0; j < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++j )
1234 {
1235 ASIOError asioError = ASIOCanSampleRate( defaultSampleRateSearchOrder_[j] );
1236 if( asioError != ASE_NoClock && asioError != ASE_NotPresent )
1237 {
1238 deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[j];
1239 foundDefaultSampleRate = true;
1240 break;
1241 }
1242 }
1243
1244 PA_DEBUG(("PaAsio_Initialize: drv:%d defaultSampleRate = %f\n", i, deviceInfo->defaultSampleRate));
1245
1246 if( foundDefaultSampleRate ){
1247
1248 /* calculate default latency values from bufferPreferredSize
1249 for default low latency, and bufferPreferredSize * 3
1250 for default high latency.
1251 use the default sample rate to convert from samples to
1252 seconds. Without knowing what sample rate the user will
1253 use this is the best we can do.
1254 */
1255
1256 double defaultLowLatency =
1257 paAsioDriverInfo.bufferPreferredSize / deviceInfo->defaultSampleRate;
1258
1259 deviceInfo->defaultLowInputLatency = defaultLowLatency;
1260 deviceInfo->defaultLowOutputLatency = defaultLowLatency;
1261
1262 long defaultHighLatencyBufferSize =
1263 paAsioDriverInfo.bufferPreferredSize * 3;
1264
1265 if( defaultHighLatencyBufferSize > paAsioDriverInfo.bufferMaxSize )
1266 defaultHighLatencyBufferSize = paAsioDriverInfo.bufferMaxSize;
1267
1268 double defaultHighLatency =
1269 defaultHighLatencyBufferSize / deviceInfo->defaultSampleRate;
1270
1271 if( defaultHighLatency < defaultLowLatency )
1272 defaultHighLatency = defaultLowLatency; /* just incase the driver returns something strange */
1273
1274 deviceInfo->defaultHighInputLatency = defaultHighLatency;
1275 deviceInfo->defaultHighOutputLatency = defaultHighLatency;
1276
1277 }else{
1278
1279 deviceInfo->defaultLowInputLatency = 0.;
1280 deviceInfo->defaultLowOutputLatency = 0.;
1281 deviceInfo->defaultHighInputLatency = 0.;
1282 deviceInfo->defaultHighOutputLatency = 0.;
1283 }
1284
1285 PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowInputLatency = %f\n", i, deviceInfo->defaultLowInputLatency));
1286 PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowOutputLatency = %f\n", i, deviceInfo->defaultLowOutputLatency));
1287 PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighInputLatency = %f\n", i, deviceInfo->defaultHighInputLatency));
1288 PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighOutputLatency = %f\n", i, deviceInfo->defaultHighOutputLatency));
1289
1290 asioDeviceInfo->minBufferSize = paAsioDriverInfo.bufferMinSize;
1291 asioDeviceInfo->maxBufferSize = paAsioDriverInfo.bufferMaxSize;
1292 asioDeviceInfo->preferredBufferSize = paAsioDriverInfo.bufferPreferredSize;
1293 asioDeviceInfo->bufferGranularity = paAsioDriverInfo.bufferGranularity;
1294
1295
1296 asioDeviceInfo->asioChannelInfos = (ASIOChannelInfo*)PaUtil_GroupAllocateMemory(
1297 asioHostApi->allocations,
1298 sizeof(ASIOChannelInfo) * (deviceInfo->maxInputChannels
1299 + deviceInfo->maxOutputChannels) );
1300 if( !asioDeviceInfo->asioChannelInfos )
1301 {
1302 result = paInsufficientMemory;
1303 goto error_unload;
1304 }
1305
1306 int a;
1307
1308 for( a=0; a < deviceInfo->maxInputChannels; ++a ){
1309 asioDeviceInfo->asioChannelInfos[a].channel = a;
1310 asioDeviceInfo->asioChannelInfos[a].isInput = ASIOTrue;
1311 ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[a] );
1312 if( asioError != ASE_OK )
1313 {
1314 result = paUnanticipatedHostError;
1315 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1316 goto error_unload;
1317 }
1318 }
1319
1320 for( a=0; a < deviceInfo->maxOutputChannels; ++a ){
1321 int b = deviceInfo->maxInputChannels + a;
1322 asioDeviceInfo->asioChannelInfos[b].channel = a;
1323 asioDeviceInfo->asioChannelInfos[b].isInput = ASIOFalse;
1324 ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[b] );
1325 if( asioError != ASE_OK )
1326 {
1327 result = paUnanticipatedHostError;
1328 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
1329 goto error_unload;
1330 }
1331 }
1332
1333
1334 /* unload the driver */
1335 UnloadAsioDriver();
1336
1337 (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
1338 ++(*hostApi)->info.deviceCount;
1339 }
1340 }
1341 }
1342
1343 if( (*hostApi)->info.deviceCount > 0 )
1344 {
1345 (*hostApi)->info.defaultInputDevice = 0;
1346 (*hostApi)->info.defaultOutputDevice = 0;
1347 }
1348 else
1349 {
1350 (*hostApi)->info.defaultInputDevice = paNoDevice;
1351 (*hostApi)->info.defaultOutputDevice = paNoDevice;
1352 }
1353
1354
1355 (*hostApi)->Terminate = Terminate;
1356 (*hostApi)->OpenStream = OpenStream;
1357 (*hostApi)->IsFormatSupported = IsFormatSupported;
1358
1359 PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream,
1360 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1361 GetStreamTime, GetStreamCpuLoad,
1362 PaUtil_DummyRead, PaUtil_DummyWrite,
1363 PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable );
1364
1365 PaUtil_InitializeStreamInterface( &asioHostApi->blockingStreamInterface, CloseStream, StartStream,
1366 StopStream, AbortStream, IsStreamStopped, IsStreamActive,
1367 GetStreamTime, PaUtil_DummyGetCpuLoad,
1368 ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable );
1369
1370 return result;
1371
1372 error_unload:
1373 UnloadAsioDriver();
1374
1375 error:
1376 if( asioHostApi )
1377 {
1378 if( asioHostApi->allocations )
1379 {
1380 PaUtil_FreeAllAllocations( asioHostApi->allocations );
1381 PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
1382 }
1383
1384 delete asioHostApi->asioDrivers;
1385 asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
1386
1387 PaUtil_FreeMemory( asioHostApi );
1388 }
1389 return result;
1390 }
1391
1392
1393 static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
1394 {
1395 PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
1396
1397 /*
1398 IMPLEMENT ME:
1399 - clean up any resources not handled by the allocation group (need to review if there are any)
1400 */
1401
1402 if( asioHostApi->allocations )
1403 {
1404 PaUtil_FreeAllAllocations( asioHostApi->allocations );
1405 PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
1406 }
1407
1408 delete asioHostApi->asioDrivers; /* calls CoUninitialize() */
1409 asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
1410
1411 PaUtil_FreeMemory( asioHostApi );
1412 }
1413
1414
1415 static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
1416 const PaStreamParameters *inputParameters,
1417 const PaStreamParameters *outputParameters,
1418 double sampleRate )
1419 {
1420 PaError result = paNoError;
1421 PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
1422 PaAsioDriverInfo *driverInfo = &asioHostApi->openAsioDriverInfo;
1423 int inputChannelCount, outputChannelCount;
1424 PaSampleFormat inputSampleFormat, outputSampleFormat;
1425 PaDeviceIndex asioDeviceIndex;
1426 ASIOError asioError;
1427
1428 if( inputParameters && outputParameters )
1429 {
1430 /* full duplex ASIO stream must use the same device for input and output */
1431
1432 if( inputParameters->device != outputParameters->device )
1433 return paBadIODeviceCombination;
1434 }
1435
1436 if( inputParameters )
1437 {
1438 inputChannelCount = inputParameters->channelCount;
1439 inputSampleFormat = inputParameters->sampleFormat;
1440
1441 /* all standard sample formats are supported by the buffer adapter,
1442 this implementation doesn't support any custom sample formats */
1443 if( inputSampleFormat & paCustomFormat )
1444 return paSampleFormatNotSupported;
1445
1446 /* unless alternate device specification is supported, reject the use of
1447 paUseHostApiSpecificDeviceSpecification */
1448
1449 if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1450 return paInvalidDevice;
1451
1452 asioDeviceIndex = inputParameters->device;
1453
1454 /* validate inputStreamInfo */
1455 /** @todo do more validation here */
1456 // if( inputParameters->hostApiSpecificStreamInfo )
1457 // return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1458 }
1459 else
1460 {
1461 inputChannelCount = 0;
1462 }
1463
1464 if( outputParameters )
1465 {
1466 outputChannelCount = outputParameters->channelCount;
1467 outputSampleFormat = outputParameters->sampleFormat;
1468
1469 /* all standard sample formats are supported by the buffer adapter,
1470 this implementation doesn't support any custom sample formats */
1471 if( outputSampleFormat & paCustomFormat )
1472 return paSampleFormatNotSupported;
1473
1474 /* unless alternate device specification is supported, reject the use of
1475 paUseHostApiSpecificDeviceSpecification */
1476
1477 if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1478 return paInvalidDevice;
1479
1480 asioDeviceIndex = outputParameters->device;
1481
1482 /* validate outputStreamInfo */
1483 /** @todo do more validation here */
1484 // if( outputParameters->hostApiSpecificStreamInfo )
1485 // return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */
1486 }
1487 else
1488 {
1489 outputChannelCount = 0;
1490 }
1491
1492
1493
1494 /* if an ASIO device is open we can only get format information for the currently open device */
1495
1496 if( asioHostApi->openAsioDeviceIndex != paNoDevice
1497 && asioHostApi->openAsioDeviceIndex != asioDeviceIndex )
1498 {
1499 return paDeviceUnavailable;
1500 }
1501
1502
1503 /* NOTE: we load the driver and use its current settings
1504 rather than the ones in our device info structure which may be stale */
1505
1506 /* open the device if it's not already open */
1507 if( asioHostApi->openAsioDeviceIndex == paNoDevice )
1508 {
1509 result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
1510 driverInfo, asioHostApi->systemSpecific );
1511 if( result != paNoError )
1512 return result;
1513 }
1514
1515 /* check that input device can support inputChannelCount */
1516 if( inputChannelCount > 0 )
1517 {
1518 if( inputChannelCount > driverInfo->inputChannelCount )
1519 {
1520 result = paInvalidChannelCount;
1521 goto done;
1522 }
1523 }
1524
1525 /* check that output device can support outputChannelCount */
1526 if( outputChannelCount )
1527 {
1528 if( outputChannelCount > driverInfo->outputChannelCount )
1529 {
1530 result = paInvalidChannelCount;
1531 goto done;
1532 }
1533 }
1534
1535 /* query for sample rate support */
1536 asioError = ASIOCanSampleRate( sampleRate );
1537 if( asioError == ASE_NoClock || asioError == ASE_NotPresent )
1538 {
1539 result = paInvalidSampleRate;
1540 goto done;
1541 }
1542
1543 done:
1544 /* close the device if it wasn't already open */
1545 if( asioHostApi->openAsioDeviceIndex == paNoDevice )
1546 {
1547 UnloadAsioDriver(); /* not sure if we should check for errors here */
1548 }
1549
1550 if( result == paNoError )
1551 return paFormatIsSupported;
1552 else
1553 return result;
1554 }
1555
1556
1557
1558 /** A data structure specifically for storing blocking i/o related data. */
1559 typedef struct PaAsioStreamBlockingState
1560 {
1561 int stopFlag; /**< Flag indicating that block processing is to be stopped. */
1562
1563 unsigned long writeBuffersRequested; /**< The number of available output buffers, requested by the #WriteStream() function. */
1564 unsigned long readFramesRequested; /**< The number of available input frames, requested by the #ReadStream() function. */
1565
1566 int writeBuffersRequestedFlag; /**< Flag to indicate that #WriteStream() has requested more output buffers to be available. */
1567 int readFramesRequestedFlag; /**< Flag to indicate that #ReadStream() requires more input frames to be available. */
1568
1569 HANDLE writeBuffersReadyEvent; /**< Event to signal that requested output buffers are available. */
1570 HANDLE readFramesReadyEvent; /**< Event to signal that requested input frames are available. */
1571
1572 void *writeRingBufferData; /**< The actual ring buffer memory, used by the output ring buffer. */
1573 void *readRingBufferData; /**< The actual ring buffer memory, used by the input ring buffer. */
1574
1575 PaUtilRingBuffer writeRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store output data (interleaved user format). */
1576 PaUtilRingBuffer readRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store input data (interleaved user format). */
1577
1578 long writeRingBufferInitialFrames; /**< The initial number of silent frames within the output ring buffer. */
1579
1580 const void **writeStreamBuffer; /**< Temp buffer, used by #WriteStream() for handling non-interleaved data. */
1581 void **readStreamBuffer; /**< Temp buffer, used by #ReadStream() for handling non-interleaved data. */
1582
1583 PaUtilBufferProcessor bufferProcessor; /**< Buffer processor, used to handle the blocking i/o ring buffers. */
1584
1585 int outputUnderflowFlag; /**< Flag to signal an output underflow from within the callback function. */
1586 int inputOverflowFlag; /**< Flag to signal an input overflow from within the callback function. */
1587 }
1588 PaAsioStreamBlockingState;
1589
1590
1591
1592 /* PaAsioStream - a stream data structure specifically for this implementation */
1593
1594 typedef struct PaAsioStream
1595 {
1596 PaUtilStreamRepresentation streamRepresentation;
1597 PaUtilCpuLoadMeasurer cpuLoadMeasurer;
1598 PaUtilBufferProcessor bufferProcessor;
1599
1600 PaAsioHostApiRepresentation *asioHostApi;
1601 unsigned long framesPerHostCallback;
1602
1603 /* ASIO driver info - these may not be needed for the life of the stream,
1604 but store them here until we work out how format conversion is going
1605 to work. */
1606
1607 ASIOBufferInfo *asioBufferInfos;
1608 ASIOChannelInfo *asioChannelInfos;
1609 long inputLatency, outputLatency; // actual latencies returned by asio
1610
1611 long inputChannelCount, outputChannelCount;
1612 bool postOutput;
1613
1614 void **bufferPtrs; /* this is carved up for inputBufferPtrs and outputBufferPtrs */
1615 void **inputBufferPtrs[2];
1616 void **outputBufferPtrs[2];
1617
1618 PaAsioBufferConverter *inputBufferConverter;
1619 long inputShift;
1620 PaAsioBufferConverter *outputBufferConverter;
1621 long outputShift;
1622
1623 volatile bool stopProcessing;
1624 int stopPlayoutCount;
1625 HANDLE completedBuffersPlayedEvent;
1626
1627 bool streamFinishedCallbackCalled;
1628 int isStopped;
1629 volatile int isActive;
1630 volatile bool zeroOutput; /* all future calls to the callback will output silence */
1631
1632 volatile long reenterCount;
1633 volatile long reenterError;
1634
1635 PaStreamCallbackFlags callbackFlags;
1636
1637 PaAsioStreamBlockingState *blockingState; /**< Blocking i/o data struct, or NULL when using callback interface. */
1638 }
1639 PaAsioStream;
1640
1641 static PaAsioStream *theAsioStream = 0; /* due to ASIO sdk limitations there can be only one stream */
1642
1643
1644 static void ZeroOutputBuffers( PaAsioStream *stream, long index )
1645 {
1646 int i;
1647
1648 for( i=0; i < stream->outputChannelCount; ++i )
1649 {
1650 void *buffer = stream->asioBufferInfos[ i + stream->inputChannelCount ].buffers[index];
1651
1652 int bytesPerSample = BytesPerAsioSample( stream->asioChannelInfos[ i + stream->inputChannelCount ].type );
1653
1654 memset( buffer, 0, stream->framesPerHostCallback * bytesPerSample );
1655 }
1656 }
1657
1658
1659 static unsigned long SelectHostBufferSize( unsigned long suggestedLatencyFrames,
1660 PaAsioDriverInfo *driverInfo )
1661 {
1662 unsigned long result;
1663
1664 if( suggestedLatencyFrames == 0 )
1665 {
1666 result = driverInfo->bufferPreferredSize;
1667 }
1668 else{
1669 if( suggestedLatencyFrames <= (unsigned long)driverInfo->bufferMinSize )
1670 {
1671 result = driverInfo->bufferMinSize;
1672 }
1673 else if( suggestedLatencyFrames >= (unsigned long)driverInfo->bufferMaxSize )
1674 {
1675 result = driverInfo->bufferMaxSize;
1676 }
1677 else
1678 {
1679 if( driverInfo->bufferGranularity == -1 )
1680 {
1681 /* power-of-two */
1682 result = 2;
1683
1684 while( result < suggestedLatencyFrames )
1685 result *= 2;
1686
1687 if( result < (unsigned long)driverInfo->bufferMinSize )
1688 result = driverInfo->bufferMinSize;
1689
1690 if( result > (unsigned long)driverInfo->bufferMaxSize )
1691 result = driverInfo->bufferMaxSize;
1692 }
1693 else if( driverInfo->bufferGranularity == 0 )
1694 {
1695 /* the documentation states that bufferGranularity should be
1696 zero when bufferMinSize, bufferMaxSize and
1697 bufferPreferredSize are the same. We assume that is the case.
1698 */
1699
1700 result = driverInfo->bufferPreferredSize;
1701 }
1702 else
1703 {
1704 /* modulo granularity */
1705
1706 unsigned long remainder =
1707 suggestedLatencyFrames % driverInfo->bufferGranularity;
1708
1709 if( remainder == 0 )
1710 {
1711 result = suggestedLatencyFrames;
1712 }
1713 else
1714 {
1715 result = suggestedLatencyFrames
1716 + (driverInfo->bufferGranularity - remainder);
1717
1718 if( result > (unsigned long)driverInfo->bufferMaxSize )
1719 result = driverInfo->bufferMaxSize;
1720 }
1721 }
1722 }
1723 }
1724
1725 return result;
1726 }
1727
1728
1729 /* returns channelSelectors if present */
1730
1731 static PaError ValidateAsioSpecificStreamInfo(
1732 const PaStreamParameters *streamParameters,
1733 const PaAsioStreamInfo *streamInfo,
1734 int deviceChannelCount,
1735 int **channelSelectors )
1736 {
1737 if( streamInfo )
1738 {
1739 if( streamInfo->size != sizeof( PaAsioStreamInfo )
1740 || streamInfo->version != 1 )
1741 {
1742 return paIncompatibleHostApiSpecificStreamInfo;
1743 }
1744
1745 if( streamInfo->flags & paAsioUseChannelSelectors )
1746 *channelSelectors = streamInfo->channelSelectors;
1747
1748 if( !(*channelSelectors) )
1749 return paIncompatibleHostApiSpecificStreamInfo;
1750
1751 for( int i=0; i < streamParameters->channelCount; ++i ){
1752 if( (*channelSelectors)[i] < 0
1753 || (*channelSelectors)[i] >= deviceChannelCount ){
1754 return paInvalidChannelCount;
1755 }
1756 }
1757 }
1758
1759 return paNoError;
1760 }
1761
1762
1763 static bool IsUsingExternalClockSource()
1764 {
1765 bool result = false;
1766 ASIOError asioError;
1767 ASIOClockSource clocks[32];
1768 long numSources=32;
1769
1770 /* davidv: listing ASIO Clock sources. there is an ongoing investigation by
1771 me about whether or not to call ASIOSetSampleRate if an external Clock is
1772 used. A few drivers expected different things here */
1773
1774 asioError = ASIOGetClockSources(clocks, &numSources);
1775 if( asioError != ASE_OK ){
1776 PA_DEBUG(("ERROR: ASIOGetClockSources: %s\n", PaAsio_GetAsioErrorText(asioError) ));
1777 }else{
1778 PA_DEBUG(("INFO ASIOGetClockSources listing %d clocks\n", numSources ));
1779 for (int i=0;i<numSources;++i){
1780 PA_DEBUG(("ASIOClockSource%d %s current:%d\n", i, clocks[i].name, clocks[i].isCurrentSource ));
1781
1782 if (clocks[i].isCurrentSource)
1783 result = true;
1784 }
1785 }
1786
1787 return result;
1788 }
1789
1790
1791 static PaError ValidateAndSetSampleRate( double sampleRate )
1792 {
1793 PaError result = paNoError;
1794 ASIOError asioError;
1795
1796 // check that the device supports the requested sample rate
1797
1798 asioError = ASIOCanSampleRate( sampleRate );
1799 PA_DEBUG(("ASIOCanSampleRate(%f):%d\n", sampleRate, asioError ));
1800
1801 if( asioError != ASE_OK )
1802 {
1803 result = paInvalidSampleRate;
1804 PA_DEBUG(("ERROR: ASIOCanSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
1805 goto error;
1806 }
1807
1808 // retrieve the current sample rate, we only change to the requested
1809 // sample rate if the device is not already in that rate.
1810
1811 ASIOSampleRate oldRate;
1812 asioError = ASIOGetSampleRate(&oldRate);
1813 if( asioError != ASE_OK )
1814 {
1815 result = paInvalidSampleRate;
1816 PA_DEBUG(("ERROR: ASIOGetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
1817 goto error;
1818 }
1819 PA_DEBUG(("ASIOGetSampleRate:%f\n",oldRate));
1820
1821 if (oldRate != sampleRate){
1822 /* Set sample rate */
1823
1824 PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
1825
1826 /*
1827 If you have problems with some drivers when externally clocked,
1828 try switching on the following line and commenting out the one after it.
1829 See IsUsingExternalClockSource() for more info.
1830 */
1831 //if( IsUsingExternalClockSource() ){
1832 if( false ){
1833 asioError = ASIOSetSampleRate( 0 );
1834 }else{
1835 asioError = ASIOSetSampleRate( sampleRate );
1836 }
1837 if( asioError != ASE_OK )
1838 {
1839 result = paInvalidSampleRate;
1840 PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
1841 goto error;
1842 }
1843 PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate));
1844 }
1845 else
1846 {
1847 PA_DEBUG(("No Need to change SR\n"));
1848 }
1849
1850 error:
1851 return result;
1852 }
1853
1854
1855 /* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
1856
1857 static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
1858 PaStream** s,
1859 const PaStreamParameters *inputParameters,
1860 const PaStreamParameters *outputParameters,
1861 double sampleRate,
1862 unsigned long framesPerBuffer,
1863 PaStreamFlags streamFlags,
1864 PaStreamCallback *streamCallback,
1865 void *userData )
1866 {
1867 PaError result = paNoError;
1868 PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
1869 PaAsioStream *stream = 0;
1870 PaAsioStreamInfo *inputStreamInfo, *outputStreamInfo;
1871 unsigned long framesPerHostBuffer;
1872 int inputChannelCount, outputChannelCount;
1873 PaSampleFormat inputSampleFormat, outputSampleFormat;
1874 PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
1875 unsigned long suggestedInputLatencyFrames;
1876 unsigned long suggestedOutputLatencyFrames;
1877 PaDeviceIndex asioDeviceIndex;
1878 ASIOError asioError;
1879 int asioIsInitialized = 0;
1880 int asioBuffersCreated = 0;
1881 int completedBuffersPlayedEventInited = 0;
1882 int i;
1883 PaAsioDriverInfo *driverInfo;
1884 int *inputChannelSelectors = 0;
1885 int *outputChannelSelectors = 0;
1886
1887 /* Are we using blocking i/o interface? */
1888 int usingBlockingIo = ( !streamCallback ) ? TRUE : FALSE;
1889 /* Blocking i/o stuff */
1890 long lBlockingBufferSize = 0; /* Desired ring buffer size in samples. */
1891 long lBlockingBufferSizePow2 = 0; /* Power-of-2 rounded ring buffer size. */
1892 long lBytesPerFrame = 0; /* Number of bytes per input/output frame. */
1893 int blockingWriteBuffersReadyEventInitialized = 0; /* Event init flag. */
1894 int blockingReadFramesReadyEventInitialized = 0; /* Event init flag. */
1895
1896 int callbackBufferProcessorInited = FALSE;
1897 int blockingBufferProcessorInited = FALSE;
1898
1899 /* unless we move to using lower level ASIO calls, we can only have
1900 one device open at a time */
1901 if( asioHostApi->openAsioDeviceIndex != paNoDevice )
1902 {
1903 PA_DEBUG(("OpenStream paDeviceUnavailable\n"));
1904 return paDeviceUnavailable;
1905 }
1906
1907 assert( theAsioStream == 0 );
1908
1909 if( inputParameters && outputParameters )
1910 {
1911 /* full duplex ASIO stream must use the same device for input and output */
1912
1913 if( inputParameters->device != outputParameters->device )
1914 {
1915 PA_DEBUG(("OpenStream paBadIODeviceCombination\n"));
1916 return paBadIODeviceCombination;
1917 }
1918 }
1919
1920 if( inputParameters )
1921 {
1922 inputChannelCount = inputParameters->channelCount;
1923 inputSampleFormat = inputParameters->sampleFormat;
1924 suggestedInputLatencyFrames = (unsigned long)((inputParameters->suggestedLatency * sampleRate)+0.5f);
1925
1926 /* unless alternate device specification is supported, reject the use of
1927 paUseHostApiSpecificDeviceSpecification */
1928 if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
1929 return paInvalidDevice;
1930
1931 asioDeviceIndex = inputParameters->device;
1932
1933 PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex];
1934
1935 /* validate hostApiSpecificStreamInfo */
1936 inputStreamInfo = (PaAsioStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
1937 result = ValidateAsioSpecificStreamInfo( inputParameters, inputStreamInfo,
1938 asioDeviceInfo->commonDeviceInfo.maxInputChannels,
1939 &inputChannelSelectors
1940 );
1941 if( result != paNoError ) return result;
1942 }
1943 else
1944 {
1945 inputChannelCount = 0;
1946 inputSampleFormat = 0;
1947 suggestedInputLatencyFrames = 0;
1948 }
1949
1950 if( outputParameters )
1951 {
1952 outputChannelCount = outputParameters->channelCount;
1953 outputSampleFormat = outputParameters->sampleFormat;
1954 suggestedOutputLatencyFrames = (unsigned long)((outputParameters->suggestedLatency * sampleRate)+0.5f);
1955
1956 /* unless alternate device specification is supported, reject the use of
1957 paUseHostApiSpecificDeviceSpecification */
1958 if( outputParameters->device == paUseHostApiSpecificDeviceSpecification )
1959 return paInvalidDevice;
1960
1961 asioDeviceIndex = outputParameters->device;
1962
1963 PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex];
1964
1965 /* validate hostApiSpecificStreamInfo */
1966 outputStreamInfo = (PaAsioStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
1967 result = ValidateAsioSpecificStreamInfo( outputParameters, outputStreamInfo,
1968 asioDeviceInfo->commonDeviceInfo.maxOutputChannels,
1969 &outputChannelSelectors
1970 );
1971 if( result != paNoError ) return result;
1972 }
1973 else
1974 {
1975 outputChannelCount = 0;
1976 outputSampleFormat = 0;
1977 suggestedOutputLatencyFrames = 0;
1978 }
1979
1980 driverInfo = &asioHostApi->openAsioDriverInfo;
1981
1982 /* NOTE: we load the driver and use its current settings
1983 rather than the ones in our device info structure which may be stale */
1984
1985 result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
1986 driverInfo, asioHostApi->systemSpecific );
1987 if( result == paNoError )
1988 asioIsInitialized = 1;
1989 else{
1990 PA_DEBUG(("OpenStream ERROR1 - LoadAsioDriver returned %d\n", result));
1991 goto error;
1992 }
1993
1994 /* check that input device can support inputChannelCount */
1995 if( inputChannelCount > 0 )
1996 {
1997 if( inputChannelCount > driverInfo->inputChannelCount )
1998 {
1999 result = paInvalidChannelCount;
2000 PA_DEBUG(("OpenStream ERROR2\n"));
2001 goto error;
2002 }
2003 }
2004
2005 /* check that output device can support outputChannelCount */
2006 if( outputChannelCount )
2007 {
2008 if( outputChannelCount > driverInfo->outputChannelCount )
2009 {
2010 result = paInvalidChannelCount;
2011 PA_DEBUG(("OpenStream ERROR3\n"));
2012 goto error;
2013 }
2014 }
2015
2016 result = ValidateAndSetSampleRate( sampleRate );
2017 if( result != paNoError )
2018 goto error;
2019
2020 /*
2021 IMPLEMENT ME:
2022 - if a full duplex stream is requested, check that the combination
2023 of input and output parameters is supported
2024 */
2025
2026 /* validate platform specific flags */
2027 if( (streamFlags & paPlatformSpecificFlags) != 0 ){
2028 PA_DEBUG(("OpenStream invalid flags!!\n"));
2029 return paInvalidFlag; /* unexpected platform specific flag */
2030 }
2031
2032
2033 stream = (PaAsioStream*)PaUtil_AllocateMemory( sizeof(PaAsioStream) );
2034 if( !stream )
2035 {
2036 result = paInsufficientMemory;
2037 PA_DEBUG(("OpenStream ERROR5\n"));
2038 goto error;
2039 }
2040 stream->blockingState = NULL; /* Blocking i/o not initialized, yet. */
2041
2042
2043 stream->completedBuffersPlayedEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
2044 if( stream->completedBuffersPlayedEvent == NULL )
2045 {
2046 result = paUnanticipatedHostError;
2047 PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
2048 PA_DEBUG(("OpenStream ERROR6\n"));
2049 goto error;
2050 }
2051 completedBuffersPlayedEventInited = 1;
2052
2053
2054 stream->asioBufferInfos = 0; /* for deallocation in error */
2055 stream->asioChannelInfos = 0; /* for deallocation in error */
2056 stream->bufferPtrs = 0; /* for deallocation in error */
2057
2058 /* Using blocking i/o interface... */
2059 if( usingBlockingIo )
2060 {
2061 /* Blocking i/o is implemented by running callback mode, using a special blocking i/o callback. */
2062 streamCallback = BlockingIoPaCallback; /* Setup PA to use the ASIO blocking i/o callback. */
2063 userData = &theAsioStream; /* The callback user data will be the PA ASIO stream. */
2064 PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2065 &asioHostApi->blockingStreamInterface, streamCallback, userData );
2066 }
2067 else /* Using callback interface... */
2068 {
2069 PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
2070 &asioHostApi->callbackStreamInterface, streamCallback, userData );
2071 }
2072
2073
2074 PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
2075
2076
2077 stream->asioBufferInfos = (ASIOBufferInfo*)PaUtil_AllocateMemory(
2078 sizeof(ASIOBufferInfo) * (inputChannelCount + outputChannelCount) );
2079 if( !stream->asioBufferInfos )
2080 {
2081 result = paInsufficientMemory;
2082 PA_DEBUG(("OpenStream ERROR7\n"));
2083 goto error;
2084 }
2085
2086
2087 for( i=0; i < inputChannelCount; ++i )
2088 {
2089 ASIOBufferInfo *info = &stream->asioBufferInfos[i];
2090
2091 info->isInput = ASIOTrue;
2092
2093 if( inputChannelSelectors ){
2094 // inputChannelSelectors values have already been validated in
2095 // ValidateAsioSpecificStreamInfo() above
2096 info->channelNum = inputChannelSelectors[i];
2097 }else{
2098 info->channelNum = i;
2099 }
2100
2101 info->buffers[0] = info->buffers[1] = 0;
2102 }
2103
2104 for( i=0; i < outputChannelCount; ++i ){
2105 ASIOBufferInfo *info = &stream->asioBufferInfos[inputChannelCount+i];
2106
2107 info->isInput = ASIOFalse;
2108
2109 if( outputChannelSelectors ){
2110 // outputChannelSelectors values have already been validated in
2111 // ValidateAsioSpecificStreamInfo() above
2112 info->channelNum = outputChannelSelectors[i];
2113 }else{
2114 info->channelNum = i;
2115 }
2116
2117 info->buffers[0] = info->buffers[1] = 0;
2118 }
2119
2120
2121 /* Using blocking i/o interface... */
2122 if( usingBlockingIo )
2123 {
2124 /** @todo REVIEW selection of host buffer size for blocking i/o */
2125 /* Use default host latency for blocking i/o. */
2126 framesPerHostBuffer = SelectHostBufferSize( 0, driverInfo );
2127
2128 }
2129 else /* Using callback interface... */
2130 {
2131 framesPerHostBuffer = SelectHostBufferSize(
2132 (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames )
2133 ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames),
2134 driverInfo );
2135 }
2136
2137
2138 PA_DEBUG(("PaAsioOpenStream: framesPerHostBuffer :%d\n", framesPerHostBuffer));
2139
2140 asioError = ASIOCreateBuffers( stream->asioBufferInfos,
2141 inputChannelCount+outputChannelCount,
2142 framesPerHostBuffer, &asioCallbacks_ );
2143
2144 if( asioError != ASE_OK
2145 && framesPerHostBuffer != (unsigned long)driverInfo->bufferPreferredSize )
2146 {
2147 PA_DEBUG(("ERROR: ASIOCreateBuffers: %s\n", PaAsio_GetAsioErrorText(asioError) ));
2148 /*
2149 Some buggy drivers (like the Hoontech DSP24) give incorrect
2150 [min, preferred, max] values They should work with the preferred size
2151 value, thus if Pa_ASIO_CreateBuffers fails with the hostBufferSize
2152 computed in SelectHostBufferSize, we try again with the preferred size.
2153 */
2154
2155 framesPerHostBuffer = driverInfo->bufferPreferredSize;
2156
2157 PA_DEBUG(("PaAsioOpenStream: CORRECTED framesPerHostBuffer :%d\n", framesPerHostBuffer));
2158
2159 ASIOError asioError2 = ASIOCreateBuffers( stream->asioBufferInfos,
2160 inputChannelCount+outputChannelCount,
2161 framesPerHostBuffer, &asioCallbacks_ );
2162 if( asioError2 == ASE_OK )
2163 asioError = ASE_OK;
2164 }
2165
2166 if( asioError != ASE_OK )
2167 {
2168 result = paUnanticipatedHostError;
2169 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
2170 PA_DEBUG(("OpenStream ERROR9\n"));
2171 goto error;
2172 }
2173
2174 asioBuffersCreated = 1;
2175
2176 stream->asioChannelInfos = (ASIOChannelInfo*)PaUtil_AllocateMemory(
2177 sizeof(ASIOChannelInfo) * (inputChannelCount + outputChannelCount) );
2178 if( !stream->asioChannelInfos )
2179 {
2180 result = paInsufficientMemory;
2181 PA_DEBUG(("OpenStream ERROR10\n"));
2182 goto error;
2183 }
2184
2185 for( i=0; i < inputChannelCount + outputChannelCount; ++i )
2186 {
2187 stream->asioChannelInfos[i].channel = stream->asioBufferInfos[i].channelNum;
2188 stream->asioChannelInfos[i].isInput = stream->asioBufferInfos[i].isInput;
2189 asioError = ASIOGetChannelInfo( &stream->asioChannelInfos[i] );
2190 if( asioError != ASE_OK )
2191 {
2192 result = paUnanticipatedHostError;
2193 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
2194 PA_DEBUG(("OpenStream ERROR11\n"));
2195 goto error;
2196 }
2197 }
2198
2199 stream->bufferPtrs = (void**)PaUtil_AllocateMemory(
2200 2 * sizeof(void*) * (inputChannelCount + outputChannelCount) );
2201 if( !stream->bufferPtrs )
2202 {
2203 result = paInsufficientMemory;
2204 PA_DEBUG(("OpenStream ERROR12\n"));
2205 goto error;
2206 }
2207
2208 if( inputChannelCount > 0 )
2209 {
2210 stream->inputBufferPtrs[0] = stream-> bufferPtrs;
2211 stream->inputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount];
2212
2213 for( i=0; i<inputChannelCount; ++i )
2214 {
2215 stream->inputBufferPtrs[0][i] = stream->asioBufferInfos[i].buffers[0];
2216 stream->inputBufferPtrs[1][i] = stream->asioBufferInfos[i].buffers[1];
2217 }
2218 }
2219 else
2220 {
2221 stream->inputBufferPtrs[0] = 0;
2222 stream->inputBufferPtrs[1] = 0;
2223 }
2224
2225 if( outputChannelCount > 0 )
2226 {
2227 stream->outputBufferPtrs[0] = &stream->bufferPtrs[inputChannelCount*2];
2228 stream->outputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount*2 + outputChannelCount];
2229
2230 for( i=0; i<outputChannelCount; ++i )
2231 {
2232 stream->outputBufferPtrs[0][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[0];
2233 stream->outputBufferPtrs[1][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[1];
2234 }
2235 }
2236 else
2237 {
2238 stream->outputBufferPtrs[0] = 0;
2239 stream->outputBufferPtrs[1] = 0;
2240 }
2241
2242 if( inputChannelCount > 0 )
2243 {
2244 /* FIXME: assume all channels use the same type for now */
2245 ASIOSampleType inputType = stream->asioChannelInfos[0].type;
2246
2247 PA_DEBUG(("ASIO Input type:%d",inputType));
2248 AsioSampleTypeLOG(inputType);
2249 hostInputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( inputType );
2250
2251 SelectAsioToPaConverter( inputType, &stream->inputBufferConverter, &stream->inputShift );
2252 }
2253 else
2254 {
2255 hostInputSampleFormat = 0;
2256 stream->inputBufferConverter = 0;
2257 }
2258
2259 if( outputChannelCount > 0 )
2260 {
2261 /* FIXME: assume all channels use the same type for now */
2262 ASIOSampleType outputType = stream->asioChannelInfos[inputChannelCount].type;
2263
2264 PA_DEBUG(("ASIO Output type:%d",outputType));
2265 AsioSampleTypeLOG(outputType);
2266 hostOutputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( outputType );
2267
2268 SelectPaToAsioConverter( outputType, &stream->outputBufferConverter, &stream->outputShift );
2269 }
2270 else
2271 {
2272 hostOutputSampleFormat = 0;
2273 stream->outputBufferConverter = 0;
2274 }
2275
2276
2277 ASIOGetLatencies( &stream->inputLatency, &stream->outputLatency );
2278
2279
2280 /* Using blocking i/o interface... */
2281 if( usingBlockingIo )
2282 {
2283 /* Allocate the blocking i/o input ring buffer memory. */
2284 stream->blockingState = (PaAsioStreamBlockingState*)PaUtil_AllocateMemory( sizeof(PaAsioStreamBlockingState) );
2285 if( !stream->blockingState )
2286 {
2287 result = paInsufficientMemory;
2288 PA_DEBUG(("ERROR! Blocking i/o interface struct allocation failed in OpenStream()\n"));
2289 goto error;
2290 }
2291
2292 /* Initialize blocking i/o interface struct. */
2293 stream->blockingState->readFramesReadyEvent = NULL; /* Uninitialized, yet. */
2294 stream->blockingState->writeBuffersReadyEvent = NULL; /* Uninitialized, yet. */
2295 stream->blockingState->readRingBufferData = NULL; /* Uninitialized, yet. */
2296 stream->blockingState->writeRingBufferData = NULL; /* Uninitialized, yet. */
2297 stream->blockingState->readStreamBuffer = NULL; /* Uninitialized, yet. */
2298 stream->blockingState->writeStreamBuffer = NULL; /* Uninitialized, yet. */
2299 stream->blockingState->stopFlag = TRUE; /* Not started, yet. */
2300
2301
2302 /* If the user buffer is unspecified */
2303 if( framesPerBuffer == paFramesPerBufferUnspecified )
2304 {
2305 /* Make the user buffer the same size as the host buffer. */
2306 framesPerBuffer = framesPerHostBuffer;
2307 }
2308
2309
2310 /* Initialize callback buffer processor. */
2311 result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor ,
2312 inputChannelCount ,
2313 inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */
2314 hostInputSampleFormat , /* Host format. */
2315 outputChannelCount ,
2316 outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */
2317 hostOutputSampleFormat , /* Host format. */
2318 sampleRate ,
2319 streamFlags ,
2320 framesPerBuffer , /* Frames per ring buffer block. */
2321 framesPerHostBuffer , /* Frames per asio buffer. */
2322 paUtilFixedHostBufferSize ,
2323 streamCallback ,
2324 userData );
2325 if( result != paNoError ){
2326 PA_DEBUG(("OpenStream ERROR13\n"));
2327 goto error;
2328 }
2329 callbackBufferProcessorInited = TRUE;
2330
2331 /* Initialize the blocking i/o buffer processor. */
2332 result = PaUtil_InitializeBufferProcessor(&stream->blockingState->bufferProcessor,
2333 inputChannelCount ,
2334 inputSampleFormat , /* User format. */
2335 inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */
2336 outputChannelCount ,
2337 outputSampleFormat , /* User format. */
2338 outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */
2339 sampleRate ,
2340 paClipOff | paDitherOff , /* Don't use dither nor clipping. */
2341 framesPerBuffer , /* Frames per user buffer. */
2342 framesPerBuffer , /* Frames per ring buffer block. */
2343 paUtilBoundedHostBufferSize ,
2344 NULL, NULL );/* No callback! */
2345 if( result != paNoError ){
2346 PA_DEBUG(("ERROR! Blocking i/o buffer processor initialization failed in OpenStream()\n"));
2347 goto error;
2348 }
2349 blockingBufferProcessorInited = TRUE;
2350
2351 /* If input is requested. */
2352 if( inputChannelCount )
2353 {
2354 /* Create the callback sync-event. */
2355 stream->blockingState->readFramesReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
2356 if( stream->blockingState->readFramesReadyEvent == NULL )
2357 {
2358 result = paUnanticipatedHostError;
2359 PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
2360 PA_DEBUG(("ERROR! Blocking i/o \"read frames ready\" event creation failed in OpenStream()\n"));
2361 goto error;
2362 }
2363 blockingReadFramesReadyEventInitialized = 1;
2364
2365
2366 /* Create pointer buffer to access non-interleaved data in ReadStream() */
2367 stream->blockingState->readStreamBuffer = (void**)PaUtil_AllocateMemory( sizeof(void*) * inputChannelCount );
2368 if( !stream->blockingState->readStreamBuffer )
2369 {
2370 result = paInsufficientMemory;
2371 PA_DEBUG(("ERROR! Blocking i/o read stream buffer allocation failed in OpenStream()\n"));
2372 goto error;
2373 }
2374
2375 /* The ring buffer should store as many data blocks as needed
2376 to achieve the requested latency. Whereas it must be large
2377 enough to store at least two complete data blocks.
2378
2379 1) Determine the amount of latency to be added to the
2380 prefered ASIO latency.
2381 2) Make sure we have at lest one additional latency frame.
2382 3) Divide the number of frames by the desired block size to
2383 get the number (rounded up to pure integer) of blocks to
2384 be stored in the buffer.
2385 4) Add one additional block for block processing and convert
2386 to samples frames.
2387 5) Get the next larger (or equal) power-of-two buffer size.
2388 */
2389 lBlockingBufferSize = suggestedInputLatencyFrames - stream->inputLatency;
2390 lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1;
2391 lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer;
2392 lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer;
2393
2394 /* Get the next larger or equal power-of-two buffersize. */
2395 lBlockingBufferSizePow2 = 1;
2396 while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) );
2397 lBlockingBufferSize = lBlockingBufferSizePow2;
2398
2399 /* Compute total intput latency in seconds */
2400 stream->streamRepresentation.streamInfo.inputLatency =
2401 (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor )
2402 + PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor)
2403 + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
2404 + stream->inputLatency )
2405 / sampleRate;
2406
2407 /* The code below prints the ASIO latency which doesn't include
2408 the buffer processor latency nor the blocking i/o latency. It
2409 reports the added latency separately.
2410 */
2411 PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
2412 stream->inputLatency,
2413 (long)( stream->inputLatency * (1000.0 / sampleRate) ),
2414 PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
2415 (long)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
2416 PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
2417 (long)( (PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
2418 ));
2419
2420 /* Determine the size of ring buffer in bytes. */
2421 lBytesPerFrame = inputChannelCount * Pa_GetSampleSize(inputSampleFormat );
2422
2423 /* Allocate the blocking i/o input ring buffer memory. */
2424 stream->blockingState->readRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame );
2425 if( !stream->blockingState->readRingBufferData )
2426 {
2427 result = paInsufficientMemory;
2428 PA_DEBUG(("ERROR! Blocking i/o input ring buffer allocation failed in OpenStream()\n"));
2429 goto error;
2430 }
2431
2432 /* Initialize the input ring buffer struct. */
2433 PaUtil_InitializeRingBuffer( &stream->blockingState->readRingBuffer ,
2434 lBytesPerFrame ,
2435 lBlockingBufferSize ,
2436 stream->blockingState->readRingBufferData );
2437 }
2438
2439 /* If output is requested. */
2440 if( outputChannelCount )
2441 {
2442 stream->blockingState->writeBuffersReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
2443 if( stream->blockingState->writeBuffersReadyEvent == NULL )
2444 {
2445 result = paUnanticipatedHostError;
2446 PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
2447 PA_DEBUG(("ERROR! Blocking i/o \"write buffers ready\" event creation failed in OpenStream()\n"));
2448 goto error;
2449 }
2450 blockingWriteBuffersReadyEventInitialized = 1;
2451
2452 /* Create pointer buffer to access non-interleaved data in WriteStream() */
2453 stream->blockingState->writeStreamBuffer = (const void**)PaUtil_AllocateMemory( sizeof(const void*) * outputChannelCount );
2454 if( !stream->blockingState->writeStreamBuffer )
2455 {
2456 result = paInsufficientMemory;
2457 PA_DEBUG(("ERROR! Blocking i/o write stream buffer allocation failed in OpenStream()\n"));
2458 goto error;
2459 }
2460
2461 /* The ring buffer should store as many data blocks as needed
2462 to achieve the requested latency. Whereas it must be large
2463 enough to store at least two complete data blocks.
2464
2465 1) Determine the amount of latency to be added to the
2466 prefered ASIO latency.
2467 2) Make sure we have at lest one additional latency frame.
2468 3) Divide the number of frames by the desired block size to
2469 get the number (rounded up to pure integer) of blocks to
2470 be stored in the buffer.
2471 4) Add one additional block for block processing and convert
2472 to samples frames.
2473 5) Get the next larger (or equal) power-of-two buffer size.
2474 */
2475 lBlockingBufferSize = suggestedOutputLatencyFrames - stream->outputLatency;
2476 lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1;
2477 lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer;
2478 lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer;
2479
2480 /* The buffer size (without the additional block) corresponds
2481 to the initial number of silent samples in the output ring
2482 buffer. */
2483 stream->blockingState->writeRingBufferInitialFrames = lBlockingBufferSize - framesPerBuffer;
2484
2485 /* Get the next larger or equal power-of-two buffersize. */
2486 lBlockingBufferSizePow2 = 1;
2487 while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) );
2488 lBlockingBufferSize = lBlockingBufferSizePow2;
2489
2490 /* Compute total output latency in seconds */
2491 stream->streamRepresentation.streamInfo.outputLatency =
2492 (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor )
2493 + PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor)
2494 + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
2495 + stream->outputLatency )
2496 / sampleRate;
2497
2498 /* The code below prints the ASIO latency which doesn't include
2499 the buffer processor latency nor the blocking i/o latency. It
2500 reports the added latency separately.
2501 */
2502 PA_DEBUG(("PaAsio : ASIO OutputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
2503 stream->outputLatency,
2504 (long)( stream->inputLatency * (1000.0 / sampleRate) ),
2505 PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
2506 (long)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
2507 PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
2508 (long)( (PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
2509 ));
2510
2511 /* Determine the size of ring buffer in bytes. */
2512 lBytesPerFrame = outputChannelCount * Pa_GetSampleSize(outputSampleFormat);
2513
2514 /* Allocate the blocking i/o output ring buffer memory. */
2515 stream->blockingState->writeRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame );
2516 if( !stream->blockingState->writeRingBufferData )
2517 {
2518 result = paInsufficientMemory;
2519 PA_DEBUG(("ERROR! Blocking i/o output ring buffer allocation failed in OpenStream()\n"));
2520 goto error;
2521 }
2522
2523 /* Initialize the output ring buffer struct. */
2524 PaUtil_InitializeRingBuffer( &stream->blockingState->writeRingBuffer ,
2525 lBytesPerFrame ,
2526 lBlockingBufferSize ,
2527 stream->blockingState->writeRingBufferData );
2528 }
2529
2530 stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2531
2532
2533 }
2534 else /* Using callback interface... */
2535 {
2536 result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
2537 inputChannelCount, inputSampleFormat, (hostInputSampleFormat | paNonInterleaved),
2538 outputChannelCount, outputSampleFormat, (hostOutputSampleFormat | paNonInterleaved),
2539 sampleRate, streamFlags, framesPerBuffer,
2540 framesPerHostBuffer, paUtilFixedHostBufferSize,
2541 streamCallback, userData );
2542 if( result != paNoError ){
2543 PA_DEBUG(("OpenStream ERROR13\n"));
2544 goto error;
2545 }
2546 callbackBufferProcessorInited = TRUE;
2547
2548 stream->streamRepresentation.streamInfo.inputLatency =
2549 (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
2550 + stream->inputLatency) / sampleRate; // seconds
2551 stream->streamRepresentation.streamInfo.outputLatency =
2552 (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
2553 + stream->outputLatency) / sampleRate; // seconds
2554 stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
2555
2556 // the code below prints the ASIO latency which doesn't include the
2557 // buffer processor latency. it reports the added latency separately
2558 PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
2559 stream->inputLatency,
2560 (long)((stream->inputLatency*1000)/ sampleRate),
2561 PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
2562 (long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
2563 ));
2564
2565 PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
2566 stream->outputLatency,
2567 (long)((stream->outputLatency*1000)/ sampleRate),
2568 PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
2569 (long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
2570 ));
2571 }
2572
2573 stream->asioHostApi = asioHostApi;
2574 stream->framesPerHostCallback = framesPerHostBuffer;
2575
2576 stream->inputChannelCount = inputChannelCount;
2577 stream->outputChannelCount = outputChannelCount;
2578 stream->postOutput = driverInfo->postOutput;
2579 stream->isStopped = 1;
2580 stream->isActive = 0;
2581
2582 asioHostApi->openAsioDeviceIndex = asioDeviceIndex;
2583
2584 theAsioStream = stream;
2585 *s = (PaStream*)stream;
2586
2587 return result;
2588
2589 error:
2590 PA_DEBUG(("goto errored\n"));
2591 if( stream )
2592 {
2593 if( stream->blockingState )
2594 {
2595 if( blockingBufferProcessorInited )
2596 PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor );
2597
2598 if( stream->blockingState->writeRingBufferData )
2599 PaUtil_FreeMemory( stream->blockingState->writeRingBufferData );
2600 if( stream->blockingState->writeStreamBuffer )
2601 PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer );
2602 if( blockingWriteBuffersReadyEventInitialized )
2603 CloseHandle( stream->blockingState->writeBuffersReadyEvent );
2604
2605 if( stream->blockingState->readRingBufferData )
2606 PaUtil_FreeMemory( stream->blockingState->readRingBufferData );
2607 if( stream->blockingState->readStreamBuffer )
2608 PaUtil_FreeMemory( stream->blockingState->readStreamBuffer );
2609 if( blockingReadFramesReadyEventInitialized )
2610 CloseHandle( stream->blockingState->readFramesReadyEvent );
2611
2612 PaUtil_FreeMemory( stream->blockingState );
2613 }
2614
2615 if( callbackBufferProcessorInited )
2616 PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2617
2618 if( completedBuffersPlayedEventInited )
2619 CloseHandle( stream->completedBuffersPlayedEvent );
2620
2621 if( stream->asioBufferInfos )
2622 PaUtil_FreeMemory( stream->asioBufferInfos );
2623
2624 if( stream->asioChannelInfos )
2625 PaUtil_FreeMemory( stream->asioChannelInfos );
2626
2627 if( stream->bufferPtrs )
2628 PaUtil_FreeMemory( stream->bufferPtrs );
2629
2630 PaUtil_FreeMemory( stream );
2631 }
2632
2633 if( asioBuffersCreated )
2634 ASIODisposeBuffers();
2635
2636 if( asioIsInitialized )
2637 {
2638 UnloadAsioDriver();
2639 }
2640 return result;
2641 }
2642
2643
2644 /*
2645 When CloseStream() is called, the multi-api layer ensures that
2646 the stream has already been stopped or aborted.
2647 */
2648 static PaError CloseStream( PaStream* s )
2649 {
2650 PaError result = paNoError;
2651 PaAsioStream *stream = (PaAsioStream*)s;
2652
2653 /*
2654 IMPLEMENT ME:
2655 - additional stream closing + cleanup
2656 */
2657
2658 PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
2659 PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation );
2660
2661 stream->asioHostApi->openAsioDeviceIndex = paNoDevice;
2662
2663 CloseHandle( stream->completedBuffersPlayedEvent );
2664
2665 /* Using blocking i/o interface... */
2666 if( stream->blockingState )
2667 {
2668 PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor );
2669
2670 if( stream->inputChannelCount ) {
2671 PaUtil_FreeMemory( stream->blockingState->readRingBufferData );
2672 PaUtil_FreeMemory( stream->blockingState->readStreamBuffer );
2673 CloseHandle( stream->blockingState->readFramesReadyEvent );
2674 }
2675 if( stream->outputChannelCount ) {
2676 PaUtil_FreeMemory( stream->blockingState->writeRingBufferData );
2677 PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer );
2678 CloseHandle( stream->blockingState->writeBuffersReadyEvent );
2679 }
2680
2681 PaUtil_FreeMemory( stream->blockingState );
2682 }
2683
2684 PaUtil_FreeMemory( stream->asioBufferInfos );
2685 PaUtil_FreeMemory( stream->asioChannelInfos );
2686 PaUtil_FreeMemory( stream->bufferPtrs );
2687 PaUtil_FreeMemory( stream );
2688
2689 ASIODisposeBuffers();
2690 UnloadAsioDriver();
2691
2692 theAsioStream = 0;
2693
2694 return result;
2695 }
2696
2697
2698 static void bufferSwitch(long index, ASIOBool directProcess)
2699 {
2700 //TAKEN FROM THE ASIO SDK
2701
2702 // the actual processing callback.
2703 // Beware that this is normally in a seperate thread, hence be sure that
2704 // you take care about thread synchronization. This is omitted here for
2705 // simplicity.
2706
2707 // as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs
2708 // to be created though it will only set the timeInfo.samplePosition and
2709 // timeInfo.systemTime fields and the according flags
2710
2711 ASIOTime timeInfo;
2712 memset( &timeInfo, 0, sizeof (timeInfo) );
2713
2714 // get the time stamp of the buffer, not necessary if no
2715 // synchronization to other media is required
2716 if( ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK)
2717 timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
2718
2719 // Call the real callback
2720 bufferSwitchTimeInfo( &timeInfo, index, directProcess );
2721 }
2722
2723
2724 // conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
2725 #if NATIVE_INT64
2726 #define ASIO64toDouble(a) (a)
2727 #else
2728 const double twoRaisedTo32 = 4294967296.;
2729 #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
2730 #endif
2731
2732 static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool directProcess )
2733 {
2734 // the actual processing callback.
2735 // Beware that this is normally in a seperate thread, hence be sure that
2736 // you take care about thread synchronization.
2737
2738
2739 /* The SDK says the following about the directProcess flag:
2740 suggests to the host whether it should immediately start processing
2741 (directProcess == ASIOTrue), or whether its process should be deferred
2742 because the call comes from a very low level (for instance, a high level
2743 priority interrupt), and direct processing would cause timing instabilities for
2744 the rest of the system. If in doubt, directProcess should be set to ASIOFalse.
2745
2746 We just ignore directProcess. This could cause incompatibilities with
2747 drivers which really don't want the audio processing to occur in this
2748 callback, but none have been identified yet.
2749 */
2750
2751 (void) directProcess; /* suppress unused parameter warning */
2752
2753 #if 0
2754 // store the timeInfo for later use
2755 asioDriverInfo.tInfo = *timeInfo;
2756
2757 // get the time stamp of the buffer, not necessary if no
2758 // synchronization to other media is required
2759
2760 if (timeInfo->timeInfo.flags & kSystemTimeValid)
2761 asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime);
2762 else
2763 asioDriverInfo.nanoSeconds = 0;
2764
2765 if (timeInfo->timeInfo.flags & kSamplePositionValid)
2766 asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
2767 else
2768 asioDriverInfo.samples = 0;
2769
2770 if (timeInfo->timeCode.flags & kTcValid)
2771 asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
2772 else
2773 asioDriverInfo.tcSamples = 0;
2774
2775 // get the system reference time
2776 asioDriverInfo.sysRefTime = get_sys_reference_time();
2777 #endif
2778
2779 #if 0
2780 // a few debug messages for the Windows device driver developer
2781 // tells you the time when driver got its interrupt and the delay until the app receives
2782 // the event notification.
2783 static double last_samples = 0;
2784 char tmp[128];
2785 sprintf (tmp, "diff: %d / %d ms / %d ms / %d samples \n", asioDriverInfo.sysRefTime - (long)(asioDriverInfo.nanoSeconds / 1000000.0), asioDriverInfo.sysRefTime, (long)(asioDriverInfo.nanoSeconds / 1000000.0), (long)(asioDriverInfo.samples - last_samples));
2786 OutputDebugString (tmp);
2787 last_samples = asioDriverInfo.samples;
2788 #endif
2789
2790
2791 if( !theAsioStream )
2792 return 0L;
2793
2794 // Keep sample position
2795 // FIXME: asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo;
2796
2797
2798 // protect against reentrancy
2799 if( PaAsio_AtomicIncrement(&theAsioStream->reenterCount) )
2800 {
2801 theAsioStream->reenterError++;
2802 //DBUG(("bufferSwitchTimeInfo : reentrancy detection = %d\n", asioDriverInfo.reenterError));
2803 return 0L;
2804 }
2805
2806 int buffersDone = 0;
2807
2808 do
2809 {
2810 if( buffersDone > 0 )
2811 {
2812 // this is a reentered buffer, we missed processing it on time
2813 // set the input overflow and output underflow flags as appropriate
2814
2815 if( theAsioStream->inputChannelCount > 0 )
2816 theAsioStream->callbackFlags |= paInputOverflow;
2817
2818 if( theAsioStream->outputChannelCount > 0 )
2819 theAsioStream->callbackFlags |= paOutputUnderflow;
2820 }
2821 else
2822 {
2823 if( theAsioStream->zeroOutput )
2824 {
2825 ZeroOutputBuffers( theAsioStream, index );
2826
2827 // Finally if the driver supports the ASIOOutputReady() optimization,
2828 // do it here, all data are in place
2829 if( theAsioStream->postOutput )
2830 ASIOOutputReady();
2831
2832 if( theAsioStream->stopProcessing )
2833 {
2834 if( theAsioStream->stopPlayoutCount < 2 )
2835 {
2836 ++theAsioStream->stopPlayoutCount;
2837 if( theAsioStream->stopPlayoutCount == 2 )
2838 {
2839 theAsioStream->isActive = 0;
2840 if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
2841 theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
2842 theAsioStream->streamFinishedCallbackCalled = true;
2843 SetEvent( theAsioStream->completedBuffersPlayedEvent );
2844 }
2845 }
2846 }
2847 }
2848 else
2849 {
2850
2851 #if 0
2852 // test code to try to detect slip conditions... these may work on some systems
2853 // but neither of them work on the RME Digi96
2854
2855 // check that sample delta matches buffer size (otherwise we must have skipped
2856 // a buffer.
2857 static double last_samples = -512;
2858 double samples;
2859 //if( timeInfo->timeCode.flags & kTcValid )
2860 // samples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples);
2861 //else
2862 samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition);
2863 int delta = samples - last_samples;
2864 //printf( "%d\n", delta);
2865 last_samples = samples;
2866
2867 if( delta > theAsioStream->framesPerHostCallback )
2868 {
2869 if( theAsioStream->inputChannelCount > 0 )
2870 theAsioStream->callbackFlags |= paInputOverflow;
2871
2872 if( theAsioStream->outputChannelCount > 0 )
2873 theAsioStream->callbackFlags |= paOutputUnderflow;
2874 }
2875
2876 // check that the buffer index is not the previous index (which would indicate
2877 // that a buffer was skipped.
2878 static int previousIndex = 1;
2879 if( index == previousIndex )
2880 {
2881 if( theAsioStream->inputChannelCount > 0 )
2882 theAsioStream->callbackFlags |= paInputOverflow;
2883
2884 if( theAsioStream->outputChannelCount > 0 )
2885 theAsioStream->callbackFlags |= paOutputUnderflow;
2886 }
2887 previousIndex = index;
2888 #endif
2889
2890 int i;
2891
2892 PaUtil_BeginCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer );
2893
2894 PaStreamCallbackTimeInfo paTimeInfo;
2895
2896 // asio systemTime is supposed to be measured according to the same
2897 // clock as timeGetTime
2898 paTimeInfo.currentTime = (ASIO64toDouble( timeInfo->timeInfo.systemTime ) * .000000001);
2899
2900 /* patch from Paul Boege */
2901 paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime -
2902 ((double)theAsioStream->inputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate);
2903
2904 paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime +
2905 ((double)theAsioStream->outputLatency/theAsioStream->streamRepresentation.streamInfo.sampleRate);
2906
2907 /* old version is buggy because the buffer processor also adds in its latency to the time parameters
2908 paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - theAsioStream->streamRepresentation.streamInfo.inputLatency;
2909 paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + theAsioStream->streamRepresentation.streamInfo.outputLatency;
2910 */
2911
2912 /* Disabled! Stopping and re-starting the stream causes an input overflow / output undeflow. S.Fischer */
2913 #if 0
2914 // detect underflows by checking inter-callback time > 2 buffer period
2915 static double previousTime = -1;
2916 if( previousTime > 0 ){
2917
2918 double delta = paTimeInfo.currentTime - previousTime;
2919
2920 if( delta >= 2. * (theAsioStream->framesPerHostCallback / theAsioStream->streamRepresentation.streamInfo.sampleRate) ){
2921 if( theAsioStream->inputChannelCount > 0 )
2922 theAsioStream->callbackFlags |= paInputOverflow;
2923
2924 if( theAsioStream->outputChannelCount > 0 )
2925 theAsioStream->callbackFlags |= paOutputUnderflow;
2926 }
2927 }
2928 previousTime = paTimeInfo.currentTime;
2929 #endif
2930
2931 // note that the above input and output times do not need to be
2932 // adjusted for the latency of the buffer processor -- the buffer
2933 // processor handles that.
2934
2935 if( theAsioStream->inputBufferConverter )
2936 {
2937 for( i=0; i<theAsioStream->inputChannelCount; i++ )
2938 {
2939 theAsioStream->inputBufferConverter( theAsioStream->inputBufferPtrs[index][i],
2940 theAsioStream->inputShift, theAsioStream->framesPerHostCallback );
2941 }
2942 }
2943
2944 PaUtil_BeginBufferProcessing( &theAsioStream->bufferProcessor, &paTimeInfo, theAsioStream->callbackFlags );
2945
2946 /* reset status flags once they've been passed to the callback */
2947 theAsioStream->callbackFlags = 0;
2948
2949 PaUtil_SetInputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
2950 for( i=0; i<theAsioStream->inputChannelCount; ++i )
2951 PaUtil_SetNonInterleavedInputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->inputBufferPtrs[index][i] );
2952
2953 PaUtil_SetOutputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ );
2954 for( i=0; i<theAsioStream->outputChannelCount; ++i )
2955 PaUtil_SetNonInterleavedOutputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->outputBufferPtrs[index][i] );
2956
2957 int callbackResult;
2958 if( theAsioStream->stopProcessing )
2959 callbackResult = paComplete;
2960 else
2961 callbackResult = paContinue;
2962 unsigned long framesProcessed = PaUtil_EndBufferProcessing( &theAsioStream->bufferProcessor, &callbackResult );
2963
2964 if( theAsioStream->outputBufferConverter )
2965 {
2966 for( i=0; i<theAsioStream->outputChannelCount; i++ )
2967 {
2968 theAsioStream->outputBufferConverter( theAsioStream->outputBufferPtrs[index][i],
2969 theAsioStream->outputShift, theAsioStream->framesPerHostCallback );
2970 }
2971 }
2972
2973 PaUtil_EndCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer, framesProcessed );
2974
2975 // Finally if the driver supports the ASIOOutputReady() optimization,
2976 // do it here, all data are in place
2977 if( theAsioStream->postOutput )
2978 ASIOOutputReady();
2979
2980 if( callbackResult == paContinue )
2981 {
2982 /* nothing special to do */
2983 }
2984 else if( callbackResult == paAbort )
2985 {
2986 /* finish playback immediately */
2987 theAsioStream->isActive = 0;
2988 if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 )
2989 theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData );
2990 theAsioStream->streamFinishedCallbackCalled = true;
2991 SetEvent( theAsioStream->completedBuffersPlayedEvent );
2992 theAsioStream->zeroOutput = true;
2993 }
2994 else /* paComplete or other non-zero value indicating complete */
2995 {
2996 /* Finish playback once currently queued audio has completed. */
2997 theAsioStream->stopProcessing = true;
2998
2999 if( PaUtil_IsBufferProcessorOutputEmpty( &theAsioStream->bufferProcessor ) )
3000 {
3001 theAsioStream->zeroOutput = true;
3002 theAsioStream->stopPlayoutCount = 0;
3003 }
3004 }
3005 }
3006 }
3007
3008 ++buffersDone;
3009 }while( PaAsio_AtomicDecrement(&theAsioStream->reenterCount) >= 0 );
3010
3011 return 0L;
3012 }
3013
3014
3015 static void sampleRateChanged(ASIOSampleRate sRate)
3016 {
3017 // TAKEN FROM THE ASIO SDK
3018 // do whatever you need to do if the sample rate changed
3019 // usually this only happens during external sync.
3020 // Audio processing is not stopped by the driver, actual sample rate
3021 // might not have even changed, maybe only the sample rate status of an
3022 // AES/EBU or S/PDIF digital input at the audio device.
3023 // You might have to update time/sample related conversion routines, etc.
3024
3025 (void) sRate; /* unused parameter */
3026 PA_DEBUG( ("sampleRateChanged : %d \n", sRate));
3027 }
3028
3029 static long asioMessages(long selector, long value, void* message, double* opt)
3030 {
3031 // TAKEN FROM THE ASIO SDK
3032 // currently the parameters "value", "message" and "opt" are not used.
3033 long ret = 0;
3034
3035 (void) message; /* unused parameters */
3036 (void) opt;
3037
3038 PA_DEBUG( ("asioMessages : %d , %d \n", selector, value));
3039
3040 switch(selector)
3041 {
3042 case kAsioSelectorSupported:
3043 if(value == kAsioResetRequest
3044 || value == kAsioEngineVersion
3045 || value == kAsioResyncRequest
3046 || value == kAsioLatenciesChanged
3047 // the following three were added for ASIO 2.0, you don't necessarily have to support them
3048 || value == kAsioSupportsTimeInfo
3049 || value == kAsioSupportsTimeCode
3050 || value == kAsioSupportsInputMonitor)
3051 ret = 1L;
3052 break;
3053
3054 case kAsioBufferSizeChange:
3055 //printf("kAsioBufferSizeChange \n");
3056 break;
3057
3058 case kAsioResetRequest:
3059 // defer the task and perform the reset of the driver during the next "safe" situation
3060 // You cannot reset the driver right now, as this code is called from the driver.
3061 // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
3062 // Afterwards you initialize the driver again.
3063
3064 /*FIXME: commented the next line out */
3065 //asioDriverInfo.stopped; // In this sample the processing will just stop
3066 ret = 1L;
3067 break;
3068
3069 case kAsioResyncRequest:
3070 // This informs the application, that the driver encountered some non fatal data loss.
3071 // It is used for synchronization purposes of different media.
3072 // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
3073 // Windows Multimedia system, which could loose data because the Mutex was hold too long
3074 // by another thread.
3075 // However a driver can issue it in other situations, too.
3076 ret = 1L;
3077 break;
3078
3079 case kAsioLatenciesChanged:
3080 // This will inform the host application that the drivers were latencies changed.
3081 // Beware, it this does not mean that the buffer sizes have changed!
3082 // You might need to update internal delay data.
3083 ret = 1L;
3084 //printf("kAsioLatenciesChanged \n");
3085 break;
3086
3087 case kAsioEngineVersion:
3088 // return the supported ASIO version of the host application
3089 // If a host applications does not implement this selector, ASIO 1.0 is assumed
3090 // by the driver
3091 ret = 2L;
3092 break;
3093
3094 case kAsioSupportsTimeInfo:
3095 // informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
3096 // is supported.
3097 // For compatibility with ASIO 1.0 drivers the host application should always support
3098 // the "old" bufferSwitch method, too.
3099 ret = 1;
3100 break;
3101
3102 case kAsioSupportsTimeCode:
3103 // informs the driver wether application is interested in time code info.
3104 // If an application does not need to know about time code, the driver has less work
3105 // to do.
3106 ret = 0;
3107 break;
3108 }
3109 return ret;
3110 }
3111
3112
3113 static PaError StartStream( PaStream *s )
3114 {
3115 PaError result = paNoError;
3116 PaAsioStream *stream = (PaAsioStream*)s;
3117 PaAsioStreamBlockingState *blockingState = stream->blockingState;
3118 ASIOError asioError;
3119
3120 if( stream->outputChannelCount > 0 )
3121 {
3122 ZeroOutputBuffers( stream, 0 );
3123 ZeroOutputBuffers( stream, 1 );
3124 }
3125
3126 PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
3127 stream->stopProcessing = false;
3128 stream->zeroOutput = false;
3129
3130 /* Reentrancy counter initialisation */
3131 stream->reenterCount = -1;
3132 stream->reenterError = 0;
3133
3134 stream->callbackFlags = 0;
3135
3136 if( ResetEvent( stream->completedBuffersPlayedEvent ) == 0 )
3137 {
3138 result = paUnanticipatedHostError;
3139 PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3140 }
3141
3142
3143 /* Using blocking i/o interface... */
3144 if( blockingState )
3145 {
3146 /* Reset blocking i/o buffer processor. */
3147 PaUtil_ResetBufferProcessor( &blockingState->bufferProcessor );
3148
3149 /* If we're about to process some input data. */
3150 if( stream->inputChannelCount )
3151 {
3152 /* Reset callback-ReadStream sync event. */
3153 if( ResetEvent( blockingState->readFramesReadyEvent ) == 0 )
3154 {
3155 result = paUnanticipatedHostError;
3156 PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3157 }
3158
3159 /* Flush blocking i/o ring buffer. */
3160 PaUtil_FlushRingBuffer( &blockingState->readRingBuffer );
3161 (*blockingState->bufferProcessor.inputZeroer)( blockingState->readRingBuffer.buffer, 1, blockingState->bufferProcessor.inputChannelCount * blockingState->readRingBuffer.bufferSize );
3162 }
3163
3164 /* If we're about to process some output data. */
3165 if( stream->outputChannelCount )
3166 {
3167 /* Reset callback-WriteStream sync event. */
3168 if( ResetEvent( blockingState->writeBuffersReadyEvent ) == 0 )
3169 {
3170 result = paUnanticipatedHostError;
3171 PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3172 }
3173
3174 /* Flush blocking i/o ring buffer. */
3175 PaUtil_FlushRingBuffer( &blockingState->writeRingBuffer );
3176 (*blockingState->bufferProcessor.outputZeroer)( blockingState->writeRingBuffer.buffer, 1, blockingState->bufferProcessor.outputChannelCount * blockingState->writeRingBuffer.bufferSize );
3177
3178 /* Initialize the output ring buffer to "silence". */
3179 PaUtil_AdvanceRingBufferWriteIndex( &blockingState->writeRingBuffer, blockingState->writeRingBufferInitialFrames );
3180 }
3181
3182 /* Clear requested frames / buffers count. */
3183 blockingState->writeBuffersRequested = 0;
3184 blockingState->readFramesRequested = 0;
3185 blockingState->writeBuffersRequestedFlag = FALSE;
3186 blockingState->readFramesRequestedFlag = FALSE;
3187 blockingState->outputUnderflowFlag = FALSE;
3188 blockingState->inputOverflowFlag = FALSE;
3189 blockingState->stopFlag = FALSE;
3190 }
3191
3192
3193 if( result == paNoError )
3194 {
3195 assert( theAsioStream == stream ); /* theAsioStream should be set correctly in OpenStream */
3196
3197 /* initialize these variables before the callback has a chance to be invoked */
3198 stream->isStopped = 0;
3199 stream->isActive = 1;
3200 stream->streamFinishedCallbackCalled = false;
3201
3202 asioError = ASIOStart();
3203 if( asioError != ASE_OK )
3204 {
3205 stream->isStopped = 1;
3206 stream->isActive = 0;
3207
3208 result = paUnanticipatedHostError;
3209 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
3210 }
3211 }
3212
3213 return result;
3214 }
3215
3216 static void EnsureCallbackHasCompleted( PaAsioStream *stream )
3217 {
3218 // make sure that the callback is not still in-flight after ASIOStop()
3219 // returns. This has been observed to happen on the Hoontech DSP24 for
3220 // example.
3221 int count = 2000; // only wait for 2 seconds, rather than hanging.
3222 while( stream->reenterCount != -1 && count > 0 )
3223 {
3224 Sleep(1);
3225 --count;
3226 }
3227 }
3228
3229 static PaError StopStream( PaStream *s )
3230 {
3231 PaError result = paNoError;
3232 PaAsioStream *stream = (PaAsioStream*)s;
3233 PaAsioStreamBlockingState *blockingState = stream->blockingState;
3234 ASIOError asioError;
3235
3236 if( stream->isActive )
3237 {
3238 /* If blocking i/o output is in use */
3239 if( blockingState && stream->outputChannelCount )
3240 {
3241 /* Request the whole output buffer to be available. */
3242 blockingState->writeBuffersRequested = blockingState->writeRingBuffer.bufferSize;
3243 /* Signalize that additional buffers are need. */
3244 blockingState->writeBuffersRequestedFlag = TRUE;
3245 /* Set flag to indicate the playback is to be stopped. */
3246 blockingState->stopFlag = TRUE;
3247
3248 /* Wait until requested number of buffers has been freed. Time
3249 out after twice the blocking i/o ouput buffer could have
3250 been consumed. */
3251 DWORD timeout = (DWORD)( 2 * blockingState->writeRingBuffer.bufferSize * 1000
3252 / stream->streamRepresentation.streamInfo.sampleRate );
3253 DWORD waitResult = WaitForSingleObject( blockingState->writeBuffersReadyEvent, timeout );
3254
3255 /* If something seriously went wrong... */
3256 if( waitResult == WAIT_FAILED )
3257 {
3258 PA_DEBUG(("WaitForSingleObject() failed in StopStream()\n"));
3259 result = paUnanticipatedHostError;
3260 PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3261 }
3262 else if( waitResult == WAIT_TIMEOUT )
3263 {
3264 PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n"));
3265 result = paTimedOut;
3266 }
3267 }
3268
3269 stream->stopProcessing = true;
3270
3271 /* wait for the stream to finish playing out enqueued buffers.
3272 timeout after four times the stream latency.
3273
3274 @todo should use a better time out value - if the user buffer
3275 length is longer than the asio buffer size then that should
3276 be taken into account.
3277 */
3278 if( WaitForSingleObject( stream->completedBuffersPlayedEvent,
3279 (DWORD)(stream->streamRepresentation.streamInfo.outputLatency * 1000. * 4.) )
3280 == WAIT_TIMEOUT )
3281 {
3282 PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n" ));
3283 }
3284 }
3285
3286 asioError = ASIOStop();
3287 if( asioError == ASE_OK )
3288 {
3289 EnsureCallbackHasCompleted( stream );
3290 }
3291 else
3292 {
3293 result = paUnanticipatedHostError;
3294 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
3295 }
3296
3297 stream->isStopped = 1;
3298 stream->isActive = 0;
3299
3300 if( !stream->streamFinishedCallbackCalled )
3301 {
3302 if( stream->streamRepresentation.streamFinishedCallback != 0 )
3303 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3304 }
3305
3306 return result;
3307 }
3308
3309 static PaError AbortStream( PaStream *s )
3310 {
3311 PaError result = paNoError;
3312 PaAsioStream *stream = (PaAsioStream*)s;
3313 ASIOError asioError;
3314
3315 stream->zeroOutput = true;
3316
3317 asioError = ASIOStop();
3318 if( asioError == ASE_OK )
3319 {
3320 EnsureCallbackHasCompleted( stream );
3321 }
3322 else
3323 {
3324 result = paUnanticipatedHostError;
3325 PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
3326 }
3327
3328 stream->isStopped = 1;
3329 stream->isActive = 0;
3330
3331 if( !stream->streamFinishedCallbackCalled )
3332 {
3333 if( stream->streamRepresentation.streamFinishedCallback != 0 )
3334 stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData );
3335 }
3336
3337 return result;
3338 }
3339
3340
3341 static PaError IsStreamStopped( PaStream *s )
3342 {
3343 PaAsioStream *stream = (PaAsioStream*)s;
3344
3345 return stream->isStopped;
3346 }
3347
3348
3349 static PaError IsStreamActive( PaStream *s )
3350 {
3351 PaAsioStream *stream = (PaAsioStream*)s;
3352
3353 return stream->isActive;
3354 }
3355
3356
3357 static PaTime GetStreamTime( PaStream *s )
3358 {
3359 (void) s; /* unused parameter */
3360 return (double)timeGetTime() * .001;
3361 }
3362
3363
3364 static double GetStreamCpuLoad( PaStream* s )
3365 {
3366 PaAsioStream *stream = (PaAsioStream*)s;
3367
3368 return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer );
3369 }
3370
3371
3372 /*
3373 As separate stream interfaces are used for blocking and callback
3374 streams, the following functions can be guaranteed to only be called
3375 for blocking streams.
3376 */
3377
3378 static PaError ReadStream( PaStream *s ,
3379 void *buffer,
3380 unsigned long frames )
3381 {
3382 PaError result = paNoError; /* Initial return value. */
3383 PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */
3384
3385 /* Pointer to the blocking i/o data struct. */
3386 PaAsioStreamBlockingState *blockingState = stream->blockingState;
3387
3388 /* Get blocking i/o buffer processor and ring buffer pointers. */
3389 PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
3390 PaUtilRingBuffer *pRb = &blockingState->readRingBuffer;
3391
3392 /* Ring buffer segment(s) used for writing. */
3393 void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */
3394 void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */
3395
3396 /* Number of frames per ring buffer segment. */
3397 long lRingBufferSize1st = 0; /* First segment. (Mandatory) */
3398 long lRingBufferSize2nd = 0; /* Second segment. (Optional) */
3399
3400 /* Get number of frames to be processed per data block. */
3401 unsigned long lFramesPerBlock = stream->bufferProcessor.framesPerUserBuffer;
3402 /* Actual number of frames that has been copied into the ring buffer. */
3403 unsigned long lFramesCopied = 0;
3404 /* The number of remaining unprocessed dtat frames. */
3405 unsigned long lFramesRemaining = frames;
3406
3407 /* Copy the input argument to avoid pointer increment! */
3408 const void *userBuffer;
3409 unsigned int i; /* Just a counter. */
3410
3411 /* About the time, needed to process 8 data blocks. */
3412 DWORD timeout = (DWORD)( 8 * lFramesPerBlock * 1000 / stream->streamRepresentation.streamInfo.sampleRate );
3413 DWORD waitResult = 0;
3414
3415
3416 /* Check if the stream is still available ready to gather new data. */
3417 if( blockingState->stopFlag || !stream->isActive )
3418 {
3419 PA_DEBUG(("Warning! Stream no longer available for reading in ReadStream()\n"));
3420 result = paStreamIsStopped;
3421 return result;
3422 }
3423
3424 /* If the stream is a input stream. */
3425 if( stream->inputChannelCount )
3426 {
3427 /* Prepare buffer access. */
3428 if( !pBp->userOutputIsInterleaved )
3429 {
3430 userBuffer = blockingState->readStreamBuffer;
3431 for( i = 0; i<pBp->inputChannelCount; ++i )
3432 {
3433 ((void**)userBuffer)[i] = ((void**)buffer)[i];
3434 }
3435 } /* Use the unchanged buffer. */
3436 else { userBuffer = buffer; }
3437
3438 do /* Internal block processing for too large user data buffers. */
3439 {
3440 /* Get the size of the current data block to be processed. */
3441 lFramesPerBlock =(lFramesPerBlock < lFramesRemaining)
3442 ? lFramesPerBlock : lFramesRemaining;
3443 /* Use predefined block size for as long there are enough
3444 buffers available, thereafter reduce the processing block
3445 size to match the number of remaining buffers. So the final
3446 data block is processed although it may be incomplete. */
3447
3448 /* If the available amount of data frames is insufficient. */
3449 if( PaUtil_GetRingBufferReadAvailable(pRb) < (long) lFramesPerBlock )
3450 {
3451 /* Make sure, the event isn't already set! */
3452 /* ResetEvent( blockingState->readFramesReadyEvent ); */
3453
3454 /* Set the number of requested buffers. */
3455 blockingState->readFramesRequested = lFramesPerBlock;
3456
3457 /* Signalize that additional buffers are need. */
3458 blockingState->readFramesRequestedFlag = TRUE;
3459
3460 /* Wait until requested number of buffers has been freed. */
3461 waitResult = WaitForSingleObject( blockingState->readFramesReadyEvent, timeout );
3462
3463 /* If something seriously went wrong... */
3464 if( waitResult == WAIT_FAILED )
3465 {
3466 PA_DEBUG(("WaitForSingleObject() failed in ReadStream()\n"));
3467 result = paUnanticipatedHostError;
3468 PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
3469 return result;
3470 }
3471 else if( waitResult == WAIT_TIMEOUT )
3472 {
3473 PA_DEBUG(("WaitForSingleObject() timed out in ReadStream()\n"));
3474
3475 /* If block processing has stopped, abort! */
3476 if( blockingState->stopFlag ) { return result = paStreamIsStopped; }
3477
3478 /* If a timeout is encountered, give up eventually. */
3479 return result = paTimedOut;
3480 }
3481 }
3482 /* Now, the ring buffer contains the required amount of data
3483 frames.
3484 (Therefor we don't need to check the return argument of
3485 PaUtil_GetRingBufferReadRegions(). ;-) )
3486 */
3487
3488 /* Retrieve pointer(s) to the ring buffer's current write
3489 position(s). If the first buffer segment is too small to
3490 store the requested number of bytes, an additional second
3491 segment is returned. Otherwise, i.e. if the first segment
3492 is large enough, the second segment's pointer will be NULL.
3493 */
3494 PaUtil_GetRingBufferReadRegions(pRb ,
3495 lFramesPerBlock ,
3496 &pRingBufferData1st,
3497 &lRingBufferSize1st,
3498 &pRingBufferData2nd,
3499 &lRingBufferSize2nd);
3500
3501 /* Set number of frames to be copied from the ring buffer. */
3502 PaUtil_SetInputFrameCount( pBp, lRingBufferSize1st );
3503 /* Setup ring buffer access. */
3504 PaUtil_SetInterleavedInputChannels(pBp , /* Buffer processor. */
3505 0 , /* The first channel's index. */
3506 pRingBufferData1st, /* First ring buffer segment. */
3507 0 ); /* Use all available channels. */
3508
3509 /* If a second ring buffer segment is required. */
3510 if( lRingBufferSize2nd ) {
3511 /* Set number of frames to be copied from the ring buffer. */
3512 PaUtil_Set2ndInputFrameCount( pBp, lRingBufferSize2nd );
3513 /* Setup ring buffer access. */
3514 PaUtil_Set2ndInterleavedInputChannels(pBp , /* Buffer processor. */
3515 0 , /* The first channel's index. */
3516 pRingBufferData2nd, /* Second ring buffer segment. */
3517 0 ); /* Use all available channels. */
3518 }
3519
3520 /* Let the buffer processor handle "copy and conversion" and
3521 update the ring buffer indices manually. */
3522 lFramesCopied = PaUtil_CopyInput( pBp, &buffer, lFramesPerBlock );
3523 PaUtil_AdvanceRingBufferReadIndex( pRb, lFramesCopied );
3524
3525 /* Decrease number of unprocessed frames. */
3526 lFramesRemaining -= lFramesCopied;
3527
3528 } /* Continue with the next data chunk. */
3529 while( lFramesRemaining );
3530
3531
3532 /* If there has been an input overflow within the callback */
3533 if( blockingState->inputOverflowFlag )
3534 {
3535 blockingState->inputOverflowFlag = FALSE;
3536
3537 /* Return the corresponding error code. */
3538 result = paInputOverflowed;
3539 }
3540
3541 } /* If this is not an input stream. */
3542 else {
3543 result = paCanNotReadFromAnOutputOnlyStream;
3544 }
3545
3546 return result;
3547 }
3548
3549 static PaError WriteStream( PaStream *s ,
3550 const void *buffer,
3551 unsigned long frames )
3552 {
3553 PaError result = paNoError; /* Initial return value. */
3554 PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */
3555
3556 /* Pointer to the blocking i/o data struct. */
3557 PaAsioStreamBlockingState *blockingState = stream->blockingState;
3558
3559 /* Get blocking i/o buffer processor and ring buffer pointers. */
3560 PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
3561 PaUtilRingBuffer *pRb = &blockingState->writeRingBuffer;
3562
3563 /* Ring buffer segment(s) used for writing. */
3564 void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */
3565 void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */
3566
3567 /* Number of frames per ring buffer segment. */
3568 long lRingBufferSize1st = 0; /* First segment. (Mandatory) */
3569 long lRingBufferSize2nd = 0; /* Second segment. (Optional) */
3570
3571 /* Get number of frames to be processed per data block. */
3572 unsigned long lFramesPerBlock