2011-12-27 17:50:38 +01:00
|
|
|
/******************************************************************************\
|
2013-01-23 11:41:13 +01:00
|
|
|
* Copyright (c) 2004-2013
|
2011-12-27 17:50:38 +01:00
|
|
|
*
|
|
|
|
* 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;
|
2012-01-22 14:46:15 +01:00
|
|
|
bool loadAsioDriver ( char* name );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
// 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 );
|
|
|
|
|
2013-12-17 21:52:38 +01:00
|
|
|
// query the latency of the driver
|
|
|
|
long lInputLatency = 0;
|
|
|
|
long lOutputLatency = 0;
|
|
|
|
|
|
|
|
if ( ASIOGetLatencies ( &lInputLatency, &lOutputLatency ) != ASE_NotPresent )
|
|
|
|
{
|
|
|
|
// add the input and output latencies (returned in number of
|
|
|
|
// samples) and calculate the time in ms
|
|
|
|
dInOutLatencyMs =
|
2013-12-17 21:55:35 +01:00
|
|
|
( static_cast<double> ( lInputLatency ) + lOutputLatency ) *
|
|
|
|
1000 / SYSTEM_SAMPLE_RATE_HZ;
|
2013-12-17 21:52:38 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// no latency available
|
|
|
|
dInOutLatencyMs = 0.0;
|
|
|
|
}
|
|
|
|
|
2011-12-27 17:50:38 +01:00
|
|
|
// 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<int16_t>& psData, void* arg ), void* arg ) :
|
|
|
|
CSoundBase ( "ASIO", true, fpNewCallback, arg ),
|
|
|
|
vSelectedInputChannels ( NUM_IN_OUT_CHANNELS ),
|
|
|
|
vSelectedOutputChannels ( NUM_IN_OUT_CHANNELS ),
|
|
|
|
lNumInChan ( 0 ),
|
2013-12-17 21:52:38 +01:00
|
|
|
lNumOutChan ( 0 ),
|
|
|
|
dInOutLatencyMs ( 0.0 ) // "0.0" means that no latency value is available
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
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 ( "<b>No ASIO audio device (driver) found.</b><br><br>"
|
|
|
|
"The " ) + APP_NAME + tr ( " software requires the low latency audio "
|
|
|
|
"interface <b>ASIO</b> 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;
|
|
|
|
|
2013-12-14 23:16:20 +01:00
|
|
|
// get references to class members
|
|
|
|
int& iASIOBufferSizeMono = pSound->iASIOBufferSizeMono;
|
|
|
|
CVector<int16_t>& vecsTmpAudioSndCrdStereo = pSound->vecsTmpAudioSndCrdStereo;
|
|
|
|
|
2011-12-27 17:50:38 +01:00
|
|
|
// 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:
|
2013-12-14 23:16:20 +01:00
|
|
|
{
|
2011-12-27 17:50:38 +01:00
|
|
|
// no type conversion required, just copy operation
|
2013-12-14 23:16:20 +01:00
|
|
|
int16_t* pASIOBuf =
|
|
|
|
static_cast<int16_t*> ( pSound->bufferInfos[i].buffers[index] );
|
|
|
|
|
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
|
|
|
pASIOBuf[iCurSample];
|
2011-12-27 17:50:38 +01:00
|
|
|
}
|
|
|
|
break;
|
2013-12-14 23:16:20 +01:00
|
|
|
}
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
case ASIOSTInt24LSB:
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
int iCurSam = 0;
|
|
|
|
memcpy ( &iCurSam, ( (char*) pSound->bufferInfos[i].buffers[index] ) + iCurSample * 3, 3 );
|
|
|
|
iCurSam >>= 8;
|
|
|
|
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( iCurSam );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB:
|
2013-12-14 23:16:20 +01:00
|
|
|
{
|
|
|
|
int32_t* pASIOBuf =
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] );
|
|
|
|
|
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
|
|
|
static_cast<int16_t> ( pASIOBuf[iCurSample] >> 16 );
|
2011-12-27 17:50:38 +01:00
|
|
|
}
|
|
|
|
break;
|
2013-12-14 23:16:20 +01:00
|
|
|
}
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( static_cast<float*> (
|
|
|
|
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
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( static_cast<double*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] * _MAXSHORT );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( static_cast<int32_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] & 0xFFFF );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( ( static_cast<int32_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] & 0x3FFFF ) >> 2 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( ( static_cast<int32_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] & 0xFFFFF ) >> 4 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( ( static_cast<int32_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] & 0xFFFFFF ) >> 8 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt16MSB:
|
|
|
|
// NOT YET TESTED
|
|
|
|
// flip bits
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
Flip16Bits ( ( static_cast<int16_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] ) )[iCurSample] );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt24MSB:
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// 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 );
|
|
|
|
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
Flip16Bits ( static_cast<int16_t> ( iCurSam ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB:
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// flip bits and convert to 16 bit
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( Flip32Bits ( static_cast<int32_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] ) >> 16 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( static_cast<float> (
|
|
|
|
Flip32Bits ( static_cast<int32_t*> (
|
|
|
|
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
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( static_cast<double> (
|
|
|
|
Flip64Bits ( static_cast<int64_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] ) ) * _MAXSHORT );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( Flip32Bits ( static_cast<int32_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] ) & 0xFFFF );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( ( Flip32Bits ( static_cast<int32_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] ) & 0x3FFFF ) >> 2 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( ( Flip32Bits ( static_cast<int32_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] ) & 0xFFFFF ) >> 4 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iInChNum] =
|
2011-12-27 17:50:38 +01:00
|
|
|
static_cast<int16_t> ( ( Flip32Bits ( static_cast<int32_t*> (
|
|
|
|
pSound->bufferInfos[i].buffers[index] )[iCurSample] ) & 0xFFFFFF ) >> 8 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// call processing callback function
|
2013-12-14 23:16:20 +01:00
|
|
|
pSound->ProcessCallback ( vecsTmpAudioSndCrdStereo );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
// 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:
|
2013-12-14 23:16:20 +01:00
|
|
|
{
|
2011-12-27 17:50:38 +01:00
|
|
|
// no type conversion required, just copy operation
|
2013-12-14 23:16:20 +01:00
|
|
|
int16_t* pASIOBuf =
|
|
|
|
static_cast<int16_t*> ( pSound->bufferInfos[i].buffers[index] );
|
|
|
|
|
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
2013-12-14 23:16:20 +01:00
|
|
|
pASIOBuf[iCurSample] =
|
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum];
|
2011-12-27 17:50:38 +01:00
|
|
|
}
|
|
|
|
break;
|
2013-12-14 23:16:20 +01:00
|
|
|
}
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
case ASIOSTInt24LSB:
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert current sample in 24 bit format
|
|
|
|
int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
iCurSam <<= 8;
|
|
|
|
|
|
|
|
memcpy ( ( (char*) pSound->bufferInfos[i].buffers[index] ) + iCurSample * 3, &iCurSam, 3 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB:
|
2013-12-14 23:16:20 +01:00
|
|
|
{
|
|
|
|
int32_t* pASIOBuf =
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] );
|
|
|
|
|
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit
|
|
|
|
const int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
2013-12-14 23:16:20 +01:00
|
|
|
pASIOBuf[iCurSample] = ( iCurSam << 16 );
|
2011-12-27 17:50:38 +01:00
|
|
|
}
|
|
|
|
break;
|
2013-12-14 23:16:20 +01:00
|
|
|
}
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
const float fCurSam = static_cast<float> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<float*> ( 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
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
const double fCurSam = static_cast<double> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<double*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
fCurSam / _MAXSHORT;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit
|
|
|
|
const int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
iCurSam;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit
|
|
|
|
const int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
( iCurSam << 2 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit
|
|
|
|
const int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
( iCurSam << 4 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit
|
|
|
|
const int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
( iCurSam << 8 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt16MSB:
|
|
|
|
// NOT YET TESTED
|
|
|
|
// flip bits
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
( (int16_t*) pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
2013-12-14 23:16:20 +01:00
|
|
|
Flip16Bits ( vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt24MSB:
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// because the bits are flipped, we do not have to perform the
|
|
|
|
// shift by 8 bits
|
|
|
|
int32_t iCurSam = static_cast<int32_t> ( Flip16Bits (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] ) );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
memcpy ( ( (char*) pSound->bufferInfos[i].buffers[index] ) + iCurSample * 3, &iCurSam, 3 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB:
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit and flip bits
|
|
|
|
int iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<int32_t*> ( 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
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
const float fCurSam = static_cast<float> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<float*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
static_cast<float> ( Flip32Bits ( static_cast<int32_t> (
|
|
|
|
fCurSam / _MAXSHORT ) ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
const double fCurSam = static_cast<double> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<float*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
static_cast<double> ( Flip64Bits ( static_cast<int64_t> (
|
|
|
|
fCurSam / _MAXSHORT ) ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit
|
|
|
|
const int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
Flip32Bits ( iCurSam );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit
|
|
|
|
const int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
Flip32Bits ( iCurSam << 2 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit
|
|
|
|
const int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
Flip32Bits ( iCurSam << 4 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
|
|
|
|
// NOT YET TESTED
|
2013-12-14 23:16:20 +01:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2011-12-27 17:50:38 +01:00
|
|
|
{
|
|
|
|
// convert to 32 bit
|
|
|
|
const int32_t iCurSam = static_cast<int32_t> (
|
2013-12-14 23:16:20 +01:00
|
|
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + iOutChNum] );
|
2011-12-27 17:50:38 +01:00
|
|
|
|
|
|
|
static_cast<int32_t*> ( pSound->bufferInfos[i].buffers[index] )[iCurSample] =
|
|
|
|
Flip32Bits ( iCurSam << 8 );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-14 23:16:20 +01:00
|
|
|
// Finally if the driver supports the ASIOOutputReady() optimization,
|
2011-12-27 17:50:38 +01:00
|
|
|
// 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;
|
2012-01-28 12:51:14 +01:00
|
|
|
|
2011-12-27 17:50:38 +01:00
|
|
|
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:
|
2012-01-28 12:51:14 +01:00
|
|
|
pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART_AND_INIT );
|
|
|
|
ret = 1L; // 1L if request is accepted or 0 otherwise
|
|
|
|
break;
|
2011-12-27 17:50:38 +01:00
|
|
|
|
2012-01-28 12:51:14 +01:00
|
|
|
case kAsioResetRequest:
|
|
|
|
pSound->EmitReinitRequestSignal ( RS_RELOAD_RESTART_AND_INIT );
|
2011-12-27 17:50:38 +01:00
|
|
|
ret = 1L; // 1L if request is accepted or 0 otherwise
|
|
|
|
break;
|
|
|
|
}
|
2012-01-28 12:51:14 +01:00
|
|
|
|
2011-12-27 17:50:38 +01:00
|
|
|
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<uint32_t> ( 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<uint64_t> ( 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;
|
|
|
|
}
|