From 73f408e40111aaf0fda303fdccda429a659a1adb Mon Sep 17 00:00:00 2001 From: Volker Fischer Date: Tue, 27 Dec 2011 16:50:38 +0000 Subject: [PATCH] make sure that sound working thread is actually killed on stopping the audio interface --- windows/sound.cpp | 2085 +++++++++++++++++++++++---------------------- 1 file changed, 1046 insertions(+), 1039 deletions(-) diff --git a/windows/sound.cpp b/windows/sound.cpp index 5e2e00df..e76dd22d 100755 --- a/windows/sound.cpp +++ b/windows/sound.cpp @@ -1,1039 +1,1046 @@ -/******************************************************************************\ - * 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::LoadAndInitializeDriver ( int iDriverIdx ) -{ - // 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(); - - // check if device is capable - if ( strStat.isEmpty() ) - { - // the device has changed, per definition we reset the channel - // mapping to the defaults (first two available channels) - ResetChannelMapping(); - - // store ID of selected driver if initialization was successful - lCurDev = iDriverIdx; - } - else - { - // driver cannot be used, clean up - asioDrivers->removeCurrentDriver(); - } - - return strStat; -} - -void CSound::UnloadCurrentDriver() -{ - // clean up ASIO stuff - ASIOStop(); - ASIODisposeBuffers(); - ASIOExit(); - asioDrivers->removeCurrentDriver(); -} - -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." ); - } - } - - // 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 ( "ASIO", 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++ ) - { - // allocate memory for driver names - 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(); - - // copy driver names to base class but internally we still have to use - // the char* variable because of the ASIO API :-( - for ( i = 0; i < lNumDevs; i++ ) - { - strDriverNames[i] = cDriverNames[i]; - } - - // init device index as not initialized (invalid) - lCurDev = INVALID_SNC_CARD_DEVICE; - - // 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; -} - - -// 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: - - -// TODO implement separate ASIO reset function in base class! - -// kAsioBufferSizeChange: -// Informs the application that the driver has a new preferred buffer size. -// -// kAsioResetRequest: -// Requests a driver reset. If accepted, this will close the driver -// (ASIOExit() ) and re-open it again (ASIOInit() etc.) at the next -// "safe" time (usually during the application IDLE time). Some -// drivers need to reconfigure for instance when the sample rate -// changes, or some basic changes have been made in ASIOControlPanel(). - - - 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; -} +/******************************************************************************\ + * 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::LoadAndInitializeDriver ( int iDriverIdx ) +{ + // 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(); + + // check if device is capable + if ( strStat.isEmpty() ) + { + // the device has changed, per definition we reset the channel + // mapping to the defaults (first two available channels) + ResetChannelMapping(); + + // store ID of selected driver if initialization was successful + lCurDev = iDriverIdx; + } + else + { + // driver cannot be used, clean up + asioDrivers->removeCurrentDriver(); + } + + return strStat; +} + +void CSound::UnloadCurrentDriver() +{ + // clean up ASIO stuff + ASIOStop(); + ASIODisposeBuffers(); + ASIOExit(); + asioDrivers->removeCurrentDriver(); +} + +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." ); + } + } + + // 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(); + + // make sure the working thread is actually done + // (by checking the locked state) + if ( ASIOMutex.tryLock ( 5000 ) ) + { + ASIOMutex.unlock(); + } +} + +CSound::CSound ( void (*fpNewCallback) ( CVector& psData, void* arg ), void* arg ) : + CSoundBase ( "ASIO", 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++ ) + { + // allocate memory for driver names + 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(); + + // copy driver names to base class but internally we still have to use + // the char* variable because of the ASIO API :-( + for ( i = 0; i < lNumDevs; i++ ) + { + strDriverNames[i] = cDriverNames[i]; + } + + // init device index as not initialized (invalid) + lCurDev = INVALID_SNC_CARD_DEVICE; + + // 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; +} + + +// 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: + + +// TODO implement separate ASIO reset function in base class! + +// kAsioBufferSizeChange: +// Informs the application that the driver has a new preferred buffer size. +// +// kAsioResetRequest: +// Requests a driver reset. If accepted, this will close the driver +// (ASIOExit() ) and re-open it again (ASIOInit() etc.) at the next +// "safe" time (usually during the application IDLE time). Some +// drivers need to reconfigure for instance when the sample rate +// changes, or some basic changes have been made in ASIOControlPanel(). + + + 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; +}