From ae6b5d89ea93b95c2990895077cf5e8f0bba9ad9 Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Mon, 2 Feb 2004 11:28:02 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r1301, which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=1302 --- pd/portaudio_v18/pa_asio/pa_asio.cpp | 3084 ++++++++++++++++++++++++++++++++++ 1 file changed, 3084 insertions(+) create mode 100644 pd/portaudio_v18/pa_asio/pa_asio.cpp (limited to 'pd/portaudio_v18/pa_asio/pa_asio.cpp') diff --git a/pd/portaudio_v18/pa_asio/pa_asio.cpp b/pd/portaudio_v18/pa_asio/pa_asio.cpp new file mode 100644 index 00000000..cec01134 --- /dev/null +++ b/pd/portaudio_v18/pa_asio/pa_asio.cpp @@ -0,0 +1,3084 @@ +/* + * $Id: pa_asio.cpp,v 1.7.4.5 2003/06/30 16:27:10 stephane Exp $ + * Portable Audio I/O Library for ASIO Drivers + * + * Author: Stephane Letz + * Based on the Open Source API proposed by Ross Bencina + * Copyright (c) 2000-2001 Stephane Letz, Phil Burk + * + * 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. + * + * 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. + * + * 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. + */ + +/* 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 ouput 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 ouput 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 Desallocate 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 and : 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 + 01-12-02 Fix Pa_GetDefaultInputDeviceID and Pa_GetDefaultOuputDeviceID result when no driver are available : S Letz + 05-12-02 More debug messages : S Letz + 01-23-03 Increased max channels to 128. Fixed comparison of (OutputChannels > kMaxInputChannels) : P Burk + 02-17-03 Better termination handling : PaHost_CloseStream is called in PaHost_term is the the stream was not explicitely closed by the application : S Letz + 04-02-03 More robust ASIO driver buffer size initialization : 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 PaHost_CalcNumHostBuffers, + we try again with the preferred size. Fix an old (never detected?) bug in the buffer adapdation code : S Letz + 30-06-03 The audio callback was not protected against reentrancy : some drivers (like the Hoontech DSP24) seems to cause this behaviour + that corrupted the buffer adapdation state and finally caused crashes. The reentrancy state is now checked in bufferSwitchTimeInfo : S Letz + + TO DO : + + - Check Pa_StopSteam and Pa_AbortStream + - Optimization for Input only or Ouput only (really necessary ??) +*/ + + +#include +#include +#include + +#include "portaudio.h" +#include "pa_host.h" +#include "pa_trace.h" + +#include "asiosys.h" +#include "asio.h" +#include "asiodrivers.h" + + +#if MAC +#include +#include +#include +#else +#include +#include +#include +#endif + +enum { + // number of input and outputs supported by the host application + // you can change these to higher or lower values + kMaxInputChannels = 128, + kMaxOutputChannels = 128 +}; + +/* ASIO specific device information. */ +typedef struct internalPortAudioDevice +{ + PaDeviceInfo pad_Info; +} internalPortAudioDevice; + + +/* ASIO driver internal data storage */ +typedef struct PaHostSoundControl +{ + // ASIOInit() + ASIODriverInfo pahsc_driverInfo; + + // ASIOGetChannels() + int32 pahsc_NumInputChannels; + int32 pahsc_NumOutputChannels; + + // ASIOGetBufferSize() - sizes in frames per buffer + int32 pahsc_minSize; + int32 pahsc_maxSize; + int32 pahsc_preferredSize; + int32 pahsc_granularity; + + // ASIOGetSampleRate() + ASIOSampleRate pahsc_sampleRate; + + // ASIOOutputReady() + bool pahsc_postOutput; + + // ASIOGetLatencies () + int32 pahsc_inputLatency; + int32 pahsc_outputLatency; + + // ASIOCreateBuffers () + ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's + + // ASIOGetChannelInfo() + ASIOChannelInfo pahsc_channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's + // The above two arrays share the same indexing, as the data in them are linked together + + // Information from ASIOGetSamplePosition() + // data is converted to double floats for easier use, however 64 bit integer can be used, too + double nanoSeconds; + double samples; + double tcSamples; // time code samples + + // bufferSwitchTimeInfo() + ASIOTime tInfo; // time info state + unsigned long sysRefTime; // system reference time, when bufferSwitch() was called + + // Signal the end of processing in this example + bool stopped; + + ASIOCallbacks pahsc_asioCallbacks; + + + int32 pahsc_userInputBufferFrameOffset; // Position in Input user buffer + int32 pahsc_userOutputBufferFrameOffset; // Position in Output user buffer + int32 pahsc_hostOutputBufferFrameOffset; // Position in Output ASIO buffer + + int32 past_FramesPerHostBuffer; // Number of frames in ASIO buffer + + int32 pahsc_InputBufferOffset; // Number of null frames for input buffer alignement + int32 pahsc_OutputBufferOffset; // Number of null frames for ouput buffer alignement + +#if MAC + UInt64 pahsc_EntryCount; + UInt64 pahsc_LastExitCount; +#elif WINDOWS + LARGE_INTEGER pahsc_EntryCount; + LARGE_INTEGER pahsc_LastExitCount; +#endif + + PaTimestamp pahsc_NumFramesDone; + + internalPortAudioStream *past; + + int32 reenterCount; // Counter of audio callback reentrancy + int32 reenterError; // Counter of audio callback reentrancy detection + +} PaHostSoundControl; + + +//---------------------------------------------------------- +#define PRINT(x) { printf x; fflush(stdout); } +#define ERR_RPT(x) PRINT(x) + +#define DBUG(x) /* PRINT(x) */ +#define DBUGX(x) /* PRINT(x) */ + +/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */ +#define CARBON_COMPATIBLE (0) +#define PA_MAX_DEVICE_INFO (32) + +#define MIN_INT8 (-0x80) +#define MAX_INT8 (0x7F) + +#define MIN_INT8_FP ((float)-0x80) +#define MAX_INT8_FP ((float)0x7F) + +#define MIN_INT16_FP ((float)-0x8000) +#define MAX_INT16_FP ((float)0x7FFF) + +#define MIN_INT16 (-0x8000) +#define MAX_INT16 (0x7FFF) + +#define MAX_INT32_FP (2147483520.0f) /* 0x0x7FFFFF80 - seems safe */ + +/************************************************************************************/ +/****************** Data ************************************************************/ +/************************************************************************************/ +static int sNumDevices = 0; +static internalPortAudioDevice sDevices[PA_MAX_DEVICE_INFO] = { 0 }; +static int32 sPaHostError = 0; +static int sDefaultOutputDeviceID = 0; +static int sDefaultInputDeviceID = 0; + +PaHostSoundControl asioDriverInfo = {0}; + +#ifdef MAC +static bool swap = true; +#elif WINDOWS +static bool swap = false; +#endif + +// Prototypes +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 void Pa_StartUsageCalculation( internalPortAudioStream *past ); +static void Pa_EndUsageCalculation( internalPortAudioStream *past ); + +static void Pa_ASIO_Convert_Inter_Input( + ASIOBufferInfo* nativeBuffer, + void* inputBuffer, + long NumInputChannels, + long NumOuputChannels, + long framePerBuffer, + long hostFrameOffset, + long userFrameOffset, + ASIOSampleType nativeFormat, + PaSampleFormat paFormat, + PaStreamFlags flags, + long index); + +static void Pa_ASIO_Convert_Inter_Output( + ASIOBufferInfo* nativeBuffer, + void* outputBuffer, + long NumInputChannels, + long NumOuputChannels, + long framePerBuffer, + long hostFrameOffset, + long userFrameOffset, + ASIOSampleType nativeFormat, + PaSampleFormat paFormat, + PaStreamFlags flags, + long index); + +static void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer, + ASIOSampleType nativeFormat, + long NumInputChannels, + long NumOuputChannels, + long index, + long hostFrameOffset, + long frames); + +static void Pa_ASIO_Callback_Input(long index); +static void Pa_ASIO_Callback_Output(long index, long framePerBuffer); +static void Pa_ASIO_Callback_End(); +static void Pa_ASIO_Clear_User_Buffers(); + +// Some external references +extern AsioDrivers* asioDrivers ; +bool loadAsioDriver(char *name); +unsigned long get_sys_reference_time(); + + +/************************************************************************************/ +/****************** Macro ************************************************************/ +/************************************************************************************/ + +#define SwapLong(v) ((((v)>>24)&0xFF)|(((v)>>8)&0xFF00)|(((v)&0xFF00)<<8)|(((v)&0xFF)<<24)) ; +#define SwapShort(v) ((((v)>>8)&0xFF)|(((v)&0xFF)<<8)) ; + +#define ClipShort(v) (((v)MAX_INT16)?MAX_INT16:(v))) +#define ClipChar(v) (((v)MAX_INT8)?MAX_INT8:(v))) +#define ClipFloat(v) (((v)<-1.0f)?-1.0f:(((v)>1.0f)?1.0f:(v))) + +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif + +#ifndef max +#define max(a,b) ((a)>=(b)?(a):(b)) +#endif + + +static bool Pa_ASIO_loadAsioDriver(char *name) +{ + #ifdef WINDOWS + CoInitialize(0); + #endif + return loadAsioDriver(name); +} + + + +// Utilities for alignement buffer size computation +static int PGCD (int a, int b) {return (b == 0) ? a : PGCD (b,a%b);} +static int PPCM (int a, int b) {return (a*b) / PGCD (a,b);} + +// Takes the size of host buffer and user buffer : returns the number of frames needed for buffer adaptation +static int Pa_ASIO_CalcFrameShift (int M, int N) +{ + int res = 0; + for (int i = M; i < PPCM (M,N) ; i+=M) { res = max (res, i%N); } + return res; +} + +// We have the following relation : +// Pa_ASIO_CalcFrameShift (M,N) + M = Pa_ASIO_CalcFrameShift (N,M) + N + +/* ASIO sample type to PortAudio sample type conversion */ +static PaSampleFormat Pa_ASIO_Convert_SampleFormat(ASIOSampleType type) +{ + switch (type) { + + case ASIOSTInt16MSB: + case ASIOSTInt16LSB: + case ASIOSTInt32MSB16: + case ASIOSTInt32LSB16: + return paInt16; + + case ASIOSTFloat32MSB: + case ASIOSTFloat32LSB: + case ASIOSTFloat64MSB: + case ASIOSTFloat64LSB: + return paFloat32; + + case ASIOSTInt32MSB: + case ASIOSTInt32LSB: + case ASIOSTInt32MSB18: + case ASIOSTInt32MSB20: + case ASIOSTInt32MSB24: + case ASIOSTInt32LSB18: + case ASIOSTInt32LSB20: + case ASIOSTInt32LSB24: + return paInt32; + + case ASIOSTInt24MSB: + case ASIOSTInt24LSB: + return paInt24; + + default: + return paCustomFormat; + } +} + + + +//-------------------------------------------------------------------------------------------------------------------- +static void PaHost_CalcBufferOffset(internalPortAudioStream *past) +{ + if (asioDriverInfo.past_FramesPerHostBuffer > past->past_FramesPerUserBuffer){ + // Computes the MINIMUM value of null frames shift for the output buffer alignement + asioDriverInfo.pahsc_OutputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer); + asioDriverInfo.pahsc_InputBufferOffset = 0; + DBUG(("PaHost_CalcBufferOffset : Minimum BufferOffset for Output = %d\n", asioDriverInfo.pahsc_OutputBufferOffset)); + }else{ + + //Computes the MINIMUM value of null frames shift for the input buffer alignement + asioDriverInfo.pahsc_InputBufferOffset = Pa_ASIO_CalcFrameShift (asioDriverInfo.past_FramesPerHostBuffer,past->past_FramesPerUserBuffer); + asioDriverInfo.pahsc_OutputBufferOffset = 0; + DBUG(("PaHost_CalcBufferOffset : Minimum BufferOffset for Input = %d\n", asioDriverInfo.pahsc_InputBufferOffset)); + } +} + +//-------------------------------------------------------------------------------------------------------------------- +/* Allocate ASIO buffers, initialise channels */ +static ASIOError Pa_ASIO_CreateBuffers (PaHostSoundControl *asioDriverInfo, long InputChannels, + long OutputChannels, long framesPerBuffer) +{ + ASIOError err; + int i; + + ASIOBufferInfo *info = asioDriverInfo->bufferInfos; + + // Check parameters + if ((InputChannels > kMaxInputChannels) || (OutputChannels > kMaxOutputChannels)) return ASE_InvalidParameter; + + for(i = 0; i < InputChannels; i++, info++){ + info->isInput = ASIOTrue; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = 0; + } + + for(i = 0; i < OutputChannels; i++, info++){ + info->isInput = ASIOFalse; + info->channelNum = i; + info->buffers[0] = info->buffers[1] = 0; + } + + // Set up the asioCallback structure and create the ASIO data buffer + asioDriverInfo->pahsc_asioCallbacks.bufferSwitch = &bufferSwitch; + asioDriverInfo->pahsc_asioCallbacks.sampleRateDidChange = &sampleRateChanged; + asioDriverInfo->pahsc_asioCallbacks.asioMessage = &asioMessages; + asioDriverInfo->pahsc_asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo; + + DBUG(("Pa_ASIO_CreateBuffers : ASIOCreateBuffers with inputChannels = %ld \n", InputChannels)); + DBUG(("Pa_ASIO_CreateBuffers : ASIOCreateBuffers with OutputChannels = %ld \n", OutputChannels)); + DBUG(("Pa_ASIO_CreateBuffers : ASIOCreateBuffers with size = %ld \n", framesPerBuffer)); + + err = ASIOCreateBuffers( asioDriverInfo->bufferInfos, InputChannels+OutputChannels, + framesPerBuffer, &asioDriverInfo->pahsc_asioCallbacks); + if (err != ASE_OK) return err; + + // Initialise buffers + for (i = 0; i < InputChannels + OutputChannels; i++) + { + asioDriverInfo->pahsc_channelInfos[i].channel = asioDriverInfo->bufferInfos[i].channelNum; + asioDriverInfo->pahsc_channelInfos[i].isInput = asioDriverInfo->bufferInfos[i].isInput; + err = ASIOGetChannelInfo(&asioDriverInfo->pahsc_channelInfos[i]); + if (err != ASE_OK) break; + } + + err = ASIOGetLatencies(&asioDriverInfo->pahsc_inputLatency, &asioDriverInfo->pahsc_outputLatency); + + DBUG(("Pa_ASIO_CreateBuffers : InputLatency = %ld latency = %ld msec \n", + asioDriverInfo->pahsc_inputLatency, + (long)((asioDriverInfo->pahsc_inputLatency*1000)/ asioDriverInfo->past->past_SampleRate))); + DBUG(("Pa_ASIO_CreateBuffers : OuputLatency = %ld latency = %ld msec \n", + asioDriverInfo->pahsc_outputLatency, + (long)((asioDriverInfo->pahsc_outputLatency*1000)/ asioDriverInfo->past->past_SampleRate))); + + return err; +} + + +/* + Query ASIO driver info : + + First we get all available ASIO drivers located in the ASIO folder, + then try to load each one. For each loaded driver, get all needed informations. +*/ +static PaError Pa_ASIO_QueryDeviceInfo( internalPortAudioDevice * ipad ) +{ + +#define NUM_STANDARDSAMPLINGRATES 3 /* 11.025, 22.05, 44.1 */ +#define NUM_CUSTOMSAMPLINGRATES 9 /* must be the same number of elements as in the array below */ +#define MAX_NUMSAMPLINGRATES (NUM_STANDARDSAMPLINGRATES+NUM_CUSTOMSAMPLINGRATES) + + ASIOSampleRate possibleSampleRates[] + = {8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0}; + + ASIOChannelInfo channelInfos; + long InputChannels,OutputChannels; + double *sampleRates; + char* names[PA_MAX_DEVICE_INFO] ; + PaDeviceInfo *dev; + int i; + int numDrivers; + ASIOError asioError; + + /* Allocate names */ + for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) { + names[i] = (char*)PaHost_AllocateFastMemory(32); + /* check memory */ + if(!names[i]) return paInsufficientMemory; + } + + /* MUST BE CHECKED : to force fragments loading on Mac */ + Pa_ASIO_loadAsioDriver("dummy"); + + /* Get names of all available ASIO drivers */ + asioDrivers->getDriverNames(names,PA_MAX_DEVICE_INFO); + + /* Check all available ASIO drivers */ +#if MAC + numDrivers = asioDrivers->getNumFragments(); +#elif WINDOWS + numDrivers = asioDrivers->asioGetNumDev(); +#endif + + DBUG(("PaASIO_QueryDeviceInfo: number of installed drivers = %d\n", numDrivers )); + + for (int driver = 0 ; driver < numDrivers ; driver++) + { + + #if WINDOWS + asioDriverInfo.pahsc_driverInfo.asioVersion = 2; // FIXME - is this right? PLB + asioDriverInfo.pahsc_driverInfo.sysRef = GetDesktopWindow(); // FIXME - is this right? PLB + #endif + + DBUG(("---------------------------------------\n")); + + DBUG(("PaASIO_QueryDeviceInfo: Driver name = %s\n", names[driver])); + + /* If the driver can be loaded : */ + if ( !Pa_ASIO_loadAsioDriver(names[driver]) ){ + DBUG(("PaASIO_QueryDeviceInfo could not loadAsioDriver %s\n", names[driver])); + } else { + + DBUG(("PaASIO_QueryDeviceInfo: loadAsioDriver OK\n")); + + if((asioError = ASIOInit(&asioDriverInfo.pahsc_driverInfo)) != ASE_OK){ + + DBUG(("PaASIO_QueryDeviceInfo: ASIOInit returned %d for %s\n", asioError, names[driver])); + + }else { + + DBUG(("PaASIO_QueryDeviceInfo: ASIOInit OK \n")); + + if(ASIOGetChannels(&InputChannels, &OutputChannels) != ASE_OK){ + + DBUG(("PaASIO_QueryDeviceInfo could not ASIOGetChannels for %s\n", names[driver])); + + }else { + + DBUG(("PaASIO_QueryDeviceInfo: ASIOGetChannels OK \n")); + + /* Gets the name */ + dev = &(ipad[sNumDevices].pad_Info); + dev->name = names[driver]; + names[driver] = 0; + + /* Gets Input and Output channels number */ + dev->maxInputChannels = InputChannels; + dev->maxOutputChannels = OutputChannels; + + DBUG(("PaASIO_QueryDeviceInfo: InputChannels = %d\n", InputChannels )); + DBUG(("PaASIO_QueryDeviceInfo: OutputChannels = %d\n", OutputChannels )); + + /* Make room in case device supports all rates. */ + sampleRates = (double*)PaHost_AllocateFastMemory(MAX_NUMSAMPLINGRATES * sizeof(double)); + /* check memory */ + if (!sampleRates) { + ASIOExit(); + return paInsufficientMemory; + } + dev->sampleRates = sampleRates; + dev->numSampleRates = 0; + + /* Loop through the possible sampling rates and check each to see if the device supports it. */ + for (int index = 0; index < MAX_NUMSAMPLINGRATES; index++) { + if (ASIOCanSampleRate(possibleSampleRates[index]) != ASE_NoClock) { + DBUG(("PaASIO_QueryDeviceInfo: possible sample rate = %d\n", (long)possibleSampleRates[index])); + dev->numSampleRates += 1; + *sampleRates = possibleSampleRates[index]; + sampleRates++; + } + } + + /* We assume that all channels have the same SampleType, so check the first */ + channelInfos.channel = 0; + channelInfos.isInput = 1; + + if ((asioError = ASIOGetChannelInfo(&channelInfos)) == ASE_NotPresent) { + DBUG(("PaASIO_QueryDeviceInfo: ASIOGetChannelInfo returned %d \n",asioError)); + } + + dev->nativeSampleFormats = Pa_ASIO_Convert_SampleFormat(channelInfos.type); + + /* unload the driver */ + if ((asioError = ASIOExit()) != ASE_OK) { + DBUG(("PaASIO_QueryDeviceInfo: ASIOExit returned %d \n",asioError)); + } + + sNumDevices++; + } + + } + } + } + + /* free only unused names */ + for (i = 0 ; i < PA_MAX_DEVICE_INFO ; i++) if (names[i]) PaHost_FreeFastMemory(names[i],32); + + return paNoError; +} + +//---------------------------------------------------------------------------------- +// TAKEN FROM THE ASIO SDK: +void sampleRateChanged(ASIOSampleRate sRate) +{ + // 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. +} + +//---------------------------------------------------------------------------------- +// TAKEN FROM THE ASIO SDK: +long asioMessages(long selector, long value, void* message, double* opt) +{ + // currently the parameters "value", "message" and "opt" are not used. + long ret = 0; + 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. + 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 wether 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 wether 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; +} + + +//---------------------------------------------------------------------------------- +// 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 processNow) +{ + // the actual processing callback. + // Beware that this is normally in a seperate thread, hence be sure that you take care + // about thread synchronization. This is omitted here for simplicity. + + // 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(); + +#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 + + // To avoid the callback accessing a desallocated stream + if (asioDriverInfo.past == NULL) return 0L; + + // Keep sample position + asioDriverInfo.pahsc_NumFramesDone = timeInfo->timeInfo.samplePosition.lo; + + // Reentrancy control + if( ++asioDriverInfo.reenterCount) { + asioDriverInfo.reenterError++; + DBUG(("bufferSwitchTimeInfo : reentrancy detection = %d\n", asioDriverInfo.reenterError)); + return 0L; + } + + do { + + /* Has a user callback returned '1' to indicate finished at the last ASIO callback? */ + if( asioDriverInfo.past->past_StopSoon ) { + + Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, + asioDriverInfo.pahsc_channelInfos[0].type, + asioDriverInfo.pahsc_NumInputChannels , + asioDriverInfo.pahsc_NumOutputChannels, + index, + 0, + asioDriverInfo.past_FramesPerHostBuffer); + + asioDriverInfo.past->past_IsActive = 0; + + // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place + if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady(); + + }else { + + /* CPU usage */ + Pa_StartUsageCalculation(asioDriverInfo.past); + + Pa_ASIO_Callback_Input(index); + + // Finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place + if (asioDriverInfo.pahsc_postOutput) ASIOOutputReady(); + + Pa_ASIO_Callback_End(); + + /* CPU usage */ + Pa_EndUsageCalculation(asioDriverInfo.past); + } + + } while(asioDriverInfo.reenterCount--); + + return 0L; +} + + +//---------------------------------------------------------------------------------- +void bufferSwitch(long index, ASIOBool processNow) +{ + // the actual processing callback. + // Beware that this is normally in a seperate 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, processNow); +} + +//---------------------------------------------------------------------------------- +unsigned long get_sys_reference_time() +{ + // get the system reference time + #if WINDOWS + return timeGetTime(); + #elif MAC + static const double twoRaisedTo32 = 4294967296.; + UnsignedWide ys; + Microseconds(&ys); + double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo); + return (unsigned long)(r / 1000.); + #endif +} + + +/************************************************************* +** Calculate 2 LSB dither signal with a triangular distribution. +** Ranged properly for adding to a 32 bit integer prior to >>15. +*/ +#define DITHER_BITS (15) +#define DITHER_SCALE (1.0f / ((1<>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS)); + /* High pass filter to reduce audibility. */ + highPass = current - previous; + previous = current; + return highPass; +} + +// TO BE COMPLETED WITH ALL SUPPORTED PA SAMPLE TYPES + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Input_Int16_Float32 (ASIOBufferInfo* nativeBuffer, float *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, bool swap) +{ + long temp; + int i,j; + + for( j=0; j>16); + userBufPtr += NumInputChannels; + } + } + } + else + { + for( j=0; j> 1) + Pa_TriangularDither(); + temp = temp >> 15; + temp = (short) ClipShort(temp); + *userBufPtr = (short)temp; + userBufPtr += NumInputChannels; + } + } + + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +// MUST BE TESTED +static void Input_Float32_Int16 (ASIOBufferInfo* nativeBuffer, short *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap) +{ + unsigned long temp; + int i,j; + + if( flags & paDitherOff ) + { + for( j=0; j>8); + userBufPtr += NumInputChannels; + } + } + } + else + { + for( j=0; j> 8; + temp = ClipShort(temp); + *userBufPtr = (char)(temp>>8); + userBufPtr += NumInputChannels; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Input_Int32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset, uint32 flags,bool swap) +{ + long temp; + int i,j; + + if( flags & paDitherOff ) + { + for( j=0; j>24); + userBufPtr += NumInputChannels; + } + } + } + else + { + for( j=0; j>16; // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED) + temp += Pa_TriangularDither() >> 8; + temp = ClipShort(temp); + *userBufPtr = (char)(temp >> 8); + userBufPtr += NumInputChannels; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +// MUST BE TESTED + +static void Input_Float32_Int8 (ASIOBufferInfo* nativeBuffer, char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap) +{ + unsigned long temp; + int i,j; + + if( flags & paDitherOff ) + { + for( j=0; j>8) + 0x80); + userBufPtr += NumInputChannels; + } + } + } + else + { + for( j=0; j> 8; + temp = ClipShort(temp); + *userBufPtr = (unsigned char)((temp>>8) + 0x80); + userBufPtr += NumInputChannels; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Input_Int32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset, int userFrameOffset,uint32 flags,bool swap) +{ + long temp; + int i,j; + + if( flags & paDitherOff ) + { + for( j=0; j>24) + 0x80); + userBufPtr += NumInputChannels; + } + } + } + else + { + for( j=0; j>16; // Shift to get a 16 bit value, then use the 16 bits to 8 bits code (MUST BE CHECHED) + temp += Pa_TriangularDither() >> 8; + temp = ClipShort(temp); + *userBufPtr = (unsigned char)((temp>>8) + 0x80); + userBufPtr += NumInputChannels; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +// MUST BE TESTED + +static void Input_Float32_IntU8 (ASIOBufferInfo* nativeBuffer, unsigned char *inBufPtr, int framePerBuffer, int NumInputChannels, int index, int hostFrameOffset,int userFrameOffset, uint32 flags,bool swap) +{ + unsigned long temp; + int i,j; + + if( flags & paDitherOff ) + { + for( j=0; j> 16); + if (swap) temp = SwapShort(temp); + asioBufPtr[i] = (short)temp; + userBufPtr += NumOuputChannels; + } + } + } + else + { + for (j= 0; j < NumOuputChannels; j++) + { + short *asioBufPtr = &((short*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; + long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; + for( i=0; i> 1) + Pa_TriangularDither(); + temp = temp >> 15; + temp = (short) ClipShort(temp); + if (swap) temp = SwapShort(temp); + asioBufPtr[i] = (short)temp; + userBufPtr += NumOuputChannels; + } + } + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Output_Int32_Int32(ASIOBufferInfo* nativeBuffer, long *outBufPtr, int framePerBuffer, int NumInputChannels, int NumOuputChannels, int index, int hostFrameOffset,int userFrameOffset,uint32 flags,bool swap) +{ + long temp; + int i,j; + + for (j= 0; j < NumOuputChannels; j++) + { + long *asioBufPtr = &((long*)nativeBuffer[j+NumInputChannels].buffers[index])[hostFrameOffset]; + long *userBufPtr = &outBufPtr[j+(userFrameOffset*NumOuputChannels)]; + for( i=0; ipast_FramesPerUserBuffer <= asioDriverInfo.past_FramesPerHostBuffer) { + asioDriverInfo.pahsc_hostOutputBufferFrameOffset = asioDriverInfo.pahsc_OutputBufferOffset; + asioDriverInfo.pahsc_userInputBufferFrameOffset = 0; // empty + asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer; // empty + DBUG(("Pa_ASIO_Adaptor_Init : shift output\n")); + DBUG(("Pa_ASIO_Adaptor_Init : userInputBufferFrameOffset %d\n",asioDriverInfo.pahsc_userInputBufferFrameOffset)); + DBUG(("Pa_ASIO_Adaptor_Init : userOutputBufferFrameOffset %d\n",asioDriverInfo.pahsc_userOutputBufferFrameOffset)); + DBUG(("Pa_ASIO_Adaptor_Init : hostOutputBufferFrameOffset %d\n",asioDriverInfo.pahsc_hostOutputBufferFrameOffset)); + + }else { + asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0; // empty + asioDriverInfo.pahsc_userInputBufferFrameOffset = asioDriverInfo.pahsc_InputBufferOffset; + asioDriverInfo.pahsc_userOutputBufferFrameOffset = asioDriverInfo.past->past_FramesPerUserBuffer; // empty + DBUG(("Pa_ASIO_Adaptor_Init : shift input\n")); + DBUG(("Pa_ASIO_Adaptor_Init : userInputBufferFrameOffset %d\n",asioDriverInfo.pahsc_userInputBufferFrameOffset)); + DBUG(("Pa_ASIO_Adaptor_Init : userOutputBufferFrameOffset %d\n",asioDriverInfo.pahsc_userOutputBufferFrameOffset)); + DBUG(("Pa_ASIO_Adaptor_Init : hostOutputBufferFrameOffset %d\n",asioDriverInfo.pahsc_hostOutputBufferFrameOffset)); + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +// FIXME : optimization for Input only or output only modes (really necessary ??) +static void Pa_ASIO_Callback_Input(long index) +{ + internalPortAudioStream *past = asioDriverInfo.past; + long framesInputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer; // number of frames available into the host input buffer + long framesInputUserBuffer; // number of frames needed to complete the user input buffer + long framesOutputHostBuffer; // number of frames needed to complete the host output buffer + long framesOuputUserBuffer; // number of frames available into the user output buffer + long userResult; + long tmp; + + /* Fill host ASIO output with remaining frames in user output */ + framesOutputHostBuffer = asioDriverInfo.past_FramesPerHostBuffer - asioDriverInfo.pahsc_hostOutputBufferFrameOffset; + framesOuputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userOutputBufferFrameOffset; + tmp = min(framesOutputHostBuffer, framesOuputUserBuffer); + framesOutputHostBuffer -= tmp; + Pa_ASIO_Callback_Output(index,tmp); + + /* Available frames in hostInputBuffer */ + while (framesInputHostBuffer > 0) { + + /* Number of frames needed to complete an user input buffer */ + framesInputUserBuffer = asioDriverInfo.past->past_FramesPerUserBuffer - asioDriverInfo.pahsc_userInputBufferFrameOffset; + + if (framesInputHostBuffer >= framesInputUserBuffer) { + + /* Convert ASIO input to user input */ + Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos, + past->past_InputBuffer, + asioDriverInfo.pahsc_NumInputChannels , + asioDriverInfo.pahsc_NumOutputChannels, + framesInputUserBuffer, + asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer, + asioDriverInfo.pahsc_userInputBufferFrameOffset, + asioDriverInfo.pahsc_channelInfos[0].type, + past->past_InputSampleFormat, + past->past_Flags, + index); + + /* Call PortAudio callback */ + userResult = asioDriverInfo.past->past_Callback(past->past_InputBuffer, past->past_OutputBuffer, + past->past_FramesPerUserBuffer,past->past_FrameCount,past->past_UserData ); + + /* User callback has asked us to stop in the middle of the host buffer */ + if( userResult != 0) { + + /* Put 0 in the end of the output buffer */ + Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, + asioDriverInfo.pahsc_channelInfos[0].type, + asioDriverInfo.pahsc_NumInputChannels , + asioDriverInfo.pahsc_NumOutputChannels, + index, + asioDriverInfo.pahsc_hostOutputBufferFrameOffset, + asioDriverInfo.past_FramesPerHostBuffer - asioDriverInfo.pahsc_hostOutputBufferFrameOffset); + + past->past_StopSoon = 1; + return; + } + + + /* Full user ouput buffer : write offset */ + asioDriverInfo.pahsc_userOutputBufferFrameOffset = 0; + + /* Empty user input buffer : read offset */ + asioDriverInfo.pahsc_userInputBufferFrameOffset = 0; + + /* Fill host ASIO output */ + tmp = min (past->past_FramesPerUserBuffer,framesOutputHostBuffer); + Pa_ASIO_Callback_Output(index,tmp); + + framesOutputHostBuffer -= tmp; + framesInputHostBuffer -= framesInputUserBuffer; + + }else { + + /* Convert ASIO input to user input */ + Pa_ASIO_Convert_Inter_Input (asioDriverInfo.bufferInfos, + past->past_InputBuffer, + asioDriverInfo.pahsc_NumInputChannels , + asioDriverInfo.pahsc_NumOutputChannels, + framesInputHostBuffer, + asioDriverInfo.past_FramesPerHostBuffer - framesInputHostBuffer, + asioDriverInfo.pahsc_userInputBufferFrameOffset, + asioDriverInfo.pahsc_channelInfos[0].type, + past->past_InputSampleFormat, + past->past_Flags, + index); + + /* Update pahsc_userInputBufferFrameOffset */ + asioDriverInfo.pahsc_userInputBufferFrameOffset += framesInputHostBuffer; + + /* Update framesInputHostBuffer */ + framesInputHostBuffer = 0; + } + } + +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Pa_ASIO_Callback_Output(long index, long framePerBuffer) +{ + internalPortAudioStream *past = asioDriverInfo.past; + + if (framePerBuffer > 0) { + + /* Convert user output to ASIO ouput */ + Pa_ASIO_Convert_Inter_Output (asioDriverInfo.bufferInfos, + past->past_OutputBuffer, + asioDriverInfo.pahsc_NumInputChannels, + asioDriverInfo.pahsc_NumOutputChannels, + framePerBuffer, + asioDriverInfo.pahsc_hostOutputBufferFrameOffset, + asioDriverInfo.pahsc_userOutputBufferFrameOffset, + asioDriverInfo.pahsc_channelInfos[0].type, + past->past_InputSampleFormat, + past->past_Flags, + index); + + /* Update hostOuputFrameOffset */ + asioDriverInfo.pahsc_hostOutputBufferFrameOffset += framePerBuffer; + + /* Update userOutputFrameOffset */ + asioDriverInfo.pahsc_userOutputBufferFrameOffset += framePerBuffer; + } +} +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Pa_ASIO_Callback_End() + { + /* Empty ASIO ouput : write offset */ + asioDriverInfo.pahsc_hostOutputBufferFrameOffset = 0; + } + +//------------------------------------------------------------------------------------------------------------------------------------------------------- +static void Pa_ASIO_Clear_User_Buffers() +{ + if( asioDriverInfo.past->past_InputBuffer != NULL ) + { + memset( asioDriverInfo.past->past_InputBuffer, 0, asioDriverInfo.past->past_InputBufferSize ); + } + if( asioDriverInfo.past->past_OutputBuffer != NULL ) + { + memset( asioDriverInfo.past->past_OutputBuffer, 0, asioDriverInfo.past->past_OutputBufferSize ); + } +} + +//------------------------------------------------------------------------------------------------------------------------------------------------------- + static void Pa_ASIO_Clear_Output(ASIOBufferInfo* nativeBuffer, + ASIOSampleType nativeFormat, + long NumInputChannels, + long NumOuputChannels, + long index, + long hostFrameOffset, + long frames) +{ + + switch (nativeFormat) { + + case ASIOSTInt16MSB: + case ASIOSTInt16LSB: + case ASIOSTInt32MSB16: + case ASIOSTInt32LSB16: + Pa_ASIO_Clear_Output_16(nativeBuffer, frames, NumInputChannels, NumOuputChannels, index, hostFrameOffset); + break; + + case ASIOSTFloat64MSB: + case ASIOSTFloat64LSB: + break; + + case ASIOSTFloat32MSB: + case ASIOSTFloat32LSB: + case ASIOSTInt32MSB: + case ASIOSTInt32LSB: + case ASIOSTInt32MSB18: + case ASIOSTInt32MSB20: + case ASIOSTInt32MSB24: + case ASIOSTInt32LSB18: + case ASIOSTInt32LSB20: + case ASIOSTInt32LSB24: + Pa_ASIO_Clear_Output_32(nativeBuffer, frames, NumInputChannels, NumOuputChannels, index, hostFrameOffset); + break; + + case ASIOSTInt24MSB: + case ASIOSTInt24LSB: + break; + + default: + break; + } +} + + +//--------------------------------------------------------------------------------------- +static void Pa_ASIO_Convert_Inter_Input( + ASIOBufferInfo* nativeBuffer, + void* inputBuffer, + long NumInputChannels, + long NumOuputChannels, + long framePerBuffer, + long hostFrameOffset, + long userFrameOffset, + ASIOSampleType nativeFormat, + PaSampleFormat paFormat, + PaStreamFlags flags, + long index) +{ + + if((NumInputChannels > 0) && (nativeBuffer != NULL)) + { + /* Convert from native format to PA format. */ + switch(paFormat) + { + case paFloat32: + { + float *inBufPtr = (float *) inputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Input_Int16_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset, swap); + break; + case ASIOSTInt16MSB: + Input_Int16_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap); + break; + case ASIOSTInt32LSB: + Input_Int32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap); + break; + case ASIOSTInt32MSB: + Input_Int32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap); + break; + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,swap); + break; + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Float32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset, userFrameOffset,!swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + } + + break; + } + + case paInt32: + { + long *inBufPtr = (long *)inputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTInt16MSB: + Input_Int16_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + case ASIOSTInt32LSB: + Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTInt32MSB: + Input_Int32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Int32(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + + } + break; + } + + case paInt16: + { + short *inBufPtr = (short *) inputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTInt16MSB: + Input_Int16_Int16(nativeBuffer, inBufPtr, framePerBuffer , NumInputChannels, index , hostFrameOffset,userFrameOffset, !swap); + break; + case ASIOSTInt32LSB: + Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); + break; + case ASIOSTInt32MSB: + Input_Int32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); + break; + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Int16(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + + } + break; + } + + case paInt8: + { + /* Convert 16 bit data to 8 bit chars */ + + char *inBufPtr = (char *) inputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap); + break; + case ASIOSTInt16MSB: + Input_Int16_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + case ASIOSTInt32LSB: + Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); + break; + case ASIOSTInt32MSB: + Input_Int32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); + break; + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_Int8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + } + break; + } + + case paUInt8: + { + /* Convert 16 bit data to 8 bit unsigned chars */ + + unsigned char *inBufPtr = (unsigned char *)inputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); + break; + case ASIOSTInt16MSB: + Input_Int16_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + case ASIOSTInt32LSB: + Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap); + break; + case ASIOSTInt32MSB: + Input_Int32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,swap); + break; + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + Input_Float32_IntU8(nativeBuffer, inBufPtr, framePerBuffer, NumInputChannels, index, hostFrameOffset,userFrameOffset,flags,!swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + + } + break; + } + + default: + break; + } + } +} + + +//--------------------------------------------------------------------------------------- +static void Pa_ASIO_Convert_Inter_Output(ASIOBufferInfo* nativeBuffer, + void* outputBuffer, + long NumInputChannels, + long NumOuputChannels, + long framePerBuffer, + long hostFrameOffset, + long userFrameOffset, + ASIOSampleType nativeFormat, + PaSampleFormat paFormat, + PaStreamFlags flags, + long index) +{ + + if((NumOuputChannels > 0) && (nativeBuffer != NULL)) + { + /* Convert from PA format to native format */ + + switch(paFormat) + { + case paFloat32: + { + float *outBufPtr = (float *) outputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags, swap); + break; + case ASIOSTInt16MSB: + Output_Float32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,!swap); + break; + case ASIOSTInt32LSB: + Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset, userFrameOffset, flags,swap); + break; + case ASIOSTInt32MSB: + Output_Float32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + case ASIOSTFloat32LSB: + Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset,flags,swap); + break; + case ASIOSTFloat32MSB: + Output_Float32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + } + break; + } + + case paInt32: + { + long *outBufPtr = (long *) outputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); + break; + case ASIOSTInt16MSB: + Output_Int32_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + case ASIOSTInt32LSB: + Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); + break; + case ASIOSTInt32MSB: + Output_Int32_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + case ASIOSTFloat32LSB: + Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,swap); + break; + case ASIOSTFloat32MSB: + Output_Int32_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, flags,!swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + } + break; + } + + case paInt16: + { + short *outBufPtr = (short *) outputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTInt16MSB: + Output_Int16_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + case ASIOSTInt32LSB: + Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTInt32MSB: + Output_Int16_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + case ASIOSTFloat32LSB: + Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTFloat32MSB: + Output_Int16_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + + } + break; + } + + + case paInt8: + { + char *outBufPtr = (char *) outputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTInt16MSB: + Output_Int8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + case ASIOSTInt32LSB: + Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTInt32MSB: + Output_Int8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + case ASIOSTFloat32LSB: + Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTFloat32MSB: + Output_Int8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + } + break; + } + + case paUInt8: + { + unsigned char *outBufPtr = (unsigned char *) outputBuffer; + + switch (nativeFormat) { + case ASIOSTInt16LSB: + Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTInt16MSB: + Output_IntU8_Int16(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + case ASIOSTInt32LSB: + Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTInt32MSB: + Output_IntU8_Int32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + case ASIOSTFloat32LSB: + Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, swap); + break; + case ASIOSTFloat32MSB: + Output_IntU8_Float32(nativeBuffer, outBufPtr, framePerBuffer, NumInputChannels, NumOuputChannels, index, hostFrameOffset,userFrameOffset, !swap); + break; + + case ASIOSTInt24LSB: // used for 20 bits as well + case ASIOSTInt24MSB: // used for 20 bits as well + + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + + + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + DBUG(("Not yet implemented : please report the problem\n")); + break; + } + break; + } + + default: + break; + } + } + +} + + + +/* Load a ASIO driver corresponding to the required device */ +static PaError Pa_ASIO_loadDevice (long device) +{ + PaDeviceInfo * dev = &(sDevices[device].pad_Info); + + if (!Pa_ASIO_loadAsioDriver((char *) dev->name)) return paHostError; + if (ASIOInit(&asioDriverInfo.pahsc_driverInfo) != ASE_OK) return paHostError; + if (ASIOGetChannels(&asioDriverInfo.pahsc_NumInputChannels, &asioDriverInfo.pahsc_NumOutputChannels) != ASE_OK) return paHostError; + if (ASIOGetBufferSize(&asioDriverInfo.pahsc_minSize, &asioDriverInfo.pahsc_maxSize, &asioDriverInfo.pahsc_preferredSize, &asioDriverInfo.pahsc_granularity) != ASE_OK) return paHostError; + + if(ASIOOutputReady() == ASE_OK) + asioDriverInfo.pahsc_postOutput = true; + else + asioDriverInfo.pahsc_postOutput = false; + + return paNoError; +} + +//--------------------------------------------------- +static int GetHighestBitPosition (unsigned long n) +{ + int pos = -1; + while( n != 0 ) + { + pos++; + n = n >> 1; + } + return pos; +} + +//------------------------------------------------------------------------------------------ +static int GetFirstMultiple(long min, long val ){ return ((min + val - 1) / val) * val; } + +//------------------------------------------------------------------------------------------ +static int GetFirstPossibleDivisor(long max, long val ) +{ + for (int i = 2; i < 20; i++) {if (((val%i) == 0) && ((val/i) <= max)) return (val/i); } + return val; +} + +//------------------------------------------------------------------------ +static int IsPowerOfTwo( unsigned long n ) { return ((n & (n-1)) == 0); } + +/******************************************************************* +* Determine size of native ASIO audio buffer size +* Input parameters : FramesPerUserBuffer, NumUserBuffers +* Output values : FramesPerHostBuffer, OutputBufferOffset or InputtBufferOffset +*/ + +static PaError PaHost_CalcNumHostBuffers( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + long requestedBufferSize; + long firstMultiple, firstDivisor; + + // Compute requestedBufferSize + if( past->past_NumUserBuffers < 1 ){ + requestedBufferSize = past->past_FramesPerUserBuffer; + }else{ + requestedBufferSize = past->past_NumUserBuffers * past->past_FramesPerUserBuffer; + } + + // Adjust FramesPerHostBuffer using requestedBufferSize, ASIO minSize and maxSize, + if (requestedBufferSize < asioDriverInfo.pahsc_minSize){ + + firstMultiple = GetFirstMultiple(asioDriverInfo.pahsc_minSize, requestedBufferSize); + + if (firstMultiple <= asioDriverInfo.pahsc_maxSize) + asioDriverInfo.past_FramesPerHostBuffer = firstMultiple; + else + asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_minSize; + + }else if (requestedBufferSize > asioDriverInfo.pahsc_maxSize){ + + firstDivisor = GetFirstPossibleDivisor(asioDriverInfo.pahsc_maxSize, requestedBufferSize); + + if ((firstDivisor >= asioDriverInfo.pahsc_minSize) && (firstDivisor <= asioDriverInfo.pahsc_maxSize)) + asioDriverInfo.past_FramesPerHostBuffer = firstDivisor; + else + asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_maxSize; + }else{ + asioDriverInfo.past_FramesPerHostBuffer = requestedBufferSize; + } + + // If ASIO buffer size needs to be a power of two + if( asioDriverInfo.pahsc_granularity < 0 ){ + // Needs to be a power of two. + + if( !IsPowerOfTwo( asioDriverInfo.past_FramesPerHostBuffer ) ) + { + int highestBit = GetHighestBitPosition(asioDriverInfo.past_FramesPerHostBuffer); + asioDriverInfo.past_FramesPerHostBuffer = 1 << (highestBit + 1); + } + } + + DBUG(("----------------------------------\n")); + DBUG(("PaHost_CalcNumHostBuffers : minSize = %ld \n",asioDriverInfo.pahsc_minSize)); + DBUG(("PaHost_CalcNumHostBuffers : preferredSize = %ld \n",asioDriverInfo.pahsc_preferredSize)); + DBUG(("PaHost_CalcNumHostBuffers : maxSize = %ld \n",asioDriverInfo.pahsc_maxSize)); + DBUG(("PaHost_CalcNumHostBuffers : granularity = %ld \n",asioDriverInfo.pahsc_granularity)); + DBUG(("PaHost_CalcNumHostBuffers : User buffer size = %d\n", asioDriverInfo.past->past_FramesPerUserBuffer )); + DBUG(("PaHost_CalcNumHostBuffers : ASIO buffer size = %d\n", asioDriverInfo.past_FramesPerHostBuffer )); + + return paNoError; +} + + +/***********************************************************************/ +int Pa_CountDevices() +{ + PaError err ; + + if( sNumDevices <= 0 ) + { + /* Force loading of ASIO drivers */ + err = Pa_ASIO_QueryDeviceInfo(sDevices); + if( err != paNoError ) goto error; + } + + return sNumDevices; + +error: + PaHost_Term(); + DBUG(("Pa_CountDevices: returns %d\n", err )); + return err; +} + +/***********************************************************************/ +PaError PaHost_Init( void ) +{ + /* Have we already initialized the device info? */ + PaError err = (PaError) Pa_CountDevices(); + return ( err < 0 ) ? err : paNoError; +} + +/***********************************************************************/ +PaError PaHost_Term( void ) +{ + int i; + PaDeviceInfo *dev; + double *rates; + PaError result = paNoError; + + if (sNumDevices > 0) { + + /* Free allocated sample rate arrays and names*/ + for( i=0; isampleRates; + if ((rates != NULL)) PaHost_FreeFastMemory(rates, MAX_NUMSAMPLINGRATES * sizeof(double)); + dev->sampleRates = NULL; + if(dev->name != NULL) PaHost_FreeFastMemory((void *) dev->name, 32); + dev->name = NULL; + } + + sNumDevices = 0; + + /* If the stream has been closed with PaHost_CloseStream, asioDriverInfo.past == null, otherwise close it now */ + if(asioDriverInfo.past != NULL) Pa_CloseStream(asioDriverInfo.past); + + /* remove the loaded ASIO driver */ + asioDrivers->removeCurrentDriver(); + } + + return result; +} + +/***********************************************************************/ +PaError PaHost_OpenStream( internalPortAudioStream *past ) +{ + PaError result = paNoError; + ASIOError err; + int32 device; + + /* Check if a stream already runs */ + if (asioDriverInfo.past != NULL) return paHostError; + + /* Check the device number */ + if ((past->past_InputDeviceID != paNoDevice) + &&(past->past_OutputDeviceID != paNoDevice) + &&(past->past_InputDeviceID != past->past_OutputDeviceID)) + { + return paInvalidDeviceId; + } + + /* Allocation */ + memset(&asioDriverInfo, 0, sizeof(PaHostSoundControl)); + past->past_DeviceData = (void*) &asioDriverInfo; + + /* Reentrancy counter initialisation */ + asioDriverInfo.reenterCount = -1; + asioDriverInfo.reenterError = 0; + + /* FIXME */ + asioDriverInfo.past = past; + + /* load the ASIO device */ + device = (past->past_InputDeviceID < 0) ? past->past_OutputDeviceID : past->past_InputDeviceID; + result = Pa_ASIO_loadDevice(device); + if (result != paNoError) goto error; + + /* Check ASIO parameters and input parameters */ + if ((past->past_NumInputChannels > asioDriverInfo.pahsc_NumInputChannels) + || (past->past_NumOutputChannels > asioDriverInfo.pahsc_NumOutputChannels)) { + result = paInvalidChannelCount; + goto error; + } + + /* Set sample rate */ + if (ASIOSetSampleRate(past->past_SampleRate) != ASE_OK) { + result = paInvalidSampleRate; + goto error; + } + + /* if OK calc buffer size */ + result = PaHost_CalcNumHostBuffers( past ); + if (result != paNoError) goto error; + + + /* + Allocating input and output buffers number for the real past_NumInputChannels and past_NumOutputChannels + optimize the data transfer. + */ + + asioDriverInfo.pahsc_NumInputChannels = past->past_NumInputChannels; + asioDriverInfo.pahsc_NumOutputChannels = past->past_NumOutputChannels; + + /* Allocate ASIO buffers and callback*/ + err = Pa_ASIO_CreateBuffers(&asioDriverInfo, + asioDriverInfo.pahsc_NumInputChannels, + asioDriverInfo.pahsc_NumOutputChannels, + asioDriverInfo.past_FramesPerHostBuffer); + + + /* + 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 PaHost_CalcNumHostBuffers, we try again with the preferred size. + */ + + if (err != ASE_OK) { + + DBUG(("PaHost_OpenStream : Pa_ASIO_CreateBuffers failed with the requested framesPerBuffer = %ld \n", asioDriverInfo.past_FramesPerHostBuffer)); + + err = Pa_ASIO_CreateBuffers(&asioDriverInfo, + asioDriverInfo.pahsc_NumInputChannels, + asioDriverInfo.pahsc_NumOutputChannels, + asioDriverInfo.pahsc_preferredSize); + + if (err == ASE_OK) { + // Adjust FramesPerHostBuffer to take the preferredSize instead of the value computed in PaHost_CalcNumHostBuffers + asioDriverInfo.past_FramesPerHostBuffer = asioDriverInfo.pahsc_preferredSize; + DBUG(("PaHost_OpenStream : Adjust FramesPerHostBuffer to take the preferredSize instead of the value computed in PaHost_CalcNumHostBuffers\n")); + } else { + DBUG(("PaHost_OpenStream : Pa_ASIO_CreateBuffers failed with the preferred framesPerBuffer = %ld \n", asioDriverInfo.pahsc_preferredSize)); + } + } + + /* Compute buffer adapdation offset */ + PaHost_CalcBufferOffset(past); + + if (err == ASE_OK) + return paNoError; + else if (err == ASE_NoMemory) + result = paInsufficientMemory; + else if (err == ASE_InvalidParameter) + result = paInvalidChannelCount; + else if (err == ASE_InvalidMode) + result = paBufferTooBig; + else + result = paHostError; + +error: + ASIOExit(); + return result; + +} + +/***********************************************************************/ +PaError PaHost_CloseStream( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + PaError result = paNoError; + + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paNoError; + + #if PA_TRACE_START_STOP + AddTraceMessage( "PaHost_CloseStream: pahsc_HWaveOut ", (int) pahsc->pahsc_HWaveOut ); + #endif + + /* Free data and device for output. */ + past->past_DeviceData = NULL; + asioDriverInfo.past = NULL; + + /* Dispose */ + if(ASIODisposeBuffers() != ASE_OK) result = paHostError; + if(ASIOExit() != ASE_OK) result = paHostError; + + return result; +} + +/***********************************************************************/ +PaError PaHost_StartOutput( internalPortAudioStream *past ) +{ + /* Clear the index 0 host output buffer */ + Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, + asioDriverInfo.pahsc_channelInfos[0].type, + asioDriverInfo.pahsc_NumInputChannels, + asioDriverInfo.pahsc_NumOutputChannels, + 0, + 0, + asioDriverInfo.past_FramesPerHostBuffer); + + /* Clear the index 1 host output buffer */ + Pa_ASIO_Clear_Output(asioDriverInfo.bufferInfos, + asioDriverInfo.pahsc_channelInfos[0].type, + asioDriverInfo.pahsc_NumInputChannels, + asioDriverInfo.pahsc_NumOutputChannels, + 1, + 0, + asioDriverInfo.past_FramesPerHostBuffer); + + Pa_ASIO_Clear_User_Buffers(); + + Pa_ASIO_Adaptor_Init(); + + return paNoError; +} + +/***********************************************************************/ +PaError PaHost_StopOutput( internalPortAudioStream *past, int abort ) +{ + /* Nothing to do ?? */ + return paNoError; +} + +/***********************************************************************/ +PaError PaHost_StartInput( internalPortAudioStream *past ) +{ + /* Nothing to do ?? */ + return paNoError; +} + +/***********************************************************************/ +PaError PaHost_StopInput( internalPortAudioStream *past, int abort ) +{ + /* Nothing to do */ + return paNoError; +} + +/***********************************************************************/ +PaError PaHost_StartEngine( internalPortAudioStream *past ) +{ + // TO DO : count of samples + past->past_IsActive = 1; + return (ASIOStart() == ASE_OK) ? paNoError : paHostError; +} + +/***********************************************************************/ +PaError PaHost_StopEngine( internalPortAudioStream *past, int abort ) +{ + // TO DO : count of samples + past->past_IsActive = 0; + return (ASIOStop() == ASE_OK) ? paNoError : paHostError; +} + +/***********************************************************************/ +// TO BE CHECKED +PaError PaHost_StreamActive( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return paInternalError; + return (PaError) past->past_IsActive; +} + +/*************************************************************************/ +PaTimestamp Pa_StreamTime( PortAudioStream *stream ) +{ + PaHostSoundControl *pahsc; + internalPortAudioStream *past = (internalPortAudioStream *) stream; + if( past == NULL ) return paBadStreamPtr; + pahsc = (PaHostSoundControl *) past->past_DeviceData; + return pahsc->pahsc_NumFramesDone; +} + +/************************************************************************* + * Allocate memory that can be accessed in real-time. + * This may need to be held in physical memory so that it is not + * paged to virtual memory. + * This call MUST be balanced with a call to PaHost_FreeFastMemory(). + */ +void *PaHost_AllocateFastMemory( long numBytes ) +{ + #if MAC + void *addr = NewPtrClear( numBytes ); + if( (addr == NULL) || (MemError () != 0) ) return NULL; + + #if (CARBON_COMPATIBLE == 0) + if( HoldMemory( addr, numBytes ) != noErr ) + { + DisposePtr( (Ptr) addr ); + return NULL; + } + #endif + return addr; + #elif WINDOWS + void *addr = malloc( numBytes ); /* FIXME - do we need physical memory? */ + if( addr != NULL ) memset( addr, 0, numBytes ); + return addr; + #endif +} + +/************************************************************************* + * Free memory that could be accessed in real-time. + * This call MUST be balanced with a call to PaHost_AllocateFastMemory(). + */ +void PaHost_FreeFastMemory( void *addr, long numBytes ) +{ + #if MAC + if( addr == NULL ) return; + #if CARBON_COMPATIBLE + (void) numBytes; + #else + UnholdMemory( addr, numBytes ); + #endif + DisposePtr( (Ptr) addr ); + #elif WINDOWS + if( addr != NULL ) free( addr ); + #endif +} + + +/*************************************************************************/ +void Pa_Sleep( long msec ) +{ + #if MAC + int32 sleepTime, endTime; + /* Convert to ticks. Round up so we sleep a MINIMUM of msec time. */ + sleepTime = ((msec * 60) + 999) / 1000; + if( sleepTime < 1 ) sleepTime = 1; + endTime = TickCount() + sleepTime; + do{ + DBUGX(("Sleep for %d ticks.\n", sleepTime )); + WaitNextEvent( 0, NULL, sleepTime, NULL ); /* Use this just to sleep without getting events. */ + sleepTime = endTime - TickCount(); + } while( sleepTime > 0 ); + #elif WINDOWS + Sleep( msec ); + #endif +} + +/*************************************************************************/ +const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceID id ) +{ + if( (id < 0) || ( id >= Pa_CountDevices()) ) return NULL; + return &sDevices[id].pad_Info; +} + +/*************************************************************************/ +PaDeviceID Pa_GetDefaultInputDeviceID( void ) +{ + return (sNumDevices > 0) ? sDefaultInputDeviceID : paNoDevice; +} + +/*************************************************************************/ +PaDeviceID Pa_GetDefaultOutputDeviceID( void ) +{ + return (sNumDevices > 0) ? sDefaultOutputDeviceID : paNoDevice; +} + +/*************************************************************************/ +int Pa_GetMinNumBuffers( int framesPerUserBuffer, double sampleRate ) +{ + // TO BE IMPLEMENTED : using the ASIOGetLatency call?? + return 2; +} + +/*************************************************************************/ +int32 Pa_GetHostError( void ) +{ + int32 err = sPaHostError; + sPaHostError = 0; + return err; +} + + +#ifdef MAC + +/**************************************************************************/ +static void Pa_StartUsageCalculation( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + UnsignedWide widePad; + if( pahsc == NULL ) return; +/* Query system timer for usage analysis and to prevent overuse of CPU. */ + Microseconds( &widePad ); + pahsc->pahsc_EntryCount = UnsignedWideToUInt64( widePad ); +} +/**************************************************************************/ +static void Pa_EndUsageCalculation( internalPortAudioStream *past ) +{ + UnsignedWide widePad; + UInt64 CurrentCount; + long InsideCount; + long TotalCount; + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; +/* Measure CPU utilization during this callback. Note that this calculation +** assumes that we had the processor the whole time. +*/ +#define LOWPASS_COEFFICIENT_0 (0.9) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + Microseconds( &widePad ); + CurrentCount = UnsignedWideToUInt64( widePad ); + if( past->past_IfLastExitValid ) + { + InsideCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_EntryCount); + TotalCount = (long) U64Subtract(CurrentCount, pahsc->pahsc_LastExitCount); +/* Low pass filter the result because sometimes we get called several times in a row. +* That can cause the TotalCount to be very low which can cause the usage to appear +* unnaturally high. So we must filter numerator and denominator separately!!! +*/ + past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) + + (LOWPASS_COEFFICIENT_1 * InsideCount)); + past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) + + (LOWPASS_COEFFICIENT_1 * TotalCount)); + past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount; + } + pahsc->pahsc_LastExitCount = CurrentCount; + past->past_IfLastExitValid = 1; +} + +#elif WINDOWS + +/********************************* BEGIN CPU UTILIZATION MEASUREMENT ****/ +static void Pa_StartUsageCalculation( internalPortAudioStream *past ) +{ + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; +/* Query system timer for usage analysis and to prevent overuse of CPU. */ + QueryPerformanceCounter( &pahsc->pahsc_EntryCount ); +} + +static void Pa_EndUsageCalculation( internalPortAudioStream *past ) +{ + LARGE_INTEGER CurrentCount = { 0, 0 }; + LONGLONG InsideCount; + LONGLONG TotalCount; +/* +** Measure CPU utilization during this callback. Note that this calculation +** assumes that we had the processor the whole time. +*/ +#define LOWPASS_COEFFICIENT_0 (0.9) +#define LOWPASS_COEFFICIENT_1 (0.99999 - LOWPASS_COEFFICIENT_0) + + PaHostSoundControl *pahsc = (PaHostSoundControl *) past->past_DeviceData; + if( pahsc == NULL ) return; + + if( QueryPerformanceCounter( &CurrentCount ) ) + { + if( past->past_IfLastExitValid ) + { + InsideCount = CurrentCount.QuadPart - pahsc->pahsc_EntryCount.QuadPart; + TotalCount = CurrentCount.QuadPart - pahsc->pahsc_LastExitCount.QuadPart; +/* Low pass filter the result because sometimes we get called several times in a row. + * That can cause the TotalCount to be very low which can cause the usage to appear + * unnaturally high. So we must filter numerator and denominator separately!!! + */ + past->past_AverageInsideCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageInsideCount) + + (LOWPASS_COEFFICIENT_1 * InsideCount)); + past->past_AverageTotalCount = (( LOWPASS_COEFFICIENT_0 * past->past_AverageTotalCount) + + (LOWPASS_COEFFICIENT_1 * TotalCount)); + past->past_Usage = past->past_AverageInsideCount / past->past_AverageTotalCount; + } + pahsc->pahsc_LastExitCount = CurrentCount; + past->past_IfLastExitValid = 1; + } +} + +#endif + + + + -- cgit v1.2.1