/*************************************************************************** * * * LinuxSampler - modular, streaming capable sampler * * * * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * * Copyright (C) 2005 - 2008 Christian Schoenebeck * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * * MA 02111-1307 USA * ***************************************************************************/ #include "AudioOutputDeviceAsio.h" #include "AudioOutputDeviceFactory.h" #define kMaxInputChannels 32 #define kMaxOutputChannels 32 #define ASIO_MAX_DEVICE_INFO 32 #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) // internal data storage typedef struct DriverInfo { // ASIOInit() ASIODriverInfo driverInfo; // ASIOGetChannels() long numInputChannels; long numOutputChannels; // ASIOGetBufferSize() long minBufSize; long maxBufSize; long preferredBufSize; long bufGranularity; long chosenBufSize; // ASIOGetSampleRate() ASIOSampleRate sampleRate; int numSampleRates; std::vector sampleRates; // ASIOOutputReady() bool ASIOOutputReadySupported; // ASIOGetLatencies () long inputLatency; long outputLatency; // ASIOCreateBuffers () long numInputBuffers; // becomes number of actual created input buffers long numOutputBuffers; // becomes number of actual created output buffers ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's // ASIOGetChannelInfo() ASIOChannelInfo 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; } DriverInfo; extern AsioDrivers* asioDrivers; extern bool loadAsioDriver(char *name); static ASIODriverInfo MyAsioDriverInfo; static DriverInfo asioDriverInfo = {0}; static bool asioDriverOpened = false; static bool AudioOutputDeviceAsioInstantiated = false; static String currentAsioDriverName; static std::vector asioDriverList; static bool asioDriverListLoaded = false; namespace LinuxSampler { // callback prototypes ASIOCallbacks asioCallbacks; ASIOBufferInfo bufferInfos[2]; AudioOutputDeviceAsio *GlobalAudioOutputDeviceAsioThisPtr; template static void floatToASIOSTInt32LSBXX(float *in, void *dest, int numSamples) { double pos_max_value = (1 << bitres) -1.0; double neg_max_value = -pos_max_value; int32_t *out = (int32_t *)dest; for(int i=0; i < numSamples ; i++) { double sample_point = in[i] * pos_max_value; if (sample_point < neg_max_value) sample_point = neg_max_value; if (sample_point > pos_max_value) sample_point = pos_max_value; out[i] = (int16_t)sample_point; } } static void floatToASIOSTInt16LSB(float *in, void *dest, int numSamples) { int16_t *out = (int16_t *)dest; for(int i=0; i < numSamples ; i++) { float sample_point = in[i] * 32768.0f; if (sample_point < -32768.0) sample_point = -32768.0; if (sample_point > 32767.0) sample_point = 32767.0; out[i] = (int16_t)sample_point; } } static void floatToASIOSTInt32LSB(float *in, void *dest, int numSamples) { int32_t *out = (int32_t *)dest; for(int i=0; i < numSamples ; i++) { double sample_point = in[i] * 2147483648.0; if (sample_point < - 2147483648.0) sample_point = -2147483648.0; if (sample_point > 2147483647.0) sample_point = 2147483647.0; out[i] = (int32_t)(sample_point); } } std::vector getAsioDriverNames(); static bool ASIO_loadAsioDriver(const char *name) { dmsg(2,("ASIO_loadAsioDriver: trying to load '%s'\n",name)); #ifdef WINDOWS CoInitialize(0); #endif return loadAsioDriver(const_cast(name)); } int ASIO_OpenAndQueryDeviceInfo(char *driverName, DriverInfo *driverInfo, ASIODriverInfo *asioDriverInfo) { 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; ASIOError asioError; // call this function in order to fill the internal vector containing the list of ASIO cards // since the reading of the list is performed only one time subsequent calls to this function will be automatically ignored // calling getAsioDriverNames() is needed in the case of a LinuxSampler client application first creating an ASIO device // which calls ASIO_OpenAndQueryDeviceInfo() and afterwards retrieve the list of available ASIO cards // since getAsioDriverNames() unloads the current driver and loads the driver 'dummy' this would cause // the audio stopping and forcing the create the audio output device again getAsioDriverNames(); dmsg(2,("ASIO_OpenAndQueryDeviceInfo driverName='%s' current='%s'\n",driverName, currentAsioDriverName.c_str() )); if(!strcmp(driverName, currentAsioDriverName.c_str() )) { // if the given ASIO driver name == current ASIO driver name and the driver // was already opened then do nothing if(asioDriverOpened) { dmsg(2,("asioDriver ALREADY OPENED, DOING NOTHING !\n")); return 0; } } else { dmsg(2,("driverName != currentAsioDriverName , new asio driver specified, opening device ....\n")); // if a new ASIO driver name was specified then first check if we need to close // the old one if(asioDriverOpened) { dmsg(2,("different asioDriver ALREADY OPENED, closing old one !\n")); asioDriverOpened = false; ASIOExit(); // close the old ASIO driver } } currentAsioDriverName = driverName; memset((void *)asioDriverInfo,0,sizeof(ASIODriverInfo)); asioDriverInfo->asioVersion=1; asioDriverInfo->sysRef = NULL; /* MUST BE CHECKED : to force fragments loading on Mac */ //ASIO_loadAsioDriver("dummy"); dmsg(2,("Before ASIO_loadAsioDriver('%s')\n",driverName)); if ( !ASIO_loadAsioDriver(driverName) ) { dmsg(2,("ASIO_OpenAndQueryDeviceInfo could not loadAsioDriver %s\n", driverName)); return -1; } dmsg(2,("Before ASIOInit()\n")); if( (asioError = ASIOInit(asioDriverInfo)) != ASE_OK ) { dmsg(2,("ASIO_OpenAndQueryDeviceInfo: ASIOInit returned %d for %s\n", asioError, driverName)); return asioError; } dmsg(2,("Before ASIOGetChannels()\n")); if( (asioError = ASIOGetChannels(&driverInfo->numInputChannels, &driverInfo->numOutputChannels) != ASE_OK)) { dmsg(2,("ASIO_OpenAndQueryDeviceInfo could not ASIOGetChannels for %s\n", driverName)); return asioError; } dmsg(2,("Before ASIOGetBufferSize()\n")); if( (asioError = ASIOGetBufferSize(&driverInfo->minBufSize,&driverInfo->maxBufSize,&driverInfo->preferredBufSize,&driverInfo->bufGranularity) != ASE_OK)) { dmsg(2,("ASIO_OpenAndQueryDeviceInfo could not ASIOGetBufferSize for %s\n", driverName)); return asioError; } dmsg(2,("ASIO_OpenAndQueryDeviceInfo: InputChannels = %d\n", driverInfo->numInputChannels )); dmsg(2,("ASIO_OpenAndQueryDeviceInfo: OutputChannels = %d\n", driverInfo->numOutputChannels )); /* Loop through the possible sampling rates and check each to see if the device supports it. */ driverInfo->numSampleRates = 0; driverInfo->sampleRates.clear(); for (int index = 0; index < MAX_NUMSAMPLINGRATES; index++) { if (ASIOCanSampleRate(possibleSampleRates[index]) != ASE_NoClock) { dmsg(2,("ASIOCanSampleRate: possible sample rate = %d\n", (long)possibleSampleRates[index])); driverInfo->sampleRates.push_back( (int)possibleSampleRates[index] ); driverInfo->numSampleRates++; } } /* get the channel infos for each output channel (including sample format) */ for(int i=0 ; i < driverInfo->numOutputChannels ; i++) { driverInfo->channelInfos[i].channel = i; driverInfo->channelInfos[i].isInput = ASIOFalse; ASIOGetChannelInfo(&driverInfo->channelInfos[i]); dmsg(2,("channelInfos[%d].type (sampleformat) = %d\n", i, driverInfo->channelInfos[i].type)); } dmsg(2,("ASIO_OpenAndQueryDeviceInfo: driver opened.\n")); asioDriverOpened = true; return ASE_OK; } std::vector getAsioDriverNames() { char* names[ASIO_MAX_DEVICE_INFO]; int numDrivers; if(asioDriverListLoaded) { dmsg(2,("getAsioDriverNames: ASIO driver list already loaded, doing returning cached list.\n")); return asioDriverList; } /* MUST BE CHECKED : to force fragments loading on Mac */ ASIO_loadAsioDriver("dummy"); #if MAC numDrivers = asioDrivers->getNumFragments(); #elif WINDOWS numDrivers = asioDrivers->asioGetNumDev(); #endif for (int i = 0 ; i < ASIO_MAX_DEVICE_INFO ; i++) { names[i] = new char[32]; memset(names[i],0,32); } /* Get names of all available ASIO drivers */ asioDrivers->getDriverNames(names,ASIO_MAX_DEVICE_INFO); dmsg(2,("getAsioDriverNames: numDrivers=%d\n",numDrivers)); for (int i = 0 ; i < numDrivers ; i++) { dmsg(2,("ASIO DRIVERLIST: i=%d name='%s'\n",i,names[i])); #if 1 asioDriverList.push_back(names[i]); #else int asioError; // FIXME: we currently try what is the best methode to exclude not connected devices or not working drivers // the code below is for testing only, it tried to load each ASIO driver and if it gives an error it is not included // in the list of available drivers (asioDriverList) if ( ASIO_loadAsioDriver(names[i]) ) { if( (asioError = ASIOInit(&MyAsioDriverInfo)) == ASE_OK ) { asioDriverList.push_back(names[i]); } else { dmsg(2,("getDriverList: ASIOInit of driver %s gave Error %d ! ignoring it.\n", names[i],asioError)); } } else { dmsg(2,("getDriverList: load driver %s failed! ignoring it.\n", names[i])); } // FIXME: we need to check this ASIOExit is needed (gave a crash on a ASIO ADSP24(WDM) card so we commented it out) //ASIOExit(); #endif //currentAsioDriverName=""; } for (int i = 0 ; i < ASIO_MAX_DEVICE_INFO ; i++) { delete[] names[i]; } dmsg(2,("getAsioDriverNames: returing from function. asioDriverList.size()=%d\n", asioDriverList.size() )); asioDriverListLoaded = true; return asioDriverList; } 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 } //---------------------------------------------------------------------------------- // 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 ASIOTime* AudioOutputDeviceAsio::bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow) { static long processedSamples = 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; // TODO: ignore for now. get the system reference time // asioDriverInfo.sysRefTime = get_sys_reference_time(); // buffer size in samples long buffSize = asioDriverInfo.chosenBufSize; int32_t *p; // tell LinuxSampler to render a fragment of buffSize samples GlobalAudioOutputDeviceAsioThisPtr->RenderAudio(buffSize); // now write and convert the samples to the ASIO buffer for (int i = 0; i < asioDriverInfo.numOutputBuffers; i++) { // do processing for the outputs only switch (asioDriverInfo.channelInfos[i].type) { case ASIOSTInt16LSB: floatToASIOSTInt16LSB(GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(), (int16_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize); break; case ASIOSTInt24LSB: // used for 20 bits as well memset (asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3); break; case ASIOSTInt32LSB: floatToASIOSTInt32LSB(GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(), (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize); break; case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture throw AudioOutputException(String("ASIO Error: ASIOSTFloat32LSB not yet supported! report to LinuxSampler developers.") ); break; case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture throw AudioOutputException(String("ASIO Error: ASIOSTFloat64LSB not yet supported! report to LinuxSampler developers.") ); break; // 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 floatToASIOSTInt32LSBXX<16>( GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(), (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize); break; case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment floatToASIOSTInt32LSBXX<18>( GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(), (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize); break; case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment floatToASIOSTInt32LSBXX<20>( GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(), (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize); break; case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment floatToASIOSTInt32LSBXX<24>( GlobalAudioOutputDeviceAsioThisPtr->Channels[i]->Buffer(), (int32_t *)asioDriverInfo.bufferInfos[i].buffers[index], buffSize); break; case ASIOSTInt16MSB: throw AudioOutputException(String("ASIO Error: ASIOSTInt16MSB not yet supported! report to LinuxSampler developers.") ); break; case ASIOSTInt24MSB: // used for 20 bits as well throw AudioOutputException(String("ASIO Error: ASIOSTInt24MSB not yet supported! report to LinuxSampler developers.") ); break; case ASIOSTInt32MSB: throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB not yet supported! report to LinuxSampler developers.") ); break; case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on bigendian architecture throw AudioOutputException(String("ASIO Error: ASIOSTFloat32MSB not yet supported! report to LinuxSampler developers.") ); break; case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on bigendian architecture throw AudioOutputException(String("ASIO Error: ASIOSTFloat64MSB not yet supported! report to LinuxSampler developers.") ); break; // 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 ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB16 not yet supported! report to LinuxSampler developers.") ); break; case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB18 not yet supported! report to LinuxSampler developers.") ); break; case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB20 not yet supported! report to LinuxSampler developers.") ); break; case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment throw AudioOutputException(String("ASIO Error: ASIOSTInt32MSB24 not yet supported! report to LinuxSampler developers.") ); break; default: throw AudioOutputException(String("ASIO Error: unknown ASIOST sample format. report error.") ); break; } } // finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place if (asioDriverInfo.ASIOOutputReadySupported) ASIOOutputReady(); processedSamples += buffSize; return 0L; } void AudioOutputDeviceAsio::bufferSwitch(long index, ASIOBool processNow) { // the actual processing callback. // 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; bufferSwitchTimeInfo (&timeInfo, index, processNow); } 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. } long asioMessages(long selector, long value, void* message, double* opt) { dmsg(2,("asioMessages selector=%d value=%d\n",selector,value)); // 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, we don't necessarily have to support them || value == kAsioSupportsTimeInfo || value == kAsioSupportsTimeCode || value == kAsioSupportsInputMonitor) ret = 1L; 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. //GlobalAudioOutputDeviceAsioThisPtr->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; 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; } // *************** ParameterCard *************** // * AudioOutputDeviceAsio::ParameterCard::ParameterCard() : DeviceCreationParameterString() { InitWithDefault(); // use default card } AudioOutputDeviceAsio::ParameterCard::ParameterCard(String s) throw (Exception) : DeviceCreationParameterString(s) { } String AudioOutputDeviceAsio::ParameterCard::Description() { return "Sound card to be used"; } bool AudioOutputDeviceAsio::ParameterCard::Fix() { return true; } bool AudioOutputDeviceAsio::ParameterCard::Mandatory() { return false; } std::map AudioOutputDeviceAsio::ParameterCard::DependsAsParameters() { return std::map(); // no dependencies } optional AudioOutputDeviceAsio::ParameterCard::DefaultAsString(std::map Parameters) { std::vector cards = PossibilitiesAsString(Parameters); if (cards.empty()) throw Exception("AudioOutputDeviceAsio: Can't find any card"); dmsg(2,("AudioOutputDeviceAsio::ParameterCard::DefaultAsString='%s'\n",cards[0].c_str() )); // if currentAsioDriverName is empty then return the first card // otherwise return the currentAsioDriverName. this avoids closing the current ASIO driver // when the LSCP client calls commands like GET AUDIO_OUTPUT_DRIVER_PARAMETER INFO ASIO CARD // which would load the default card (without this check it would always be the same which would cause the above problem) if( currentAsioDriverName == "" ) { return cards[0]; // first card by default } else { return currentAsioDriverName; } } std::vector AudioOutputDeviceAsio::ParameterCard::PossibilitiesAsString(std::map Parameters) { dmsg(2,("AudioOutputDeviceAsio::ParameterCard::PossibilitiesAsString:\n")); return getAsioDriverNames(); } void AudioOutputDeviceAsio::ParameterCard::OnSetValue(String s) throw (Exception) { // not posssible, as parameter is fix } String AudioOutputDeviceAsio::ParameterCard::Name() { return "CARD"; } // *************** ParameterSampleRate *************** // * AudioOutputDeviceAsio::ParameterSampleRate::ParameterSampleRate() : AudioOutputDevice::ParameterSampleRate::ParameterSampleRate() { } AudioOutputDeviceAsio::ParameterSampleRate::ParameterSampleRate(String s) : AudioOutputDevice::ParameterSampleRate::ParameterSampleRate(s) { } std::map AudioOutputDeviceAsio::ParameterSampleRate::DependsAsParameters() { static ParameterCard card; std::map dependencies; dependencies[card.Name()] = &card; return dependencies; } optional AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt(std::map Parameters) { if (!Parameters.count("CARD")) { dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt returning optional::nothing (parameter CARD not supplied)\n")); return optional::nothing; } // return the default samplerate. first try 44100 then 48000, then the first samplerate found in the list ParameterCard card(Parameters["CARD"]); if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) { throw AudioOutputException(String("Error: ASIO Initalization Error") ); } for(int i = 0 ; i < asioDriverInfo.sampleRates.size() ; i++) { if(asioDriverInfo.sampleRates[i] == 44100) { return 44100; dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt returning 44100\n")); } if(asioDriverInfo.sampleRates[i] == 48000) { dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt returning 48000\n")); return 48000; } } dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::DefaultAsInt returning %d\n",asioDriverInfo.sampleRates[0])); return asioDriverInfo.sampleRates[0]; } std::vector AudioOutputDeviceAsio::ParameterSampleRate::PossibilitiesAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::PossibilitiesAsInt\n")); if (!Parameters.count("CARD")) { dmsg(2,("AudioOutputDeviceAsio::ParameterSampleRate::PossibilitiesAsInt returning empty vector (parameter CARD not supplied)\n")); return std::vector(); } ParameterCard card(Parameters["CARD"]); if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) { throw AudioOutputException(String("Error: ASIO Initalization Error") ); } for(int i=0;i AudioOutputDeviceAsio::ParameterChannels::DependsAsParameters() { static ParameterCard card; std::map dependencies; dependencies[card.Name()] = &card; return dependencies; } optional AudioOutputDeviceAsio::ParameterChannels::DefaultAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::DefaultAsInt\n")); if (!Parameters.count("CARD")) { dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::DefaultAsInt returning optional::nothing (CARD parameter not supplied)\n")); return optional::nothing; } ParameterCard card(Parameters["CARD"]); if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) { throw AudioOutputException(String("Error: ASIO Initialization Error") ); } dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::DefaultAsInt returning %d\n",asioDriverInfo.numOutputChannels)); return asioDriverInfo.numOutputChannels; } optional AudioOutputDeviceAsio::ParameterChannels::RangeMinAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMinAsInt!\n")); if (!Parameters.count("CARD")) { dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMinAsInt returning optional::nothing (CARD parameter not supplied)\n")); return optional::nothing; } dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMinAsInt returning 1\n")); return 1; } optional AudioOutputDeviceAsio::ParameterChannels::RangeMaxAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMaxAsInt!\n")); if (!Parameters.count("CARD")) { dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMaxAsInt returning optional::nothing (CARD parameter not supplied)\n")); return optional::nothing; } ParameterCard card(Parameters["CARD"]); if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) { throw AudioOutputException(String("Error: ASIO Init Error") ); } dmsg(2,("AudioOutputDeviceAsio::ParameterChannels::RangeMaxAsInt returning %d\n",asioDriverInfo.numOutputChannels)); return asioDriverInfo.numOutputChannels; } // *************** ParameterFragmentSize *************** // * AudioOutputDeviceAsio::ParameterFragmentSize::ParameterFragmentSize() : DeviceCreationParameterInt() { InitWithDefault(); } AudioOutputDeviceAsio::ParameterFragmentSize::ParameterFragmentSize(String s) throw (Exception) : DeviceCreationParameterInt(s) { } String AudioOutputDeviceAsio::ParameterFragmentSize::Description() { return "Size of each buffer fragment"; } bool AudioOutputDeviceAsio::ParameterFragmentSize::Fix() { return true; } bool AudioOutputDeviceAsio::ParameterFragmentSize::Mandatory() { return false; } std::map AudioOutputDeviceAsio::ParameterFragmentSize::DependsAsParameters() { static ParameterCard card; std::map dependencies; dependencies[card.Name()] = &card; return dependencies; } optional AudioOutputDeviceAsio::ParameterFragmentSize::DefaultAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::DefaultAsInt!\n")); if (!Parameters.count("CARD")) { dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::DefaultAsInt returning optional::nothing (no CARD parameter supplied\n")); return optional::nothing; } ParameterCard card(Parameters["CARD"]); if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) { throw AudioOutputException(String("Error: ASIO Initialization Error") ); } dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::DefaultAsInt returning %d\n",asioDriverInfo.preferredBufSize)); return asioDriverInfo.preferredBufSize; } optional AudioOutputDeviceAsio::ParameterFragmentSize::RangeMinAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMinAsInt!\n")); if (!Parameters.count("CARD")) { dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMinAsInt returning optional::nothing (no CARD parameter supplied\n")); return optional::nothing; } ParameterCard card(Parameters["CARD"]); if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) { throw AudioOutputException(String("Error: ASIO Initalization Error") ); } dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMinAsInt returning %d\n",asioDriverInfo.minBufSize)); return asioDriverInfo.minBufSize; } optional AudioOutputDeviceAsio::ParameterFragmentSize::RangeMaxAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMaxAsInt!\n")); if (!Parameters.count("CARD")) { dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMaxAsInt returning optional::nothing (no CARD parameter supplied\n")); return optional::nothing; } ParameterCard card(Parameters["CARD"]); if( ASIO_OpenAndQueryDeviceInfo((char *)card.ValueAsString().c_str(), &asioDriverInfo, &MyAsioDriverInfo) != ASE_OK) { throw AudioOutputException(String("Error: ASIO Initialization Error") ); } dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::RangeMaxAsInt returning %d\n",asioDriverInfo.maxBufSize)); return asioDriverInfo.maxBufSize; } std::vector AudioOutputDeviceAsio::ParameterFragmentSize::PossibilitiesAsInt(std::map Parameters) { dmsg(2,("AudioOutputDeviceAsio::ParameterFragmentSize::PossibilitiesAsInt!\n")); return std::vector(); } void AudioOutputDeviceAsio::ParameterFragmentSize::OnSetValue(int i) throw (Exception) { // not posssible, as parameter is fix } String AudioOutputDeviceAsio::ParameterFragmentSize::Name() { return "FRAGMENTSIZE"; } // *************** AudioOutputDeviceAsio *************** // * /** * Create and initialize Asio audio output device with given parameters. * * @param Parameters - optional parameters * @throws AudioOutputException if output device cannot be opened */ AudioOutputDeviceAsio::AudioOutputDeviceAsio(std::map Parameters) : AudioOutputDevice(Parameters) { if(AudioOutputDeviceAsioInstantiated) throw Exception("AudioOutputDeviceAsio: Sorry, only one ASIO card at time can be opened"); AudioOutputDeviceAsioInstantiated = true; asioDriverOpened = false; this->uiAsioChannels = ((DeviceCreationParameterInt*)Parameters["CHANNELS"])->ValueAsInt(); this->uiSamplerate = ((DeviceCreationParameterInt*)Parameters["SAMPLERATE"])->ValueAsInt(); this->FragmentSize = ((DeviceCreationParameterInt*)Parameters["FRAGMENTSIZE"])->ValueAsInt(); String Card = ((DeviceCreationParameterString*)Parameters["CARD"])->ValueAsString(); dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio constructor\n")); asioIsPlaying = false; ASIO_OpenAndQueryDeviceInfo((char *)Card.c_str(), &asioDriverInfo, &MyAsioDriverInfo); dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio: after ASIO_OpenAndQueryDeviceInfo\n")); if( ASIOSetSampleRate(uiSamplerate) != ASE_OK ) { throw AudioOutputException(String("Error: ASIOSetSampleRate: cannot set samplerate. ") ); } dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio: after ASIOSetSampleRate\n")); if(ASIOOutputReady() == ASE_OK) asioDriverInfo.ASIOOutputReadySupported = true; else asioDriverInfo.ASIOOutputReadySupported = false; dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio: after ASIOOutputReady\n")); asioDriverInfo.numInputBuffers = 0; asioDriverInfo.numOutputBuffers = uiAsioChannels; asioDriverInfo.chosenBufSize = FragmentSize; for( int i = 0 ; i < uiAsioChannels ; i++) { asioDriverInfo.bufferInfos[i].isInput = ASIOFalse; asioDriverInfo.bufferInfos[i].channelNum = i; } asioCallbacks.bufferSwitch = &bufferSwitch; asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.asioMessage = &asioMessages; asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo; if ( ASIOCreateBuffers(asioDriverInfo.bufferInfos, asioDriverInfo.numOutputBuffers, asioDriverInfo.chosenBufSize = FragmentSize, &asioCallbacks) != ASE_OK ){ throw AudioOutputException(String("AudioOutputDeviceAsio: Error: ASIOCreateBuffers failed.") ); } dmsg(2,("AudioOutputDeviceAsio::AudioOutputDeviceAsio: after ASIOCreateBuffers\n")); // create audio channels for this audio device to which the sampler engines can write to for (int i = 0; i < uiAsioChannels; i++) this->Channels.push_back(new AudioChannel(i, FragmentSize)); // FIXME: temporary global variable used to store the this pointer for the ASIO callbacks wanting to access the AudioOutputDeviceAsio methods GlobalAudioOutputDeviceAsioThisPtr = this; if (((DeviceCreationParameterBool*)Parameters["ACTIVE"])->ValueAsBool()) { Play(); } } AudioOutputDeviceAsio::~AudioOutputDeviceAsio() { ASIOExit(); asioDriverOpened = false; AudioOutputDeviceAsioInstantiated = false; } void AudioOutputDeviceAsio::Play() { dmsg(2,("AudioOutputDeviceAsio::Play() !\n")); if ( ASIOStart() != ASE_OK ){ asioIsPlaying = false; throw AudioOutputException(String("AudioOutputDeviceAsio: Error: ASIOStart failed.") ); } else asioIsPlaying = true; } bool AudioOutputDeviceAsio::IsPlaying() { return asioIsPlaying; } void AudioOutputDeviceAsio::Stop() { dmsg(2,("AudioOutputDeviceAsio::Stop() !\n")); ASIOStop(); asioIsPlaying = false; } AudioChannel* AudioOutputDeviceAsio::CreateChannel(uint ChannelNr) { dmsg(2,("AudioOutputDeviceAsio::CreateChannel value=%d uiAsioChannels=%d\n",ChannelNr,uiAsioChannels)); // just create a mix channel return new AudioChannel(ChannelNr, Channel(ChannelNr % uiAsioChannels)); } uint AudioOutputDeviceAsio::MaxSamplesPerCycle() { dmsg(2,("AudioOutputDeviceAsio::MaxSamplesPerCycle value=%d\n",FragmentSize)); return FragmentSize; } uint AudioOutputDeviceAsio::SampleRate() { dmsg(2,("AudioOutputDeviceAsio::SampleRate value=%d\n",uiSamplerate)); fflush(stdout); return uiSamplerate; } String AudioOutputDeviceAsio::Name() { return "ASIO"; } String AudioOutputDeviceAsio::Driver() { return Name(); } String AudioOutputDeviceAsio::Description() { return "Audio Streaming Input Output 2.2"; } String AudioOutputDeviceAsio::Version() { String s = "$Revision: 1.4 $"; return s.substr(11, s.size() - 13); // cut dollar signs, spaces and CVS macro keyword } } // namespace LinuxSampler