/******************************************************************************\ * Copyright (c) 2004-2011 * * Author(s): * Volker Fischer * * Description: * Sound card interface for Windows operating systems * ****************************************************************************** * * 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 "Sound.h" /* Implementation *************************************************************/ // external references extern AsioDrivers* asioDrivers; bool loadAsioDriver ( char *name ); // pointer to our sound object CSound* pSound; /******************************************************************************\ * Common * \******************************************************************************/ QString CSound::SetDev ( const int iNewDev ) { QString strReturn = ""; // init with no error bool bTryLoadAnyDriver = false; // check if an ASIO driver was already initialized if ( lCurDev >= 0 ) { // a device was already been initialized and is used, first clean up // dispose ASIO buffers ASIODisposeBuffers(); // remove old driver ASIOExit(); asioDrivers->removeCurrentDriver(); const QString strErrorMessage = LoadAndInitializeDriver ( iNewDev ); if ( !strErrorMessage.isEmpty() ) { if ( iNewDev != lCurDev ) { // loading and initializing the new driver failed, go back to // original driver and display error message LoadAndInitializeDriver ( lCurDev ); } else { // the same driver is used but the driver properties seems to // have changed so that they are not compatible to our // software anymore QMessageBox::critical ( 0, APP_NAME, QString ( tr ( "The audio driver properties " "have changed to a state which is incompatible to this " "software. The selected audio device could not be used " "because of the following error: " ) ) + strErrorMessage + QString ( tr ( "

Please restart the software." ) ), "Close", 0 ); _exit ( 0 ); } // store error return message strReturn = strErrorMessage; } Init ( iASIOBufferSizeStereo ); } else { if ( iNewDev != INVALID_SNC_CARD_DEVICE ) { // This is the first time a driver is to be initialized, we first // try to load the selected driver, if this fails, we try to load // the first available driver in the system. If this fails, too, we // throw an error that no driver is available -> it does not make // sense to start the llcon software if no audio hardware is // available. if ( !LoadAndInitializeDriver ( iNewDev ).isEmpty() ) { // loading and initializing the new driver failed, try to find // at least one usable driver bTryLoadAnyDriver = true; } } else { // try to find one usable driver (select the first valid driver) bTryLoadAnyDriver = true; } } if ( bTryLoadAnyDriver ) { // try to load and initialize any valid driver QVector vsErrorList = LoadAndInitializeFirstValidDriver(); if ( !vsErrorList.isEmpty() ) { // create error message with all details QString sErrorMessage = tr ( "No usable ASIO audio device " "(driver) found.

" "In the following there is a list of all available drivers " "with the associated error message:"; throw CGenErr ( sErrorMessage ); } } return strReturn; } QVector CSound::LoadAndInitializeFirstValidDriver() { QVector vsErrorList; // load and initialize first valid ASIO driver bool bValidDriverDetected = false; int iCurDriverIdx = 0; // try all available drivers in the system ("lNumDevs" devices) while ( !bValidDriverDetected && ( iCurDriverIdx < lNumDevs ) ) { // try to load and initialize current driver, store error message const QString strCurError = LoadAndInitializeDriver ( iCurDriverIdx ); vsErrorList.append ( strCurError ); if ( strCurError.isEmpty() ) { // initialization was successful bValidDriverDetected = true; // store ID of selected driver lCurDev = iCurDriverIdx; // empty error list shows that init was successful vsErrorList.clear(); } // try next driver iCurDriverIdx++; } return vsErrorList; } QString CSound::LoadAndInitializeDriver ( int iDriverIdx ) { // first check and correct input parameter if ( iDriverIdx >= lNumDevs ) { // we assume here that at least one driver is in the system iDriverIdx = 0; } // load driver loadAsioDriver ( cDriverNames[iDriverIdx] ); if ( ASIOInit ( &driverInfo ) != ASE_OK ) { // clean up and return error string asioDrivers->removeCurrentDriver(); return tr ( "The audio driver could not be initialized." ); } // check device capabilities if it fullfills our requirements const QString strStat = CheckDeviceCapabilities(); // store ID of selected driver if initialization was successful if ( strStat.isEmpty() ) { lCurDev = iDriverIdx; } else { // driver cannot be used, clean up asioDrivers->removeCurrentDriver(); } return strStat; } QString CSound::CheckDeviceCapabilities() { // This function checks if our required input/output channel // properties are supported by the selected device. If the return // string is empty, the device can be used, otherwise the error // message is returned. // check the sample rate const ASIOError CanSaRateReturn = ASIOCanSampleRate ( SYSTEM_SAMPLE_RATE_HZ ); if ( ( CanSaRateReturn == ASE_NoClock ) || ( CanSaRateReturn == ASE_NotPresent ) ) { // return error string return tr ( "The audio device does not support the " "required sample rate. The required sample rate is: " ) + QString().setNum ( SYSTEM_SAMPLE_RATE_HZ ) + " Hz"; } // check the number of available channels ASIOGetChannels ( &lNumInChan, &lNumOutChan ); if ( ( lNumInChan < NUM_IN_OUT_CHANNELS ) || ( lNumOutChan < NUM_IN_OUT_CHANNELS ) ) { // return error string return tr ( "The audio device does not support the " "required number of channels. The required number of channels " "for input and output is: " ) + QString().setNum ( NUM_IN_OUT_CHANNELS ); } // clip number of input/output channels to our maximum if ( lNumInChan > MAX_NUM_IN_OUT_CHANNELS ) { lNumInChan = MAX_NUM_IN_OUT_CHANNELS; } if ( lNumOutChan > MAX_NUM_IN_OUT_CHANNELS ) { lNumOutChan = MAX_NUM_IN_OUT_CHANNELS; } // query channel infos for all available input channels for ( int i = 0; i < lNumInChan; i++ ) { // setup for input channels channelInfosInput[i].isInput = ASIOTrue; channelInfosInput[i].channel = i; ASIOGetChannelInfo ( &channelInfosInput[i] ); // Check supported sample formats. // Actually, it would be enough to have at least two channels which // support the required sample format. But since we have support for // all known sample types, the following check should always pass and // therefore we throw the error message on any channel which does not // fullfill the sample format requirement (quick hack solution). if ( !CheckSampleTypeSupported ( channelInfosInput[i].type ) ) { // return error string return tr ( "Required audio sample format not available." ); } } // query channel infos for all available output channels for ( int i = 0; i < lNumOutChan; i++ ) { // setup for output channels channelInfosOutput[i].isInput = ASIOFalse; channelInfosOutput[i].channel = i; ASIOGetChannelInfo ( &channelInfosOutput[i] ); // Check supported sample formats. // Actually, it would be enough to have at least two channels which // support the required sample format. But since we have support for // all known sample types, the following check should always pass and // therefore we throw the error message on any channel which does not // fullfill the sample format requirement (quick hack solution). if ( !CheckSampleTypeSupported ( channelInfosOutput[i].type ) ) { // return error string return tr ( "Required audio sample format not available." ); } } // the device has changed, per definition we reset the channel // mapping to the defaults (first two available channels) ResetChannelMapping(); // everything is ok, return empty string for "no error" case return ""; } void CSound::SetLeftInputChannel ( const int iNewChan ) { // apply parameter after input parameter check if ( ( iNewChan >= 0 ) && ( iNewChan < lNumInChan ) && ( iNewChan != vSelectedInputChannels[1] ) ) { vSelectedInputChannels[0] = iNewChan; } } void CSound::SetRightInputChannel ( const int iNewChan ) { // apply parameter after input parameter check if ( ( iNewChan >= 0 ) && ( iNewChan < lNumInChan ) && ( iNewChan != vSelectedInputChannels[0] ) ) { vSelectedInputChannels[1] = iNewChan; } } void CSound::SetLeftOutputChannel ( const int iNewChan ) { // apply parameter after input parameter check if ( ( iNewChan >= 0 ) && ( iNewChan < lNumOutChan ) && ( iNewChan != vSelectedOutputChannels[1] ) ) { vSelectedOutputChannels[0] = iNewChan; } } void CSound::SetRightOutputChannel ( const int iNewChan ) { // apply parameter after input parameter check if ( ( iNewChan >= 0 ) && ( iNewChan < lNumOutChan ) && ( iNewChan != vSelectedOutputChannels[0] ) ) { vSelectedOutputChannels[1] = iNewChan; } } int CSound::GetActualBufferSize ( const int iDesiredBufferSizeMono ) { int iActualBufferSizeMono; // query the usable buffer sizes ASIOGetBufferSize ( &HWBufferInfo.lMinSize, &HWBufferInfo.lMaxSize, &HWBufferInfo.lPreferredSize, &HWBufferInfo.lGranularity ); // calculate "nearest" buffer size and set internal parameter accordingly // first check minimum and maximum values if ( iDesiredBufferSizeMono <= HWBufferInfo.lMinSize ) { iActualBufferSizeMono = HWBufferInfo.lMinSize; } else { if ( iDesiredBufferSizeMono >= HWBufferInfo.lMaxSize ) { iActualBufferSizeMono = HWBufferInfo.lMaxSize; } else { // ASIO SDK 2.2: "Notes: When minimum and maximum buffer size are // equal, the preferred buffer size has to be the same value as // well; granularity should be 0 in this case." if ( HWBufferInfo.lMinSize == HWBufferInfo.lMaxSize ) { iActualBufferSizeMono = HWBufferInfo.lMinSize; } else { if ( ( HWBufferInfo.lGranularity < -1 ) || ( HWBufferInfo.lGranularity == 0 ) ) { // Special case (seen for EMU audio cards): granularity is // zero or less than zero (make sure to exclude the special // case of -1). // There is no definition of this case in the ASIO SDK // document. We assume here that all buffer sizes in between // minimum and maximum buffer sizes are allowed. iActualBufferSizeMono = iDesiredBufferSizeMono; } else { // General case -------------------------------------------- // initialization int iTrialBufSize = HWBufferInfo.lMinSize; int iLastTrialBufSize = HWBufferInfo.lMinSize; bool bSizeFound = false; // test loop while ( ( iTrialBufSize <= HWBufferInfo.lMaxSize ) && ( !bSizeFound ) ) { if ( iTrialBufSize >= iDesiredBufferSizeMono ) { // test which buffer size fits better: the old one or the // current one if ( ( iTrialBufSize - iDesiredBufferSizeMono ) > ( iDesiredBufferSizeMono - iLastTrialBufSize ) ) { iTrialBufSize = iLastTrialBufSize; } // exit while loop bSizeFound = true; } if ( !bSizeFound ) { // store old trial buffer size iLastTrialBufSize = iTrialBufSize; // increment trial buffer size (check for special // case first) if ( HWBufferInfo.lGranularity == -1 ) { // special case: buffer sizes are a power of 2 iTrialBufSize *= 2; } else { iTrialBufSize += HWBufferInfo.lGranularity; } } } // clip trial buffer size (it may happen in the while // routine that "iTrialBufSize" is larger than "lMaxSize" in // case "lMaxSize - lMinSize" is not divisible by the // granularity) if ( iTrialBufSize > HWBufferInfo.lMaxSize ) { iTrialBufSize = HWBufferInfo.lMaxSize; } // set ASIO buffer size iActualBufferSizeMono = iTrialBufSize; } } } } return iActualBufferSizeMono; } int CSound::Init ( const int iNewPrefMonoBufferSize ) { ASIOMutex.lock(); // get mutex lock { // get the actual sound card buffer size which is supported // by the audio hardware iASIOBufferSizeMono = GetActualBufferSize ( iNewPrefMonoBufferSize ); // init base class CSoundBase::Init ( iASIOBufferSizeMono ); // set internal buffer size value and calculate stereo buffer size iASIOBufferSizeStereo = 2 * iASIOBufferSizeMono; // set the sample rate ASIOSetSampleRate ( SYSTEM_SAMPLE_RATE_HZ ); // create memory for intermediate audio buffer vecsTmpAudioSndCrdStereo.Init ( iASIOBufferSizeStereo ); // create and activate ASIO buffers (buffer size in samples), // dispose old buffers (if any) ASIODisposeBuffers(); // init buffer infos, we always want to have two input and // two output channels for ( int i = 0; i < NUM_IN_OUT_CHANNELS; i++ ) { // prepare input channels bufferInfos[i].isInput = ASIOTrue; bufferInfos[i].channelNum = vSelectedInputChannels[i]; bufferInfos[i].buffers[0] = 0; bufferInfos[i].buffers[1] = 0; // prepare output channels bufferInfos[NUM_IN_OUT_CHANNELS + i].isInput = ASIOFalse; bufferInfos[NUM_IN_OUT_CHANNELS + i].channelNum = vSelectedOutputChannels[i]; bufferInfos[NUM_IN_OUT_CHANNELS + i].buffers[0] = 0; bufferInfos[NUM_IN_OUT_CHANNELS + i].buffers[1] = 0; } ASIOCreateBuffers ( bufferInfos, 2 /* in/out */ * NUM_IN_OUT_CHANNELS /* stereo */, iASIOBufferSizeMono, &asioCallbacks ); // check wether the driver requires the ASIOOutputReady() optimization // (can be used by the driver to reduce output latency by one block) bASIOPostOutput = ( ASIOOutputReady() == ASE_OK ); } ASIOMutex.unlock(); return iASIOBufferSizeMono; } void CSound::Start() { // start audio ASIOStart(); // call base class CSoundBase::Start(); } void CSound::Stop() { // stop audio ASIOStop(); // call base class CSoundBase::Stop(); } CSound::CSound ( void (*fpNewCallback) ( CVector& psData, void* arg ), void* arg ) : CSoundBase ( true, fpNewCallback, arg ), vSelectedInputChannels ( NUM_IN_OUT_CHANNELS ), vSelectedOutputChannels ( NUM_IN_OUT_CHANNELS ), lNumInChan ( 0 ), lNumOutChan ( 0 ) { int i; // init pointer to our sound object pSound = this; // get available ASIO driver names in system for ( i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ ) { cDriverNames[i] = new char[32]; } loadAsioDriver ( "dummy" ); // to initialize external object lNumDevs = asioDrivers->getDriverNames ( cDriverNames, MAX_NUMBER_SOUND_CARDS ); // in case we do not have a driver available, throw error if ( lNumDevs == 0 ) { throw CGenErr ( tr ( "No ASIO audio device (driver) found.

" "The " ) + APP_NAME + tr ( " software requires the low latency audio " "interface ASIO to work properly. This is no standard " "Windows audio interface and therefore a special audio driver is " "required. Either your sound card has a native ASIO driver (which " "is recommended) or you might want to use alternative drivers like " "the ASIO4All or kX driver." ) ); } asioDrivers->removeCurrentDriver(); // init device index with illegal value to show that driver is not initialized lCurDev = -1; // init channel mapping ResetChannelMapping(); // set up the asioCallback structure asioCallbacks.bufferSwitch = &bufferSwitch; asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.asioMessage = &asioMessages; asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo; } void CSound::ResetChannelMapping() { // init selected channel numbers with defaults: use first available // channels for input and output vSelectedInputChannels[0] = 0; vSelectedInputChannels[1] = 1; vSelectedOutputChannels[0] = 0; vSelectedOutputChannels[1] = 1; } CSound::~CSound() { // cleanup ASIO stuff ASIOStop(); ASIODisposeBuffers(); ASIOExit(); asioDrivers->removeCurrentDriver(); } // ASIO callbacks ------------------------------------------------------------- ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime*, long index, ASIOBool processNow ) { bufferSwitch ( index, processNow ); return 0L; } bool CSound::CheckSampleTypeSupported ( const ASIOSampleType SamType ) { // check for supported sample types return ( ( SamType == ASIOSTInt16LSB ) || ( SamType == ASIOSTInt24LSB ) || ( SamType == ASIOSTInt32LSB ) || ( SamType == ASIOSTFloat32LSB ) || ( SamType == ASIOSTFloat64LSB ) || ( SamType == ASIOSTInt32LSB16 ) || ( SamType == ASIOSTInt32LSB18 ) || ( SamType == ASIOSTInt32LSB20 ) || ( SamType == ASIOSTInt32LSB24 ) || ( SamType == ASIOSTInt16MSB ) || ( SamType == ASIOSTInt24MSB ) || ( SamType == ASIOSTInt32MSB ) || ( SamType == ASIOSTFloat32MSB ) || ( SamType == ASIOSTFloat64MSB ) || ( SamType == ASIOSTInt32MSB16 ) || ( SamType == ASIOSTInt32MSB18 ) || ( SamType == ASIOSTInt32MSB20 ) || ( SamType == ASIOSTInt32MSB24 ) ); } void CSound::bufferSwitch ( long index, ASIOBool ) { int iCurSample; // perform the processing for input and output pSound->ASIOMutex.lock(); // get mutex lock { // CAPTURE ------------------------------------------------------------- for ( int i = 0; i < NUM_IN_OUT_CHANNELS; i++ ) { const int iInChNum = i; // copy new captured block in thread transfer buffer (copy // mono data interleaved in stereo buffer) switch ( pSound->channelInfosInput[pSound->vSelectedInputChannels[i]].type ) { case ASIOSTInt16LSB: // no type conversion required, just copy operation for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample]; } break; case ASIOSTInt24LSB: // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { int iCurSam = 0; memcpy ( &iCurSam, ( (char*) pSound->bufferInfos[i].buffers[index] ) + iCurSample * 3, 3 ); iCurSam >>= 8; pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( iCurSam ); } break; case ASIOSTInt32LSB: // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] >> 16 ); } break; case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] * _MAXSHORT ); } break; case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] * _MAXSHORT ); } break; case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] & 0xFFFF ); } break; case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] & 0x3FFFF ) >> 2 ); } break; case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] & 0xFFFFF ) >> 4 ); } break; case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] & 0xFFFFFF ) >> 8 ); } break; case ASIOSTInt16MSB: // NOT YET TESTED // flip bits for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = Flip16Bits ( ( static_cast ( pSound->bufferInfos[i].buffers[index] ) )[iCurSample] ); } break; case ASIOSTInt24MSB: // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // because the bits are flipped, we do not have to perform the // shift by 8 bits int iCurSam = 0; memcpy ( &iCurSam, ( (char*) pSound->bufferInfos[i].buffers[index] ) + iCurSample * 3, 3 ); pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = Flip16Bits ( static_cast ( iCurSam ) ); } break; case ASIOSTInt32MSB: // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // flip bits and convert to 16 bit pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( Flip32Bits ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] ) >> 16 ); } break; case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( static_cast ( Flip32Bits ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] ) ) * _MAXSHORT ); } break; case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( static_cast ( Flip64Bits ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] ) ) * _MAXSHORT ); } break; case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( Flip32Bits ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] ) & 0xFFFF ); } break; case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( ( Flip32Bits ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] ) & 0x3FFFF ) >> 2 ); } break; case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( ( Flip32Bits ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] ) & 0xFFFFF ) >> 4 ); } break; case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] = static_cast ( ( Flip32Bits ( static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] ) & 0xFFFFFF ) >> 8 ); } break; } } // call processing callback function pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo ); // PLAYBACK ------------------------------------------------------------ for ( int i = NUM_IN_OUT_CHANNELS; i < 2 * NUM_IN_OUT_CHANNELS; i++ ) { const int iOutChNum = i - NUM_IN_OUT_CHANNELS; // copy data from sound card in output buffer (copy // interleaved stereo data in mono sound card buffer) switch ( pSound->channelInfosOutput[pSound->vSelectedOutputChannels[iOutChNum]].type ) { case ASIOSTInt16LSB: // no type conversion required, just copy operation for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum]; } break; case ASIOSTInt24LSB: // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert current sample in 24 bit format int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); iCurSam <<= 8; memcpy ( ( (char*) pSound->bufferInfos[i].buffers[index] ) + iCurSample * 3, &iCurSam, 3 ); } break; case ASIOSTInt32LSB: // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit const int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = ( iCurSam << 16 ); } break; case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { const float fCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = fCurSam / _MAXSHORT; } break; case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { const double fCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = fCurSam / _MAXSHORT; } break; case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit const int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = iCurSam; } break; case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit const int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = ( iCurSam << 2 ); } break; case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit const int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = ( iCurSam << 4 ); } break; case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit const int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = ( iCurSam << 8 ); } break; case ASIOSTInt16MSB: // NOT YET TESTED // flip bits for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { ( (int16_t*) pSound->bufferInfos[i].buffers[index] )[iCurSample] = Flip16Bits ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); } break; case ASIOSTInt24MSB: // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // because the bits are flipped, we do not have to perform the // shift by 8 bits int32_t iCurSam = static_cast ( Flip16Bits ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ) ); memcpy ( ( (char*) pSound->bufferInfos[i].buffers[index] ) + iCurSample * 3, &iCurSam, 3 ); } break; case ASIOSTInt32MSB: // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit and flip bits int iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam << 16 ); } break; case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { const float fCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = static_cast ( Flip32Bits ( static_cast ( fCurSam / _MAXSHORT ) ) ); } break; case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { const double fCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = static_cast ( Flip64Bits ( static_cast ( fCurSam / _MAXSHORT ) ) ); } break; case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit const int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam ); } break; case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit const int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam << 2 ); } break; case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit const int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam << 4 ); } break; case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment // NOT YET TESTED for ( iCurSample = 0; iCurSample < pSound->iASIOBufferSizeMono; iCurSample++ ) { // convert to 32 bit const int32_t iCurSam = static_cast ( pSound->vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ); static_cast ( pSound->bufferInfos[i].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam << 8 ); } break; } } // finally if the driver supports the ASIOOutputReady() optimization, // do it here, all data are in place ----------------------------------- if ( pSound->bASIOPostOutput ) { ASIOOutputReady(); } } pSound->ASIOMutex.unlock(); } long CSound::asioMessages ( long selector, long, void*, double* ) { long ret = 0; switch ( selector ) { case kAsioEngineVersion: // return the supported ASIO version of the host application ret = 2L; // Host ASIO implementation version, 2 or higher break; // both messages might be send if the buffer size changes case kAsioBufferSizeChange: case kAsioResetRequest: pSound->EmitReinitRequestSignal(); ret = 1L; // 1L if request is accepted or 0 otherwise break; } return ret; } int16_t CSound::Flip16Bits ( const int16_t iIn ) { uint16_t iMask = ( 1 << 15 ); int16_t iOut = 0; for ( unsigned int i = 0; i < 16; i++ ) { // copy current bit to correct position iOut |= ( iIn & iMask ) ? 1 : 0; // shift out value and mask by one bit iOut <<= 1; iMask >>= 1; } return iOut; } int32_t CSound::Flip32Bits ( const int32_t iIn ) { uint32_t iMask = ( static_cast ( 1 ) << 31 ); int32_t iOut = 0; for ( unsigned int i = 0; i < 32; i++ ) { // copy current bit to correct position iOut |= ( iIn & iMask ) ? 1 : 0; // shift out value and mask by one bit iOut <<= 1; iMask >>= 1; } return iOut; } int64_t CSound::Flip64Bits ( const int64_t iIn ) { uint64_t iMask = ( static_cast ( 1 ) << 63 ); int64_t iOut = 0; for ( unsigned int i = 0; i < 64; i++ ) { // copy current bit to correct position iOut |= ( iIn & iMask ) ? 1 : 0; // shift out value and mask by one bit iOut <<= 1; iMask >>= 1; } return iOut; }