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