jamulus/windows/sound.cpp

1572 lines
47 KiB
C++
Raw Normal View History

/******************************************************************************\
2008-04-08 20:38:55 +02:00
* Copyright (c) 2004-2008
*
* 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 *************************************************************/
#ifdef USE_ASIO_SND_INTERFACE
#include <qmutex.h>
2007-12-18 21:52:48 +01:00
// external references
extern AsioDrivers* asioDrivers;
bool loadAsioDriver ( char *name );
// 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
// event
HANDLE m_ASIOEvent;
// wave in
short* psCaptureBuffer;
int iBufferPosCapture;
bool bCaptureBufferOverrun;
// wave out
short* psPlayBuffer;
int iBufferPosPlay;
bool bPlayBufferUnderrun;
int iMinNumSndBuf;
int iCurNumSndBufIn;
int iCurNumSndBufOut;
2008-07-14 00:57:31 +02:00
int iNewNumSndBufIn;
int iNewNumSndBufOut;
bool bSetNumSndBufToMinimumValue;
// we must implement these functions here to get access to global variables
int CSound::GetOutNumBuf() { return iNewNumSndBufOut; }
int CSound::GetInNumBuf() { return iNewNumSndBufIn; }
2007-12-18 21:52:48 +01:00
/******************************************************************************\
* Wave in *
\******************************************************************************/
bool CSound::Read ( CVector<short>& psData )
{
int i;
2008-07-14 00:57:31 +02:00
bool bError;
// check if device must be opened or reinitialized
2008-01-27 11:05:15 +01:00
if ( bChangParamIn )
{
// reinit sound interface
InitRecordingAndPlayback();
2008-07-13 22:03:37 +02:00
// reset flag
2008-01-27 11:05:15 +01:00
bChangParamIn = false;
}
// wait until enough data is available
2008-07-17 20:39:24 +02:00
int iWaitCount = 0;
while ( iBufferPosCapture < iBufferSizeStereo )
{
2008-07-17 20:39:24 +02:00
if ( bBlockingRec )
{
2008-07-17 20:39:24 +02:00
if ( !bCaptureBufferOverrun )
{
// 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 )
{
WaitForSingleObject ( m_ASIOEvent, INFINITE );
iWaitCount++;
}
else
{
return true;
}
}
}
else
{
return true;
}
}
ASIOMutex.lock(); // get mutex lock
{
2008-07-14 00:57:31 +02:00
// check for buffer overrun in ASIO thread
bError = bCaptureBufferOverrun;
if ( bCaptureBufferOverrun )
2008-07-14 00:57:31 +02:00
{
// reset flag
bCaptureBufferOverrun = false;
2008-07-14 00:57:31 +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++ )
{
psData[i] = psCaptureBuffer[i];
}
// move all other data in buffer
const int iLenCopyRegion = iBufferPosCapture - iBufferSizeStereo;
2008-07-17 20:39:24 +02:00
for ( i = 0; i < iLenCopyRegion; i++ )
{
psCaptureBuffer[i] = psCaptureBuffer[iBufferSizeStereo + i];
}
// adjust "current block to write" pointer
iBufferPosCapture -= iBufferSizeStereo;
2008-07-13 22:03:37 +02:00
// in case more than one buffer was ready, reset event
ResetEvent ( m_ASIOEvent );
}
ASIOMutex.unlock();
return bError;
}
void CSound::SetInNumBuf ( int iNewNum )
{
// check new parameter
if ( ( iNewNum < MAX_SND_BUF_IN ) && ( iNewNum >= iMinNumSndBuf ) )
{
// change only if parameter is different
if ( iNewNum != iNewNumSndBufIn )
{
iNewNumSndBufIn = iNewNum;
bChangParamIn = true;
}
}
}
/******************************************************************************\
* Wave out *
\******************************************************************************/
bool CSound::Write ( CVector<short>& psData )
{
2008-07-14 00:57:31 +02:00
bool bError;
// check if device must be opened or reinitialized
2008-01-27 11:05:15 +01:00
if ( bChangParamOut )
{
// reinit sound interface
InitRecordingAndPlayback();
// reset flag
2008-01-27 11:05:15 +01:00
bChangParamOut = false;
}
ASIOMutex.lock(); // get mutex lock
{
2008-07-14 00:57:31 +02:00
// check for buffer underrun in ASIO thread
bError = bPlayBufferUnderrun;
if ( bPlayBufferUnderrun )
2008-07-14 00:57:31 +02:00
{
// reset flag
bPlayBufferUnderrun = false;
2008-07-14 00:57:31 +02:00
}
// first check if enough data in buffer is available
const int iPlayBufferLen = iCurNumSndBufOut * iBufferSizeStereo;
if ( iBufferPosPlay + iBufferSizeStereo > iPlayBufferLen )
{
// 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++ )
{
psPlayBuffer[iBufferPosPlay + i] = psData[i];
}
iBufferPosPlay += iBufferSizeStereo;
}
}
ASIOMutex.unlock();
return bError;
}
2007-12-19 21:16:50 +01:00
void CSound::SetOutNumBuf ( int iNewNum )
{
2007-12-19 21:16:50 +01:00
// check new parameter
if ( ( iNewNum < MAX_SND_BUF_OUT ) && ( iNewNum >= iMinNumSndBuf ) )
2007-12-19 21:16:50 +01:00
{
// change only if parameter is different
if ( iNewNum != iNewNumSndBufOut )
{
iNewNumSndBufOut = iNewNum;
bChangParamOut = true;
}
}
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 )
{
// 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
// 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 )
{
SetEvent ( m_ASIOEvent );
}
// wait for the thread to terminate
Sleep ( 500 );
// 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() )
{
// 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 );
InitRecordingAndPlayback();
2008-10-31 00:23:26 +01:00
throw CGenErr ( strErrorMessage.c_str() );
}
InitRecordingAndPlayback();
2008-10-31 00:23:26 +01:00
}
else
{
if ( iNewDev != INVALID_SNC_CARD_DEVICE )
2008-10-31 00:23:26 +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." );
}
}
}
}
std::string CSound::LoadAndInitializeDriver ( int iDriverIdx )
2008-10-31 00:23:26 +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
// store ID of selected driver if initialization was successful
if ( strStat.empty() )
2008-10-31 00:23:26 +01:00
{
lCurDev = iDriverIdx;
2008-10-31 00:23:26 +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() )
{
// initialization was successful
bValidDriverDetected = true;
2008-10-31 00:23:26 +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
{
int i;
int iDesiredBufferSizeMono;
2007-12-19 21:16:50 +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.";
}
// 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
{
// 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
// query the usable buffer sizes
ASIOGetBufferSize ( &HWBufferInfo.lMinSize,
&HWBufferInfo.lMaxSize,
&HWBufferInfo.lPreferredSize,
&HWBufferInfo.lGranularity );
// 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;
}
// calculate "nearest" buffer size and set internal parameter accordingly
// first check minimum and maximum values
if ( iDesiredBufferSizeMono < HWBufferInfo.lMinSize )
{
iASIOBufferSizeMono = HWBufferInfo.lMinSize;
}
else
{
if ( iDesiredBufferSizeMono > HWBufferInfo.lMaxSize )
2008-04-13 18:43:21 +02:00
{
iASIOBufferSizeMono = HWBufferInfo.lMaxSize;
2008-04-13 18:43:21 +02:00
}
else
{
// initialization
int iTrialBufSize = HWBufferInfo.lMinSize;
int iLastTrialBufSize = HWBufferInfo.lMinSize;
bool bSizeFound = false;
// test loop
while ( ( iTrialBufSize <= HWBufferInfo.lMaxSize ) && ( !bSizeFound ) )
{
if ( iTrialBufSize >= iDesiredBufferSizeMono )
2008-04-13 18:43:21 +02:00
{
// test which buffer size fits better: the old one or the
// current one
if ( ( iTrialBufSize - iDesiredBufferSizeMono ) >
( iDesiredBufferSizeMono - iLastTrialBufSize ) )
2008-04-13 18:43:21 +02:00
{
iTrialBufSize = iLastTrialBufSize;
2008-04-13 18:43:21 +02:00
}
// exit while loop
bSizeFound = true;
2008-04-13 18:43:21 +02:00
}
2008-07-13 09:26:16 +02: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
}
// set ASIO buffer size
iASIOBufferSizeMono = iTrialBufSize;
2008-04-13 18:43:21 +02:00
}
}
2008-04-08 20:38:55 +02: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;
}
Q_ASSERT ( iMinNumSndBuf < MAX_SND_BUF_IN );
Q_ASSERT ( iMinNumSndBuf < MAX_SND_BUF_OUT );
// 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;
// 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). "
"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 );
}
// prepare input channels
for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ )
{
bufferInfos[i].isInput = ASIOTrue;
bufferInfos[i].channelNum = i;
bufferInfos[i].buffers[0] = 0;
bufferInfos[i].buffers[1] = 0;
}
2008-07-13 15:16:31 +02:00
// prepare output channels
for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ )
{
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;
bufferInfos[NUM_IN_OUT_CHANNELS + i].buffers[1] = 0;
}
2008-07-13 01:33:27 +02:00
// create and activate ASIO buffers (buffer size in samples)
ASIOCreateBuffers ( bufferInfos, 2 /* in/out */ * NUM_IN_OUT_CHANNELS /* stereo */,
iASIOBufferSizeMono, &asioCallbacks );
2008-04-08 20:38:55 +02:00
// now get some buffer details
for ( i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ )
{
channelInfos[i].channel = bufferInfos[i].channelNum;
channelInfos[i].isInput = bufferInfos[i].isInput;
ASIOGetChannelInfo ( &channelInfos[i] );
// 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).";
}
}
// 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 "";
}
void CSound::InitRecordingAndPlayback()
{
// first, stop audio and dispose ASIO buffers
ASIOStop();
ASIOMutex.lock(); // get mutex lock
{
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
iBufferPosCapture = 0;
bCaptureBufferOverrun = false;
// create memory for capture buffer
if ( psCaptureBuffer != NULL )
{
delete[] psCaptureBuffer;
}
psCaptureBuffer = new short[iCurNumSndBufIn * iBufferSizeStereo];
2008-07-14 00:57:31 +02:00
// initialize write block pointer out and underrun flag
iBufferPosPlay = 0;
bPlayBufferUnderrun = false;
// create memory for play buffer
if ( psPlayBuffer != NULL )
{
delete[] psPlayBuffer;
}
psPlayBuffer = new short[iCurNumSndBufOut * iBufferSizeStereo];
// clear new buffer
for ( int i = 0; i < iCurNumSndBufOut * iBufferSizeStereo; i++ )
{
psPlayBuffer[i] = 0;
}
// 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()
{
// stop driver
ASIOStop();
// set event to ensure that thread leaves the waiting function
if ( m_ASIOEvent != NULL )
{
SetEvent ( m_ASIOEvent );
}
// 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;
}
CSound::CSound ( const int iNewBufferSizeStereo )
{
// set internal buffer size value and calculate mono buffer size
iBufferSizeStereo = iNewBufferSizeStereo;
iBufferSizeMono = iBufferSizeStereo / 2;
// init number of sound buffers
2008-07-14 00:57:31 +02:00
iNewNumSndBufIn = NUM_SOUND_BUFFERS_IN;
iCurNumSndBufIn = NUM_SOUND_BUFFERS_IN;
2008-07-14 00:57:31 +02:00
iNewNumSndBufOut = NUM_SOUND_BUFFERS_OUT;
iCurNumSndBufOut = NUM_SOUND_BUFFERS_OUT;
iMinNumSndBuf = 1;
// should be initialized because an error can occur during init
m_ASIOEvent = NULL;
2007-12-18 21:52:48 +01:00
// get available ASIO driver names in system
2008-10-31 00:23:26 +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
2008-10-31 00:23:26 +01:00
// set up the asioCallback structure
2008-03-16 14:10:46 +01:00
asioCallbacks.bufferSwitch = &bufferSwitch;
asioCallbacks.sampleRateDidChange = &sampleRateChanged;
asioCallbacks.asioMessage = &asioMessages;
2007-12-19 21:16:50 +01:00
asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
// init buffer pointer to zero
psCaptureBuffer = NULL;
psPlayBuffer = NULL;
// we use an event controlled structure
// create event
m_ASIOEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
// init flags
bChangParamIn = false;
bChangParamOut = false;
bSetNumSndBufToMinimumValue = false;
}
CSound::~CSound()
{
2007-12-19 21:16:50 +01:00
// cleanup ASIO stuff
ASIOStop();
ASIODisposeBuffers();
ASIOExit();
asioDrivers->removeCurrentDriver();
// delete allocated memory
if ( psCaptureBuffer != NULL )
{
delete[] psCaptureBuffer;
}
if ( psPlayBuffer != NULL )
{
delete[] psPlayBuffer;
}
// close the handle for the event
if ( m_ASIOEvent != NULL )
{
CloseHandle ( m_ASIOEvent );
}
}
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
{
2008-03-16 14:10:46 +01:00
bufferSwitch ( index, processNow );
return 0L;
}
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
{
int iCurSample;
ASIOMutex.lock(); // get mutex lock
{
// first check buffer state of capture and play buffers
const int iCaptureBufferLen = iCurNumSndBufIn * iBufferSizeStereo;
bCaptureBufferOverrun =
( iBufferPosCapture + 2 * iASIOBufferSizeMono > iCaptureBufferLen );
bPlayBufferUnderrun = ( 2 * iASIOBufferSizeMono > iBufferPosPlay );
// perform the processing for input and output
2008-07-13 12:56:40 +02:00
for ( int i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ ) // stereo
{
if ( bufferInfos[i].isInput == ASIOTrue )
{
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
if ( !bCaptureBufferOverrun )
{
// copy new captured block in thread transfer buffer (copy
// mono data interleaved in stereo buffer)
switch ( channelInfos[i].type )
{
case ASIOSTInt16LSB:
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];
}
break;
case ASIOSTInt24LSB:
// 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 );
iCurSam >>= 8;
psCaptureBuffer[iBufferPosCapture +
2 * iCurSample + bufferInfos[i].channelNum] = static_cast<short> ( iCurSam );
}
break;
case ASIOSTInt32LSB:
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
// convert to 16 bit
psCaptureBuffer[iBufferPosCapture +
2 * iCurSample + bufferInfos[i].channelNum] =
(((int*) bufferInfos[i].buffers[index])[iCurSample] >> 16);
}
break;
}
}
}
else
{
// PLAYBACK ----------------------------------------------------
if ( !bPlayBufferUnderrun )
{
// copy data from sound card in output buffer (copy
// interleaved stereo data in mono sound card buffer)
switch ( channelInfos[i].type )
{
case ASIOSTInt16LSB:
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
2008-11-01 23:26:17 +01:00
( (short*) bufferInfos[i].buffers[index] )[iCurSample] =
psPlayBuffer[2 * iCurSample + bufferInfos[i].channelNum];
}
break;
case ASIOSTInt24LSB:
// not yet tested, horrible things might happen with the following code ;-)
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
// 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 );
}
break;
case ASIOSTInt32LSB:
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
// 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 );
}
break;
}
}
2008-07-13 01:33:27 +02:00
}
}
// Manage thread interface buffers for input and output ----------------
// capture
if ( !bCaptureBufferOverrun )
{
iBufferPosCapture += 2 * iASIOBufferSizeMono;
}
// play
if ( !bPlayBufferUnderrun )
2008-07-13 01:33:27 +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
{
psPlayBuffer[iCurSample] =
psPlayBuffer[2 * iASIOBufferSizeMono + iCurSample];
}
2008-07-13 01:33:27 +02:00
// adjust "current block to write" pointer
iBufferPosPlay -= 2 * iASIOBufferSizeMono;
2008-07-13 01:33:27 +02:00
}
2008-07-13 15:16:31 +02:00
2008-07-13 22:03:37 +02:00
// finally if the driver supports the ASIOOutputReady() optimization,
// do it here, all data are in place -----------------------------------
2008-07-13 22:03:37 +02:00
if ( bASIOPostOutput )
{
ASIOOutputReady();
}
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 )
{
long ret = 0;
switch(selector)
{
case kAsioEngineVersion:
// return the supported ASIO version of the host application
ret = 2L;
break;
}
return ret;
}
#else // USE_ASIO_SND_INTERFACE
/******************************************************************************\
* Wave in *
\******************************************************************************/
2007-09-08 12:45:14 +02:00
bool CSound::Read ( CVector<short>& psData )
{
2008-01-27 11:05:15 +01:00
int i;
bool bError;
2007-09-08 12:45:14 +02:00
// check if device must be opened or reinitialized
2008-01-27 11:05:15 +01:00
if ( bChangParamIn )
{
OpenInDevice();
2007-09-08 12:45:14 +02:00
// Reinit sound interface
InitRecording ( iBufferSizeIn, bBlockingRec );
2007-09-08 12:45:14 +02:00
// Reset flag
2008-01-27 11:05:15 +01:00
bChangParamIn = false;
}
2007-09-08 12:45:14 +02:00
// wait until data is available
if ( ! ( m_WaveInHeader[iWhichBufferIn].dwFlags & WHDR_DONE ) )
{
2008-01-27 11:05:15 +01:00
if ( bBlockingRec )
2007-09-08 12:45:14 +02:00
{
WaitForSingleObject ( m_WaveInEvent, INFINITE );
}
else
2007-09-08 12:45:14 +02:00
{
2008-01-27 11:05:15 +01:00
return false;
2007-09-08 12:45:14 +02:00
}
}
2007-09-08 12:45:14 +02:00
// check if buffers got lost
int iNumInBufDone = 0;
2007-09-08 12:45:14 +02:00
for ( i = 0; i < iCurNumSndBufIn; i++ )
{
2007-09-08 12:45:14 +02:00
if ( m_WaveInHeader[i].dwFlags & WHDR_DONE )
{
iNumInBufDone++;
2007-09-08 12:45:14 +02:00
}
}
/* If the number of done buffers equals the total number of buffers, it is
very likely that a buffer got lost -> set error flag */
2007-09-08 12:45:14 +02:00
if ( iNumInBufDone == iCurNumSndBufIn )
{
2008-01-27 11:05:15 +01:00
bError = true;
2007-09-08 12:45:14 +02:00
}
else
2007-09-08 12:45:14 +02:00
{
2008-01-27 11:05:15 +01:00
bError = false;
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// copy data from sound card in output buffer
for ( i = 0; i < iBufferSizeIn; i++ )
{
psData[i] = psSoundcardBuffer[iWhichBufferIn][i];
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// add the buffer so that it can be filled with new samples
AddInBuffer();
2007-09-08 12:45:14 +02:00
// in case more than one buffer was ready, reset event
ResetEvent ( m_WaveInEvent );
return bError;
}
void CSound::AddInBuffer()
{
2007-09-08 12:45:14 +02:00
// unprepare old wave-header
waveInUnprepareHeader (
m_WaveIn, &m_WaveInHeader[iWhichBufferIn], sizeof ( WAVEHDR ) );
2007-09-08 12:45:14 +02:00
// prepare buffers for sending to sound interface
PrepareInBuffer ( iWhichBufferIn );
2007-09-08 12:45:14 +02:00
// send buffer to driver for filling with new data
waveInAddBuffer ( m_WaveIn, &m_WaveInHeader[iWhichBufferIn], sizeof ( WAVEHDR ) );
2007-09-08 12:45:14 +02:00
// toggle buffers
iWhichBufferIn++;
2007-09-08 12:45:14 +02:00
if ( iWhichBufferIn == iCurNumSndBufIn )
{
iWhichBufferIn = 0;
2007-09-08 12:45:14 +02:00
}
}
2007-09-08 12:45:14 +02:00
void CSound::PrepareInBuffer ( int iBufNum )
{
2007-09-08 12:45:14 +02:00
// set struct entries
m_WaveInHeader[iBufNum].lpData = (LPSTR) &psSoundcardBuffer[iBufNum][0];
m_WaveInHeader[iBufNum].dwBufferLength = iBufferSizeIn * BYTES_PER_SAMPLE;
2007-09-08 12:45:14 +02:00
m_WaveInHeader[iBufNum].dwFlags = 0;
2007-09-08 12:45:14 +02:00
// prepare wave-header
waveInPrepareHeader ( m_WaveIn, &m_WaveInHeader[iBufNum], sizeof ( WAVEHDR ) );
}
2007-09-08 12:45:14 +02:00
void CSound::InitRecording ( int iNewBufferSize, bool bNewBlocking )
{
2007-09-08 12:45:14 +02:00
// check if device must be opened or reinitialized
2008-01-27 11:05:15 +01:00
if ( bChangParamIn )
{
OpenInDevice();
2007-09-08 12:45:14 +02:00
// reset flag
2008-01-27 11:05:15 +01:00
bChangParamIn = false;
}
2007-09-08 12:45:14 +02:00
// set internal parameter
iBufferSizeIn = iNewBufferSize;
2008-04-08 20:38:55 +02:00
bBlockingRec = bNewBlocking;
2007-09-08 12:45:14 +02:00
// reset interface so that all buffers are returned from the interface
waveInReset ( m_WaveIn );
waveInStop ( m_WaveIn );
2007-09-08 12:45:14 +02:00
/* reset current buffer ID (it is important to do this BEFORE calling
"AddInBuffer()" */
iWhichBufferIn = 0;
2007-09-08 12:45:14 +02:00
// create memory for sound card buffer
for ( int i = 0; i < iCurNumSndBufIn; i++ )
{
/* Unprepare old wave-header in case that we "re-initialized" this
module. Calling "waveInUnprepareHeader()" with an unprepared
buffer (when the module is initialized for the first time) has
simply no effect */
2007-09-08 12:45:14 +02:00
waveInUnprepareHeader ( m_WaveIn, &m_WaveInHeader[i], sizeof ( WAVEHDR ) );
2007-09-08 12:45:14 +02:00
if ( psSoundcardBuffer[i] != NULL )
{
delete[] psSoundcardBuffer[i];
2007-09-08 12:45:14 +02:00
}
psSoundcardBuffer[i] = new short[iBufferSizeIn];
2008-01-22 20:58:53 +01:00
// Send all buffers to driver for filling the queue --------------------
2007-09-08 12:45:14 +02:00
// prepare buffers before sending them to the sound interface
PrepareInBuffer ( i );
AddInBuffer();
}
2007-09-08 12:45:14 +02:00
// notify that sound capturing can start now
waveInStart ( m_WaveIn );
/* This reset event is very important for initialization, otherwise we will
get errors! */
2007-09-08 12:45:14 +02:00
ResetEvent ( m_WaveInEvent );
}
void CSound::OpenInDevice()
{
2007-09-08 12:45:14 +02:00
// open wave-input and set call-back mechanism to event handle
if ( m_WaveIn != NULL )
{
2007-09-08 12:45:14 +02:00
waveInReset ( m_WaveIn );
waveInClose ( m_WaveIn );
}
2007-09-08 12:45:14 +02:00
MMRESULT result = waveInOpen ( &m_WaveIn, iCurInDev, &sWaveFormatEx,
(DWORD) m_WaveInEvent, NULL, CALLBACK_EVENT );
2007-09-08 12:45:14 +02:00
if ( result != MMSYSERR_NOERROR )
{
2007-09-08 12:45:14 +02:00
throw CGenErr ( "Sound Interface Start, waveInOpen() failed. This error "
"usually occurs if another application blocks the sound in." );
}
}
2007-09-08 12:45:14 +02:00
void CSound::SetInDev ( int iNewDev )
{
2007-09-08 12:45:14 +02:00
// set device to wave mapper if iNewDev is invalid
if ( ( iNewDev >= iNumDevs ) || ( iNewDev < 0 ) )
{
iNewDev = WAVE_MAPPER;
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// change only in case new device id is not already active
if ( iNewDev != iCurInDev )
{
2007-09-08 12:45:14 +02:00
iCurInDev = iNewDev;
2008-01-27 11:05:15 +01:00
bChangParamIn = true;
}
}
2007-09-08 12:45:14 +02:00
void CSound::SetInNumBuf ( int iNewNum )
{
2007-09-08 12:45:14 +02:00
// check new parameter
if ( ( iNewNum >= MAX_SND_BUF_IN ) || ( iNewNum < 1 ) )
{
iNewNum = NUM_SOUND_BUFFERS_IN;
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// change only if parameter is different
if ( iNewNum != iCurNumSndBufIn )
{
iCurNumSndBufIn = iNewNum;
2008-01-27 11:05:15 +01:00
bChangParamIn = true;
}
}
/******************************************************************************\
* Wave out *
\******************************************************************************/
2007-09-08 12:45:14 +02:00
bool CSound::Write ( CVector<short>& psData )
{
int i, j;
int iCntPrepBuf;
int iIndexDoneBuf;
bool bError;
2007-09-08 12:45:14 +02:00
// check if device must be opened or reinitialized
2008-01-27 11:05:15 +01:00
if ( bChangParamOut )
{
OpenOutDevice();
2007-09-08 12:45:14 +02:00
// reinit sound interface
InitPlayback ( iBufferSizeOut, bBlockingPlay );
2007-09-08 12:45:14 +02:00
// reset flag
2008-01-27 11:05:15 +01:00
bChangParamOut = false;
}
2007-09-08 12:45:14 +02:00
// get number of "done"-buffers and position of one of them
GetDoneBuffer ( iCntPrepBuf, iIndexDoneBuf );
2007-09-08 12:45:14 +02:00
// now check special cases (Buffer is full or empty)
if ( iCntPrepBuf == 0 )
{
2008-01-27 11:05:15 +01:00
if ( bBlockingPlay )
{
2008-01-27 11:05:15 +01:00
/* Blocking wave out routine. Always
ensure that the buffer is completely filled to avoid buffer
underruns */
2007-09-08 12:45:14 +02:00
while ( iCntPrepBuf == 0 )
{
2007-09-08 12:45:14 +02:00
WaitForSingleObject ( m_WaveOutEvent, INFINITE );
2007-09-08 12:45:14 +02:00
GetDoneBuffer ( iCntPrepBuf, iIndexDoneBuf );
}
}
else
{
2008-01-22 20:58:53 +01:00
// All buffers are filled, dump new block --------------------------
// It would be better to kill half of the buffer blocks to set the start
// back to the middle: TODO
2008-01-27 11:05:15 +01:00
return true; // an error occurred
}
}
else
2007-09-08 12:45:14 +02:00
{
if ( iCntPrepBuf == iCurNumSndBufOut )
{
/* -----------------------------------------------------------------
Buffer is empty -> send as many cleared blocks to the sound-
interface until half of the buffer size is reached */
// send half of the buffer size blocks to the sound-interface
for ( j = 0; j < iCurNumSndBufOut / 2; j++ )
{
// first, clear these buffers
for ( i = 0; i < iBufferSizeOut; i++ )
{
psPlaybackBuffer[j][i] = 0;
}
// then send them to the interface
AddOutBuffer ( j );
}
// set index for done buffer
iIndexDoneBuf = iCurNumSndBufOut / 2;
2008-01-27 11:05:15 +01:00
bError = true;
2007-09-08 12:45:14 +02:00
}
else
{
2008-01-27 11:05:15 +01:00
bError = false;
2007-09-08 12:45:14 +02:00
}
}
// copy stereo data from input in soundcard buffer
for ( i = 0; i < iBufferSizeOut; i++ )
{
psPlaybackBuffer[iIndexDoneBuf][i] = psData[i];
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// now, send the current block
AddOutBuffer ( iIndexDoneBuf );
return bError;
}
2007-09-08 12:45:14 +02:00
void CSound::GetDoneBuffer ( int& iCntPrepBuf, int& iIndexDoneBuf )
{
2007-09-08 12:45:14 +02:00
// get number of "done"-buffers and position of one of them
iCntPrepBuf = 0;
2007-09-08 12:45:14 +02:00
for ( int i = 0; i < iCurNumSndBufOut; i++ )
{
2007-09-08 12:45:14 +02:00
if ( m_WaveOutHeader[i].dwFlags & WHDR_DONE )
{
iCntPrepBuf++;
iIndexDoneBuf = i;
}
}
}
2007-09-08 12:45:14 +02:00
void CSound::AddOutBuffer ( int iBufNum )
{
2008-01-22 20:58:53 +01:00
// unprepare old wave-header
2007-09-08 12:45:14 +02:00
waveOutUnprepareHeader (
m_WaveOut, &m_WaveOutHeader[iBufNum], sizeof ( WAVEHDR ) );
2008-01-22 20:58:53 +01:00
// prepare buffers for sending to sound interface
2007-09-08 12:45:14 +02:00
PrepareOutBuffer ( iBufNum );
2008-01-22 20:58:53 +01:00
// send buffer to driver for filling with new data
2007-09-08 12:45:14 +02:00
waveOutWrite ( m_WaveOut, &m_WaveOutHeader[iBufNum], sizeof ( WAVEHDR ) );
}
2007-09-08 12:45:14 +02:00
void CSound::PrepareOutBuffer ( int iBufNum )
{
2008-01-22 20:58:53 +01:00
// set Header data
2007-09-08 12:45:14 +02:00
m_WaveOutHeader[iBufNum].lpData = (LPSTR) &psPlaybackBuffer[iBufNum][0];
m_WaveOutHeader[iBufNum].dwBufferLength = iBufferSizeOut * BYTES_PER_SAMPLE;
2007-09-08 12:45:14 +02:00
m_WaveOutHeader[iBufNum].dwFlags = 0;
2008-01-22 20:58:53 +01:00
// prepare wave-header
2007-09-08 12:45:14 +02:00
waveOutPrepareHeader ( m_WaveOut, &m_WaveOutHeader[iBufNum], sizeof ( WAVEHDR ) );
}
2007-09-08 12:45:14 +02:00
void CSound::InitPlayback ( int iNewBufferSize, bool bNewBlocking )
{
int i, j;
2007-09-08 12:45:14 +02:00
// check if device must be opened or reinitialized
2008-01-27 11:05:15 +01:00
if ( bChangParamOut )
{
OpenOutDevice();
2007-09-08 12:45:14 +02:00
// reset flag
2008-01-27 11:05:15 +01:00
bChangParamOut = false;
}
2007-09-08 12:45:14 +02:00
// set internal parameters
iBufferSizeOut = iNewBufferSize;
2007-09-08 12:45:14 +02:00
bBlockingPlay = bNewBlocking;
2007-09-08 12:45:14 +02:00
// reset interface
waveOutReset ( m_WaveOut );
2007-09-08 12:45:14 +02:00
for ( j = 0; j < iCurNumSndBufOut; j++ )
{
/* Unprepare old wave-header (in case header was not prepared before,
simply nothing happens with this function call */
2007-09-08 12:45:14 +02:00
waveOutUnprepareHeader ( m_WaveOut, &m_WaveOutHeader[j], sizeof ( WAVEHDR ) );
2007-09-08 12:45:14 +02:00
// create memory for playback buffer
if ( psPlaybackBuffer[j] != NULL )
{
delete[] psPlaybackBuffer[j];
2007-09-08 12:45:14 +02:00
}
psPlaybackBuffer[j] = new short[iBufferSizeOut];
2007-09-08 12:45:14 +02:00
// clear new buffer
for ( i = 0; i < iBufferSizeOut; i++ )
{
psPlaybackBuffer[j][i] = 0;
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// prepare buffer for sending to the sound interface
PrepareOutBuffer ( j );
2007-09-08 12:45:14 +02:00
// initially, send all buffers to the interface
AddOutBuffer ( j );
}
}
void CSound::OpenOutDevice()
{
2007-09-08 12:45:14 +02:00
if ( m_WaveOut != NULL )
{
2007-09-08 12:45:14 +02:00
waveOutReset ( m_WaveOut );
waveOutClose ( m_WaveOut );
}
2007-09-08 12:45:14 +02:00
MMRESULT result = waveOutOpen ( &m_WaveOut, iCurOutDev, &sWaveFormatEx,
(DWORD) m_WaveOutEvent, NULL, CALLBACK_EVENT );
2007-09-08 12:45:14 +02:00
if ( result != MMSYSERR_NOERROR )
{
throw CGenErr ( "Sound Interface Start, waveOutOpen() failed." );
}
}
2007-09-08 12:45:14 +02:00
void CSound::SetOutDev ( int iNewDev )
{
2007-09-08 12:45:14 +02:00
// set device to wave mapper if iNewDev is invalid
if ( ( iNewDev >= iNumDevs ) || ( iNewDev < 0 ) )
{
iNewDev = WAVE_MAPPER;
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// change only in case new device id is not already active
if ( iNewDev != iCurOutDev )
{
2007-09-08 12:45:14 +02:00
iCurOutDev = iNewDev;
2008-01-27 11:05:15 +01:00
bChangParamOut = true;
}
}
2007-09-08 12:45:14 +02:00
void CSound::SetOutNumBuf ( int iNewNum )
{
2007-09-08 12:45:14 +02:00
// check new parameter
if ( ( iNewNum >= MAX_SND_BUF_OUT ) || ( iNewNum < 1 ) )
{
iNewNum = NUM_SOUND_BUFFERS_OUT;
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// change only if parameter is different
if ( iNewNum != iCurNumSndBufOut )
{
iCurNumSndBufOut = iNewNum;
2008-01-27 11:05:15 +01:00
bChangParamOut = true;
}
}
/******************************************************************************\
* Common *
\******************************************************************************/
void CSound::Close()
{
2008-01-27 11:05:15 +01:00
int i;
MMRESULT result;
2007-09-08 12:45:14 +02:00
// reset audio driver
if ( m_WaveOut != NULL )
{
2007-09-08 12:45:14 +02:00
result = waveOutReset ( m_WaveOut );
if ( result != MMSYSERR_NOERROR )
{
throw CGenErr ( "Sound Interface, waveOutReset() failed." );
}
}
2007-09-08 12:45:14 +02:00
if ( m_WaveIn != NULL )
{
2007-09-08 12:45:14 +02:00
result = waveInReset ( m_WaveIn );
if ( result != MMSYSERR_NOERROR )
{
throw CGenErr ( "Sound Interface, waveInReset() failed." );
}
}
2007-09-08 12:45:14 +02:00
// set event to ensure that thread leaves the waiting function
if ( m_WaveInEvent != NULL )
{
SetEvent(m_WaveInEvent);
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// wait for the thread to terminate
Sleep ( 500 );
2007-09-08 12:45:14 +02:00
// unprepare wave-headers
if ( m_WaveIn != NULL )
{
2007-09-08 12:45:14 +02:00
for ( i = 0; i < iCurNumSndBufIn; i++ )
{
2007-09-08 12:45:14 +02:00
result = waveInUnprepareHeader (
m_WaveIn, &m_WaveInHeader[i], sizeof ( WAVEHDR ) );
2007-09-08 12:45:14 +02:00
if ( result != MMSYSERR_NOERROR )
{
2007-09-08 12:45:14 +02:00
throw CGenErr ( "Sound Interface, waveInUnprepareHeader()"
" failed." );
}
}
2007-09-08 12:45:14 +02:00
// close the sound in device
result = waveInClose ( m_WaveIn );
if ( result != MMSYSERR_NOERROR )
{
throw CGenErr ( "Sound Interface, waveInClose() failed." );
}
}
2007-09-08 12:45:14 +02:00
if ( m_WaveOut != NULL )
{
2007-09-08 12:45:14 +02:00
for ( i = 0; i < iCurNumSndBufOut; i++ )
{
2007-09-08 12:45:14 +02:00
result = waveOutUnprepareHeader (
m_WaveOut, &m_WaveOutHeader[i], sizeof ( WAVEHDR ) );
2007-09-08 12:45:14 +02:00
if ( result != MMSYSERR_NOERROR )
{
2007-09-08 12:45:14 +02:00
throw CGenErr ( "Sound Interface, waveOutUnprepareHeader()"
" failed." );
}
}
2007-09-08 12:45:14 +02:00
// close the sound out device
result = waveOutClose ( m_WaveOut );
if ( result != MMSYSERR_NOERROR )
{
throw CGenErr ( "Sound Interface, waveOutClose() failed." );
}
}
2007-09-08 12:45:14 +02:00
// set flag to open devices the next time it is initialized
2008-01-27 11:05:15 +01:00
bChangParamIn = true;
bChangParamOut = true;
}
CSound::CSound()
{
int i;
2007-09-08 12:45:14 +02:00
// init number of sound buffers
iCurNumSndBufIn = NUM_SOUND_BUFFERS_IN;
iCurNumSndBufOut = NUM_SOUND_BUFFERS_OUT;
2007-09-08 12:45:14 +02:00
// should be initialized because an error can occur during init
m_WaveInEvent = NULL;
m_WaveOutEvent = NULL;
2007-09-08 12:45:14 +02:00
m_WaveIn = NULL;
m_WaveOut = NULL;
2007-09-08 12:45:14 +02:00
// init buffer pointer to zero
for ( i = 0; i < MAX_SND_BUF_IN; i++ )
{
2007-09-08 12:45:14 +02:00
memset ( &m_WaveInHeader[i], 0, sizeof ( WAVEHDR ) );
psSoundcardBuffer[i] = NULL;
}
2007-09-08 12:45:14 +02:00
for ( i = 0; i < MAX_SND_BUF_OUT; i++ )
{
2007-09-08 12:45:14 +02:00
memset ( &m_WaveOutHeader[i], 0, sizeof ( WAVEHDR ) );
psPlaybackBuffer[i] = NULL;
}
2007-09-08 12:45:14 +02:00
// init wave-format structure
2008-01-27 11:05:15 +01:00
sWaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
sWaveFormatEx.nChannels = NUM_IN_OUT_CHANNELS;
sWaveFormatEx.wBitsPerSample = BITS_PER_SAMPLE;
sWaveFormatEx.nSamplesPerSec = SND_CRD_SAMPLE_RATE;
sWaveFormatEx.nBlockAlign = sWaveFormatEx.nChannels *
sWaveFormatEx.wBitsPerSample / 8;
sWaveFormatEx.nAvgBytesPerSec = sWaveFormatEx.nBlockAlign *
sWaveFormatEx.nSamplesPerSec;
2008-01-27 11:05:15 +01:00
sWaveFormatEx.cbSize = 0;
2007-09-08 12:45:14 +02:00
// get the number of digital audio devices in this computer, check range
iNumDevs = waveInGetNumDevs();
2007-09-08 12:45:14 +02:00
if ( iNumDevs > MAX_NUMBER_SOUND_CARDS )
{
iNumDevs = MAX_NUMBER_SOUND_CARDS;
2007-09-08 12:45:14 +02:00
}
2007-09-08 12:45:14 +02:00
// at least one device must exist in the system
if ( iNumDevs == 0 )
{
throw CGenErr ( "No audio device found." );
}
2007-09-08 12:45:14 +02:00
// get info about the devices and store the names
for ( i = 0; i < iNumDevs; i++ )
{
2007-09-08 12:45:14 +02:00
if ( !waveInGetDevCaps ( i, &m_WaveInDevCaps, sizeof ( WAVEINCAPS ) ) )
{
pstrDevices[i] = m_WaveInDevCaps.szPname;
2007-09-08 12:45:14 +02:00
}
}
2007-09-08 12:45:14 +02:00
// we use an event controlled wave-in (wave-out) structure
// create events
2008-01-27 11:05:15 +01:00
m_WaveInEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
2007-09-08 12:45:14 +02:00
m_WaveOutEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
2007-09-08 12:45:14 +02:00
// set flag to open devices
2008-01-27 11:05:15 +01:00
bChangParamIn = true;
bChangParamOut = true;
2007-09-08 12:45:14 +02:00
// default device number, "wave mapper"
iCurInDev = WAVE_MAPPER;
iCurOutDev = WAVE_MAPPER;
2007-09-08 12:45:14 +02:00
// non-blocking wave out is default
2008-01-27 11:05:15 +01:00
bBlockingPlay = false;
2007-09-08 12:45:14 +02:00
// blocking wave in is default
2008-01-27 11:05:15 +01:00
bBlockingRec = true;
}
CSound::~CSound()
{
int i;
2007-09-08 12:45:14 +02:00
// delete allocated memory
for ( i = 0; i < iCurNumSndBufIn; i++ )
{
2007-09-08 12:45:14 +02:00
if ( psSoundcardBuffer[i] != NULL )
{
delete[] psSoundcardBuffer[i];
2007-09-08 12:45:14 +02:00
}
}
2007-09-08 12:45:14 +02:00
for ( i = 0; i < iCurNumSndBufOut; i++ )
{
2007-09-08 12:45:14 +02:00
if ( psPlaybackBuffer[i] != NULL )
{
delete[] psPlaybackBuffer[i];
2007-09-08 12:45:14 +02:00
}
}
2007-09-08 12:45:14 +02:00
// close the handle for the events
if ( m_WaveInEvent != NULL )
{
CloseHandle ( m_WaveInEvent );
}
2007-09-08 12:45:14 +02:00
if ( m_WaveOutEvent != NULL )
{
CloseHandle ( m_WaveOutEvent );
}
}
#endif // USE_ASIO_SND_INTERFACE