2006-12-18 15:39:33 +01:00
|
|
|
/******************************************************************************\
|
2009-02-21 18:48:58 +01:00
|
|
|
* Copyright (c) 2004-2009
|
2006-12-18 15:39:33 +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 *************************************************************/
|
2008-07-12 13:48:51 +02:00
|
|
|
#include <qmutex.h>
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-18 21:52:48 +01:00
|
|
|
// external references
|
|
|
|
extern AsioDrivers* asioDrivers;
|
|
|
|
bool loadAsioDriver ( char *name );
|
|
|
|
|
2008-07-12 13:48:51 +02:00
|
|
|
// mutex
|
|
|
|
QMutex ASIOMutex;
|
|
|
|
|
|
|
|
// TODO the following variables should be in the class definition but we cannot
|
|
|
|
// do it here since we have static callback functions which cannot access the
|
|
|
|
// class members :-(((
|
|
|
|
|
2008-07-11 23:06:15 +02:00
|
|
|
// ASIO stuff
|
|
|
|
ASIODriverInfo driverInfo;
|
|
|
|
ASIOBufferInfo bufferInfos[2 * NUM_IN_OUT_CHANNELS]; // for input and output buffers -> "2 *"
|
|
|
|
ASIOChannelInfo channelInfos[2 * NUM_IN_OUT_CHANNELS];
|
|
|
|
bool bASIOPostOutput;
|
|
|
|
ASIOCallbacks asioCallbacks;
|
2008-07-13 01:33:27 +02:00
|
|
|
int iBufferSizeMono;
|
|
|
|
int iBufferSizeStereo;
|
2008-07-13 09:26:16 +02:00
|
|
|
int iASIOBufferSizeMono;
|
2008-07-11 23:06:15 +02:00
|
|
|
|
2008-07-12 13:48:51 +02:00
|
|
|
// event
|
|
|
|
HANDLE m_ASIOEvent;
|
|
|
|
|
|
|
|
// wave in
|
2008-07-16 00:09:48 +02:00
|
|
|
short* psCaptureBuffer;
|
|
|
|
int iBufferPosCapture;
|
|
|
|
bool bCaptureBufferOverrun;
|
2008-07-12 13:48:51 +02:00
|
|
|
|
|
|
|
// wave out
|
2008-07-16 00:09:48 +02:00
|
|
|
short* psPlayBuffer;
|
|
|
|
int iBufferPosPlay;
|
|
|
|
bool bPlayBufferUnderrun;
|
2008-07-12 13:48:51 +02:00
|
|
|
|
2008-11-01 12:48:17 +01:00
|
|
|
int iMinNumSndBuf;
|
2008-07-12 13:48:51 +02:00
|
|
|
int iCurNumSndBufIn;
|
|
|
|
int iCurNumSndBufOut;
|
2008-07-14 00:57:31 +02:00
|
|
|
int iNewNumSndBufIn;
|
|
|
|
int iNewNumSndBufOut;
|
2008-11-02 09:31:48 +01:00
|
|
|
bool bSetNumSndBufToMinimumValue;
|
2008-07-12 13:48:51 +02:00
|
|
|
|
|
|
|
// we must implement these functions here to get access to global variables
|
2008-07-16 00:09:48 +02:00
|
|
|
int CSound::GetOutNumBuf() { return iNewNumSndBufOut; }
|
|
|
|
int CSound::GetInNumBuf() { return iNewNumSndBufIn; }
|
2008-07-12 13:48:51 +02:00
|
|
|
|
2007-12-18 21:52:48 +01:00
|
|
|
|
2007-12-17 22:28:40 +01:00
|
|
|
/******************************************************************************\
|
|
|
|
* Wave in *
|
|
|
|
\******************************************************************************/
|
|
|
|
bool CSound::Read ( CVector<short>& psData )
|
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
int i;
|
2008-07-14 00:57:31 +02:00
|
|
|
bool bError;
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
// check if device must be opened or reinitialized
|
2008-01-27 11:05:15 +01:00
|
|
|
if ( bChangParamIn )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2008-10-31 21:27:55 +01:00
|
|
|
// reinit sound interface
|
2009-02-24 23:56:19 +01:00
|
|
|
Init ( iBufferSizeStereo );
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-07-13 22:03:37 +02:00
|
|
|
// reset flag
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamIn = false;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
// wait until enough data is available
|
2008-07-17 20:39:24 +02:00
|
|
|
int iWaitCount = 0;
|
2008-07-16 00:09:48 +02:00
|
|
|
while ( iBufferPosCapture < iBufferSizeStereo )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2009-02-23 21:13:03 +01:00
|
|
|
if ( !bCaptureBufferOverrun )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2009-02-23 21:13:03 +01:00
|
|
|
// regular case
|
|
|
|
WaitForSingleObject ( m_ASIOEvent, INFINITE );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// it seems that the buffers are too small, wait
|
|
|
|
// just one time to avoid CPU to go up to 100% and
|
|
|
|
// then leave this function
|
|
|
|
if ( iWaitCount == 0 )
|
2008-07-17 20:39:24 +02:00
|
|
|
{
|
|
|
|
WaitForSingleObject ( m_ASIOEvent, INFINITE );
|
2009-02-23 21:13:03 +01:00
|
|
|
iWaitCount++;
|
2008-07-17 20:39:24 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-02-23 21:13:03 +01:00
|
|
|
return true;
|
2008-07-17 20:39:24 +02:00
|
|
|
}
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
2008-07-12 13:48:51 +02:00
|
|
|
ASIOMutex.lock(); // get mutex lock
|
|
|
|
{
|
2008-07-14 00:57:31 +02:00
|
|
|
// check for buffer overrun in ASIO thread
|
2008-07-16 00:09:48 +02:00
|
|
|
bError = bCaptureBufferOverrun;
|
|
|
|
if ( bCaptureBufferOverrun )
|
2008-07-14 00:57:31 +02:00
|
|
|
{
|
|
|
|
// reset flag
|
2008-07-16 00:09:48 +02:00
|
|
|
bCaptureBufferOverrun = false;
|
2008-07-14 00:57:31 +02:00
|
|
|
}
|
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
// copy data from sound card capture buffer in function output buffer
|
2008-07-13 01:33:27 +02:00
|
|
|
for ( i = 0; i < iBufferSizeStereo; i++ )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
psData[i] = psCaptureBuffer[i];
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2008-01-02 23:16:38 +01:00
|
|
|
|
2008-07-12 13:48:51 +02:00
|
|
|
// move all other data in buffer
|
2008-07-16 00:09:48 +02:00
|
|
|
const int iLenCopyRegion = iBufferPosCapture - iBufferSizeStereo;
|
2008-07-17 20:39:24 +02:00
|
|
|
for ( i = 0; i < iLenCopyRegion; i++ )
|
2008-07-12 13:48:51 +02:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
psCaptureBuffer[i] = psCaptureBuffer[iBufferSizeStereo + i];
|
2008-07-12 13:48:51 +02:00
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-07-12 13:48:51 +02:00
|
|
|
// adjust "current block to write" pointer
|
2008-07-16 00:09:48 +02:00
|
|
|
iBufferPosCapture -= iBufferSizeStereo;
|
2008-07-13 22:03:37 +02:00
|
|
|
|
|
|
|
// in case more than one buffer was ready, reset event
|
|
|
|
ResetEvent ( m_ASIOEvent );
|
2008-07-12 13:48:51 +02:00
|
|
|
}
|
|
|
|
ASIOMutex.unlock();
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
return bError;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::SetInNumBuf ( int iNewNum )
|
|
|
|
{
|
|
|
|
// check new parameter
|
2008-11-01 12:48:17 +01:00
|
|
|
if ( ( iNewNum < MAX_SND_BUF_IN ) && ( iNewNum >= iMinNumSndBuf ) )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-11-01 12:48:17 +01:00
|
|
|
// change only if parameter is different
|
|
|
|
if ( iNewNum != iNewNumSndBufIn )
|
|
|
|
{
|
|
|
|
iNewNumSndBufIn = iNewNum;
|
|
|
|
bChangParamIn = true;
|
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************\
|
|
|
|
* Wave out *
|
|
|
|
\******************************************************************************/
|
|
|
|
bool CSound::Write ( CVector<short>& psData )
|
|
|
|
{
|
2008-07-14 00:57:31 +02:00
|
|
|
bool bError;
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
// check if device must be opened or reinitialized
|
2008-01-27 11:05:15 +01:00
|
|
|
if ( bChangParamOut )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2008-10-31 21:27:55 +01:00
|
|
|
// reinit sound interface
|
2009-02-24 23:56:19 +01:00
|
|
|
Init ( iBufferSizeStereo );
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
// reset flag
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamOut = false;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
2008-07-12 23:10:17 +02:00
|
|
|
ASIOMutex.lock(); // get mutex lock
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2008-07-14 00:57:31 +02:00
|
|
|
// check for buffer underrun in ASIO thread
|
2008-07-16 00:09:48 +02:00
|
|
|
bError = bPlayBufferUnderrun;
|
|
|
|
if ( bPlayBufferUnderrun )
|
2008-07-14 00:57:31 +02:00
|
|
|
{
|
|
|
|
// reset flag
|
2008-07-16 00:09:48 +02:00
|
|
|
bPlayBufferUnderrun = false;
|
2008-07-14 00:57:31 +02:00
|
|
|
}
|
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
// first check if enough data in buffer is available
|
|
|
|
const int iPlayBufferLen = iCurNumSndBufOut * iBufferSizeStereo;
|
|
|
|
|
|
|
|
if ( iBufferPosPlay + iBufferSizeStereo > iPlayBufferLen )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
// buffer overrun, return error
|
|
|
|
bError = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// copy stereo data from function input in soundcard play buffer
|
2008-07-13 01:33:27 +02:00
|
|
|
for ( int i = 0; i < iBufferSizeStereo; i++ )
|
2008-07-12 23:10:17 +02:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
psPlayBuffer[iBufferPosPlay + i] = psData[i];
|
2008-07-12 23:10:17 +02:00
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
iBufferPosPlay += iBufferSizeStereo;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
}
|
2008-07-12 23:10:17 +02:00
|
|
|
ASIOMutex.unlock();
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
return bError;
|
|
|
|
}
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
void CSound::SetOutNumBuf ( int iNewNum )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
// check new parameter
|
2008-11-01 12:48:17 +01:00
|
|
|
if ( ( iNewNum < MAX_SND_BUF_OUT ) && ( iNewNum >= iMinNumSndBuf ) )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-11-01 12:48:17 +01:00
|
|
|
// change only if parameter is different
|
|
|
|
if ( iNewNum != iNewNumSndBufOut )
|
|
|
|
{
|
|
|
|
iNewNumSndBufOut = iNewNum;
|
|
|
|
bChangParamOut = true;
|
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
2007-12-19 21:16:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************\
|
|
|
|
* Common *
|
|
|
|
\******************************************************************************/
|
2008-10-31 00:23:26 +01:00
|
|
|
void CSound::SetDev ( const int iNewDev )
|
|
|
|
{
|
|
|
|
// check if an ASIO driver was already initialized
|
|
|
|
if ( lCurDev >= 0 )
|
|
|
|
{
|
2008-11-02 09:31:48 +01:00
|
|
|
// the new driver was not selected before, use default settings for
|
|
|
|
// buffer sizes
|
|
|
|
bSetNumSndBufToMinimumValue = true;
|
|
|
|
|
2008-10-31 00:23:26 +01:00
|
|
|
// a device was already been initialized and is used, kill working
|
|
|
|
// thread and clean up
|
2008-10-31 21:27:55 +01:00
|
|
|
// stop driver
|
|
|
|
ASIOStop();
|
|
|
|
|
2008-10-31 00:23:26 +01:00
|
|
|
// set event to ensure that thread leaves the waiting function
|
|
|
|
if ( m_ASIOEvent != NULL )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-10-31 00:23:26 +01:00
|
|
|
SetEvent ( m_ASIOEvent );
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2008-10-31 00:23:26 +01:00
|
|
|
|
|
|
|
// wait for the thread to terminate
|
|
|
|
Sleep ( 500 );
|
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// dispose ASIO buffers
|
2008-10-31 00:23:26 +01:00
|
|
|
ASIODisposeBuffers();
|
|
|
|
|
|
|
|
// remove old driver
|
|
|
|
ASIOExit();
|
|
|
|
asioDrivers->removeCurrentDriver();
|
|
|
|
|
|
|
|
const std::string strErrorMessage = LoadAndInitializeDriver ( iNewDev );
|
|
|
|
|
|
|
|
if ( !strErrorMessage.empty() )
|
|
|
|
{
|
2008-11-02 09:31:48 +01:00
|
|
|
// The new driver initializing was not successful, try to preserve
|
|
|
|
// the old buffer settings -> this is possible, if errornous driver
|
|
|
|
// had failed before the buffer setting was done. If it failed after
|
|
|
|
// setting the minimum buffer sizes, the following flag modification
|
|
|
|
// does not have any effect which means the old settings cannot be
|
|
|
|
// recovered anymore (TODO better solution)
|
|
|
|
bSetNumSndBufToMinimumValue = false;
|
|
|
|
|
2008-10-31 00:23:26 +01:00
|
|
|
// loading and initializing the new driver failed, go back to original
|
|
|
|
// driver and display error message
|
|
|
|
LoadAndInitializeDriver ( lCurDev );
|
2009-02-24 23:56:19 +01:00
|
|
|
Init ( iBufferSizeStereo );
|
2008-10-31 00:23:26 +01:00
|
|
|
|
|
|
|
throw CGenErr ( strErrorMessage.c_str() );
|
|
|
|
}
|
2009-02-24 23:56:19 +01:00
|
|
|
Init ( iBufferSizeStereo );
|
2008-10-31 00:23:26 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-11-02 09:31:48 +01:00
|
|
|
if ( iNewDev != INVALID_SNC_CARD_DEVICE )
|
2008-10-31 00:23:26 +01:00
|
|
|
{
|
2008-11-02 09:31:48 +01:00
|
|
|
// This is the first time a driver is to be initialized, we first try
|
|
|
|
// to load the selected driver, if this fails, we try to load the first
|
|
|
|
// available driver in the system. If this fails, too, we throw an error
|
|
|
|
// that no driver is available -> it does not make sense to start the llcon
|
|
|
|
// software if no audio hardware is available
|
|
|
|
const std::string strErrorMessage = LoadAndInitializeDriver ( iNewDev );
|
|
|
|
|
|
|
|
if ( !strErrorMessage.empty() )
|
|
|
|
{
|
|
|
|
// loading and initializing the new driver failed, try to find at
|
|
|
|
// least one usable driver
|
|
|
|
if ( !LoadAndInitializeFirstValidDriver() )
|
|
|
|
{
|
|
|
|
throw CGenErr ( "No usable ASIO audio device (driver) found." );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// the new driver was not selected before, use default settings for
|
|
|
|
// buffer sizes
|
|
|
|
bSetNumSndBufToMinimumValue = true;
|
|
|
|
|
|
|
|
// try to find one usable driver (select the first valid driver)
|
2008-10-31 00:23:26 +01:00
|
|
|
if ( !LoadAndInitializeFirstValidDriver() )
|
|
|
|
{
|
|
|
|
throw CGenErr ( "No usable ASIO audio device (driver) found." );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
std::string CSound::LoadAndInitializeDriver ( int iDriverIdx )
|
2008-10-31 00:23:26 +01:00
|
|
|
{
|
2008-10-31 21:27:55 +01:00
|
|
|
// first check and correct input parameter
|
|
|
|
if ( iDriverIdx >= lNumDevs )
|
|
|
|
{
|
|
|
|
// we assume here that at least one driver is in the system
|
|
|
|
iDriverIdx = 0;
|
|
|
|
}
|
|
|
|
|
2008-10-31 00:23:26 +01:00
|
|
|
// load driver
|
|
|
|
loadAsioDriver ( cDriverNames[iDriverIdx] );
|
|
|
|
if ( ASIOInit ( &driverInfo ) != ASE_OK )
|
|
|
|
{
|
|
|
|
// clean up and return error string
|
|
|
|
asioDrivers->removeCurrentDriver();
|
|
|
|
return "The audio driver could not be initialized.";
|
|
|
|
}
|
|
|
|
|
2008-11-01 09:47:31 +01:00
|
|
|
const std::string strStat = PrepareDriver();
|
2008-10-31 00:23:26 +01:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// store ID of selected driver if initialization was successful
|
|
|
|
if ( strStat.empty() )
|
2008-10-31 00:23:26 +01:00
|
|
|
{
|
2008-10-31 21:27:55 +01:00
|
|
|
lCurDev = iDriverIdx;
|
2008-10-31 00:23:26 +01:00
|
|
|
}
|
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
return strStat;
|
2008-10-31 00:23:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool CSound::LoadAndInitializeFirstValidDriver()
|
|
|
|
{
|
|
|
|
// load and initialize first valid ASIO driver
|
|
|
|
bool bValidDriverDetected = false;
|
|
|
|
int iCurDriverIdx = 0;
|
|
|
|
|
|
|
|
// try all available drivers in the system ("lNumDevs" devices)
|
|
|
|
while ( !bValidDriverDetected && iCurDriverIdx < lNumDevs )
|
|
|
|
{
|
|
|
|
if ( loadAsioDriver ( cDriverNames[iCurDriverIdx] ) )
|
|
|
|
{
|
|
|
|
if ( ASIOInit ( &driverInfo ) == ASE_OK )
|
|
|
|
{
|
2008-11-01 09:47:31 +01:00
|
|
|
if ( PrepareDriver().empty() )
|
2008-10-31 21:27:55 +01:00
|
|
|
{
|
|
|
|
// initialization was successful
|
|
|
|
bValidDriverDetected = true;
|
2008-10-31 00:23:26 +01:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// store ID of selected driver
|
|
|
|
lCurDev = iCurDriverIdx;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// driver could not be loaded, free memory
|
|
|
|
asioDrivers->removeCurrentDriver();
|
|
|
|
}
|
2008-10-31 00:23:26 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// driver could not be loaded, free memory
|
|
|
|
asioDrivers->removeCurrentDriver();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// try next driver
|
|
|
|
iCurDriverIdx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bValidDriverDetected;
|
|
|
|
}
|
|
|
|
|
2008-11-01 09:47:31 +01:00
|
|
|
std::string CSound::PrepareDriver()
|
2007-12-19 21:16:50 +01:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
int i;
|
2009-02-08 23:44:18 +01:00
|
|
|
int iDesiredBufferSizeMono;
|
2007-12-19 21:16:50 +01:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// check the number of available channels
|
|
|
|
long lNumInChan;
|
|
|
|
long lNumOutChan;
|
|
|
|
ASIOGetChannels ( &lNumInChan, &lNumOutChan );
|
|
|
|
if ( ( lNumInChan < NUM_IN_OUT_CHANNELS ) ||
|
|
|
|
( lNumOutChan < NUM_IN_OUT_CHANNELS ) )
|
|
|
|
{
|
|
|
|
// clean up and return error string
|
|
|
|
ASIOExit();
|
|
|
|
asioDrivers->removeCurrentDriver();
|
|
|
|
return "The audio device does not support the "
|
|
|
|
"required number of channels.";
|
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// set the sample rate and check if sample rate is supported
|
|
|
|
ASIOSetSampleRate ( SND_CRD_SAMPLE_RATE );
|
|
|
|
|
|
|
|
ASIOSampleRate sampleRate;
|
|
|
|
ASIOGetSampleRate ( &sampleRate );
|
|
|
|
if ( sampleRate != SND_CRD_SAMPLE_RATE )
|
2008-04-13 18:43:21 +02:00
|
|
|
{
|
2008-10-31 21:27:55 +01:00
|
|
|
// clean up and return error string
|
|
|
|
ASIOExit();
|
|
|
|
asioDrivers->removeCurrentDriver();
|
|
|
|
return "The audio device does not support the "
|
|
|
|
"required sample rate.";
|
|
|
|
}
|
2008-07-13 01:33:27 +02:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// query the usable buffer sizes
|
|
|
|
ASIOGetBufferSize ( &HWBufferInfo.lMinSize,
|
|
|
|
&HWBufferInfo.lMaxSize,
|
|
|
|
&HWBufferInfo.lPreferredSize,
|
|
|
|
&HWBufferInfo.lGranularity );
|
|
|
|
|
2009-02-08 23:44:18 +01:00
|
|
|
// calculate the desired mono buffer size
|
|
|
|
|
|
|
|
// TEST -> put this in the GUI and implement the code for the Linux driver, too
|
|
|
|
// setting this variable to false sets the previous behaviour
|
|
|
|
const bool bPreferPowerOfTwoAudioBufferSize = false;
|
|
|
|
|
|
|
|
if ( bPreferPowerOfTwoAudioBufferSize )
|
|
|
|
{
|
|
|
|
// use next power of 2 for desired block size mono
|
|
|
|
iDesiredBufferSizeMono = LlconMath().NextPowerOfTwo ( iBufferSizeMono );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
iDesiredBufferSizeMono = iBufferSizeMono;
|
|
|
|
}
|
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// calculate "nearest" buffer size and set internal parameter accordingly
|
|
|
|
// first check minimum and maximum values
|
2009-02-08 23:44:18 +01:00
|
|
|
if ( iDesiredBufferSizeMono < HWBufferInfo.lMinSize )
|
2008-10-31 21:27:55 +01:00
|
|
|
{
|
|
|
|
iASIOBufferSizeMono = HWBufferInfo.lMinSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-02-08 23:44:18 +01:00
|
|
|
if ( iDesiredBufferSizeMono > HWBufferInfo.lMaxSize )
|
2008-04-13 18:43:21 +02:00
|
|
|
{
|
2008-10-31 21:27:55 +01:00
|
|
|
iASIOBufferSizeMono = HWBufferInfo.lMaxSize;
|
2008-04-13 18:43:21 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-10-31 21:27:55 +01:00
|
|
|
// initialization
|
|
|
|
int iTrialBufSize = HWBufferInfo.lMinSize;
|
|
|
|
int iLastTrialBufSize = HWBufferInfo.lMinSize;
|
|
|
|
bool bSizeFound = false;
|
2008-07-12 13:48:51 +02:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// test loop
|
|
|
|
while ( ( iTrialBufSize <= HWBufferInfo.lMaxSize ) && ( !bSizeFound ) )
|
|
|
|
{
|
2009-02-08 23:44:18 +01:00
|
|
|
if ( iTrialBufSize >= iDesiredBufferSizeMono )
|
2008-04-13 18:43:21 +02:00
|
|
|
{
|
2008-10-31 21:27:55 +01:00
|
|
|
// test which buffer size fits better: the old one or the
|
|
|
|
// current one
|
2009-02-08 23:44:18 +01:00
|
|
|
if ( ( iTrialBufSize - iDesiredBufferSizeMono ) >
|
|
|
|
( iDesiredBufferSizeMono - iLastTrialBufSize ) )
|
2008-04-13 18:43:21 +02:00
|
|
|
{
|
2009-02-08 23:44:18 +01:00
|
|
|
iTrialBufSize = iLastTrialBufSize;
|
2008-04-13 18:43:21 +02:00
|
|
|
}
|
2008-10-31 21:27:55 +01:00
|
|
|
|
|
|
|
// exit while loop
|
|
|
|
bSizeFound = true;
|
2008-04-13 18:43:21 +02:00
|
|
|
}
|
2008-07-13 09:26:16 +02:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2008-04-13 18:43:21 +02:00
|
|
|
}
|
2008-10-31 21:27:55 +01:00
|
|
|
|
|
|
|
// set ASIO buffer size
|
|
|
|
iASIOBufferSizeMono = iTrialBufSize;
|
2008-04-13 18:43:21 +02:00
|
|
|
}
|
2008-10-31 21:27:55 +01:00
|
|
|
}
|
2008-04-08 20:38:55 +02:00
|
|
|
|
2008-11-01 12:48:17 +01:00
|
|
|
// calculate the minimum required number of soundcard buffers
|
|
|
|
iMinNumSndBuf = static_cast<int> (
|
|
|
|
ceil ( static_cast<double> ( iASIOBufferSizeMono ) / iBufferSizeMono ) );
|
|
|
|
|
2008-11-01 23:26:17 +01:00
|
|
|
// TODO better solution
|
|
|
|
// For some ASIO buffer sizes, the above calculation seems not to work although
|
|
|
|
// it should be correct. Maybe there is a misunderstanding or a bug in the
|
|
|
|
// sound interface implementation. As a workaround, we implement a table here, to
|
|
|
|
// get working parameters for the most common ASIO buffer settings
|
|
|
|
// Interesting observation: only 256 samples seems to be wrong, all other tested
|
|
|
|
// buffer sizes like 192, 512, 384, etc. are correct...
|
|
|
|
if ( iASIOBufferSizeMono == 256 )
|
|
|
|
{
|
|
|
|
iMinNumSndBuf = 4;
|
|
|
|
}
|
2008-11-01 12:48:17 +01:00
|
|
|
Q_ASSERT ( iMinNumSndBuf < MAX_SND_BUF_IN );
|
|
|
|
Q_ASSERT ( iMinNumSndBuf < MAX_SND_BUF_OUT );
|
|
|
|
|
2008-11-02 09:31:48 +01:00
|
|
|
// set or just check the sound card buffer sizes
|
|
|
|
if ( bSetNumSndBufToMinimumValue )
|
|
|
|
{
|
|
|
|
// use minimum buffer sizes as default
|
|
|
|
iNewNumSndBufIn = iMinNumSndBuf;
|
|
|
|
iNewNumSndBufOut = iMinNumSndBuf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// correct number of sound card buffers if required
|
|
|
|
iNewNumSndBufIn = max ( iMinNumSndBuf, iNewNumSndBufIn );
|
|
|
|
iNewNumSndBufOut = max ( iMinNumSndBuf, iNewNumSndBufOut );
|
|
|
|
}
|
|
|
|
iCurNumSndBufIn = iNewNumSndBufIn;
|
|
|
|
iCurNumSndBufOut = iNewNumSndBufOut;
|
2008-11-01 12:48:17 +01:00
|
|
|
|
|
|
|
// display warning in case the ASIO buffer is too big
|
|
|
|
if ( iMinNumSndBuf > 6 )
|
|
|
|
{
|
|
|
|
QMessageBox::critical ( 0, APP_NAME,
|
|
|
|
QString ( "The ASIO buffer size of the selected audio driver is ") +
|
|
|
|
QString().number ( iASIOBufferSizeMono ) +
|
|
|
|
QString ( " samples which is too large. Please try to modify "
|
|
|
|
"the ASIO buffer size value in your ASIO driver settings (most ASIO "
|
2008-11-01 23:26:17 +01:00
|
|
|
"drivers like ASIO4All or kx driver allow to change the ASIO buffer size). "
|
2008-11-02 09:31:48 +01:00
|
|
|
"Recommended settings are 96 or 128 samples. Please make sure that "
|
|
|
|
"before you try to change the ASIO driver buffer size all ASIO "
|
|
|
|
"applications including llcon are closed." ), "Ok", 0 );
|
2008-11-01 12:48:17 +01:00
|
|
|
}
|
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// prepare input channels
|
|
|
|
for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ )
|
|
|
|
{
|
2009-02-22 12:07:18 +01:00
|
|
|
bufferInfos[i].isInput = ASIOTrue;
|
|
|
|
bufferInfos[i].channelNum = i;
|
|
|
|
bufferInfos[i].buffers[0] = 0;
|
2008-10-31 21:27:55 +01:00
|
|
|
bufferInfos[i].buffers[1] = 0;
|
|
|
|
}
|
2008-07-13 15:16:31 +02:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// prepare output channels
|
|
|
|
for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ )
|
|
|
|
{
|
2009-02-22 12:07:18 +01:00
|
|
|
bufferInfos[NUM_IN_OUT_CHANNELS + i].isInput = ASIOFalse;
|
|
|
|
bufferInfos[NUM_IN_OUT_CHANNELS + i].channelNum = i;
|
|
|
|
bufferInfos[NUM_IN_OUT_CHANNELS + i].buffers[0] = 0;
|
2008-10-31 21:27:55 +01:00
|
|
|
bufferInfos[NUM_IN_OUT_CHANNELS + i].buffers[1] = 0;
|
|
|
|
}
|
2008-07-13 01:33:27 +02:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// create and activate ASIO buffers (buffer size in samples)
|
|
|
|
ASIOCreateBuffers ( bufferInfos, 2 /* in/out */ * NUM_IN_OUT_CHANNELS /* stereo */,
|
2009-02-22 12:07:18 +01:00
|
|
|
iASIOBufferSizeMono, &asioCallbacks );
|
2008-04-08 20:38:55 +02:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
// now get some buffer details
|
|
|
|
for ( i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ )
|
|
|
|
{
|
2009-02-22 12:07:18 +01:00
|
|
|
channelInfos[i].channel = bufferInfos[i].channelNum;
|
|
|
|
channelInfos[i].isInput = bufferInfos[i].isInput;
|
|
|
|
ASIOGetChannelInfo ( &channelInfos[i] );
|
2008-10-31 21:27:55 +01:00
|
|
|
|
|
|
|
// only 16/24/32 LSB is supported
|
|
|
|
if ( ( channelInfos[i].type != ASIOSTInt16LSB ) &&
|
|
|
|
( channelInfos[i].type != ASIOSTInt24LSB ) &&
|
|
|
|
( channelInfos[i].type != ASIOSTInt32LSB ) )
|
|
|
|
{
|
|
|
|
// clean up and return error string
|
|
|
|
ASIODisposeBuffers();
|
|
|
|
ASIOExit();
|
|
|
|
asioDrivers->removeCurrentDriver();
|
|
|
|
return "Required audio sample format not available (16/24/32 bit LSB).";
|
|
|
|
}
|
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-10-31 21:27:55 +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 );
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2009-02-24 23:56:19 +01:00
|
|
|
void CSound::Init ( const int iNewStereoBufferSize )
|
2008-10-31 21:27:55 +01:00
|
|
|
{
|
|
|
|
// first, stop audio and dispose ASIO buffers
|
|
|
|
ASIOStop();
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-10-31 21:27:55 +01:00
|
|
|
ASIOMutex.lock(); // get mutex lock
|
|
|
|
{
|
2009-02-24 23:56:19 +01:00
|
|
|
// init base clasee
|
|
|
|
CSoundBase::Init ( iNewStereoBufferSize );
|
|
|
|
|
|
|
|
// set internal buffer size value and calculate mono buffer size
|
|
|
|
iBufferSizeStereo = iNewStereoBufferSize;
|
|
|
|
iBufferSizeMono = iBufferSizeStereo / 2;
|
|
|
|
|
2008-07-14 00:57:31 +02:00
|
|
|
// store new buffer number values
|
|
|
|
iCurNumSndBufIn = iNewNumSndBufIn;
|
|
|
|
iCurNumSndBufOut = iNewNumSndBufOut;
|
|
|
|
|
|
|
|
// initialize write block pointer in and overrun flag
|
2008-07-16 00:09:48 +02:00
|
|
|
iBufferPosCapture = 0;
|
|
|
|
bCaptureBufferOverrun = false;
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
// create memory for capture buffer
|
|
|
|
if ( psCaptureBuffer != NULL )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
delete[] psCaptureBuffer;
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2008-07-16 00:09:48 +02:00
|
|
|
psCaptureBuffer = new short[iCurNumSndBufIn * iBufferSizeStereo];
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-07-14 00:57:31 +02:00
|
|
|
// initialize write block pointer out and underrun flag
|
2008-07-16 00:09:48 +02:00
|
|
|
iBufferPosPlay = 0;
|
|
|
|
bPlayBufferUnderrun = false;
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
// create memory for play buffer
|
|
|
|
if ( psPlayBuffer != NULL )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
delete[] psPlayBuffer;
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2008-07-16 00:09:48 +02:00
|
|
|
psPlayBuffer = new short[iCurNumSndBufOut * iBufferSizeStereo];
|
2008-07-15 19:16:41 +02:00
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
// clear new buffer
|
2008-10-31 21:27:55 +01:00
|
|
|
for ( int i = 0; i < iCurNumSndBufOut * iBufferSizeStereo; i++ )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
psPlayBuffer[i] = 0;
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2008-07-15 19:16:41 +02:00
|
|
|
|
2008-07-12 13:48:51 +02:00
|
|
|
// reset event
|
|
|
|
ResetEvent ( m_ASIOEvent );
|
|
|
|
}
|
|
|
|
ASIOMutex.unlock();
|
2007-12-19 21:16:50 +01:00
|
|
|
|
|
|
|
// initialization is done, (re)start audio
|
|
|
|
ASIOStart();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::Close()
|
|
|
|
{
|
2008-10-31 21:27:55 +01:00
|
|
|
// stop driver
|
|
|
|
ASIOStop();
|
|
|
|
|
2007-12-17 22:28:40 +01:00
|
|
|
// set event to ensure that thread leaves the waiting function
|
2008-07-12 13:48:51 +02:00
|
|
|
if ( m_ASIOEvent != NULL )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-12 23:10:17 +02:00
|
|
|
SetEvent ( m_ASIOEvent );
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
// wait for the thread to terminate
|
|
|
|
Sleep ( 500 );
|
|
|
|
|
|
|
|
// set flag to open devices the next time it is initialized
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamIn = true;
|
|
|
|
bChangParamOut = true;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
2009-02-24 23:56:19 +01:00
|
|
|
CSound::CSound ( void (*fpNewCallback) ( CVector<short>& psData, void* arg ), void* arg ) :
|
|
|
|
CSoundBase ( fpNewCallback, arg )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
|
|
|
// init number of sound buffers
|
2008-07-14 00:57:31 +02:00
|
|
|
iNewNumSndBufIn = NUM_SOUND_BUFFERS_IN;
|
2007-12-17 22:28:40 +01:00
|
|
|
iCurNumSndBufIn = NUM_SOUND_BUFFERS_IN;
|
2008-07-14 00:57:31 +02:00
|
|
|
iNewNumSndBufOut = NUM_SOUND_BUFFERS_OUT;
|
2007-12-17 22:28:40 +01:00
|
|
|
iCurNumSndBufOut = NUM_SOUND_BUFFERS_OUT;
|
2008-11-01 12:48:17 +01:00
|
|
|
iMinNumSndBuf = 1;
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
// should be initialized because an error can occur during init
|
2008-07-12 13:48:51 +02:00
|
|
|
m_ASIOEvent = NULL;
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-18 21:52:48 +01:00
|
|
|
// get available ASIO driver names in system
|
2009-02-22 12:07:18 +01:00
|
|
|
for ( int i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ )
|
2007-12-18 21:52:48 +01:00
|
|
|
{
|
|
|
|
cDriverNames[i] = new char[32];
|
|
|
|
}
|
|
|
|
|
|
|
|
loadAsioDriver ( "dummy" ); // to initialize external object
|
2008-10-31 00:23:26 +01:00
|
|
|
lNumDevs = asioDrivers->getDriverNames ( cDriverNames, MAX_NUMBER_SOUND_CARDS );
|
2007-12-18 21:52:48 +01:00
|
|
|
|
|
|
|
// in case we do not have a driver available, throw error
|
2008-10-31 00:23:26 +01:00
|
|
|
if ( lNumDevs == 0 )
|
2007-12-19 21:16:50 +01:00
|
|
|
{
|
2008-10-31 00:23:26 +01:00
|
|
|
throw CGenErr ( "No ASIO audio device (driver) found." );
|
2007-12-19 21:16:50 +01:00
|
|
|
}
|
2007-12-18 21:52:48 +01:00
|
|
|
|
2008-10-31 00:23:26 +01:00
|
|
|
asioDrivers->removeCurrentDriver();
|
2007-12-19 21:16:50 +01:00
|
|
|
|
2008-10-31 00:23:26 +01:00
|
|
|
// init device index with illegal value to show that driver is not initialized
|
|
|
|
lCurDev = -1;
|
2007-12-19 21:16:50 +01:00
|
|
|
|
2009-02-22 12:07:18 +01:00
|
|
|
// set up the asioCallback structure
|
|
|
|
asioCallbacks.bufferSwitch = &bufferSwitch;
|
|
|
|
asioCallbacks.sampleRateDidChange = &sampleRateChanged;
|
|
|
|
asioCallbacks.asioMessage = &asioMessages;
|
|
|
|
asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
|
2007-12-19 21:16:50 +01:00
|
|
|
|
|
|
|
// init buffer pointer to zero
|
2008-07-16 00:09:48 +02:00
|
|
|
psCaptureBuffer = NULL;
|
|
|
|
psPlayBuffer = NULL;
|
2008-07-15 19:16:41 +02:00
|
|
|
|
2008-07-12 13:48:51 +02:00
|
|
|
// we use an event controlled structure
|
|
|
|
// create event
|
|
|
|
m_ASIOEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-11-02 09:31:48 +01:00
|
|
|
// init flags
|
|
|
|
bChangParamIn = false;
|
|
|
|
bChangParamOut = false;
|
|
|
|
bSetNumSndBufToMinimumValue = false;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
CSound::~CSound()
|
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
// cleanup ASIO stuff
|
|
|
|
ASIOStop();
|
|
|
|
ASIODisposeBuffers();
|
|
|
|
ASIOExit();
|
|
|
|
asioDrivers->removeCurrentDriver();
|
|
|
|
|
2007-12-17 22:28:40 +01:00
|
|
|
// delete allocated memory
|
2008-07-16 00:09:48 +02:00
|
|
|
if ( psCaptureBuffer != NULL )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
delete[] psCaptureBuffer;
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2008-07-16 00:09:48 +02:00
|
|
|
if ( psPlayBuffer != NULL )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
delete[] psPlayBuffer;
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-07-12 13:48:51 +02:00
|
|
|
// close the handle for the event
|
|
|
|
if ( m_ASIOEvent != NULL )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-12 13:48:51 +02:00
|
|
|
CloseHandle ( m_ASIOEvent );
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
// ASIO callbacks -------------------------------------------------------------
|
2008-07-13 09:26:16 +02:00
|
|
|
ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime *timeInfo,
|
|
|
|
long index,
|
|
|
|
ASIOBool processNow )
|
2007-12-19 21:16:50 +01:00
|
|
|
{
|
2009-02-22 12:07:18 +01:00
|
|
|
bufferSwitch ( index, processNow );
|
|
|
|
return 0L;
|
2008-03-16 14:10:46 +01:00
|
|
|
}
|
2007-12-19 21:16:50 +01:00
|
|
|
|
2008-07-13 12:56:40 +02:00
|
|
|
void CSound::bufferSwitch ( long index, ASIOBool processNow )
|
2008-03-16 14:10:46 +01:00
|
|
|
{
|
2008-07-12 23:10:17 +02:00
|
|
|
int iCurSample;
|
|
|
|
|
2008-07-12 13:48:51 +02:00
|
|
|
ASIOMutex.lock(); // get mutex lock
|
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
// first check buffer state of capture and play buffers
|
|
|
|
const int iCaptureBufferLen = iCurNumSndBufIn * iBufferSizeStereo;
|
2008-07-15 19:16:41 +02:00
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
bCaptureBufferOverrun =
|
|
|
|
( iBufferPosCapture + 2 * iASIOBufferSizeMono > iCaptureBufferLen );
|
2008-07-15 19:16:41 +02:00
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
bPlayBufferUnderrun = ( 2 * iASIOBufferSizeMono > iBufferPosPlay );
|
2008-07-15 19:16:41 +02:00
|
|
|
|
2008-07-16 00:09:48 +02:00
|
|
|
// perform the processing for input and output
|
2009-02-22 12:07:18 +01:00
|
|
|
for ( int i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ ) // stereo
|
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
if ( bufferInfos[i].isInput == ASIOTrue )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-13 01:33:27 +02:00
|
|
|
// CAPTURE -----------------------------------------------------
|
2008-07-13 12:56:40 +02:00
|
|
|
// first check if space in buffer is available
|
2008-07-16 00:09:48 +02:00
|
|
|
if ( !bCaptureBufferOverrun )
|
2008-07-12 13:48:51 +02:00
|
|
|
{
|
2008-07-24 20:15:48 +02:00
|
|
|
// copy new captured block in thread transfer buffer (copy
|
|
|
|
// mono data interleaved in stereo buffer)
|
2009-02-22 12:07:18 +01:00
|
|
|
switch ( channelInfos[i].type )
|
|
|
|
{
|
|
|
|
case ASIOSTInt16LSB:
|
2008-07-24 20:15:48 +02:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
|
|
|
{
|
|
|
|
psCaptureBuffer[iBufferPosCapture +
|
|
|
|
2 * iCurSample + bufferInfos[i].channelNum] =
|
2008-11-01 23:26:17 +01:00
|
|
|
( (short*) bufferInfos[i].buffers[index] )[iCurSample];
|
2008-07-24 20:15:48 +02:00
|
|
|
}
|
2009-02-22 12:07:18 +01:00
|
|
|
break;
|
2008-07-24 20:15:48 +02:00
|
|
|
|
2009-02-22 12:07:18 +01:00
|
|
|
case ASIOSTInt24LSB:
|
2008-07-24 20:15:48 +02:00
|
|
|
|
|
|
|
// not yet tested, horrible things might happen with the following code ;-)
|
|
|
|
|
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
|
|
|
{
|
|
|
|
// convert current sample in 16 bit format
|
|
|
|
int iCurSam = 0;
|
2008-11-01 23:26:17 +01:00
|
|
|
memcpy ( &iCurSam, ( (char*) bufferInfos[i].buffers[index] ) + iCurSample * 3, 3 );
|
2008-07-24 20:15:48 +02:00
|
|
|
iCurSam >>= 8;
|
|
|
|
|
|
|
|
psCaptureBuffer[iBufferPosCapture +
|
|
|
|
2 * iCurSample + bufferInfos[i].channelNum] = static_cast<short> ( iCurSam );
|
|
|
|
}
|
2009-02-22 12:07:18 +01:00
|
|
|
break;
|
2008-07-24 20:15:48 +02:00
|
|
|
|
2009-02-22 12:07:18 +01:00
|
|
|
case ASIOSTInt32LSB:
|
2008-07-24 20:15:48 +02:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
|
|
|
{
|
|
|
|
// convert to 16 bit
|
|
|
|
psCaptureBuffer[iBufferPosCapture +
|
|
|
|
2 * iCurSample + bufferInfos[i].channelNum] =
|
|
|
|
(((int*) bufferInfos[i].buffers[index])[iCurSample] >> 16);
|
|
|
|
}
|
2009-02-22 12:07:18 +01:00
|
|
|
break;
|
2008-07-12 13:48:51 +02:00
|
|
|
}
|
|
|
|
}
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2008-07-16 00:09:48 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// PLAYBACK ----------------------------------------------------
|
|
|
|
if ( !bPlayBufferUnderrun )
|
|
|
|
{
|
2008-07-24 20:15:48 +02:00
|
|
|
// copy data from sound card in output buffer (copy
|
|
|
|
// interleaved stereo data in mono sound card buffer)
|
2009-02-22 12:07:18 +01:00
|
|
|
switch ( channelInfos[i].type )
|
|
|
|
{
|
|
|
|
case ASIOSTInt16LSB:
|
2008-07-24 20:15:48 +02:00
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-11-01 23:26:17 +01:00
|
|
|
( (short*) bufferInfos[i].buffers[index] )[iCurSample] =
|
2008-07-24 20:15:48 +02:00
|
|
|
psPlayBuffer[2 * iCurSample + bufferInfos[i].channelNum];
|
|
|
|
}
|
2009-02-22 12:07:18 +01:00
|
|
|
break;
|
2008-07-24 20:15:48 +02:00
|
|
|
|
2009-02-22 12:07:18 +01:00
|
|
|
case ASIOSTInt24LSB:
|
2008-07-24 20:15:48 +02:00
|
|
|
|
|
|
|
// not yet tested, horrible things might happen with the following code ;-)
|
|
|
|
|
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
2009-02-22 12:07:18 +01:00
|
|
|
{
|
2008-07-24 20:15:48 +02:00
|
|
|
// convert current sample in 24 bit format
|
|
|
|
int iCurSam = static_cast<int> ( psPlayBuffer[2 * iCurSample + bufferInfos[i].channelNum] );
|
|
|
|
iCurSam <<= 8;
|
|
|
|
|
2008-11-01 23:26:17 +01:00
|
|
|
memcpy ( ( (char*) bufferInfos[i].buffers[index] ) + iCurSample * 3, &iCurSam, 3 );
|
2008-07-24 20:15:48 +02:00
|
|
|
}
|
2009-02-22 12:07:18 +01:00
|
|
|
break;
|
2008-07-24 20:15:48 +02:00
|
|
|
|
2009-02-22 12:07:18 +01:00
|
|
|
case ASIOSTInt32LSB:
|
|
|
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
|
|
|
{
|
2008-07-24 20:15:48 +02:00
|
|
|
// convert to 32 bit
|
|
|
|
int iCurSam = static_cast<int> ( psPlayBuffer[2 * iCurSample + bufferInfos[i].channelNum] );
|
2008-11-01 23:26:17 +01:00
|
|
|
( (int*) bufferInfos[i].buffers[index] )[iCurSample] = ( iCurSam << 16 );
|
2008-07-24 20:15:48 +02:00
|
|
|
}
|
|
|
|
break;
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2008-07-16 00:09:48 +02:00
|
|
|
}
|
2008-07-13 01:33:27 +02:00
|
|
|
}
|
2009-02-22 12:07:18 +01:00
|
|
|
}
|
2008-07-13 01:33:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
// Manage thread interface buffers for input and output ----------------
|
2008-07-16 00:09:48 +02:00
|
|
|
// capture
|
|
|
|
if ( !bCaptureBufferOverrun )
|
|
|
|
{
|
|
|
|
iBufferPosCapture += 2 * iASIOBufferSizeMono;
|
|
|
|
}
|
|
|
|
|
|
|
|
// play
|
|
|
|
if ( !bPlayBufferUnderrun )
|
2008-07-13 01:33:27 +02:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
// move all other data in play buffer
|
|
|
|
const int iLenCopyRegion = iBufferPosPlay - 2 * iASIOBufferSizeMono;
|
|
|
|
for ( iCurSample = 0; iCurSample < iLenCopyRegion; iCurSample++ )
|
2008-07-13 01:33:27 +02:00
|
|
|
{
|
2008-07-16 00:09:48 +02:00
|
|
|
psPlayBuffer[iCurSample] =
|
|
|
|
psPlayBuffer[2 * iASIOBufferSizeMono + iCurSample];
|
2008-07-12 13:48:51 +02:00
|
|
|
}
|
2008-07-13 01:33:27 +02:00
|
|
|
|
|
|
|
// adjust "current block to write" pointer
|
2008-07-16 00:09:48 +02:00
|
|
|
iBufferPosPlay -= 2 * iASIOBufferSizeMono;
|
2008-07-13 01:33:27 +02:00
|
|
|
}
|
2008-07-13 15:16:31 +02:00
|
|
|
|
2008-07-12 23:10:17 +02:00
|
|
|
|
2008-07-13 22:03:37 +02:00
|
|
|
// finally if the driver supports the ASIOOutputReady() optimization,
|
2008-07-16 00:09:48 +02:00
|
|
|
// do it here, all data are in place -----------------------------------
|
2008-07-13 22:03:37 +02:00
|
|
|
if ( bASIOPostOutput )
|
|
|
|
{
|
2009-02-22 12:07:18 +01:00
|
|
|
ASIOOutputReady();
|
2008-07-13 22:03:37 +02:00
|
|
|
}
|
2008-07-12 23:10:17 +02:00
|
|
|
|
2008-07-13 22:03:37 +02:00
|
|
|
// set event
|
|
|
|
SetEvent ( m_ASIOEvent );
|
|
|
|
}
|
|
|
|
ASIOMutex.unlock();
|
2007-12-19 21:16:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
long CSound::asioMessages ( long selector, long value, void* message, double* opt )
|
|
|
|
{
|
2009-02-22 12:07:18 +01:00
|
|
|
long ret = 0;
|
|
|
|
switch(selector)
|
|
|
|
{
|
|
|
|
case kAsioEngineVersion:
|
|
|
|
// return the supported ASIO version of the host application
|
|
|
|
ret = 2L;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ret;
|
2007-12-19 21:16:50 +01:00
|
|
|
}
|