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