diff options
author | sanine <sanine.not@pm.me> | 2022-08-27 23:52:56 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-08-27 23:52:56 -0500 |
commit | a4dd0ad63c00f4dee3b86dfd3075d1d61b2b3180 (patch) | |
tree | 13bd5bfa15e6fea2a12f176bae79adf9c6fd0933 /portaudio/src/hostapi/asio/pa_asio.cpp | |
parent | bde3e4f1bb7b8f8abca0884a7d994ee1c17a66b1 (diff) |
add plibsys
Diffstat (limited to 'portaudio/src/hostapi/asio/pa_asio.cpp')
-rw-r--r-- | portaudio/src/hostapi/asio/pa_asio.cpp | 4250 |
1 files changed, 0 insertions, 4250 deletions
diff --git a/portaudio/src/hostapi/asio/pa_asio.cpp b/portaudio/src/hostapi/asio/pa_asio.cpp deleted file mode 100644 index bc5f0e4..0000000 --- a/portaudio/src/hostapi/asio/pa_asio.cpp +++ /dev/null @@ -1,4250 +0,0 @@ -/* - * $Id$ - * Portable Audio I/O Library for ASIO Drivers - * - * Author: Stephane Letz - * Based on the Open Source API proposed by Ross Bencina - * Copyright (c) 2000-2002 Stephane Letz, Phil Burk, Ross Bencina - * Blocking i/o implementation by Sven Fischer, Institute of Hearing - * Technology and Audiology (www.hoertechnik-audiologie.de) - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * The text above constitutes the entire PortAudio license; however, - * the PortAudio community also makes the following non-binding requests: - * - * Any person wishing to distribute modifications to the Software is - * requested to send the modifications to the original developer so that - * they can be incorporated into the canonical version. It is also - * requested that these non-binding requests be included along with the - * license above. - */ - -/* Modification History - - 08-03-01 First version : Stephane Letz - 08-06-01 Tweaks for PC, use C++, buffer allocation, Float32 to Int32 conversion : Phil Burk - 08-20-01 More conversion, PA_StreamTime, Pa_GetHostError : Stephane Letz - 08-21-01 PaUInt8 bug correction, implementation of ASIOSTFloat32LSB and ASIOSTFloat32MSB native formats : Stephane Letz - 08-24-01 MAX_INT32_FP hack, another Uint8 fix : Stephane and Phil - 08-27-01 Implementation of hostBufferSize < userBufferSize case, better management of the output buffer when - the stream is stopped : Stephane Letz - 08-28-01 Check the stream pointer for null in bufferSwitchTimeInfo, correct bug in bufferSwitchTimeInfo when - the stream is stopped : Stephane Letz - 10-12-01 Correct the PaHost_CalcNumHostBuffers function: computes FramesPerHostBuffer to be the lowest that - respect requested FramesPerUserBuffer and userBuffersPerHostBuffer : Stephane Letz - 10-26-01 Management of hostBufferSize and userBufferSize of any size : Stephane Letz - 10-27-01 Improve calculus of hostBufferSize to be multiple or divisor of userBufferSize if possible : Stephane and Phil - 10-29-01 Change MAX_INT32_FP to (2147483520.0f) to prevent roundup to 0x80000000 : Phil Burk - 10-31-01 Clear the output buffer and user buffers in PaHost_StartOutput, correct bug in GetFirstMultiple : Stephane Letz - 11-06-01 Rename functions : Stephane Letz - 11-08-01 New Pa_ASIO_Adaptor_Init function to init Callback adpatation variables, cleanup of Pa_ASIO_Callback_Input: Stephane Letz - 11-29-01 Break apart device loading to debug random failure in Pa_ASIO_QueryDeviceInfo ; Phil Burk - 01-03-02 Deallocate all resources in PaHost_Term for cases where Pa_CloseStream is not called properly : Stephane Letz - 02-01-02 Cleanup, test of multiple-stream opening : Stephane Letz - 19-02-02 New Pa_ASIO_loadDriver that calls CoInitialize on each thread on Windows : Stephane Letz - 09-04-02 Correct error code management in PaHost_Term, removes various compiler warning : Stephane Letz - 12-04-02 Add Mac includes for <Devices.h> and <Timer.h> : Phil Burk - 13-04-02 Removes another compiler warning : Stephane Letz - 30-04-02 Pa_ASIO_QueryDeviceInfo bug correction, memory allocation checking, better error handling : D Viens, P Burk, S Letz - 12-06-02 Rehashed into new multi-api infrastructure, added support for all ASIO sample formats : Ross Bencina - 18-06-02 Added pa_asio.h, PaAsio_GetAvailableLatencyValues() : Ross B. - 21-06-02 Added SelectHostBufferSize() which selects host buffer size based on user latency parameters : Ross Bencina - ** NOTE maintenance history is now stored in CVS ** -*/ - -/** @file - @ingroup hostapi_src - - Note that specific support for paInputUnderflow, paOutputOverflow and - paNeverDropInput is not necessary or possible with this driver due to the - synchronous full duplex double-buffered architecture of ASIO. -*/ - - -#include <stdio.h> -#include <assert.h> -#include <string.h> -//#include <values.h> -#include <new> - -#include <windows.h> -#include <mmsystem.h> - -#include "portaudio.h" -#include "pa_asio.h" -#include "pa_util.h" -#include "pa_allocation.h" -#include "pa_hostapi.h" -#include "pa_stream.h" -#include "pa_cpuload.h" -#include "pa_process.h" -#include "pa_debugprint.h" -#include "pa_ringbuffer.h" - -#include "pa_win_coinitialize.h" - -/* This version of pa_asio.cpp is currently only targeted at Win32, - It would require a few tweaks to work with pre-OS X Macintosh. - To make configuration easier, we define WIN32 here to make sure - that the ASIO SDK knows this is Win32. -*/ -#ifndef WIN32 -#define WIN32 -#endif - -#include "asiosys.h" -#include "asio.h" -#include "asiodrivers.h" -#include "iasiothiscallresolver.h" - -/* -#if MAC -#include <Devices.h> -#include <Timer.h> -#include <Math64.h> -#else -*/ -/* -#include <math.h> -#include <windows.h> -#include <mmsystem.h> -*/ -/* -#endif -*/ - - -/* winmm.lib is needed for timeGetTime() (this is in winmm.a if you're using gcc) */ -#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */ -#pragma comment(lib, "winmm.lib") -#endif - - -/* external reference to ASIO SDK's asioDrivers. - - This is a bit messy because we want to explicitly manage - allocation/deallocation of this structure, but some layers of the SDK - which we currently use (eg the implementation in asio.cpp) still - use this global version. - - For now we keep it in sync with our local instance in the host - API representation structure, but later we should be able to remove - all dependence on it. -*/ -extern AsioDrivers* asioDrivers; - - -/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */ -/* not tested at all since new V19 code was introduced. */ -#define CARBON_COMPATIBLE (0) - - -/* prototypes for functions declared in this file */ - -extern "C" PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ); -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ); -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ); -static PaError CloseStream( PaStream* stream ); -static PaError StartStream( PaStream *stream ); -static PaError StopStream( PaStream *stream ); -static PaError AbortStream( PaStream *stream ); -static PaError IsStreamStopped( PaStream *s ); -static PaError IsStreamActive( PaStream *stream ); -static PaTime GetStreamTime( PaStream *stream ); -static double GetStreamCpuLoad( PaStream* stream ); -static PaError ReadStream( PaStream* stream, void *buffer, unsigned long frames ); -static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long frames ); -static signed long GetStreamReadAvailable( PaStream* stream ); -static signed long GetStreamWriteAvailable( PaStream* stream ); - -/* Blocking i/o callback function. */ -static int BlockingIoPaCallback(const void *inputBuffer , - void *outputBuffer , - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *timeInfo , - PaStreamCallbackFlags statusFlags , - void *userData ); - -/* our ASIO callback functions */ - -static void bufferSwitch(long index, ASIOBool processNow); -static ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow); -static void sampleRateChanged(ASIOSampleRate sRate); -static long asioMessages(long selector, long value, void* message, double* opt); - -static ASIOCallbacks asioCallbacks_ = - { bufferSwitch, sampleRateChanged, asioMessages, bufferSwitchTimeInfo }; - - -#define PA_ASIO_SET_LAST_HOST_ERROR( errorCode, errorText ) \ - PaUtil_SetLastHostErrorInfo( paASIO, errorCode, errorText ) - - -static void PaAsio_SetLastSystemError( DWORD errorCode ) -{ - LPVOID lpMsgBuf; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - errorCode, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &lpMsgBuf, - 0, - NULL - ); - PaUtil_SetLastHostErrorInfo( paASIO, errorCode, (const char*)lpMsgBuf ); - LocalFree( lpMsgBuf ); -} - -#define PA_ASIO_SET_LAST_SYSTEM_ERROR( errorCode ) \ - PaAsio_SetLastSystemError( errorCode ) - - -static const char* PaAsio_GetAsioErrorText( ASIOError asioError ) -{ - const char *result; - - switch( asioError ){ - case ASE_OK: - case ASE_SUCCESS: result = "Success"; break; - case ASE_NotPresent: result = "Hardware input or output is not present or available"; break; - case ASE_HWMalfunction: result = "Hardware is malfunctioning"; break; - case ASE_InvalidParameter: result = "Input parameter invalid"; break; - case ASE_InvalidMode: result = "Hardware is in a bad mode or used in a bad mode"; break; - case ASE_SPNotAdvancing: result = "Hardware is not running when sample position is inquired"; break; - case ASE_NoClock: result = "Sample clock or rate cannot be determined or is not present"; break; - case ASE_NoMemory: result = "Not enough memory for completing the request"; break; - default: result = "Unknown ASIO error"; break; - } - - return result; -} - - -#define PA_ASIO_SET_LAST_ASIO_ERROR( asioError ) \ - PaUtil_SetLastHostErrorInfo( paASIO, asioError, PaAsio_GetAsioErrorText( asioError ) ) - - - - -// Atomic increment and decrement operations -#if MAC - /* need to be implemented on Mac */ - inline long PaAsio_AtomicIncrement(volatile long* v) {return ++(*const_cast<long*>(v));} - inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast<long*>(v));} -#elif WINDOWS - inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast<long*>(v));} - inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast<long*>(v));} -#endif - - - -typedef struct PaAsioDriverInfo -{ - ASIODriverInfo asioDriverInfo; - long inputChannelCount, outputChannelCount; - long bufferMinSize, bufferMaxSize, bufferPreferredSize, bufferGranularity; - bool postOutput; -} -PaAsioDriverInfo; - - -/* PaAsioHostApiRepresentation - host api datastructure specific to this implementation */ - -typedef struct -{ - PaUtilHostApiRepresentation inheritedHostApiRep; - PaUtilStreamInterface callbackStreamInterface; - PaUtilStreamInterface blockingStreamInterface; - - PaUtilAllocationGroup *allocations; - - PaWinUtilComInitializationResult comInitializationResult; - - AsioDrivers *asioDrivers; - void *systemSpecific; - - /* the ASIO C API only allows one ASIO driver to be open at a time, - so we keep track of whether we have the driver open here, and - use this information to return errors from OpenStream if the - driver is already open. - - openAsioDeviceIndex will be PaNoDevice if there is no device open - and a valid pa_asio (not global) device index otherwise. - - openAsioDriverInfo is populated with the driver info for the - currently open device (if any) - */ - PaDeviceIndex openAsioDeviceIndex; - PaAsioDriverInfo openAsioDriverInfo; -} -PaAsioHostApiRepresentation; - - -/* - Retrieve <driverCount> driver names from ASIO, returned in a char** - allocated in <group>. -*/ -static char **GetAsioDriverNames( PaAsioHostApiRepresentation *asioHostApi, PaUtilAllocationGroup *group, long driverCount ) -{ - char **result = 0; - int i; - - result =(char**)PaUtil_GroupAllocateMemory( - group, sizeof(char*) * driverCount ); - if( !result ) - goto error; - - result[0] = (char*)PaUtil_GroupAllocateMemory( - group, 32 * driverCount ); - if( !result[0] ) - goto error; - - for( i=0; i<driverCount; ++i ) - result[i] = result[0] + (32 * i); - - asioHostApi->asioDrivers->getDriverNames( result, driverCount ); - -error: - return result; -} - - -static PaSampleFormat AsioSampleTypeToPaNativeSampleFormat(ASIOSampleType type) -{ - switch (type) { - case ASIOSTInt16MSB: - case ASIOSTInt16LSB: - return paInt16; - - case ASIOSTFloat32MSB: - case ASIOSTFloat32LSB: - case ASIOSTFloat64MSB: - case ASIOSTFloat64LSB: - return paFloat32; - - case ASIOSTInt32MSB: - case ASIOSTInt32LSB: - case ASIOSTInt32MSB16: - case ASIOSTInt32LSB16: - case ASIOSTInt32MSB18: - case ASIOSTInt32MSB20: - case ASIOSTInt32MSB24: - case ASIOSTInt32LSB18: - case ASIOSTInt32LSB20: - case ASIOSTInt32LSB24: - return paInt32; - - case ASIOSTInt24MSB: - case ASIOSTInt24LSB: - return paInt24; - - default: - return paCustomFormat; - } -} - -void AsioSampleTypeLOG(ASIOSampleType type) -{ - switch (type) { - case ASIOSTInt16MSB: PA_DEBUG(("ASIOSTInt16MSB\n")); break; - case ASIOSTInt16LSB: PA_DEBUG(("ASIOSTInt16LSB\n")); break; - case ASIOSTFloat32MSB:PA_DEBUG(("ASIOSTFloat32MSB\n"));break; - case ASIOSTFloat32LSB:PA_DEBUG(("ASIOSTFloat32LSB\n"));break; - case ASIOSTFloat64MSB:PA_DEBUG(("ASIOSTFloat64MSB\n"));break; - case ASIOSTFloat64LSB:PA_DEBUG(("ASIOSTFloat64LSB\n"));break; - case ASIOSTInt32MSB: PA_DEBUG(("ASIOSTInt32MSB\n")); break; - case ASIOSTInt32LSB: PA_DEBUG(("ASIOSTInt32LSB\n")); break; - case ASIOSTInt32MSB16:PA_DEBUG(("ASIOSTInt32MSB16\n"));break; - case ASIOSTInt32LSB16:PA_DEBUG(("ASIOSTInt32LSB16\n"));break; - case ASIOSTInt32MSB18:PA_DEBUG(("ASIOSTInt32MSB18\n"));break; - case ASIOSTInt32MSB20:PA_DEBUG(("ASIOSTInt32MSB20\n"));break; - case ASIOSTInt32MSB24:PA_DEBUG(("ASIOSTInt32MSB24\n"));break; - case ASIOSTInt32LSB18:PA_DEBUG(("ASIOSTInt32LSB18\n"));break; - case ASIOSTInt32LSB20:PA_DEBUG(("ASIOSTInt32LSB20\n"));break; - case ASIOSTInt32LSB24:PA_DEBUG(("ASIOSTInt32LSB24\n"));break; - case ASIOSTInt24MSB: PA_DEBUG(("ASIOSTInt24MSB\n")); break; - case ASIOSTInt24LSB: PA_DEBUG(("ASIOSTInt24LSB\n")); break; - default: PA_DEBUG(("Custom Format%d\n",type));break; - - } -} - -static int BytesPerAsioSample( ASIOSampleType sampleType ) -{ - switch (sampleType) { - case ASIOSTInt16MSB: - case ASIOSTInt16LSB: - return 2; - - case ASIOSTFloat64MSB: - case ASIOSTFloat64LSB: - return 8; - - case ASIOSTFloat32MSB: - case ASIOSTFloat32LSB: - case ASIOSTInt32MSB: - case ASIOSTInt32LSB: - case ASIOSTInt32MSB16: - case ASIOSTInt32LSB16: - case ASIOSTInt32MSB18: - case ASIOSTInt32MSB20: - case ASIOSTInt32MSB24: - case ASIOSTInt32LSB18: - case ASIOSTInt32LSB20: - case ASIOSTInt32LSB24: - return 4; - - case ASIOSTInt24MSB: - case ASIOSTInt24LSB: - return 3; - - default: - return 0; - } -} - - -static void Swap16( void *buffer, long shift, long count ) -{ - unsigned short *p = (unsigned short*)buffer; - unsigned short temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - temp = *p; - *p++ = (unsigned short)((temp<<8) | (temp>>8)); - } -} - -static void Swap24( void *buffer, long shift, long count ) -{ - unsigned char *p = (unsigned char*)buffer; - unsigned char temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - temp = *p; - *p = *(p+2); - *(p+2) = temp; - p += 3; - } -} - -#define PA_SWAP32_( x ) ((x>>24) | ((x>>8)&0xFF00) | ((x<<8)&0xFF0000) | (x<<24)); - -static void Swap32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - temp = *p; - *p++ = PA_SWAP32_( temp); - } -} - -static void SwapShiftLeft32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - - while( count-- ) - { - temp = *p; - temp = PA_SWAP32_( temp); - *p++ = temp << shift; - } -} - -static void ShiftRightSwap32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - - while( count-- ) - { - temp = *p >> shift; - *p++ = PA_SWAP32_( temp); - } -} - -static void ShiftLeft32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - - while( count-- ) - { - temp = *p; - *p++ = temp << shift; - } -} - -static void ShiftRight32( void *buffer, long shift, long count ) -{ - unsigned long *p = (unsigned long*)buffer; - unsigned long temp; - - while( count-- ) - { - temp = *p; - *p++ = temp >> shift; - } -} - -#define PA_SWAP_( x, y ) temp=x; x = y; y = temp; - -static void Swap64ConvertFloat64ToFloat32( void *buffer, long shift, long count ) -{ - double *in = (double*)buffer; - float *out = (float*)buffer; - unsigned char *p; - unsigned char temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - p = (unsigned char*)in; - PA_SWAP_( p[0], p[7] ); - PA_SWAP_( p[1], p[6] ); - PA_SWAP_( p[2], p[5] ); - PA_SWAP_( p[3], p[4] ); - - *out++ = (float) (*in++); - } -} - -static void ConvertFloat64ToFloat32( void *buffer, long shift, long count ) -{ - double *in = (double*)buffer; - float *out = (float*)buffer; - (void) shift; /* unused parameter */ - - while( count-- ) - *out++ = (float) (*in++); -} - -static void ConvertFloat32ToFloat64Swap64( void *buffer, long shift, long count ) -{ - float *in = ((float*)buffer) + (count-1); - double *out = ((double*)buffer) + (count-1); - unsigned char *p; - unsigned char temp; - (void) shift; /* unused parameter */ - - while( count-- ) - { - *out = *in--; - - p = (unsigned char*)out; - PA_SWAP_( p[0], p[7] ); - PA_SWAP_( p[1], p[6] ); - PA_SWAP_( p[2], p[5] ); - PA_SWAP_( p[3], p[4] ); - - out--; - } -} - -static void ConvertFloat32ToFloat64( void *buffer, long shift, long count ) -{ - float *in = ((float*)buffer) + (count-1); - double *out = ((double*)buffer) + (count-1); - (void) shift; /* unused parameter */ - - while( count-- ) - *out-- = *in--; -} - -#ifdef MAC -#define PA_MSB_IS_NATIVE_ -#undef PA_LSB_IS_NATIVE_ -#endif - -#ifdef WINDOWS -#undef PA_MSB_IS_NATIVE_ -#define PA_LSB_IS_NATIVE_ -#endif - -typedef void PaAsioBufferConverter( void *, long, long ); - -static void SelectAsioToPaConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift ) -{ - *shift = 0; - *converter = 0; - - switch (type) { - case ASIOSTInt16MSB: - /* dest: paInt16, no conversion necessary, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap16; - #endif - break; - case ASIOSTInt16LSB: - /* dest: paInt16, no conversion necessary, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap16; - #endif - break; - case ASIOSTFloat32MSB: - /* dest: paFloat32, no conversion necessary, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTFloat32LSB: - /* dest: paFloat32, no conversion necessary, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTFloat64MSB: - /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap64ConvertFloat64ToFloat32; - #else - *converter = ConvertFloat64ToFloat32; - #endif - break; - case ASIOSTFloat64LSB: - /* dest: paFloat32, in-place conversion to/from float32, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap64ConvertFloat64ToFloat32; - #else - *converter = ConvertFloat64ToFloat32; - #endif - break; - case ASIOSTInt32MSB: - /* dest: paInt32, no conversion necessary, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTInt32LSB: - /* dest: paInt32, no conversion necessary, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTInt32MSB16: - /* dest: paInt32, 16 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 16; - break; - case ASIOSTInt32MSB18: - /* dest: paInt32, 14 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 14; - break; - case ASIOSTInt32MSB20: - /* dest: paInt32, 12 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 12; - break; - case ASIOSTInt32MSB24: - /* dest: paInt32, 8 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 8; - break; - case ASIOSTInt32LSB16: - /* dest: paInt32, 16 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 16; - break; - case ASIOSTInt32LSB18: - /* dest: paInt32, 14 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 14; - break; - case ASIOSTInt32LSB20: - /* dest: paInt32, 12 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 12; - break; - case ASIOSTInt32LSB24: - /* dest: paInt32, 8 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = SwapShiftLeft32; - #else - *converter = ShiftLeft32; - #endif - *shift = 8; - break; - case ASIOSTInt24MSB: - /* dest: paInt24, no conversion necessary, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap24; - #endif - break; - case ASIOSTInt24LSB: - /* dest: paInt24, no conversion necessary, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap24; - #endif - break; - } -} - - -static void SelectPaToAsioConverter( ASIOSampleType type, PaAsioBufferConverter **converter, long *shift ) -{ - *shift = 0; - *converter = 0; - - switch (type) { - case ASIOSTInt16MSB: - /* src: paInt16, no conversion necessary, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap16; - #endif - break; - case ASIOSTInt16LSB: - /* src: paInt16, no conversion necessary, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap16; - #endif - break; - case ASIOSTFloat32MSB: - /* src: paFloat32, no conversion necessary, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTFloat32LSB: - /* src: paFloat32, no conversion necessary, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTFloat64MSB: - /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ConvertFloat32ToFloat64Swap64; - #else - *converter = ConvertFloat32ToFloat64; - #endif - break; - case ASIOSTFloat64LSB: - /* src: paFloat32, in-place conversion to/from float32, possible byte swap*/ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ConvertFloat32ToFloat64Swap64; - #else - *converter = ConvertFloat32ToFloat64; - #endif - break; - case ASIOSTInt32MSB: - /* src: paInt32, no conversion necessary, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTInt32LSB: - /* src: paInt32, no conversion necessary, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap32; - #endif - break; - case ASIOSTInt32MSB16: - /* src: paInt32, 16 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 16; - break; - case ASIOSTInt32MSB18: - /* src: paInt32, 14 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 14; - break; - case ASIOSTInt32MSB20: - /* src: paInt32, 12 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 12; - break; - case ASIOSTInt32MSB24: - /* src: paInt32, 8 bit shift, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 8; - break; - case ASIOSTInt32LSB16: - /* src: paInt32, 16 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 16; - break; - case ASIOSTInt32LSB18: - /* src: paInt32, 14 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 14; - break; - case ASIOSTInt32LSB20: - /* src: paInt32, 12 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 12; - break; - case ASIOSTInt32LSB24: - /* src: paInt32, 8 bit shift, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = ShiftRightSwap32; - #else - *converter = ShiftRight32; - #endif - *shift = 8; - break; - case ASIOSTInt24MSB: - /* src: paInt24, no conversion necessary, possible byte swap */ - #ifdef PA_LSB_IS_NATIVE_ - *converter = Swap24; - #endif - break; - case ASIOSTInt24LSB: - /* src: paInt24, no conversion necessary, possible byte swap */ - #ifdef PA_MSB_IS_NATIVE_ - *converter = Swap24; - #endif - break; - } -} - - -typedef struct PaAsioDeviceInfo -{ - PaDeviceInfo commonDeviceInfo; - long minBufferSize; - long maxBufferSize; - long preferredBufferSize; - long bufferGranularity; - - ASIOChannelInfo *asioChannelInfos; -} -PaAsioDeviceInfo; - - -PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device, - long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity ) -{ - PaError result; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiDevice; - - result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); - - if( result == paNoError ) - { - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi ); - - if( result == paNoError ) - { - PaAsioDeviceInfo *asioDeviceInfo = - (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - - *minBufferSizeFrames = asioDeviceInfo->minBufferSize; - *maxBufferSizeFrames = asioDeviceInfo->maxBufferSize; - *preferredBufferSizeFrames = asioDeviceInfo->preferredBufferSize; - *granularity = asioDeviceInfo->bufferGranularity; - } - } - - return result; -} - -/* Unload whatever we loaded in LoadAsioDriver(). -*/ -static void UnloadAsioDriver( void ) -{ - ASIOExit(); -} - -/* - load the asio driver named by <driverName> and return statistics about - the driver in info. If no error occurred, the driver will remain open - and must be closed by the called by calling UnloadAsioDriver() - if an error - is returned the driver will already be unloaded. -*/ -static PaError LoadAsioDriver( PaAsioHostApiRepresentation *asioHostApi, const char *driverName, - PaAsioDriverInfo *driverInfo, void *systemSpecific ) -{ - PaError result = paNoError; - ASIOError asioError; - int asioIsInitialized = 0; - - if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(driverName) ) ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" ); - goto error; - } - - memset( &driverInfo->asioDriverInfo, 0, sizeof(ASIODriverInfo) ); - driverInfo->asioDriverInfo.asioVersion = 2; - driverInfo->asioDriverInfo.sysRef = systemSpecific; - if( (asioError = ASIOInit( &driverInfo->asioDriverInfo )) != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - else - { - asioIsInitialized = 1; - } - - if( (asioError = ASIOGetChannels(&driverInfo->inputChannelCount, - &driverInfo->outputChannelCount)) != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - - if( (asioError = ASIOGetBufferSize(&driverInfo->bufferMinSize, - &driverInfo->bufferMaxSize, &driverInfo->bufferPreferredSize, - &driverInfo->bufferGranularity)) != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - - if( ASIOOutputReady() == ASE_OK ) - driverInfo->postOutput = true; - else - driverInfo->postOutput = false; - - return result; - -error: - if( asioIsInitialized ) - { - ASIOExit(); - } - - return result; -} - - -#define PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_ 13 /* must be the same number of elements as in the array below */ -static ASIOSampleRate defaultSampleRateSearchOrder_[] - = {44100.0, 48000.0, 32000.0, 24000.0, 22050.0, 88200.0, 96000.0, - 192000.0, 16000.0, 12000.0, 11025.0, 9600.0, 8000.0 }; - - -static PaError InitPaDeviceInfoFromAsioDriver( PaAsioHostApiRepresentation *asioHostApi, - const char *driverName, int driverIndex, - PaDeviceInfo *deviceInfo, PaAsioDeviceInfo *asioDeviceInfo ) -{ - PaError result = paNoError; - - /* Due to the headless design of the ASIO API, drivers are free to write over data given to them (like M-Audio - drivers f.i.). This is an attempt to overcome that. */ - union _tag_local { - PaAsioDriverInfo info; - char _padding[4096]; - } paAsioDriver; - - asioDeviceInfo->asioChannelInfos = 0; /* we check this below to handle error cleanup */ - - result = LoadAsioDriver( asioHostApi, driverName, &paAsioDriver.info, asioHostApi->systemSpecific ); - if( result == paNoError ) - { - PA_DEBUG(("PaAsio_Initialize: drv:%d name = %s\n", driverIndex,deviceInfo->name)); - PA_DEBUG(("PaAsio_Initialize: drv:%d inputChannels = %d\n", driverIndex, paAsioDriver.info.inputChannelCount)); - PA_DEBUG(("PaAsio_Initialize: drv:%d outputChannels = %d\n", driverIndex, paAsioDriver.info.outputChannelCount)); - PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMinSize = %d\n", driverIndex, paAsioDriver.info.bufferMinSize)); - PA_DEBUG(("PaAsio_Initialize: drv:%d bufferMaxSize = %d\n", driverIndex, paAsioDriver.info.bufferMaxSize)); - PA_DEBUG(("PaAsio_Initialize: drv:%d bufferPreferredSize = %d\n", driverIndex, paAsioDriver.info.bufferPreferredSize)); - PA_DEBUG(("PaAsio_Initialize: drv:%d bufferGranularity = %d\n", driverIndex, paAsioDriver.info.bufferGranularity)); - - deviceInfo->maxInputChannels = paAsioDriver.info.inputChannelCount; - deviceInfo->maxOutputChannels = paAsioDriver.info.outputChannelCount; - - deviceInfo->defaultSampleRate = 0.; - bool foundDefaultSampleRate = false; - for( int j=0; j < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++j ) - { - ASIOError asioError = ASIOCanSampleRate( defaultSampleRateSearchOrder_[j] ); - if( asioError != ASE_NoClock && asioError != ASE_NotPresent ) - { - deviceInfo->defaultSampleRate = defaultSampleRateSearchOrder_[j]; - foundDefaultSampleRate = true; - break; - } - } - - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultSampleRate = %f\n", driverIndex, deviceInfo->defaultSampleRate)); - - if( foundDefaultSampleRate ){ - - /* calculate default latency values from bufferPreferredSize - for default low latency, and bufferMaxSize - for default high latency. - use the default sample rate to convert from samples to - seconds. Without knowing what sample rate the user will - use this is the best we can do. - */ - - double defaultLowLatency = - paAsioDriver.info.bufferPreferredSize / deviceInfo->defaultSampleRate; - - deviceInfo->defaultLowInputLatency = defaultLowLatency; - deviceInfo->defaultLowOutputLatency = defaultLowLatency; - - double defaultHighLatency = - paAsioDriver.info.bufferMaxSize / deviceInfo->defaultSampleRate; - - if( defaultHighLatency < defaultLowLatency ) - defaultHighLatency = defaultLowLatency; /* just in case the driver returns something strange */ - - deviceInfo->defaultHighInputLatency = defaultHighLatency; - deviceInfo->defaultHighOutputLatency = defaultHighLatency; - - }else{ - - deviceInfo->defaultLowInputLatency = 0.; - deviceInfo->defaultLowOutputLatency = 0.; - deviceInfo->defaultHighInputLatency = 0.; - deviceInfo->defaultHighOutputLatency = 0.; - } - - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowInputLatency = %f\n", driverIndex, deviceInfo->defaultLowInputLatency)); - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultLowOutputLatency = %f\n", driverIndex, deviceInfo->defaultLowOutputLatency)); - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighInputLatency = %f\n", driverIndex, deviceInfo->defaultHighInputLatency)); - PA_DEBUG(("PaAsio_Initialize: drv:%d defaultHighOutputLatency = %f\n", driverIndex, deviceInfo->defaultHighOutputLatency)); - - asioDeviceInfo->minBufferSize = paAsioDriver.info.bufferMinSize; - asioDeviceInfo->maxBufferSize = paAsioDriver.info.bufferMaxSize; - asioDeviceInfo->preferredBufferSize = paAsioDriver.info.bufferPreferredSize; - asioDeviceInfo->bufferGranularity = paAsioDriver.info.bufferGranularity; - - - asioDeviceInfo->asioChannelInfos = (ASIOChannelInfo*)PaUtil_GroupAllocateMemory( - asioHostApi->allocations, - sizeof(ASIOChannelInfo) * (deviceInfo->maxInputChannels - + deviceInfo->maxOutputChannels) ); - if( !asioDeviceInfo->asioChannelInfos ) - { - result = paInsufficientMemory; - goto error_unload; - } - - int a; - - for( a=0; a < deviceInfo->maxInputChannels; ++a ){ - asioDeviceInfo->asioChannelInfos[a].channel = a; - asioDeviceInfo->asioChannelInfos[a].isInput = ASIOTrue; - ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[a] ); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error_unload; - } - } - - for( a=0; a < deviceInfo->maxOutputChannels; ++a ){ - int b = deviceInfo->maxInputChannels + a; - asioDeviceInfo->asioChannelInfos[b].channel = a; - asioDeviceInfo->asioChannelInfos[b].isInput = ASIOFalse; - ASIOError asioError = ASIOGetChannelInfo( &asioDeviceInfo->asioChannelInfos[b] ); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error_unload; - } - } - - /* unload the driver */ - UnloadAsioDriver(); - } - - return result; - -error_unload: - UnloadAsioDriver(); - - if( asioDeviceInfo->asioChannelInfos ){ - PaUtil_GroupFreeMemory( asioHostApi->allocations, asioDeviceInfo->asioChannelInfos ); - asioDeviceInfo->asioChannelInfos = 0; - } - - return result; -} - - -/* we look up IsDebuggerPresent at runtime incase it isn't present (on Win95 for example) */ -typedef BOOL (WINAPI *IsDebuggerPresentPtr)(VOID); -IsDebuggerPresentPtr IsDebuggerPresent_ = 0; -//FARPROC IsDebuggerPresent_ = 0; // this is the current way to do it apparently according to davidv - -PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) -{ - PaError result = paNoError; - int i, driverCount; - PaAsioHostApiRepresentation *asioHostApi; - PaAsioDeviceInfo *deviceInfoArray; - char **names; - asioHostApi = (PaAsioHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaAsioHostApiRepresentation) ); - if( !asioHostApi ) - { - result = paInsufficientMemory; - goto error; - } - - memset( asioHostApi, 0, sizeof(PaAsioHostApiRepresentation) ); /* ensure all fields are zeroed. especially asioHostApi->allocations */ - - /* - We initialize COM ourselves here and uninitialize it in Terminate(). - This should be the only COM initialization needed in this module. - - The ASIO SDK may also initialize COM but since we want to reduce dependency - on the ASIO SDK we manage COM initialization ourselves. - - There used to be code that initialized COM in other situations - such as when creating a Stream. This made PA work when calling Pa_CreateStream - from a non-main thread. However we currently consider initialization - of COM in non-main threads to be the caller's responsibility. - */ - result = PaWinUtil_CoInitialize( paASIO, &asioHostApi->comInitializationResult ); - if( result != paNoError ) - { - goto error; - } - - asioHostApi->asioDrivers = 0; /* avoid surprises in our error handler below */ - - asioHostApi->allocations = PaUtil_CreateAllocationGroup(); - if( !asioHostApi->allocations ) - { - result = paInsufficientMemory; - goto error; - } - - /* Allocate the AsioDrivers() driver list (class from ASIO SDK) */ - try - { - asioHostApi->asioDrivers = new AsioDrivers(); /* invokes CoInitialize(0) in AsioDriverList::AsioDriverList */ - } - catch (std::bad_alloc) - { - asioHostApi->asioDrivers = 0; - } - /* some implementations of new (ie MSVC, see http://support.microsoft.com/?kbid=167733) - don't throw std::bad_alloc, so we also explicitly test for a null return. */ - if( asioHostApi->asioDrivers == 0 ) - { - result = paInsufficientMemory; - goto error; - } - - asioDrivers = asioHostApi->asioDrivers; /* keep SDK global in sync until we stop depending on it */ - - asioHostApi->systemSpecific = 0; - asioHostApi->openAsioDeviceIndex = paNoDevice; - - *hostApi = &asioHostApi->inheritedHostApiRep; - (*hostApi)->info.structVersion = 1; - - (*hostApi)->info.type = paASIO; - (*hostApi)->info.name = "ASIO"; - (*hostApi)->info.deviceCount = 0; - - #ifdef WINDOWS - /* use desktop window as system specific ptr */ - asioHostApi->systemSpecific = GetDesktopWindow(); - #endif - - /* driverCount is the number of installed drivers - not necessarily - the number of installed physical devices. */ - #if MAC - driverCount = asioHostApi->asioDrivers->getNumFragments(); - #elif WINDOWS - driverCount = asioHostApi->asioDrivers->asioGetNumDev(); - #endif - - if( driverCount > 0 ) - { - names = GetAsioDriverNames( asioHostApi, asioHostApi->allocations, driverCount ); - if( !names ) - { - result = paInsufficientMemory; - goto error; - } - - - /* allocate enough space for all drivers, even if some aren't installed */ - - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - asioHostApi->allocations, sizeof(PaDeviceInfo*) * driverCount ); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } - - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaAsioDeviceInfo*)PaUtil_GroupAllocateMemory( - asioHostApi->allocations, sizeof(PaAsioDeviceInfo) * driverCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } - - IsDebuggerPresent_ = (IsDebuggerPresentPtr)GetProcAddress( LoadLibraryA( "Kernel32.dll" ), "IsDebuggerPresent" ); - - for( i=0; i < driverCount; ++i ) - { - PA_DEBUG(("ASIO names[%d]:%s\n",i,names[i])); - - // Since portaudio opens ALL ASIO drivers, and no one else does that, - // we face fact that some drivers were not meant for it, drivers which act - // like shells on top of REAL drivers, for instance. - // so we get duplicate handles, locks and other problems. - // so lets NOT try to load any such wrappers. - // The ones i [davidv] know of so far are: - - if ( strcmp (names[i],"ASIO DirectX Full Duplex Driver") == 0 - || strcmp (names[i],"ASIO Multimedia Driver") == 0 - || strncmp(names[i],"Premiere",8) == 0 //"Premiere Elements Windows Sound 1.0" - || strncmp(names[i],"Adobe",5) == 0 //"Adobe Default Windows Sound 1.5" - ) - { - PA_DEBUG(("BLACKLISTED!!!\n")); - continue; - } - - - if( IsDebuggerPresent_ && IsDebuggerPresent_() ) - { - /* ASIO Digidesign Driver uses PACE copy protection which quits out - if a debugger is running. So we don't load it if a debugger is running. */ - if( strcmp(names[i], "ASIO Digidesign Driver") == 0 ) - { - PA_DEBUG(("BLACKLISTED!!! ASIO Digidesign Driver would quit the debugger\n")); - continue; - } - } - - - /* Attempt to init device info from the asio driver... */ - { - PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ]; - PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo; - - deviceInfo->structVersion = 2; - deviceInfo->hostApi = hostApiIndex; - - deviceInfo->name = names[i]; - - if( InitPaDeviceInfoFromAsioDriver( asioHostApi, names[i], i, deviceInfo, asioDeviceInfo ) == paNoError ) - { - (*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo; - ++(*hostApi)->info.deviceCount; - } - else - { - PA_DEBUG(("Skipping ASIO device:%s\n",names[i])); - continue; - } - } - } - } - - if( (*hostApi)->info.deviceCount > 0 ) - { - (*hostApi)->info.defaultInputDevice = 0; - (*hostApi)->info.defaultOutputDevice = 0; - } - else - { - (*hostApi)->info.defaultInputDevice = paNoDevice; - (*hostApi)->info.defaultOutputDevice = paNoDevice; - } - - - (*hostApi)->Terminate = Terminate; - (*hostApi)->OpenStream = OpenStream; - (*hostApi)->IsFormatSupported = IsFormatSupported; - - PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, GetStreamCpuLoad, - PaUtil_DummyRead, PaUtil_DummyWrite, - PaUtil_DummyGetReadAvailable, PaUtil_DummyGetWriteAvailable ); - - PaUtil_InitializeStreamInterface( &asioHostApi->blockingStreamInterface, CloseStream, StartStream, - StopStream, AbortStream, IsStreamStopped, IsStreamActive, - GetStreamTime, PaUtil_DummyGetCpuLoad, - ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable ); - - return result; - -error: - if( asioHostApi ) - { - if( asioHostApi->allocations ) - { - PaUtil_FreeAllAllocations( asioHostApi->allocations ); - PaUtil_DestroyAllocationGroup( asioHostApi->allocations ); - } - - delete asioHostApi->asioDrivers; - asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */ - - PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult ); - - PaUtil_FreeMemory( asioHostApi ); - } - - return result; -} - - -static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) -{ - PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi; - - /* - IMPLEMENT ME: - - clean up any resources not handled by the allocation group (need to review if there are any) - */ - - if( asioHostApi->allocations ) - { - PaUtil_FreeAllAllocations( asioHostApi->allocations ); - PaUtil_DestroyAllocationGroup( asioHostApi->allocations ); - } - - delete asioHostApi->asioDrivers; - asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */ - - PaWinUtil_CoUninitialize( paASIO, &asioHostApi->comInitializationResult ); - - PaUtil_FreeMemory( asioHostApi ); -} - - -static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate ) -{ - PaError result = paNoError; - PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi; - PaAsioDriverInfo *driverInfo = &asioHostApi->openAsioDriverInfo; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaDeviceIndex asioDeviceIndex; - ASIOError asioError; - - if( inputParameters && outputParameters ) - { - /* full duplex ASIO stream must use the same device for input and output */ - - if( inputParameters->device != outputParameters->device ) - return paBadIODeviceCombination; - } - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( inputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - asioDeviceIndex = inputParameters->device; - - /* validate inputStreamInfo */ - /** @todo do more validation here */ - // if( inputParameters->hostApiSpecificStreamInfo ) - // return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - inputChannelCount = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - - /* all standard sample formats are supported by the buffer adapter, - this implementation doesn't support any custom sample formats */ - if( outputSampleFormat & paCustomFormat ) - return paSampleFormatNotSupported; - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - asioDeviceIndex = outputParameters->device; - - /* validate outputStreamInfo */ - /** @todo do more validation here */ - // if( outputParameters->hostApiSpecificStreamInfo ) - // return paIncompatibleHostApiSpecificStreamInfo; /* this implementation doesn't use custom stream info */ - } - else - { - outputChannelCount = 0; - } - - - - /* if an ASIO device is open we can only get format information for the currently open device */ - - if( asioHostApi->openAsioDeviceIndex != paNoDevice - && asioHostApi->openAsioDeviceIndex != asioDeviceIndex ) - { - return paDeviceUnavailable; - } - - - /* NOTE: we load the driver and use its current settings - rather than the ones in our device info structure which may be stale */ - - /* open the device if it's not already open */ - if( asioHostApi->openAsioDeviceIndex == paNoDevice ) - { - result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name, - driverInfo, asioHostApi->systemSpecific ); - if( result != paNoError ) - return result; - } - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > 0 ) - { - if( inputChannelCount > driverInfo->inputChannelCount ) - { - result = paInvalidChannelCount; - goto done; - } - } - - /* check that output device can support outputChannelCount */ - if( outputChannelCount ) - { - if( outputChannelCount > driverInfo->outputChannelCount ) - { - result = paInvalidChannelCount; - goto done; - } - } - - /* query for sample rate support */ - asioError = ASIOCanSampleRate( sampleRate ); - if( asioError == ASE_NoClock || asioError == ASE_NotPresent ) - { - result = paInvalidSampleRate; - goto done; - } - -done: - /* close the device if it wasn't already open */ - if( asioHostApi->openAsioDeviceIndex == paNoDevice ) - { - UnloadAsioDriver(); /* not sure if we should check for errors here */ - } - - if( result == paNoError ) - return paFormatIsSupported; - else - return result; -} - - - -/** A data structure specifically for storing blocking i/o related data. */ -typedef struct PaAsioStreamBlockingState -{ - int stopFlag; /**< Flag indicating that block processing is to be stopped. */ - - unsigned long writeBuffersRequested; /**< The number of available output buffers, requested by the #WriteStream() function. */ - unsigned long readFramesRequested; /**< The number of available input frames, requested by the #ReadStream() function. */ - - int writeBuffersRequestedFlag; /**< Flag to indicate that #WriteStream() has requested more output buffers to be available. */ - int readFramesRequestedFlag; /**< Flag to indicate that #ReadStream() requires more input frames to be available. */ - - HANDLE writeBuffersReadyEvent; /**< Event to signal that requested output buffers are available. */ - HANDLE readFramesReadyEvent; /**< Event to signal that requested input frames are available. */ - - void *writeRingBufferData; /**< The actual ring buffer memory, used by the output ring buffer. */ - void *readRingBufferData; /**< The actual ring buffer memory, used by the input ring buffer. */ - - PaUtilRingBuffer writeRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store output data (interleaved user format). */ - PaUtilRingBuffer readRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store input data (interleaved user format). */ - - long writeRingBufferInitialFrames; /**< The initial number of silent frames within the output ring buffer. */ - - const void **writeStreamBuffer; /**< Temp buffer, used by #WriteStream() for handling non-interleaved data. */ - void **readStreamBuffer; /**< Temp buffer, used by #ReadStream() for handling non-interleaved data. */ - - PaUtilBufferProcessor bufferProcessor; /**< Buffer processor, used to handle the blocking i/o ring buffers. */ - - int outputUnderflowFlag; /**< Flag to signal an output underflow from within the callback function. */ - int inputOverflowFlag; /**< Flag to signal an input overflow from within the callback function. */ -} -PaAsioStreamBlockingState; - - - -/* PaAsioStream - a stream data structure specifically for this implementation */ - -typedef struct PaAsioStream -{ - PaUtilStreamRepresentation streamRepresentation; - PaUtilCpuLoadMeasurer cpuLoadMeasurer; - PaUtilBufferProcessor bufferProcessor; - - PaAsioHostApiRepresentation *asioHostApi; - unsigned long framesPerHostCallback; - - /* ASIO driver info - these may not be needed for the life of the stream, - but store them here until we work out how format conversion is going - to work. */ - - ASIOBufferInfo *asioBufferInfos; - ASIOChannelInfo *asioChannelInfos; - long asioInputLatencyFrames, asioOutputLatencyFrames; // actual latencies returned by asio - - long inputChannelCount, outputChannelCount; - bool postOutput; - - void **bufferPtrs; /* this is carved up for inputBufferPtrs and outputBufferPtrs */ - void **inputBufferPtrs[2]; - void **outputBufferPtrs[2]; - - PaAsioBufferConverter *inputBufferConverter; - long inputShift; - PaAsioBufferConverter *outputBufferConverter; - long outputShift; - - volatile bool stopProcessing; - int stopPlayoutCount; - HANDLE completedBuffersPlayedEvent; - - bool streamFinishedCallbackCalled; - int isStopped; - volatile int isActive; - volatile bool zeroOutput; /* all future calls to the callback will output silence */ - - volatile long reenterCount; - volatile long reenterError; - - PaStreamCallbackFlags callbackFlags; - - PaAsioStreamBlockingState *blockingState; /**< Blocking i/o data struct, or NULL when using callback interface. */ -} -PaAsioStream; - -static PaAsioStream *theAsioStream = 0; /* due to ASIO sdk limitations there can be only one stream */ - - -static void ZeroOutputBuffers( PaAsioStream *stream, long index ) -{ - int i; - - for( i=0; i < stream->outputChannelCount; ++i ) - { - void *buffer = stream->asioBufferInfos[ i + stream->inputChannelCount ].buffers[index]; - - int bytesPerSample = BytesPerAsioSample( stream->asioChannelInfos[ i + stream->inputChannelCount ].type ); - - memset( buffer, 0, stream->framesPerHostCallback * bytesPerSample ); - } -} - - -/* return the next power of two >= x. - Returns the input parameter if it is already a power of two. - http://stackoverflow.com/questions/364985/algorithm-for-finding-the-smallest-power-of-two-thats-greater-or-equal-to-a-giv -*/ -static unsigned long NextPowerOfTwo( unsigned long x ) -{ - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - /* If you needed to deal with numbers > 2^32 the following would be needed. - For latencies, we don't deal with values this large. - x |= x >> 16; - */ - - return x + 1; -} - - -static unsigned long SelectHostBufferSizeForUnspecifiedUserFramesPerBuffer( - unsigned long targetBufferingLatencyFrames, PaAsioDriverInfo *driverInfo ) -{ - /* Choose a host buffer size based only on targetBufferingLatencyFrames and the - device's supported buffer sizes. Always returns a valid value. - */ - - unsigned long result; - - if( targetBufferingLatencyFrames <= (unsigned long)driverInfo->bufferMinSize ) - { - result = driverInfo->bufferMinSize; - } - else if( targetBufferingLatencyFrames >= (unsigned long)driverInfo->bufferMaxSize ) - { - result = driverInfo->bufferMaxSize; - } - else - { - if( driverInfo->bufferGranularity == 0 ) /* single fixed host buffer size */ - { - /* The documentation states that bufferGranularity should be zero - when bufferMinSize, bufferMaxSize and bufferPreferredSize are the - same. We assume that is the case. - */ - - result = driverInfo->bufferPreferredSize; - } - else if( driverInfo->bufferGranularity == -1 ) /* power-of-two */ - { - /* We assume bufferMinSize and bufferMaxSize are powers of two. */ - - result = NextPowerOfTwo( targetBufferingLatencyFrames ); - - if( result < (unsigned long)driverInfo->bufferMinSize ) - result = driverInfo->bufferMinSize; - - if( result > (unsigned long)driverInfo->bufferMaxSize ) - result = driverInfo->bufferMaxSize; - } - else /* modulo bufferGranularity */ - { - /* round up to the next multiple of granularity */ - unsigned long n = (targetBufferingLatencyFrames + driverInfo->bufferGranularity - 1) - / driverInfo->bufferGranularity; - - result = n * driverInfo->bufferGranularity; - - if( result < (unsigned long)driverInfo->bufferMinSize ) - result = driverInfo->bufferMinSize; - - if( result > (unsigned long)driverInfo->bufferMaxSize ) - result = driverInfo->bufferMaxSize; - } - } - - return result; -} - - -static unsigned long SelectHostBufferSizeForSpecifiedUserFramesPerBuffer( - unsigned long targetBufferingLatencyFrames, unsigned long userFramesPerBuffer, - PaAsioDriverInfo *driverInfo ) -{ - /* Select a host buffer size conforming to targetBufferingLatencyFrames - and the device's supported buffer sizes. - The return value will always be a multiple of userFramesPerBuffer. - If a valid buffer size can not be found the function returns 0. - - The current implementation uses a simple iterative search for clarity. - Feel free to suggest a closed form solution. - */ - unsigned long result = 0; - - assert( userFramesPerBuffer != 0 ); - - if( driverInfo->bufferGranularity == 0 ) /* single fixed host buffer size */ - { - /* The documentation states that bufferGranularity should be zero - when bufferMinSize, bufferMaxSize and bufferPreferredSize are the - same. We assume that is the case. - */ - - if( (driverInfo->bufferPreferredSize % userFramesPerBuffer) == 0 ) - result = driverInfo->bufferPreferredSize; - } - else if( driverInfo->bufferGranularity == -1 ) /* power-of-two */ - { - /* We assume bufferMinSize and bufferMaxSize are powers of two. */ - - /* Search all powers of two in the range [bufferMinSize,bufferMaxSize] - for multiples of userFramesPerBuffer. We prefer the first multiple - that is equal or greater than targetBufferingLatencyFrames, or - failing that, the largest multiple less than - targetBufferingLatencyFrames. - */ - unsigned long x = (unsigned long)driverInfo->bufferMinSize; - do { - if( (x % userFramesPerBuffer) == 0 ) - { - /* any multiple of userFramesPerBuffer is acceptable */ - result = x; - if( result >= targetBufferingLatencyFrames ) - break; /* stop. a value >= to targetBufferingLatencyFrames is ideal. */ - } - - x *= 2; - } while( x <= (unsigned long)driverInfo->bufferMaxSize ); - } - else /* modulo granularity */ - { - /* We assume bufferMinSize is a multiple of bufferGranularity. */ - - /* Search all multiples of bufferGranularity in the range - [bufferMinSize,bufferMaxSize] for multiples of userFramesPerBuffer. - We prefer the first multiple that is equal or greater than - targetBufferingLatencyFrames, or failing that, the largest multiple - less than targetBufferingLatencyFrames. - */ - unsigned long x = (unsigned long)driverInfo->bufferMinSize; - do { - if( (x % userFramesPerBuffer) == 0 ) - { - /* any multiple of userFramesPerBuffer is acceptable */ - result = x; - if( result >= targetBufferingLatencyFrames ) - break; /* stop. a value >= to targetBufferingLatencyFrames is ideal. */ - } - - x += driverInfo->bufferGranularity; - } while( x <= (unsigned long)driverInfo->bufferMaxSize ); - } - - return result; -} - - -static unsigned long SelectHostBufferSize( - unsigned long targetBufferingLatencyFrames, - unsigned long userFramesPerBuffer, PaAsioDriverInfo *driverInfo ) -{ - unsigned long result = 0; - - /* We select a host buffer size based on the following requirements - (in priority order): - - 1. The host buffer size must be permissible according to the ASIO - driverInfo buffer size constraints (min, max, granularity or - powers-of-two). - - 2. If the user specifies a non-zero framesPerBuffer parameter - (userFramesPerBuffer here) the host buffer should be a multiple of - this (subject to the constraints in (1) above). - - [NOTE: Where no permissible host buffer size is a multiple of - userFramesPerBuffer, we choose a value as if userFramesPerBuffer were - zero (i.e. we ignore it). This strategy is open for review ~ perhaps - there are still "more optimal" buffer sizes related to - userFramesPerBuffer that we could use.] - - 3. The host buffer size should be greater than or equal to - targetBufferingLatencyFrames, subject to (1) and (2) above. Where it - is not possible to select a host buffer size equal or greater than - targetBufferingLatencyFrames, the highest buffer size conforming to - (1) and (2) should be chosen. - */ - - if( userFramesPerBuffer != 0 ) - { - /* userFramesPerBuffer is specified, try to find a buffer size that's - a multiple of it */ - result = SelectHostBufferSizeForSpecifiedUserFramesPerBuffer( - targetBufferingLatencyFrames, userFramesPerBuffer, driverInfo ); - } - - if( result == 0 ) - { - /* either userFramesPerBuffer was not specified, or we couldn't find a - host buffer size that is a multiple of it. Select a host buffer size - according to targetBufferingLatencyFrames and the ASIO driverInfo - buffer size constraints. - */ - result = SelectHostBufferSizeForUnspecifiedUserFramesPerBuffer( - targetBufferingLatencyFrames, driverInfo ); - } - - return result; -} - - -/* returns channelSelectors if present */ - -static PaError ValidateAsioSpecificStreamInfo( - const PaStreamParameters *streamParameters, - const PaAsioStreamInfo *streamInfo, - int deviceChannelCount, - int **channelSelectors ) -{ - if( streamInfo ) - { - if( streamInfo->size != sizeof( PaAsioStreamInfo ) - || streamInfo->version != 1 ) - { - return paIncompatibleHostApiSpecificStreamInfo; - } - - if( streamInfo->flags & paAsioUseChannelSelectors ) - *channelSelectors = streamInfo->channelSelectors; - - if( !(*channelSelectors) ) - return paIncompatibleHostApiSpecificStreamInfo; - - for( int i=0; i < streamParameters->channelCount; ++i ){ - if( (*channelSelectors)[i] < 0 - || (*channelSelectors)[i] >= deviceChannelCount ){ - return paInvalidChannelCount; - } - } - } - - return paNoError; -} - - -static bool IsUsingExternalClockSource() -{ - bool result = false; - ASIOError asioError; - ASIOClockSource clocks[32]; - long numSources=32; - - /* davidv: listing ASIO Clock sources. there is an ongoing investigation by - me about whether or not to call ASIOSetSampleRate if an external Clock is - used. A few drivers expected different things here */ - - asioError = ASIOGetClockSources(clocks, &numSources); - if( asioError != ASE_OK ){ - PA_DEBUG(("ERROR: ASIOGetClockSources: %s\n", PaAsio_GetAsioErrorText(asioError) )); - }else{ - PA_DEBUG(("INFO ASIOGetClockSources listing %d clocks\n", numSources )); - for (int i=0;i<numSources;++i){ - PA_DEBUG(("ASIOClockSource%d %s current:%d\n", i, clocks[i].name, clocks[i].isCurrentSource )); - - if (clocks[i].isCurrentSource) - result = true; - } - } - - return result; -} - - -static PaError ValidateAndSetSampleRate( double sampleRate ) -{ - PaError result = paNoError; - ASIOError asioError; - - // check that the device supports the requested sample rate - - asioError = ASIOCanSampleRate( sampleRate ); - PA_DEBUG(("ASIOCanSampleRate(%f):%d\n", sampleRate, asioError )); - - if( asioError != ASE_OK ) - { - result = paInvalidSampleRate; - PA_DEBUG(("ERROR: ASIOCanSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) )); - goto error; - } - - // retrieve the current sample rate, we only change to the requested - // sample rate if the device is not already in that rate. - - ASIOSampleRate oldRate; - asioError = ASIOGetSampleRate(&oldRate); - if( asioError != ASE_OK ) - { - result = paInvalidSampleRate; - PA_DEBUG(("ERROR: ASIOGetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) )); - goto error; - } - PA_DEBUG(("ASIOGetSampleRate:%f\n",oldRate)); - - if (oldRate != sampleRate){ - /* Set sample rate */ - - PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate)); - - /* - If you have problems with some drivers when externally clocked, - try switching on the following line and commenting out the one after it. - See IsUsingExternalClockSource() for more info. - */ - //if( IsUsingExternalClockSource() ){ - if( false ){ - asioError = ASIOSetSampleRate( 0 ); - }else{ - asioError = ASIOSetSampleRate( sampleRate ); - } - if( asioError != ASE_OK ) - { - result = paInvalidSampleRate; - PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) )); - goto error; - } - PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate)); - } - else - { - PA_DEBUG(("No Need to change SR\n")); - } - -error: - return result; -} - - -/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */ - -static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, - PaStream** s, - const PaStreamParameters *inputParameters, - const PaStreamParameters *outputParameters, - double sampleRate, - unsigned long framesPerBuffer, - PaStreamFlags streamFlags, - PaStreamCallback *streamCallback, - void *userData ) -{ - PaError result = paNoError; - PaAsioHostApiRepresentation *asioHostApi = (PaAsioHostApiRepresentation*)hostApi; - PaAsioStream *stream = 0; - PaAsioStreamInfo *inputStreamInfo, *outputStreamInfo; - unsigned long framesPerHostBuffer; - int inputChannelCount, outputChannelCount; - PaSampleFormat inputSampleFormat, outputSampleFormat; - PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat; - unsigned long suggestedInputLatencyFrames; - unsigned long suggestedOutputLatencyFrames; - PaDeviceIndex asioDeviceIndex; - ASIOError asioError; - int asioIsInitialized = 0; - int asioBuffersCreated = 0; - int completedBuffersPlayedEventInited = 0; - int i; - PaAsioDriverInfo *driverInfo; - int *inputChannelSelectors = 0; - int *outputChannelSelectors = 0; - - /* Are we using blocking i/o interface? */ - int usingBlockingIo = ( !streamCallback ) ? TRUE : FALSE; - /* Blocking i/o stuff */ - long lBlockingBufferSize = 0; /* Desired ring buffer size in samples. */ - long lBlockingBufferSizePow2 = 0; /* Power-of-2 rounded ring buffer size. */ - long lBytesPerFrame = 0; /* Number of bytes per input/output frame. */ - int blockingWriteBuffersReadyEventInitialized = 0; /* Event init flag. */ - int blockingReadFramesReadyEventInitialized = 0; /* Event init flag. */ - - int callbackBufferProcessorInited = FALSE; - int blockingBufferProcessorInited = FALSE; - - /* unless we move to using lower level ASIO calls, we can only have - one device open at a time */ - if( asioHostApi->openAsioDeviceIndex != paNoDevice ) - { - PA_DEBUG(("OpenStream paDeviceUnavailable\n")); - return paDeviceUnavailable; - } - - assert( theAsioStream == 0 ); - - if( inputParameters && outputParameters ) - { - /* full duplex ASIO stream must use the same device for input and output */ - - if( inputParameters->device != outputParameters->device ) - { - PA_DEBUG(("OpenStream paBadIODeviceCombination\n")); - return paBadIODeviceCombination; - } - } - - if( inputParameters ) - { - inputChannelCount = inputParameters->channelCount; - inputSampleFormat = inputParameters->sampleFormat; - suggestedInputLatencyFrames = (unsigned long)((inputParameters->suggestedLatency * sampleRate)+0.5f); - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - if( inputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - asioDeviceIndex = inputParameters->device; - - PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex]; - - /* validate hostApiSpecificStreamInfo */ - inputStreamInfo = (PaAsioStreamInfo*)inputParameters->hostApiSpecificStreamInfo; - result = ValidateAsioSpecificStreamInfo( inputParameters, inputStreamInfo, - asioDeviceInfo->commonDeviceInfo.maxInputChannels, - &inputChannelSelectors - ); - if( result != paNoError ) return result; - } - else - { - inputChannelCount = 0; - inputSampleFormat = 0; - suggestedInputLatencyFrames = 0; - } - - if( outputParameters ) - { - outputChannelCount = outputParameters->channelCount; - outputSampleFormat = outputParameters->sampleFormat; - suggestedOutputLatencyFrames = (unsigned long)((outputParameters->suggestedLatency * sampleRate)+0.5f); - - /* unless alternate device specification is supported, reject the use of - paUseHostApiSpecificDeviceSpecification */ - if( outputParameters->device == paUseHostApiSpecificDeviceSpecification ) - return paInvalidDevice; - - asioDeviceIndex = outputParameters->device; - - PaAsioDeviceInfo *asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[asioDeviceIndex]; - - /* validate hostApiSpecificStreamInfo */ - outputStreamInfo = (PaAsioStreamInfo*)outputParameters->hostApiSpecificStreamInfo; - result = ValidateAsioSpecificStreamInfo( outputParameters, outputStreamInfo, - asioDeviceInfo->commonDeviceInfo.maxOutputChannels, - &outputChannelSelectors - ); - if( result != paNoError ) return result; - } - else - { - outputChannelCount = 0; - outputSampleFormat = 0; - suggestedOutputLatencyFrames = 0; - } - - driverInfo = &asioHostApi->openAsioDriverInfo; - - /* NOTE: we load the driver and use its current settings - rather than the ones in our device info structure which may be stale */ - - result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name, - driverInfo, asioHostApi->systemSpecific ); - if( result == paNoError ) - asioIsInitialized = 1; - else{ - PA_DEBUG(("OpenStream ERROR1 - LoadAsioDriver returned %d\n", result)); - goto error; - } - - /* check that input device can support inputChannelCount */ - if( inputChannelCount > 0 ) - { - if( inputChannelCount > driverInfo->inputChannelCount ) - { - result = paInvalidChannelCount; - PA_DEBUG(("OpenStream ERROR2\n")); - goto error; - } - } - - /* check that output device can support outputChannelCount */ - if( outputChannelCount ) - { - if( outputChannelCount > driverInfo->outputChannelCount ) - { - result = paInvalidChannelCount; - PA_DEBUG(("OpenStream ERROR3\n")); - goto error; - } - } - - result = ValidateAndSetSampleRate( sampleRate ); - if( result != paNoError ) - goto error; - - /* - IMPLEMENT ME: - - if a full duplex stream is requested, check that the combination - of input and output parameters is supported - */ - - /* validate platform specific flags */ - if( (streamFlags & paPlatformSpecificFlags) != 0 ){ - PA_DEBUG(("OpenStream invalid flags!!\n")); - return paInvalidFlag; /* unexpected platform specific flag */ - } - - - stream = (PaAsioStream*)PaUtil_AllocateMemory( sizeof(PaAsioStream) ); - if( !stream ) - { - result = paInsufficientMemory; - PA_DEBUG(("OpenStream ERROR5\n")); - goto error; - } - stream->blockingState = NULL; /* Blocking i/o not initialized, yet. */ - - - stream->completedBuffersPlayedEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); - if( stream->completedBuffersPlayedEvent == NULL ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - PA_DEBUG(("OpenStream ERROR6\n")); - goto error; - } - completedBuffersPlayedEventInited = 1; - - - stream->asioBufferInfos = 0; /* for deallocation in error */ - stream->asioChannelInfos = 0; /* for deallocation in error */ - stream->bufferPtrs = 0; /* for deallocation in error */ - - /* Using blocking i/o interface... */ - if( usingBlockingIo ) - { - /* Blocking i/o is implemented by running callback mode, using a special blocking i/o callback. */ - streamCallback = BlockingIoPaCallback; /* Setup PA to use the ASIO blocking i/o callback. */ - userData = &theAsioStream; /* The callback user data will be the PA ASIO stream. */ - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &asioHostApi->blockingStreamInterface, streamCallback, userData ); - } - else /* Using callback interface... */ - { - PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation, - &asioHostApi->callbackStreamInterface, streamCallback, userData ); - } - - - PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); - - - stream->asioBufferInfos = (ASIOBufferInfo*)PaUtil_AllocateMemory( - sizeof(ASIOBufferInfo) * (inputChannelCount + outputChannelCount) ); - if( !stream->asioBufferInfos ) - { - result = paInsufficientMemory; - PA_DEBUG(("OpenStream ERROR7\n")); - goto error; - } - - - for( i=0; i < inputChannelCount; ++i ) - { - ASIOBufferInfo *info = &stream->asioBufferInfos[i]; - - info->isInput = ASIOTrue; - - if( inputChannelSelectors ){ - // inputChannelSelectors values have already been validated in - // ValidateAsioSpecificStreamInfo() above - info->channelNum = inputChannelSelectors[i]; - }else{ - info->channelNum = i; - } - - info->buffers[0] = info->buffers[1] = 0; - } - - for( i=0; i < outputChannelCount; ++i ){ - ASIOBufferInfo *info = &stream->asioBufferInfos[inputChannelCount+i]; - - info->isInput = ASIOFalse; - - if( outputChannelSelectors ){ - // outputChannelSelectors values have already been validated in - // ValidateAsioSpecificStreamInfo() above - info->channelNum = outputChannelSelectors[i]; - }else{ - info->channelNum = i; - } - - info->buffers[0] = info->buffers[1] = 0; - } - - - /* Using blocking i/o interface... */ - if( usingBlockingIo ) - { -/** @todo REVIEW selection of host buffer size for blocking i/o */ - - framesPerHostBuffer = SelectHostBufferSize( 0, framesPerBuffer, driverInfo ); - - } - else /* Using callback interface... */ - { - /* Select the host buffer size based on user framesPerBuffer and the - maximum of suggestedInputLatencyFrames and - suggestedOutputLatencyFrames. - - We should subtract any fixed known driver latency from - suggestedLatencyFrames before computing the host buffer size. - However, the ASIO API doesn't provide a method for determining fixed - latencies independent of the host buffer size. ASIOGetLatencies() - only returns latencies after the buffer size has been configured, so - we can't reliably use it to determine fixed latencies here. - - We could set the preferred buffer size and then subtract it from - the values returned from ASIOGetLatencies, but this would not be 100% - reliable, so we don't do it. - */ - - unsigned long targetBufferingLatencyFrames = - (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames ) - ? suggestedInputLatencyFrames - : suggestedOutputLatencyFrames); - - framesPerHostBuffer = SelectHostBufferSize( targetBufferingLatencyFrames, - framesPerBuffer, driverInfo ); - } - - - PA_DEBUG(("PaAsioOpenStream: framesPerHostBuffer :%d\n", framesPerHostBuffer)); - - asioError = ASIOCreateBuffers( stream->asioBufferInfos, - inputChannelCount+outputChannelCount, - framesPerHostBuffer, &asioCallbacks_ ); - - if( asioError != ASE_OK - && framesPerHostBuffer != (unsigned long)driverInfo->bufferPreferredSize ) - { - PA_DEBUG(("ERROR: ASIOCreateBuffers: %s\n", PaAsio_GetAsioErrorText(asioError) )); - /* - Some buggy drivers (like the Hoontech DSP24) give incorrect - [min, preferred, max] values They should work with the preferred size - value, thus if Pa_ASIO_CreateBuffers fails with the hostBufferSize - computed in SelectHostBufferSize, we try again with the preferred size. - */ - - framesPerHostBuffer = driverInfo->bufferPreferredSize; - - PA_DEBUG(("PaAsioOpenStream: CORRECTED framesPerHostBuffer :%d\n", framesPerHostBuffer)); - - ASIOError asioError2 = ASIOCreateBuffers( stream->asioBufferInfos, - inputChannelCount+outputChannelCount, - framesPerHostBuffer, &asioCallbacks_ ); - if( asioError2 == ASE_OK ) - asioError = ASE_OK; - } - - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - PA_DEBUG(("OpenStream ERROR9\n")); - goto error; - } - - asioBuffersCreated = 1; - - stream->asioChannelInfos = (ASIOChannelInfo*)PaUtil_AllocateMemory( - sizeof(ASIOChannelInfo) * (inputChannelCount + outputChannelCount) ); - if( !stream->asioChannelInfos ) - { - result = paInsufficientMemory; - PA_DEBUG(("OpenStream ERROR10\n")); - goto error; - } - - for( i=0; i < inputChannelCount + outputChannelCount; ++i ) - { - stream->asioChannelInfos[i].channel = stream->asioBufferInfos[i].channelNum; - stream->asioChannelInfos[i].isInput = stream->asioBufferInfos[i].isInput; - asioError = ASIOGetChannelInfo( &stream->asioChannelInfos[i] ); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - PA_DEBUG(("OpenStream ERROR11\n")); - goto error; - } - } - - stream->bufferPtrs = (void**)PaUtil_AllocateMemory( - 2 * sizeof(void*) * (inputChannelCount + outputChannelCount) ); - if( !stream->bufferPtrs ) - { - result = paInsufficientMemory; - PA_DEBUG(("OpenStream ERROR12\n")); - goto error; - } - - if( inputChannelCount > 0 ) - { - stream->inputBufferPtrs[0] = stream-> bufferPtrs; - stream->inputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount]; - - for( i=0; i<inputChannelCount; ++i ) - { - stream->inputBufferPtrs[0][i] = stream->asioBufferInfos[i].buffers[0]; - stream->inputBufferPtrs[1][i] = stream->asioBufferInfos[i].buffers[1]; - } - } - else - { - stream->inputBufferPtrs[0] = 0; - stream->inputBufferPtrs[1] = 0; - } - - if( outputChannelCount > 0 ) - { - stream->outputBufferPtrs[0] = &stream->bufferPtrs[inputChannelCount*2]; - stream->outputBufferPtrs[1] = &stream->bufferPtrs[inputChannelCount*2 + outputChannelCount]; - - for( i=0; i<outputChannelCount; ++i ) - { - stream->outputBufferPtrs[0][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[0]; - stream->outputBufferPtrs[1][i] = stream->asioBufferInfos[inputChannelCount+i].buffers[1]; - } - } - else - { - stream->outputBufferPtrs[0] = 0; - stream->outputBufferPtrs[1] = 0; - } - - if( inputChannelCount > 0 ) - { - /* FIXME: assume all channels use the same type for now - - see: "ASIO devices with multiple sample formats are unsupported" - http://www.portaudio.com/trac/ticket/106 - */ - ASIOSampleType inputType = stream->asioChannelInfos[0].type; - - PA_DEBUG(("ASIO Input type:%d",inputType)); - AsioSampleTypeLOG(inputType); - hostInputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( inputType ); - - SelectAsioToPaConverter( inputType, &stream->inputBufferConverter, &stream->inputShift ); - } - else - { - hostInputSampleFormat = 0; - stream->inputBufferConverter = 0; - } - - if( outputChannelCount > 0 ) - { - /* FIXME: assume all channels use the same type for now - - see: "ASIO devices with multiple sample formats are unsupported" - http://www.portaudio.com/trac/ticket/106 - */ - ASIOSampleType outputType = stream->asioChannelInfos[inputChannelCount].type; - - PA_DEBUG(("ASIO Output type:%d",outputType)); - AsioSampleTypeLOG(outputType); - hostOutputSampleFormat = AsioSampleTypeToPaNativeSampleFormat( outputType ); - - SelectPaToAsioConverter( outputType, &stream->outputBufferConverter, &stream->outputShift ); - } - else - { - hostOutputSampleFormat = 0; - stream->outputBufferConverter = 0; - } - - /* Values returned by ASIOGetLatencies() include the latency introduced by - the ASIO double buffer. */ - ASIOGetLatencies( &stream->asioInputLatencyFrames, &stream->asioOutputLatencyFrames ); - - - /* Using blocking i/o interface... */ - if( usingBlockingIo ) - { - /* Allocate the blocking i/o input ring buffer memory. */ - stream->blockingState = (PaAsioStreamBlockingState*)PaUtil_AllocateMemory( sizeof(PaAsioStreamBlockingState) ); - if( !stream->blockingState ) - { - result = paInsufficientMemory; - PA_DEBUG(("ERROR! Blocking i/o interface struct allocation failed in OpenStream()\n")); - goto error; - } - - /* Initialize blocking i/o interface struct. */ - stream->blockingState->readFramesReadyEvent = NULL; /* Uninitialized, yet. */ - stream->blockingState->writeBuffersReadyEvent = NULL; /* Uninitialized, yet. */ - stream->blockingState->readRingBufferData = NULL; /* Uninitialized, yet. */ - stream->blockingState->writeRingBufferData = NULL; /* Uninitialized, yet. */ - stream->blockingState->readStreamBuffer = NULL; /* Uninitialized, yet. */ - stream->blockingState->writeStreamBuffer = NULL; /* Uninitialized, yet. */ - stream->blockingState->stopFlag = TRUE; /* Not started, yet. */ - - - /* If the user buffer is unspecified */ - if( framesPerBuffer == paFramesPerBufferUnspecified ) - { - /* Make the user buffer the same size as the host buffer. */ - framesPerBuffer = framesPerHostBuffer; - } - - - /* Initialize callback buffer processor. */ - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor , - inputChannelCount , - inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */ - (hostInputSampleFormat | paNonInterleaved), /* Host format. */ - outputChannelCount , - outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */ - (hostOutputSampleFormat | paNonInterleaved), /* Host format. */ - sampleRate , - streamFlags , - framesPerBuffer , /* Frames per ring buffer block. */ - framesPerHostBuffer , /* Frames per asio buffer. */ - paUtilFixedHostBufferSize , - streamCallback , - userData ); - if( result != paNoError ){ - PA_DEBUG(("OpenStream ERROR13\n")); - goto error; - } - callbackBufferProcessorInited = TRUE; - - /* Initialize the blocking i/o buffer processor. */ - result = PaUtil_InitializeBufferProcessor(&stream->blockingState->bufferProcessor, - inputChannelCount , - inputSampleFormat , /* User format. */ - inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */ - outputChannelCount , - outputSampleFormat , /* User format. */ - outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */ - sampleRate , - paClipOff | paDitherOff , /* Don't use dither nor clipping. */ - framesPerBuffer , /* Frames per user buffer. */ - framesPerBuffer , /* Frames per ring buffer block. */ - paUtilBoundedHostBufferSize , - NULL, NULL );/* No callback! */ - if( result != paNoError ){ - PA_DEBUG(("ERROR! Blocking i/o buffer processor initialization failed in OpenStream()\n")); - goto error; - } - blockingBufferProcessorInited = TRUE; - - /* If input is requested. */ - if( inputChannelCount ) - { - /* Create the callback sync-event. */ - stream->blockingState->readFramesReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - if( stream->blockingState->readFramesReadyEvent == NULL ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - PA_DEBUG(("ERROR! Blocking i/o \"read frames ready\" event creation failed in OpenStream()\n")); - goto error; - } - blockingReadFramesReadyEventInitialized = 1; - - - /* Create pointer buffer to access non-interleaved data in ReadStream() */ - stream->blockingState->readStreamBuffer = (void**)PaUtil_AllocateMemory( sizeof(void*) * inputChannelCount ); - if( !stream->blockingState->readStreamBuffer ) - { - result = paInsufficientMemory; - PA_DEBUG(("ERROR! Blocking i/o read stream buffer allocation failed in OpenStream()\n")); - goto error; - } - - /* The ring buffer should store as many data blocks as needed - to achieve the requested latency. Whereas it must be large - enough to store at least two complete data blocks. - - 1) Determine the amount of latency to be added to the - preferred ASIO latency. - 2) Make sure we have at lest one additional latency frame. - 3) Divide the number of frames by the desired block size to - get the number (rounded up to pure integer) of blocks to - be stored in the buffer. - 4) Add one additional block for block processing and convert - to samples frames. - 5) Get the next larger (or equal) power-of-two buffer size. - */ - lBlockingBufferSize = suggestedInputLatencyFrames - stream->asioInputLatencyFrames; - lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1; - lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer; - lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer; - - /* Get the next larger or equal power-of-two buffersize. */ - lBlockingBufferSizePow2 = 1; - while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) ); - lBlockingBufferSize = lBlockingBufferSizePow2; - - /* Compute total input latency in seconds */ - stream->streamRepresentation.streamInfo.inputLatency = - (double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor ) - + PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) - + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer - + stream->asioInputLatencyFrames ) - / sampleRate; - - /* The code below prints the ASIO latency which doesn't include - the buffer processor latency nor the blocking i/o latency. It - reports the added latency separately. - */ - PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n", - stream->asioInputLatencyFrames, - (long)( stream->asioInputLatencyFrames * (1000.0 / sampleRate) ), - PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor), - (long)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ), - PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, - (long)( (PaUtil_GetBufferProcessorInputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) - )); - - /* Determine the size of ring buffer in bytes. */ - lBytesPerFrame = inputChannelCount * Pa_GetSampleSize(inputSampleFormat ); - - /* Allocate the blocking i/o input ring buffer memory. */ - stream->blockingState->readRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame ); - if( !stream->blockingState->readRingBufferData ) - { - result = paInsufficientMemory; - PA_DEBUG(("ERROR! Blocking i/o input ring buffer allocation failed in OpenStream()\n")); - goto error; - } - - /* Initialize the input ring buffer struct. */ - PaUtil_InitializeRingBuffer( &stream->blockingState->readRingBuffer , - lBytesPerFrame , - lBlockingBufferSize , - stream->blockingState->readRingBufferData ); - } - - /* If output is requested. */ - if( outputChannelCount ) - { - stream->blockingState->writeBuffersReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL ); - if( stream->blockingState->writeBuffersReadyEvent == NULL ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - PA_DEBUG(("ERROR! Blocking i/o \"write buffers ready\" event creation failed in OpenStream()\n")); - goto error; - } - blockingWriteBuffersReadyEventInitialized = 1; - - /* Create pointer buffer to access non-interleaved data in WriteStream() */ - stream->blockingState->writeStreamBuffer = (const void**)PaUtil_AllocateMemory( sizeof(const void*) * outputChannelCount ); - if( !stream->blockingState->writeStreamBuffer ) - { - result = paInsufficientMemory; - PA_DEBUG(("ERROR! Blocking i/o write stream buffer allocation failed in OpenStream()\n")); - goto error; - } - - /* The ring buffer should store as many data blocks as needed - to achieve the requested latency. Whereas it must be large - enough to store at least two complete data blocks. - - 1) Determine the amount of latency to be added to the - preferred ASIO latency. - 2) Make sure we have at lest one additional latency frame. - 3) Divide the number of frames by the desired block size to - get the number (rounded up to pure integer) of blocks to - be stored in the buffer. - 4) Add one additional block for block processing and convert - to samples frames. - 5) Get the next larger (or equal) power-of-two buffer size. - */ - lBlockingBufferSize = suggestedOutputLatencyFrames - stream->asioOutputLatencyFrames; - lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1; - lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer; - lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer; - - /* The buffer size (without the additional block) corresponds - to the initial number of silent samples in the output ring - buffer. */ - stream->blockingState->writeRingBufferInitialFrames = lBlockingBufferSize - framesPerBuffer; - - /* Get the next larger or equal power-of-two buffersize. */ - lBlockingBufferSizePow2 = 1; - while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) ); - lBlockingBufferSize = lBlockingBufferSizePow2; - - /* Compute total output latency in seconds */ - stream->streamRepresentation.streamInfo.outputLatency = - (double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) - + PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) - + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer - + stream->asioOutputLatencyFrames ) - / sampleRate; - - /* The code below prints the ASIO latency which doesn't include - the buffer processor latency nor the blocking i/o latency. It - reports the added latency separately. - */ - PA_DEBUG(("PaAsio : ASIO OutputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n", - stream->asioOutputLatencyFrames, - (long)( stream->asioOutputLatencyFrames * (1000.0 / sampleRate) ), - PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor), - (long)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) * (1000.0 / sampleRate) ), - PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer, - (long)( (PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) ) - )); - - /* Determine the size of ring buffer in bytes. */ - lBytesPerFrame = outputChannelCount * Pa_GetSampleSize(outputSampleFormat); - - /* Allocate the blocking i/o output ring buffer memory. */ - stream->blockingState->writeRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame ); - if( !stream->blockingState->writeRingBufferData ) - { - result = paInsufficientMemory; - PA_DEBUG(("ERROR! Blocking i/o output ring buffer allocation failed in OpenStream()\n")); - goto error; - } - - /* Initialize the output ring buffer struct. */ - PaUtil_InitializeRingBuffer( &stream->blockingState->writeRingBuffer , - lBytesPerFrame , - lBlockingBufferSize , - stream->blockingState->writeRingBufferData ); - } - - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - - } - else /* Using callback interface... */ - { - result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor, - inputChannelCount, inputSampleFormat, (hostInputSampleFormat | paNonInterleaved), - outputChannelCount, outputSampleFormat, (hostOutputSampleFormat | paNonInterleaved), - sampleRate, streamFlags, framesPerBuffer, - framesPerHostBuffer, paUtilFixedHostBufferSize, - streamCallback, userData ); - if( result != paNoError ){ - PA_DEBUG(("OpenStream ERROR13\n")); - goto error; - } - callbackBufferProcessorInited = TRUE; - - stream->streamRepresentation.streamInfo.inputLatency = - (double)( PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) - + stream->asioInputLatencyFrames) / sampleRate; // seconds - stream->streamRepresentation.streamInfo.outputLatency = - (double)( PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) - + stream->asioOutputLatencyFrames) / sampleRate; // seconds - stream->streamRepresentation.streamInfo.sampleRate = sampleRate; - - // the code below prints the ASIO latency which doesn't include the - // buffer processor latency. it reports the added latency separately - PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n", - stream->asioInputLatencyFrames, - (long)((stream->asioInputLatencyFrames*1000)/ sampleRate), - PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor), - (long)((PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate) - )); - - PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n", - stream->asioOutputLatencyFrames, - (long)((stream->asioOutputLatencyFrames*1000)/ sampleRate), - PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor), - (long)((PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)*1000)/ sampleRate) - )); - } - - stream->asioHostApi = asioHostApi; - stream->framesPerHostCallback = framesPerHostBuffer; - - stream->inputChannelCount = inputChannelCount; - stream->outputChannelCount = outputChannelCount; - stream->postOutput = driverInfo->postOutput; - stream->isStopped = 1; - stream->isActive = 0; - - asioHostApi->openAsioDeviceIndex = asioDeviceIndex; - - theAsioStream = stream; - *s = (PaStream*)stream; - - return result; - -error: - PA_DEBUG(("goto errored\n")); - if( stream ) - { - if( stream->blockingState ) - { - if( blockingBufferProcessorInited ) - PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor ); - - if( stream->blockingState->writeRingBufferData ) - PaUtil_FreeMemory( stream->blockingState->writeRingBufferData ); - if( stream->blockingState->writeStreamBuffer ) - PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer ); - if( blockingWriteBuffersReadyEventInitialized ) - CloseHandle( stream->blockingState->writeBuffersReadyEvent ); - - if( stream->blockingState->readRingBufferData ) - PaUtil_FreeMemory( stream->blockingState->readRingBufferData ); - if( stream->blockingState->readStreamBuffer ) - PaUtil_FreeMemory( stream->blockingState->readStreamBuffer ); - if( blockingReadFramesReadyEventInitialized ) - CloseHandle( stream->blockingState->readFramesReadyEvent ); - - PaUtil_FreeMemory( stream->blockingState ); - } - - if( callbackBufferProcessorInited ) - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - - if( completedBuffersPlayedEventInited ) - CloseHandle( stream->completedBuffersPlayedEvent ); - - if( stream->asioBufferInfos ) - PaUtil_FreeMemory( stream->asioBufferInfos ); - - if( stream->asioChannelInfos ) - PaUtil_FreeMemory( stream->asioChannelInfos ); - - if( stream->bufferPtrs ) - PaUtil_FreeMemory( stream->bufferPtrs ); - - PaUtil_FreeMemory( stream ); - } - - if( asioBuffersCreated ) - ASIODisposeBuffers(); - - if( asioIsInitialized ) - { - UnloadAsioDriver(); - } - return result; -} - - -/* - When CloseStream() is called, the multi-api layer ensures that - the stream has already been stopped or aborted. -*/ -static PaError CloseStream( PaStream* s ) -{ - PaError result = paNoError; - PaAsioStream *stream = (PaAsioStream*)s; - - /* - IMPLEMENT ME: - - additional stream closing + cleanup - */ - - PaUtil_TerminateBufferProcessor( &stream->bufferProcessor ); - PaUtil_TerminateStreamRepresentation( &stream->streamRepresentation ); - - stream->asioHostApi->openAsioDeviceIndex = paNoDevice; - - CloseHandle( stream->completedBuffersPlayedEvent ); - - /* Using blocking i/o interface... */ - if( stream->blockingState ) - { - PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor ); - - if( stream->inputChannelCount ) { - PaUtil_FreeMemory( stream->blockingState->readRingBufferData ); - PaUtil_FreeMemory( stream->blockingState->readStreamBuffer ); - CloseHandle( stream->blockingState->readFramesReadyEvent ); - } - if( stream->outputChannelCount ) { - PaUtil_FreeMemory( stream->blockingState->writeRingBufferData ); - PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer ); - CloseHandle( stream->blockingState->writeBuffersReadyEvent ); - } - - PaUtil_FreeMemory( stream->blockingState ); - } - - PaUtil_FreeMemory( stream->asioBufferInfos ); - PaUtil_FreeMemory( stream->asioChannelInfos ); - PaUtil_FreeMemory( stream->bufferPtrs ); - PaUtil_FreeMemory( stream ); - - ASIODisposeBuffers(); - UnloadAsioDriver(); - - theAsioStream = 0; - - return result; -} - - -static void bufferSwitch(long index, ASIOBool directProcess) -{ -//TAKEN FROM THE ASIO SDK - - // the actual processing callback. - // Beware that this is normally in a separate thread, hence be sure that - // you take care about thread synchronization. This is omitted here for - // simplicity. - - // as this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs - // to be created though it will only set the timeInfo.samplePosition and - // timeInfo.systemTime fields and the according flags - - ASIOTime timeInfo; - memset( &timeInfo, 0, sizeof (timeInfo) ); - - // get the time stamp of the buffer, not necessary if no - // synchronization to other media is required - if( ASIOGetSamplePosition(&timeInfo.timeInfo.samplePosition, &timeInfo.timeInfo.systemTime) == ASE_OK) - timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid; - - // Call the real callback - bufferSwitchTimeInfo( &timeInfo, index, directProcess ); -} - - -// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float -#if NATIVE_INT64 - #define ASIO64toDouble(a) (a) -#else - const double twoRaisedTo32 = 4294967296.; - #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32) -#endif - -static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool directProcess ) -{ - // the actual processing callback. - // Beware that this is normally in a separate thread, hence be sure that - // you take care about thread synchronization. - - - /* The SDK says the following about the directProcess flag: - suggests to the host whether it should immediately start processing - (directProcess == ASIOTrue), or whether its process should be deferred - because the call comes from a very low level (for instance, a high level - priority interrupt), and direct processing would cause timing instabilities for - the rest of the system. If in doubt, directProcess should be set to ASIOFalse. - - We just ignore directProcess. This could cause incompatibilities with - drivers which really don't want the audio processing to occur in this - callback, but none have been identified yet. - */ - - (void) directProcess; /* suppress unused parameter warning */ - -#if 0 - // store the timeInfo for later use - asioDriverInfo.tInfo = *timeInfo; - - // get the time stamp of the buffer, not necessary if no - // synchronization to other media is required - - if (timeInfo->timeInfo.flags & kSystemTimeValid) - asioDriverInfo.nanoSeconds = ASIO64toDouble(timeInfo->timeInfo.systemTime); - else - asioDriverInfo.nanoSeconds = 0; - - if (timeInfo->timeInfo.flags & kSamplePositionValid) - asioDriverInfo.samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition); - else - asioDriverInfo.samples = 0; - - if (timeInfo->timeCode.flags & kTcValid) - asioDriverInfo.tcSamples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples); - else - asioDriverInfo.tcSamples = 0; - - // get the system reference time - asioDriverInfo.sysRefTime = get_sys_reference_time(); -#endif - -#if 0 - // a few debug messages for the Windows device driver developer - // tells you the time when driver got its interrupt and the delay until the app receives - // the event notification. - static double last_samples = 0; - char tmp[128]; - 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)); - OutputDebugString (tmp); - last_samples = asioDriverInfo.samples; -#endif - - - if( !theAsioStream ) - return 0L; - - // protect against reentrancy - if( PaAsio_AtomicIncrement(&theAsioStream->reenterCount) ) - { - theAsioStream->reenterError++; - //DBUG(("bufferSwitchTimeInfo : reentrancy detection = %d\n", asioDriverInfo.reenterError)); - return 0L; - } - - int buffersDone = 0; - - do - { - if( buffersDone > 0 ) - { - // this is a reentered buffer, we missed processing it on time - // set the input overflow and output underflow flags as appropriate - - if( theAsioStream->inputChannelCount > 0 ) - theAsioStream->callbackFlags |= paInputOverflow; - - if( theAsioStream->outputChannelCount > 0 ) - theAsioStream->callbackFlags |= paOutputUnderflow; - } - else - { - if( theAsioStream->zeroOutput ) - { - ZeroOutputBuffers( theAsioStream, index ); - - // Finally if the driver supports the ASIOOutputReady() optimization, - // do it here, all data are in place - if( theAsioStream->postOutput ) - ASIOOutputReady(); - - if( theAsioStream->stopProcessing ) - { - if( theAsioStream->stopPlayoutCount < 2 ) - { - ++theAsioStream->stopPlayoutCount; - if( theAsioStream->stopPlayoutCount == 2 ) - { - theAsioStream->isActive = 0; - if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 ) - theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData ); - theAsioStream->streamFinishedCallbackCalled = true; - SetEvent( theAsioStream->completedBuffersPlayedEvent ); - } - } - } - } - else - { - -#if 0 -/* - see: "ASIO callback underflow/overflow buffer slip detection doesn't work" - http://www.portaudio.com/trac/ticket/110 -*/ - -// test code to try to detect slip conditions... these may work on some systems -// but neither of them work on the RME Digi96 - -// check that sample delta matches buffer size (otherwise we must have skipped -// a buffer. -static double last_samples = -512; -double samples; -//if( timeInfo->timeCode.flags & kTcValid ) -// samples = ASIO64toDouble(timeInfo->timeCode.timeCodeSamples); -//else - samples = ASIO64toDouble(timeInfo->timeInfo.samplePosition); -int delta = samples - last_samples; -//printf( "%d\n", delta); -last_samples = samples; - -if( delta > theAsioStream->framesPerHostCallback ) -{ - if( theAsioStream->inputChannelCount > 0 ) - theAsioStream->callbackFlags |= paInputOverflow; - - if( theAsioStream->outputChannelCount > 0 ) - theAsioStream->callbackFlags |= paOutputUnderflow; -} - -// check that the buffer index is not the previous index (which would indicate -// that a buffer was skipped. -static int previousIndex = 1; -if( index == previousIndex ) -{ - if( theAsioStream->inputChannelCount > 0 ) - theAsioStream->callbackFlags |= paInputOverflow; - - if( theAsioStream->outputChannelCount > 0 ) - theAsioStream->callbackFlags |= paOutputUnderflow; -} -previousIndex = index; -#endif - - int i; - - PaUtil_BeginCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer ); - - PaStreamCallbackTimeInfo paTimeInfo; - - // asio systemTime is supposed to be measured according to the same - // clock as timeGetTime - paTimeInfo.currentTime = (ASIO64toDouble( timeInfo->timeInfo.systemTime ) * .000000001); - - /* patch from Paul Boege */ - paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - - ((double)theAsioStream->asioInputLatencyFrames/theAsioStream->streamRepresentation.streamInfo.sampleRate); - - paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + - ((double)theAsioStream->asioOutputLatencyFrames/theAsioStream->streamRepresentation.streamInfo.sampleRate); - - /* old version is buggy because the buffer processor also adds in its latency to the time parameters - paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - theAsioStream->streamRepresentation.streamInfo.inputLatency; - paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + theAsioStream->streamRepresentation.streamInfo.outputLatency; - */ - -/* Disabled! Stopping and re-starting the stream causes an input overflow / output underflow. S.Fischer */ -#if 0 -// detect underflows by checking inter-callback time > 2 buffer period -static double previousTime = -1; -if( previousTime > 0 ){ - - double delta = paTimeInfo.currentTime - previousTime; - - if( delta >= 2. * (theAsioStream->framesPerHostCallback / theAsioStream->streamRepresentation.streamInfo.sampleRate) ){ - if( theAsioStream->inputChannelCount > 0 ) - theAsioStream->callbackFlags |= paInputOverflow; - - if( theAsioStream->outputChannelCount > 0 ) - theAsioStream->callbackFlags |= paOutputUnderflow; - } -} -previousTime = paTimeInfo.currentTime; -#endif - - // note that the above input and output times do not need to be - // adjusted for the latency of the buffer processor -- the buffer - // processor handles that. - - if( theAsioStream->inputBufferConverter ) - { - for( i=0; i<theAsioStream->inputChannelCount; i++ ) - { - theAsioStream->inputBufferConverter( theAsioStream->inputBufferPtrs[index][i], - theAsioStream->inputShift, theAsioStream->framesPerHostCallback ); - } - } - - PaUtil_BeginBufferProcessing( &theAsioStream->bufferProcessor, &paTimeInfo, theAsioStream->callbackFlags ); - - /* reset status flags once they've been passed to the callback */ - theAsioStream->callbackFlags = 0; - - PaUtil_SetInputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ ); - for( i=0; i<theAsioStream->inputChannelCount; ++i ) - PaUtil_SetNonInterleavedInputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->inputBufferPtrs[index][i] ); - - PaUtil_SetOutputFrameCount( &theAsioStream->bufferProcessor, 0 /* default to host buffer size */ ); - for( i=0; i<theAsioStream->outputChannelCount; ++i ) - PaUtil_SetNonInterleavedOutputChannel( &theAsioStream->bufferProcessor, i, theAsioStream->outputBufferPtrs[index][i] ); - - int callbackResult; - if( theAsioStream->stopProcessing ) - callbackResult = paComplete; - else - callbackResult = paContinue; - unsigned long framesProcessed = PaUtil_EndBufferProcessing( &theAsioStream->bufferProcessor, &callbackResult ); - - if( theAsioStream->outputBufferConverter ) - { - for( i=0; i<theAsioStream->outputChannelCount; i++ ) - { - theAsioStream->outputBufferConverter( theAsioStream->outputBufferPtrs[index][i], - theAsioStream->outputShift, theAsioStream->framesPerHostCallback ); - } - } - - PaUtil_EndCpuLoadMeasurement( &theAsioStream->cpuLoadMeasurer, framesProcessed ); - - // Finally if the driver supports the ASIOOutputReady() optimization, - // do it here, all data are in place - if( theAsioStream->postOutput ) - ASIOOutputReady(); - - if( callbackResult == paContinue ) - { - /* nothing special to do */ - } - else if( callbackResult == paAbort ) - { - /* finish playback immediately */ - theAsioStream->isActive = 0; - if( theAsioStream->streamRepresentation.streamFinishedCallback != 0 ) - theAsioStream->streamRepresentation.streamFinishedCallback( theAsioStream->streamRepresentation.userData ); - theAsioStream->streamFinishedCallbackCalled = true; - SetEvent( theAsioStream->completedBuffersPlayedEvent ); - theAsioStream->zeroOutput = true; - } - else /* paComplete or other non-zero value indicating complete */ - { - /* Finish playback once currently queued audio has completed. */ - theAsioStream->stopProcessing = true; - - if( PaUtil_IsBufferProcessorOutputEmpty( &theAsioStream->bufferProcessor ) ) - { - theAsioStream->zeroOutput = true; - theAsioStream->stopPlayoutCount = 0; - } - } - } - } - - ++buffersDone; - }while( PaAsio_AtomicDecrement(&theAsioStream->reenterCount) >= 0 ); - - return 0L; -} - - -static void sampleRateChanged(ASIOSampleRate sRate) -{ - // TAKEN FROM THE ASIO SDK - // do whatever you need to do if the sample rate changed - // usually this only happens during external sync. - // Audio processing is not stopped by the driver, actual sample rate - // might not have even changed, maybe only the sample rate status of an - // AES/EBU or S/PDIF digital input at the audio device. - // You might have to update time/sample related conversion routines, etc. - - (void) sRate; /* unused parameter */ - PA_DEBUG( ("sampleRateChanged : %d \n", sRate)); -} - -static long asioMessages(long selector, long value, void* message, double* opt) -{ -// TAKEN FROM THE ASIO SDK - // currently the parameters "value", "message" and "opt" are not used. - long ret = 0; - - (void) message; /* unused parameters */ - (void) opt; - - PA_DEBUG( ("asioMessages : %d , %d \n", selector, value)); - - switch(selector) - { - case kAsioSelectorSupported: - if(value == kAsioResetRequest - || value == kAsioEngineVersion - || value == kAsioResyncRequest - || value == kAsioLatenciesChanged - // the following three were added for ASIO 2.0, you don't necessarily have to support them - || value == kAsioSupportsTimeInfo - || value == kAsioSupportsTimeCode - || value == kAsioSupportsInputMonitor) - ret = 1L; - break; - - case kAsioBufferSizeChange: - //printf("kAsioBufferSizeChange \n"); - break; - - case kAsioResetRequest: - // defer the task and perform the reset of the driver during the next "safe" situation - // You cannot reset the driver right now, as this code is called from the driver. - // Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction - // Afterwards you initialize the driver again. - - /*FIXME: commented the next line out - - see: "PA/ASIO ignores some driver notifications it probably shouldn't" - http://www.portaudio.com/trac/ticket/108 - */ - //asioDriverInfo.stopped; // In this sample the processing will just stop - ret = 1L; - break; - - case kAsioResyncRequest: - // This informs the application, that the driver encountered some non fatal data loss. - // It is used for synchronization purposes of different media. - // Added mainly to work around the Win16Mutex problems in Windows 95/98 with the - // Windows Multimedia system, which could loose data because the Mutex was hold too long - // by another thread. - // However a driver can issue it in other situations, too. - ret = 1L; - break; - - case kAsioLatenciesChanged: - // This will inform the host application that the drivers were latencies changed. - // Beware, it this does not mean that the buffer sizes have changed! - // You might need to update internal delay data. - ret = 1L; - //printf("kAsioLatenciesChanged \n"); - break; - - case kAsioEngineVersion: - // return the supported ASIO version of the host application - // If a host applications does not implement this selector, ASIO 1.0 is assumed - // by the driver - ret = 2L; - break; - - case kAsioSupportsTimeInfo: - // informs the driver whether the asioCallbacks.bufferSwitchTimeInfo() callback - // is supported. - // For compatibility with ASIO 1.0 drivers the host application should always support - // the "old" bufferSwitch method, too. - ret = 1; - break; - - case kAsioSupportsTimeCode: - // informs the driver whether application is interested in time code info. - // If an application does not need to know about time code, the driver has less work - // to do. - ret = 0; - break; - } - return ret; -} - - -static PaError StartStream( PaStream *s ) -{ - PaError result = paNoError; - PaAsioStream *stream = (PaAsioStream*)s; - PaAsioStreamBlockingState *blockingState = stream->blockingState; - ASIOError asioError; - - if( stream->outputChannelCount > 0 ) - { - ZeroOutputBuffers( stream, 0 ); - ZeroOutputBuffers( stream, 1 ); - } - - PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); - stream->stopProcessing = false; - stream->zeroOutput = false; - - /* Reentrancy counter initialisation */ - stream->reenterCount = -1; - stream->reenterError = 0; - - stream->callbackFlags = 0; - - if( ResetEvent( stream->completedBuffersPlayedEvent ) == 0 ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - - - /* Using blocking i/o interface... */ - if( blockingState ) - { - /* Reset blocking i/o buffer processor. */ - PaUtil_ResetBufferProcessor( &blockingState->bufferProcessor ); - - /* If we're about to process some input data. */ - if( stream->inputChannelCount ) - { - /* Reset callback-ReadStream sync event. */ - if( ResetEvent( blockingState->readFramesReadyEvent ) == 0 ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - - /* Flush blocking i/o ring buffer. */ - PaUtil_FlushRingBuffer( &blockingState->readRingBuffer ); - (*blockingState->bufferProcessor.inputZeroer)( blockingState->readRingBuffer.buffer, 1, blockingState->bufferProcessor.inputChannelCount * blockingState->readRingBuffer.bufferSize ); - } - - /* If we're about to process some output data. */ - if( stream->outputChannelCount ) - { - /* Reset callback-WriteStream sync event. */ - if( ResetEvent( blockingState->writeBuffersReadyEvent ) == 0 ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - - /* Flush blocking i/o ring buffer. */ - PaUtil_FlushRingBuffer( &blockingState->writeRingBuffer ); - (*blockingState->bufferProcessor.outputZeroer)( blockingState->writeRingBuffer.buffer, 1, blockingState->bufferProcessor.outputChannelCount * blockingState->writeRingBuffer.bufferSize ); - - /* Initialize the output ring buffer to "silence". */ - PaUtil_AdvanceRingBufferWriteIndex( &blockingState->writeRingBuffer, blockingState->writeRingBufferInitialFrames ); - } - - /* Clear requested frames / buffers count. */ - blockingState->writeBuffersRequested = 0; - blockingState->readFramesRequested = 0; - blockingState->writeBuffersRequestedFlag = FALSE; - blockingState->readFramesRequestedFlag = FALSE; - blockingState->outputUnderflowFlag = FALSE; - blockingState->inputOverflowFlag = FALSE; - blockingState->stopFlag = FALSE; - } - - - if( result == paNoError ) - { - assert( theAsioStream == stream ); /* theAsioStream should be set correctly in OpenStream */ - - /* initialize these variables before the callback has a chance to be invoked */ - stream->isStopped = 0; - stream->isActive = 1; - stream->streamFinishedCallbackCalled = false; - - asioError = ASIOStart(); - if( asioError != ASE_OK ) - { - stream->isStopped = 1; - stream->isActive = 0; - - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - } - } - - return result; -} - -static void EnsureCallbackHasCompleted( PaAsioStream *stream ) -{ - // make sure that the callback is not still in-flight after ASIOStop() - // returns. This has been observed to happen on the Hoontech DSP24 for - // example. - int count = 2000; // only wait for 2 seconds, rather than hanging. - while( stream->reenterCount != -1 && count > 0 ) - { - Sleep(1); - --count; - } -} - -static PaError StopStream( PaStream *s ) -{ - PaError result = paNoError; - PaAsioStream *stream = (PaAsioStream*)s; - PaAsioStreamBlockingState *blockingState = stream->blockingState; - ASIOError asioError; - - if( stream->isActive ) - { - /* If blocking i/o output is in use */ - if( blockingState && stream->outputChannelCount ) - { - /* Request the whole output buffer to be available. */ - blockingState->writeBuffersRequested = blockingState->writeRingBuffer.bufferSize; - /* Signalize that additional buffers are need. */ - blockingState->writeBuffersRequestedFlag = TRUE; - /* Set flag to indicate the playback is to be stopped. */ - blockingState->stopFlag = TRUE; - - /* Wait until requested number of buffers has been freed. Time - out after twice the blocking i/o output buffer could have - been consumed. */ - DWORD timeout = (DWORD)( 2 * blockingState->writeRingBuffer.bufferSize * 1000 - / stream->streamRepresentation.streamInfo.sampleRate ); - DWORD waitResult = WaitForSingleObject( blockingState->writeBuffersReadyEvent, timeout ); - - /* If something seriously went wrong... */ - if( waitResult == WAIT_FAILED ) - { - PA_DEBUG(("WaitForSingleObject() failed in StopStream()\n")); - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - } - else if( waitResult == WAIT_TIMEOUT ) - { - PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n")); - result = paTimedOut; - } - } - - stream->stopProcessing = true; - - /* wait for the stream to finish playing out enqueued buffers. - timeout after four times the stream latency. - - @todo should use a better time out value - if the user buffer - length is longer than the asio buffer size then that should - be taken into account. - */ - if( WaitForSingleObject( stream->completedBuffersPlayedEvent, - (DWORD)(stream->streamRepresentation.streamInfo.outputLatency * 1000. * 4.) ) - == WAIT_TIMEOUT ) - { - PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n" )); - } - } - - asioError = ASIOStop(); - if( asioError == ASE_OK ) - { - EnsureCallbackHasCompleted( stream ); - } - else - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - } - - stream->isStopped = 1; - stream->isActive = 0; - - if( !stream->streamFinishedCallbackCalled ) - { - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - - return result; -} - -static PaError AbortStream( PaStream *s ) -{ - PaError result = paNoError; - PaAsioStream *stream = (PaAsioStream*)s; - ASIOError asioError; - - stream->zeroOutput = true; - - asioError = ASIOStop(); - if( asioError == ASE_OK ) - { - EnsureCallbackHasCompleted( stream ); - } - else - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - } - - stream->isStopped = 1; - stream->isActive = 0; - - if( !stream->streamFinishedCallbackCalled ) - { - if( stream->streamRepresentation.streamFinishedCallback != 0 ) - stream->streamRepresentation.streamFinishedCallback( stream->streamRepresentation.userData ); - } - - return result; -} - - -static PaError IsStreamStopped( PaStream *s ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - return stream->isStopped; -} - - -static PaError IsStreamActive( PaStream *s ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - return stream->isActive; -} - - -static PaTime GetStreamTime( PaStream *s ) -{ - (void) s; /* unused parameter */ - - return (double)timeGetTime() * .001; -} - - -static double GetStreamCpuLoad( PaStream* s ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); -} - - -/* - As separate stream interfaces are used for blocking and callback - streams, the following functions can be guaranteed to only be called - for blocking streams. -*/ - -static PaError ReadStream( PaStream *s , - void *buffer, - unsigned long frames ) -{ - PaError result = paNoError; /* Initial return value. */ - PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */ - - /* Pointer to the blocking i/o data struct. */ - PaAsioStreamBlockingState *blockingState = stream->blockingState; - - /* Get blocking i/o buffer processor and ring buffer pointers. */ - PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor; - PaUtilRingBuffer *pRb = &blockingState->readRingBuffer; - - /* Ring buffer segment(s) used for writing. */ - void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */ - void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */ - - /* Number of frames per ring buffer segment. */ - long lRingBufferSize1st = 0; /* First segment. (Mandatory) */ - long lRingBufferSize2nd = 0; /* Second segment. (Optional) */ - - /* Get number of frames to be processed per data block. */ - unsigned long lFramesPerBlock = stream->bufferProcessor.framesPerUserBuffer; - /* Actual number of frames that has been copied into the ring buffer. */ - unsigned long lFramesCopied = 0; - /* The number of remaining unprocessed dtat frames. */ - unsigned long lFramesRemaining = frames; - - /* Copy the input argument to avoid pointer increment! */ - const void *userBuffer; - unsigned int i; /* Just a counter. */ - - /* About the time, needed to process 8 data blocks. */ - DWORD timeout = (DWORD)( 8 * lFramesPerBlock * 1000 / stream->streamRepresentation.streamInfo.sampleRate ); - DWORD waitResult = 0; - - - /* Check if the stream is still available ready to gather new data. */ - if( blockingState->stopFlag || !stream->isActive ) - { - PA_DEBUG(("Warning! Stream no longer available for reading in ReadStream()\n")); - result = paStreamIsStopped; - return result; - } - - /* If the stream is a input stream. */ - if( stream->inputChannelCount ) - { - /* Prepare buffer access. */ - if( !pBp->userOutputIsInterleaved ) - { - userBuffer = blockingState->readStreamBuffer; - for( i = 0; i<pBp->inputChannelCount; ++i ) - { - ((void**)userBuffer)[i] = ((void**)buffer)[i]; - } - } /* Use the unchanged buffer. */ - else { userBuffer = buffer; } - - do /* Internal block processing for too large user data buffers. */ - { - /* Get the size of the current data block to be processed. */ - lFramesPerBlock =(lFramesPerBlock < lFramesRemaining) - ? lFramesPerBlock : lFramesRemaining; - /* Use predefined block size for as long there are enough - buffers available, thereafter reduce the processing block - size to match the number of remaining buffers. So the final - data block is processed although it may be incomplete. */ - - /* If the available amount of data frames is insufficient. */ - if( PaUtil_GetRingBufferReadAvailable(pRb) < (long) lFramesPerBlock ) - { - /* Make sure, the event isn't already set! */ - /* ResetEvent( blockingState->readFramesReadyEvent ); */ - - /* Set the number of requested buffers. */ - blockingState->readFramesRequested = lFramesPerBlock; - - /* Signalize that additional buffers are need. */ - blockingState->readFramesRequestedFlag = TRUE; - - /* Wait until requested number of buffers has been freed. */ - waitResult = WaitForSingleObject( blockingState->readFramesReadyEvent, timeout ); - - /* If something seriously went wrong... */ - if( waitResult == WAIT_FAILED ) - { - PA_DEBUG(("WaitForSingleObject() failed in ReadStream()\n")); - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - return result; - } - else if( waitResult == WAIT_TIMEOUT ) - { - PA_DEBUG(("WaitForSingleObject() timed out in ReadStream()\n")); - - /* If block processing has stopped, abort! */ - if( blockingState->stopFlag ) { return result = paStreamIsStopped; } - - /* If a timeout is encountered, give up eventually. */ - return result = paTimedOut; - } - } - /* Now, the ring buffer contains the required amount of data - frames. - (Therefor we don't need to check the return argument of - PaUtil_GetRingBufferReadRegions(). ;-) ) - */ - - /* Retrieve pointer(s) to the ring buffer's current write - position(s). If the first buffer segment is too small to - store the requested number of bytes, an additional second - segment is returned. Otherwise, i.e. if the first segment - is large enough, the second segment's pointer will be NULL. - */ - PaUtil_GetRingBufferReadRegions(pRb , - lFramesPerBlock , - &pRingBufferData1st, - &lRingBufferSize1st, - &pRingBufferData2nd, - &lRingBufferSize2nd); - - /* Set number of frames to be copied from the ring buffer. */ - PaUtil_SetInputFrameCount( pBp, lRingBufferSize1st ); - /* Setup ring buffer access. */ - PaUtil_SetInterleavedInputChannels(pBp , /* Buffer processor. */ - 0 , /* The first channel's index. */ - pRingBufferData1st, /* First ring buffer segment. */ - 0 ); /* Use all available channels. */ - - /* If a second ring buffer segment is required. */ - if( lRingBufferSize2nd ) { - /* Set number of frames to be copied from the ring buffer. */ - PaUtil_Set2ndInputFrameCount( pBp, lRingBufferSize2nd ); - /* Setup ring buffer access. */ - PaUtil_Set2ndInterleavedInputChannels(pBp , /* Buffer processor. */ - 0 , /* The first channel's index. */ - pRingBufferData2nd, /* Second ring buffer segment. */ - 0 ); /* Use all available channels. */ - } - - /* Let the buffer processor handle "copy and conversion" and - update the ring buffer indices manually. */ - lFramesCopied = PaUtil_CopyInput( pBp, &buffer, lFramesPerBlock ); - PaUtil_AdvanceRingBufferReadIndex( pRb, lFramesCopied ); - - /* Decrease number of unprocessed frames. */ - lFramesRemaining -= lFramesCopied; - - } /* Continue with the next data chunk. */ - while( lFramesRemaining ); - - - /* If there has been an input overflow within the callback */ - if( blockingState->inputOverflowFlag ) - { - blockingState->inputOverflowFlag = FALSE; - - /* Return the corresponding error code. */ - result = paInputOverflowed; - } - - } /* If this is not an input stream. */ - else { - result = paCanNotReadFromAnOutputOnlyStream; - } - - return result; -} - -static PaError WriteStream( PaStream *s , - const void *buffer, - unsigned long frames ) -{ - PaError result = paNoError; /* Initial return value. */ - PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */ - - /* Pointer to the blocking i/o data struct. */ - PaAsioStreamBlockingState *blockingState = stream->blockingState; - - /* Get blocking i/o buffer processor and ring buffer pointers. */ - PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor; - PaUtilRingBuffer *pRb = &blockingState->writeRingBuffer; - - /* Ring buffer segment(s) used for writing. */ - void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */ - void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */ - - /* Number of frames per ring buffer segment. */ - long lRingBufferSize1st = 0; /* First segment. (Mandatory) */ - long lRingBufferSize2nd = 0; /* Second segment. (Optional) */ - - /* Get number of frames to be processed per data block. */ - unsigned long lFramesPerBlock = stream->bufferProcessor.framesPerUserBuffer; - /* Actual number of frames that has been copied into the ring buffer. */ - unsigned long lFramesCopied = 0; - /* The number of remaining unprocessed dtat frames. */ - unsigned long lFramesRemaining = frames; - - /* About the time, needed to process 8 data blocks. */ - DWORD timeout = (DWORD)( 8 * lFramesPerBlock * 1000 / stream->streamRepresentation.streamInfo.sampleRate ); - DWORD waitResult = 0; - - /* Copy the input argument to avoid pointer increment! */ - const void *userBuffer; - unsigned int i; /* Just a counter. */ - - - /* Check if the stream is still available ready to receive new data. */ - if( blockingState->stopFlag || !stream->isActive ) - { - PA_DEBUG(("Warning! Stream no longer available for writing in WriteStream()\n")); - result = paStreamIsStopped; - return result; - } - - /* If the stream is a output stream. */ - if( stream->outputChannelCount ) - { - /* Prepare buffer access. */ - if( !pBp->userOutputIsInterleaved ) - { - userBuffer = blockingState->writeStreamBuffer; - for( i = 0; i<pBp->outputChannelCount; ++i ) - { - ((const void**)userBuffer)[i] = ((const void**)buffer)[i]; - } - } /* Use the unchanged buffer. */ - else { userBuffer = buffer; } - - - do /* Internal block processing for too large user data buffers. */ - { - /* Get the size of the current data block to be processed. */ - lFramesPerBlock =(lFramesPerBlock < lFramesRemaining) - ? lFramesPerBlock : lFramesRemaining; - /* Use predefined block size for as long there are enough - frames available, thereafter reduce the processing block - size to match the number of remaining frames. So the final - data block is processed although it may be incomplete. */ - - /* If the available amount of buffers is insufficient. */ - if( PaUtil_GetRingBufferWriteAvailable(pRb) < (long) lFramesPerBlock ) - { - /* Make sure, the event isn't already set! */ - /* ResetEvent( blockingState->writeBuffersReadyEvent ); */ - - /* Set the number of requested buffers. */ - blockingState->writeBuffersRequested = lFramesPerBlock; - - /* Signalize that additional buffers are need. */ - blockingState->writeBuffersRequestedFlag = TRUE; - - /* Wait until requested number of buffers has been freed. */ - waitResult = WaitForSingleObject( blockingState->writeBuffersReadyEvent, timeout ); - - /* If something seriously went wrong... */ - if( waitResult == WAIT_FAILED ) - { - PA_DEBUG(("WaitForSingleObject() failed in WriteStream()\n")); - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() ); - return result; - } - else if( waitResult == WAIT_TIMEOUT ) - { - PA_DEBUG(("WaitForSingleObject() timed out in WriteStream()\n")); - - /* If block processing has stopped, abort! */ - if( blockingState->stopFlag ) { return result = paStreamIsStopped; } - - /* If a timeout is encountered, give up eventually. */ - return result = paTimedOut; - } - } - /* Now, the ring buffer contains the required amount of free - space to store the provided number of data frames. - (Therefor we don't need to check the return argument of - PaUtil_GetRingBufferWriteRegions(). ;-) ) - */ - - /* Retrieve pointer(s) to the ring buffer's current write - position(s). If the first buffer segment is too small to - store the requested number of bytes, an additional second - segment is returned. Otherwise, i.e. if the first segment - is large enough, the second segment's pointer will be NULL. - */ - PaUtil_GetRingBufferWriteRegions(pRb , - lFramesPerBlock , - &pRingBufferData1st, - &lRingBufferSize1st, - &pRingBufferData2nd, - &lRingBufferSize2nd); - - /* Set number of frames to be copied to the ring buffer. */ - PaUtil_SetOutputFrameCount( pBp, lRingBufferSize1st ); - /* Setup ring buffer access. */ - PaUtil_SetInterleavedOutputChannels(pBp , /* Buffer processor. */ - 0 , /* The first channel's index. */ - pRingBufferData1st, /* First ring buffer segment. */ - 0 ); /* Use all available channels. */ - - /* If a second ring buffer segment is required. */ - if( lRingBufferSize2nd ) { - /* Set number of frames to be copied to the ring buffer. */ - PaUtil_Set2ndOutputFrameCount( pBp, lRingBufferSize2nd ); - /* Setup ring buffer access. */ - PaUtil_Set2ndInterleavedOutputChannels(pBp , /* Buffer processor. */ - 0 , /* The first channel's index. */ - pRingBufferData2nd, /* Second ring buffer segment. */ - 0 ); /* Use all available channels. */ - } - - /* Let the buffer processor handle "copy and conversion" and - update the ring buffer indices manually. */ - lFramesCopied = PaUtil_CopyOutput( pBp, &userBuffer, lFramesPerBlock ); - PaUtil_AdvanceRingBufferWriteIndex( pRb, lFramesCopied ); - - /* Decrease number of unprocessed frames. */ - lFramesRemaining -= lFramesCopied; - - } /* Continue with the next data chunk. */ - while( lFramesRemaining ); - - - /* If there has been an output underflow within the callback */ - if( blockingState->outputUnderflowFlag ) - { - blockingState->outputUnderflowFlag = FALSE; - - /* Return the corresponding error code. */ - result = paOutputUnderflowed; - } - - } /* If this is not an output stream. */ - else - { - result = paCanNotWriteToAnInputOnlyStream; - } - - return result; -} - - -static signed long GetStreamReadAvailable( PaStream* s ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - /* Call buffer utility routine to get the number of available frames. */ - return PaUtil_GetRingBufferReadAvailable( &stream->blockingState->readRingBuffer ); -} - - -static signed long GetStreamWriteAvailable( PaStream* s ) -{ - PaAsioStream *stream = (PaAsioStream*)s; - - /* Call buffer utility routine to get the number of empty buffers. */ - return PaUtil_GetRingBufferWriteAvailable( &stream->blockingState->writeRingBuffer ); -} - - -/* This routine will be called by the PortAudio engine when audio is needed. -** It may called at interrupt level on some machines so don't do anything -** that could mess up the system like calling malloc() or free(). -*/ -static int BlockingIoPaCallback(const void *inputBuffer , - void *outputBuffer , - unsigned long framesPerBuffer, - const PaStreamCallbackTimeInfo *timeInfo , - PaStreamCallbackFlags statusFlags , - void *userData ) -{ - PaError result = paNoError; /* Initial return value. */ - PaAsioStream *stream = *(PaAsioStream**)userData; /* The PA ASIO stream. */ - PaAsioStreamBlockingState *blockingState = stream->blockingState; /* Persume blockingState is valid, otherwise the callback wouldn't be running. */ - - /* Get a pointer to the stream's blocking i/o buffer processor. */ - PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor; - PaUtilRingBuffer *pRb = NULL; - - /* If output data has been requested. */ - if( stream->outputChannelCount ) - { - /* If the callback input argument signalizes a output underflow, - make sure the WriteStream() function knows about it, too! */ - if( statusFlags & paOutputUnderflowed ) { - blockingState->outputUnderflowFlag = TRUE; - } - - /* Access the corresponding ring buffer. */ - pRb = &blockingState->writeRingBuffer; - - /* If the blocking i/o buffer contains enough output data, */ - if( PaUtil_GetRingBufferReadAvailable(pRb) >= (long) framesPerBuffer ) - { - /* Extract the requested data from the ring buffer. */ - PaUtil_ReadRingBuffer( pRb, outputBuffer, framesPerBuffer ); - } - else /* If no output data is available :-( */ - { - /* Signalize a write-buffer underflow. */ - blockingState->outputUnderflowFlag = TRUE; - - /* Fill the output buffer with silence. */ - (*pBp->outputZeroer)( outputBuffer, 1, pBp->outputChannelCount * framesPerBuffer ); - - /* If playback is to be stopped */ - if( blockingState->stopFlag && PaUtil_GetRingBufferReadAvailable(pRb) < (long) framesPerBuffer ) - { - /* Extract all the remaining data from the ring buffer, - whether it is a complete data block or not. */ - PaUtil_ReadRingBuffer( pRb, outputBuffer, PaUtil_GetRingBufferReadAvailable(pRb) ); - } - } - - /* Set blocking i/o event? */ - if( blockingState->writeBuffersRequestedFlag && PaUtil_GetRingBufferWriteAvailable(pRb) >= (long) blockingState->writeBuffersRequested ) - { - /* Reset buffer request. */ - blockingState->writeBuffersRequestedFlag = FALSE; - blockingState->writeBuffersRequested = 0; - /* Signalize that requested buffers are ready. */ - SetEvent( blockingState->writeBuffersReadyEvent ); - /* What do we do if SetEvent() returns zero, i.e. the event - could not be set? How to return errors from within the - callback? - S.Fischer */ - } - } - - /* If input data has been supplied. */ - if( stream->inputChannelCount ) - { - /* If the callback input argument signalizes a input overflow, - make sure the ReadStream() function knows about it, too! */ - if( statusFlags & paInputOverflowed ) { - blockingState->inputOverflowFlag = TRUE; - } - - /* Access the corresponding ring buffer. */ - pRb = &blockingState->readRingBuffer; - - /* If the blocking i/o buffer contains not enough input buffers */ - if( PaUtil_GetRingBufferWriteAvailable(pRb) < (long) framesPerBuffer ) - { - /* Signalize a read-buffer overflow. */ - blockingState->inputOverflowFlag = TRUE; - - /* Remove some old data frames from the buffer. */ - PaUtil_AdvanceRingBufferReadIndex( pRb, framesPerBuffer ); - } - - /* Insert the current input data into the ring buffer. */ - PaUtil_WriteRingBuffer( pRb, inputBuffer, framesPerBuffer ); - - /* Set blocking i/o event? */ - if( blockingState->readFramesRequestedFlag && PaUtil_GetRingBufferReadAvailable(pRb) >= (long) blockingState->readFramesRequested ) - { - /* Reset buffer request. */ - blockingState->readFramesRequestedFlag = FALSE; - blockingState->readFramesRequested = 0; - /* Signalize that requested buffers are ready. */ - SetEvent( blockingState->readFramesReadyEvent ); - /* What do we do if SetEvent() returns zero, i.e. the event - could not be set? How to return errors from within the - callback? - S.Fischer */ - /** @todo report an error with PA_DEBUG */ - } - } - - return paContinue; -} - - -PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiDevice; - ASIODriverInfo asioDriverInfo; - ASIOError asioError; - int asioIsInitialized = 0; - PaAsioHostApiRepresentation *asioHostApi; - PaAsioDeviceInfo *asioDeviceInfo; - PaWinUtilComInitializationResult comInitializationResult; - - /* initialize COM again here, we might be in another thread */ - result = PaWinUtil_CoInitialize( paASIO, &comInitializationResult ); - if( result != paNoError ) - return result; - - result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); - if( result != paNoError ) - goto error; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi ); - if( result != paNoError ) - goto error; - - /* - In theory we could proceed if the currently open device was the same - one for which the control panel was requested, however because the - window pointer is not available until this function is called we - currently need to call ASIOInit() again here, which of course can't be - done safely while a stream is open. - */ - - asioHostApi = (PaAsioHostApiRepresentation*)hostApi; - if( asioHostApi->openAsioDeviceIndex != paNoDevice ) - { - result = paDeviceUnavailable; - goto error; - } - - asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - - if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) ) - { - result = paUnanticipatedHostError; - goto error; - } - - /* CRUCIAL!!! */ - memset( &asioDriverInfo, 0, sizeof(ASIODriverInfo) ); - asioDriverInfo.asioVersion = 2; - asioDriverInfo.sysRef = systemSpecific; - asioError = ASIOInit( &asioDriverInfo ); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - else - { - asioIsInitialized = 1; - } - -PA_DEBUG(("PaAsio_ShowControlPanel: ASIOInit(): %s\n", PaAsio_GetAsioErrorText(asioError) )); -PA_DEBUG(("asioVersion: ASIOInit(): %ld\n", asioDriverInfo.asioVersion )); -PA_DEBUG(("driverVersion: ASIOInit(): %ld\n", asioDriverInfo.driverVersion )); -PA_DEBUG(("Name: ASIOInit(): %s\n", asioDriverInfo.name )); -PA_DEBUG(("ErrorMessage: ASIOInit(): %s\n", asioDriverInfo.errorMessage )); - - asioError = ASIOControlPanel(); - if( asioError != ASE_OK ) - { - PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) )); - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - goto error; - } - -PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErrorText(asioError) )); - - asioError = ASIOExit(); - if( asioError != ASE_OK ) - { - result = paUnanticipatedHostError; - PA_ASIO_SET_LAST_ASIO_ERROR( asioError ); - asioIsInitialized = 0; - goto error; - } - -PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) )); - - return result; - -error: - if( asioIsInitialized ) - { - ASIOExit(); - } - - PaWinUtil_CoUninitialize( paASIO, &comInitializationResult ); - - return result; -} - - -PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex, - const char** channelName ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiDevice; - PaAsioDeviceInfo *asioDeviceInfo; - - - result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); - if( result != paNoError ) - goto error; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi ); - if( result != paNoError ) - goto error; - - asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - - if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxInputChannels ){ - result = paInvalidChannelCount; - goto error; - } - - *channelName = asioDeviceInfo->asioChannelInfos[channelIndex].name; - - return paNoError; - -error: - return result; -} - - -PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex, - const char** channelName ) -{ - PaError result = paNoError; - PaUtilHostApiRepresentation *hostApi; - PaDeviceIndex hostApiDevice; - PaAsioDeviceInfo *asioDeviceInfo; - - - result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); - if( result != paNoError ) - goto error; - - result = PaUtil_DeviceIndexToHostApiDeviceIndex( &hostApiDevice, device, hostApi ); - if( result != paNoError ) - goto error; - - asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice]; - - if( channelIndex < 0 || channelIndex >= asioDeviceInfo->commonDeviceInfo.maxOutputChannels ){ - result = paInvalidChannelCount; - goto error; - } - - *channelName = asioDeviceInfo->asioChannelInfos[ - asioDeviceInfo->commonDeviceInfo.maxInputChannels + channelIndex].name; - - return paNoError; - -error: - return result; -} - - -/* NOTE: the following functions are ASIO-stream specific, and are called directly - by client code. We need to check for many more error conditions here because - we don't have the benefit of pa_front.c's parameter checking. -*/ - -static PaError GetAsioStreamPointer( PaAsioStream **stream, PaStream *s ) -{ - PaError result; - PaUtilHostApiRepresentation *hostApi; - PaAsioHostApiRepresentation *asioHostApi; - - result = PaUtil_ValidateStreamPointer( s ); - if( result != paNoError ) - return result; - - result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO ); - if( result != paNoError ) - return result; - - asioHostApi = (PaAsioHostApiRepresentation*)hostApi; - - if( PA_STREAM_REP( s )->streamInterface == &asioHostApi->callbackStreamInterface - || PA_STREAM_REP( s )->streamInterface == &asioHostApi->blockingStreamInterface ) - { - /* s is an ASIO stream */ - *stream = (PaAsioStream *)s; - return paNoError; - } - else - { - return paIncompatibleStreamHostApi; - } -} - - -PaError PaAsio_SetStreamSampleRate( PaStream* s, double sampleRate ) -{ - PaAsioStream *stream; - PaError result = GetAsioStreamPointer( &stream, s ); - if( result != paNoError ) - return result; - - if( stream != theAsioStream ) - return paBadStreamPtr; - - return ValidateAndSetSampleRate( sampleRate ); -} |