2006-12-18 15:39:33 +01:00
|
|
|
/******************************************************************************\
|
2008-04-08 20:38:55 +02:00
|
|
|
* Copyright (c) 2004-2008
|
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 *************************************************************/
|
2007-12-17 22:28:40 +01:00
|
|
|
#ifdef USE_ASIO_SND_INTERFACE
|
|
|
|
|
2007-12-18 21:52:48 +01:00
|
|
|
// external references
|
|
|
|
extern AsioDrivers* asioDrivers;
|
|
|
|
bool loadAsioDriver ( char *name );
|
|
|
|
|
|
|
|
|
2007-12-17 22:28:40 +01:00
|
|
|
/******************************************************************************\
|
|
|
|
* Wave in *
|
|
|
|
\******************************************************************************/
|
|
|
|
bool CSound::Read ( CVector<short>& psData )
|
|
|
|
{
|
|
|
|
int i;
|
2008-01-22 22:15:04 +01:00
|
|
|
bool bError = false;
|
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
|
|
|
{
|
|
|
|
// Reinit sound interface
|
2007-12-19 21:16:50 +01:00
|
|
|
InitRecordingAndPlayback ( iBufferSize );
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
// Reset flag
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamIn = false;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
// wait until data is available
|
|
|
|
if ( ! ( m_WaveInHeader[iWhichBufferIn].dwFlags & WHDR_DONE ) )
|
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
if ( bBlockingRec )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
|
|
|
WaitForSingleObject ( m_WaveInEvent, INFINITE );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
return false;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
// check if buffers got lost
|
|
|
|
int iNumInBufDone = 0;
|
|
|
|
for ( i = 0; i < iCurNumSndBufIn; i++ )
|
|
|
|
{
|
|
|
|
if ( m_WaveInHeader[i].dwFlags & WHDR_DONE )
|
|
|
|
{
|
|
|
|
iNumInBufDone++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
// If the number of done buffers equals the total number of buffers, it is
|
|
|
|
// very likely that a buffer got lost -> set error flag
|
|
|
|
if ( iNumInBufDone == iCurNumSndBufIn )
|
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
bError = true;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
bError = false;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2008-01-02 23:16:38 +01:00
|
|
|
|
|
|
|
/*
|
2007-12-17 22:28:40 +01:00
|
|
|
// copy data from sound card in output buffer
|
2007-12-19 21:16:50 +01:00
|
|
|
for ( i = 0; i < iBufferSize; i++ )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
|
|
|
psData[i] = psSoundcardBuffer[iWhichBufferIn][i];
|
|
|
|
}
|
|
|
|
|
|
|
|
// add the buffer so that it can be filled with new samples
|
|
|
|
AddInBuffer();
|
|
|
|
|
|
|
|
// in case more than one buffer was ready, reset event
|
|
|
|
ResetEvent ( m_WaveInEvent );
|
2008-01-02 23:16:38 +01:00
|
|
|
*/
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
return bError;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::AddInBuffer()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
// unprepare old wave-header
|
|
|
|
waveInUnprepareHeader (
|
|
|
|
m_WaveIn, &m_WaveInHeader[iWhichBufferIn], sizeof ( WAVEHDR ) );
|
|
|
|
|
|
|
|
// prepare buffers for sending to sound interface
|
|
|
|
PrepareInBuffer ( iWhichBufferIn );
|
|
|
|
|
|
|
|
// send buffer to driver for filling with new data
|
|
|
|
waveInAddBuffer ( m_WaveIn, &m_WaveInHeader[iWhichBufferIn], sizeof ( WAVEHDR ) );
|
|
|
|
*/
|
|
|
|
|
|
|
|
// toggle buffers
|
|
|
|
iWhichBufferIn++;
|
|
|
|
if ( iWhichBufferIn == iCurNumSndBufIn )
|
|
|
|
{
|
|
|
|
iWhichBufferIn = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::PrepareInBuffer ( int iBufNum )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
// set struct entries
|
|
|
|
m_WaveInHeader[iBufNum].lpData = (LPSTR) &psSoundcardBuffer[iBufNum][0];
|
|
|
|
m_WaveInHeader[iBufNum].dwBufferLength = iBufferSizeIn * BYTES_PER_SAMPLE;
|
|
|
|
m_WaveInHeader[iBufNum].dwFlags = 0;
|
|
|
|
|
|
|
|
// prepare wave-header
|
|
|
|
waveInPrepareHeader ( m_WaveIn, &m_WaveInHeader[iBufNum], sizeof ( WAVEHDR ) );
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::SetInNumBuf ( int iNewNum )
|
|
|
|
{
|
|
|
|
// check new parameter
|
|
|
|
if ( ( iNewNum >= MAX_SND_BUF_IN ) || ( iNewNum < 1 ) )
|
|
|
|
{
|
|
|
|
iNewNum = NUM_SOUND_BUFFERS_IN;
|
|
|
|
}
|
|
|
|
|
|
|
|
// change only if parameter is different
|
|
|
|
if ( iNewNum != iCurNumSndBufIn )
|
|
|
|
{
|
|
|
|
iCurNumSndBufIn = iNewNum;
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamIn = true;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************\
|
|
|
|
* Wave out *
|
|
|
|
\******************************************************************************/
|
|
|
|
bool CSound::Write ( CVector<short>& psData )
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
int iCntPrepBuf;
|
|
|
|
int iIndexDoneBuf;
|
|
|
|
bool bError;
|
|
|
|
|
|
|
|
// 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
|
|
|
{
|
|
|
|
// reinit sound interface
|
2007-12-19 21:16:50 +01:00
|
|
|
InitRecordingAndPlayback ( iBufferSize );
|
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-01-02 23:16:38 +01:00
|
|
|
/*
|
2007-12-17 22:28:40 +01:00
|
|
|
// get number of "done"-buffers and position of one of them
|
|
|
|
GetDoneBuffer ( iCntPrepBuf, iIndexDoneBuf );
|
|
|
|
|
|
|
|
// now check special cases (Buffer is full or empty)
|
|
|
|
if ( iCntPrepBuf == 0 )
|
|
|
|
{
|
2008-01-02 23:16:38 +01:00
|
|
|
// Blocking wave out routine. Needed for transmitter. Always
|
|
|
|
// ensure that the buffer is completely filled to avoid buffer
|
|
|
|
// underruns
|
2007-12-19 21:16:50 +01:00
|
|
|
while ( iCntPrepBuf == 0 )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
WaitForSingleObject ( m_WaveOutEvent, INFINITE );
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
GetDoneBuffer ( iCntPrepBuf, iIndexDoneBuf );
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( iCntPrepBuf == iCurNumSndBufOut )
|
|
|
|
{
|
2008-01-02 23:16:38 +01:00
|
|
|
// -----------------------------------------------------------------
|
|
|
|
// Buffer is empty -> send as many cleared blocks to the sound-
|
|
|
|
// interface until half of the buffer size is reached
|
2007-12-17 22:28:40 +01:00
|
|
|
// send half of the buffer size blocks to the sound-interface
|
|
|
|
for ( j = 0; j < iCurNumSndBufOut / 2; j++ )
|
|
|
|
{
|
|
|
|
// first, clear these buffers
|
2007-12-19 21:16:50 +01:00
|
|
|
for ( i = 0; i < iBufferSize; i++ )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
|
|
|
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-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
bError = false;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
}
|
2008-01-02 23:16:38 +01:00
|
|
|
*/
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-28 22:39:16 +01:00
|
|
|
/*
|
2007-12-17 22:28:40 +01:00
|
|
|
// copy stereo data from input in soundcard buffer
|
2007-12-19 21:16:50 +01:00
|
|
|
for ( i = 0; i < iBufferSize; i++ )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
|
|
|
psPlaybackBuffer[iIndexDoneBuf][i] = psData[i];
|
|
|
|
}
|
2007-12-28 22:39:16 +01:00
|
|
|
*/
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-01-02 23:16:38 +01:00
|
|
|
/*
|
2007-12-17 22:28:40 +01:00
|
|
|
// now, send the current block
|
|
|
|
AddOutBuffer ( iIndexDoneBuf );
|
2008-01-02 23:16:38 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
// TEST
|
|
|
|
Sleep(10);
|
|
|
|
return true;
|
|
|
|
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
return bError;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::GetDoneBuffer ( int& iCntPrepBuf, int& iIndexDoneBuf )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
// get number of "done"-buffers and position of one of them
|
|
|
|
iCntPrepBuf = 0;
|
|
|
|
for ( int i = 0; i < iCurNumSndBufOut; i++ )
|
|
|
|
{
|
|
|
|
if ( m_WaveOutHeader[i].dwFlags & WHDR_DONE )
|
|
|
|
{
|
|
|
|
iCntPrepBuf++;
|
|
|
|
iIndexDoneBuf = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::AddOutBuffer ( int iBufNum )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
// Unprepare old wave-header
|
|
|
|
waveOutUnprepareHeader (
|
|
|
|
m_WaveOut, &m_WaveOutHeader[iBufNum], sizeof ( WAVEHDR ) );
|
|
|
|
|
|
|
|
// Prepare buffers for sending to sound interface
|
|
|
|
PrepareOutBuffer ( iBufNum );
|
|
|
|
|
|
|
|
// Send buffer to driver for filling with new data
|
|
|
|
waveOutWrite ( m_WaveOut, &m_WaveOutHeader[iBufNum], sizeof ( WAVEHDR ) );
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::PrepareOutBuffer ( int iBufNum )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
// Set Header data
|
|
|
|
m_WaveOutHeader[iBufNum].lpData = (LPSTR) &psPlaybackBuffer[iBufNum][0];
|
|
|
|
m_WaveOutHeader[iBufNum].dwBufferLength = iBufferSizeOut * BYTES_PER_SAMPLE;
|
|
|
|
m_WaveOutHeader[iBufNum].dwFlags = 0;
|
|
|
|
|
|
|
|
// Prepare wave-header
|
|
|
|
waveOutPrepareHeader ( m_WaveOut, &m_WaveOutHeader[iBufNum], sizeof ( WAVEHDR ) );
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
if ( ( iNewNum >= MAX_SND_BUF_OUT ) || ( iNewNum < 1 ) )
|
|
|
|
{
|
|
|
|
iNewNum = NUM_SOUND_BUFFERS_OUT;
|
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
// change only if parameter is different
|
|
|
|
if ( iNewNum != iCurNumSndBufOut )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
iCurNumSndBufOut = iNewNum;
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamOut = true;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
2007-12-19 21:16:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************\
|
|
|
|
* Common *
|
|
|
|
\******************************************************************************/
|
|
|
|
void CSound::InitRecordingAndPlayback ( int iNewBufferSize )
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
// first, stop audio
|
|
|
|
ASIOStop();
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-04-13 18:43:21 +02:00
|
|
|
// calculate "nearest" buffer size and set internal parameter accordingly
|
|
|
|
// first check minimum and maximum values
|
|
|
|
if ( iNewBufferSize < HWBufferInfo.lMinSize )
|
|
|
|
{
|
|
|
|
iBufferSize = HWBufferInfo.lMinSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( iNewBufferSize > HWBufferInfo.lMaxSize )
|
|
|
|
{
|
|
|
|
iBufferSize = HWBufferInfo.lMaxSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// initialization
|
|
|
|
int iTrialBufSize = HWBufferInfo.lMinSize;
|
|
|
|
int iLastTrialBufSize = HWBufferInfo.lMinSize;
|
|
|
|
bool bSizeFound = false;
|
2007-12-19 21:16:50 +01:00
|
|
|
|
2008-04-13 18:43:21 +02:00
|
|
|
// test loop
|
|
|
|
while ( ( iTrialBufSize <= HWBufferInfo.lMaxSize ) && ( !bSizeFound ) )
|
|
|
|
{
|
|
|
|
if ( iTrialBufSize > iNewBufferSize )
|
|
|
|
{
|
|
|
|
// test which buffer size fits better: the old one or the
|
|
|
|
// current one
|
|
|
|
if ( ( iTrialBufSize - iNewBufferSize ) < ( iNewBufferSize - iLastTrialBufSize ) )
|
|
|
|
{
|
|
|
|
iBufferSize = iTrialBufSize;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
iBufferSize = iLastTrialBufSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
// exit while loop
|
|
|
|
bSizeFound = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2008-04-08 20:38:55 +02:00
|
|
|
// create and activate buffers
|
|
|
|
ASIOCreateBuffers(bufferInfos, 2 * NUM_IN_OUT_CHANNELS,
|
|
|
|
iBufferSize * BYTES_PER_SAMPLE, &asioCallbacks);
|
|
|
|
|
|
|
|
// now set all the buffer details
|
|
|
|
for ( i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ )
|
|
|
|
{
|
|
|
|
channelInfos[i].channel = NUM_IN_OUT_CHANNELS;
|
|
|
|
channelInfos[i].isInput = bufferInfos[i].isInput;
|
|
|
|
ASIOGetChannelInfo ( &channelInfos[i] );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-17 22:28:40 +01:00
|
|
|
/*
|
2007-12-19 21:16:50 +01:00
|
|
|
// reset interface so that all buffers are returned from the interface
|
|
|
|
waveInReset ( m_WaveIn );
|
|
|
|
waveInStop ( m_WaveIn );
|
2007-12-17 22:28:40 +01:00
|
|
|
*/
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
// reset current buffer ID (it is important to do this BEFORE calling
|
|
|
|
// "AddInBuffer()"
|
|
|
|
iWhichBufferIn = 0;
|
|
|
|
|
|
|
|
// create memory for sound card buffer
|
|
|
|
for ( i = 0; i < iCurNumSndBufIn; i++ )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
|
|
|
/*
|
2007-12-19 21:16:50 +01:00
|
|
|
// 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
|
|
|
|
waveInUnprepareHeader ( m_WaveIn, &m_WaveInHeader[i], sizeof ( WAVEHDR ) );
|
2007-12-17 22:28:40 +01:00
|
|
|
*/
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
if ( psSoundcardBuffer[i] != NULL )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
delete[] psSoundcardBuffer[i];
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
psSoundcardBuffer[i] = new short[iBufferSize];
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
/* Send all buffers to driver for filling the queue ----------------- */
|
|
|
|
// prepare buffers before sending them to the sound interface
|
|
|
|
PrepareInBuffer ( i );
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
AddInBuffer();
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2007-12-19 21:16:50 +01:00
|
|
|
// notify that sound capturing can start now
|
|
|
|
waveInStart ( m_WaveIn );
|
|
|
|
*/
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
// This reset event is very important for initialization, otherwise we will
|
|
|
|
// get errors!
|
|
|
|
ResetEvent ( m_WaveInEvent );
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
/*
|
2007-12-19 21:16:50 +01:00
|
|
|
// reset interface
|
|
|
|
waveOutReset ( m_WaveOut );
|
|
|
|
*/
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
for ( j = 0; j < iCurNumSndBufOut; j++ )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
/*
|
|
|
|
// Unprepare old wave-header (in case header was not prepared before,
|
|
|
|
// simply nothing happens with this function call
|
|
|
|
waveOutUnprepareHeader ( m_WaveOut, &m_WaveOutHeader[j], sizeof ( WAVEHDR ) );
|
|
|
|
*/
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
// create memory for playback buffer
|
|
|
|
if ( psPlaybackBuffer[j] != NULL )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
delete[] psPlaybackBuffer[j];
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
psPlaybackBuffer[j] = new short[iBufferSize];
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
// clear new buffer
|
|
|
|
for ( i = 0; i < iBufferSize; i++ )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
psPlaybackBuffer[j][i] = 0;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
2007-12-19 21:16:50 +01:00
|
|
|
|
|
|
|
// prepare buffer for sending to the sound interface
|
|
|
|
PrepareOutBuffer ( j );
|
|
|
|
|
|
|
|
// initially, send all buffers to the interface
|
|
|
|
AddOutBuffer ( j );
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
|
|
|
|
// initialization is done, (re)start audio
|
|
|
|
ASIOStart();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::Close()
|
|
|
|
{
|
2007-12-17 22:28:40 +01:00
|
|
|
// set event to ensure that thread leaves the waiting function
|
|
|
|
if ( m_WaveInEvent != NULL )
|
|
|
|
{
|
|
|
|
SetEvent(m_WaveInEvent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
}
|
|
|
|
|
|
|
|
CSound::CSound()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// init number of sound buffers
|
|
|
|
iCurNumSndBufIn = NUM_SOUND_BUFFERS_IN;
|
|
|
|
iCurNumSndBufOut = NUM_SOUND_BUFFERS_OUT;
|
|
|
|
|
|
|
|
// should be initialized because an error can occur during init
|
|
|
|
m_WaveInEvent = NULL;
|
|
|
|
m_WaveOutEvent = NULL;
|
|
|
|
|
2007-12-18 21:52:48 +01:00
|
|
|
// get available ASIO driver names in system
|
|
|
|
char* cDriverNames[MAX_NUMBER_SOUND_CARDS];
|
|
|
|
for ( i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ )
|
|
|
|
{
|
|
|
|
cDriverNames[i] = new char[32];
|
|
|
|
}
|
|
|
|
|
|
|
|
loadAsioDriver ( "dummy" ); // to initialize external object
|
|
|
|
const long lNumDetDriv = asioDrivers->getDriverNames ( cDriverNames, MAX_NUMBER_SOUND_CARDS );
|
|
|
|
|
|
|
|
|
|
|
|
// load and initialize first valid ASIO driver
|
|
|
|
bool bValidDriverDetected = false;
|
|
|
|
int iCurDriverIdx = 0;
|
|
|
|
|
|
|
|
while ( !bValidDriverDetected && iCurDriverIdx < lNumDetDriv )
|
|
|
|
{
|
|
|
|
if ( loadAsioDriver ( cDriverNames[iCurDriverIdx] ) )
|
|
|
|
{
|
|
|
|
if ( ASIOInit ( &driverInfo ) == ASE_OK )
|
|
|
|
{
|
|
|
|
bValidDriverDetected = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// driver could not be loaded, free memory
|
|
|
|
asioDrivers->removeCurrentDriver();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// try next driver
|
|
|
|
iCurDriverIdx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// in case we do not have a driver available, throw error
|
|
|
|
if ( !bValidDriverDetected )
|
|
|
|
{
|
|
|
|
throw CGenErr ( "No suitable ASIO audio device found." );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TEST we only use one driver for a first try
|
|
|
|
iNumDevs = 1;
|
|
|
|
pstrDevices[0] = driverInfo.name;
|
|
|
|
|
|
|
|
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
// check the number of available channels
|
|
|
|
long lNumInChan;
|
|
|
|
long lNumOutChan;
|
|
|
|
ASIOGetChannels ( &lNumInChan, &lNumOutChan );
|
2008-04-08 20:38:55 +02:00
|
|
|
if ( ( lNumInChan < NUM_IN_OUT_CHANNELS ) || ( lNumOutChan < NUM_IN_OUT_CHANNELS ) )
|
2007-12-19 21:16:50 +01:00
|
|
|
{
|
|
|
|
throw CGenErr ( "The audio device does not support required number of channels." );
|
|
|
|
}
|
2007-12-18 21:52:48 +01:00
|
|
|
|
2008-04-13 18:43:21 +02:00
|
|
|
// query the usable buffer sizes
|
|
|
|
ASIOGetBufferSize ( &HWBufferInfo.lMinSize,
|
|
|
|
&HWBufferInfo.lMaxSize,
|
|
|
|
&HWBufferInfo.lPreferredSize,
|
|
|
|
&HWBufferInfo.lGranularity );
|
2007-12-17 22:28:40 +01:00
|
|
|
|
2007-12-19 21:16:50 +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 )
|
2007-12-17 22:28:40 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
throw CGenErr ( "The audio device does not support required sample rate." );
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 );
|
|
|
|
|
|
|
|
// set up the asioCallback structure and create the ASIO data buffer
|
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;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// init buffer pointer to zero
|
|
|
|
for ( i = 0; i < MAX_SND_BUF_IN; i++ )
|
|
|
|
{
|
|
|
|
psSoundcardBuffer[i] = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( i = 0; i < MAX_SND_BUF_OUT; i++ )
|
|
|
|
{
|
|
|
|
psPlaybackBuffer[i] = NULL;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// we use an event controlled wave-in (wave-out) structure
|
|
|
|
// create events
|
2007-12-19 21:16:50 +01:00
|
|
|
m_WaveInEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
|
2007-12-17 22:28:40 +01:00
|
|
|
m_WaveOutEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
|
|
|
|
|
|
|
|
// set flag to open devices
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamIn = true;
|
|
|
|
bChangParamOut = true;
|
2007-12-17 22:28:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
CSound::~CSound()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
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
|
|
|
|
for ( i = 0; i < iCurNumSndBufIn; i++ )
|
|
|
|
{
|
|
|
|
if ( psSoundcardBuffer[i] != NULL )
|
|
|
|
{
|
|
|
|
delete[] psSoundcardBuffer[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for ( i = 0; i < iCurNumSndBufOut; i++ )
|
|
|
|
{
|
|
|
|
if ( psPlaybackBuffer[i] != NULL )
|
|
|
|
{
|
|
|
|
delete[] psPlaybackBuffer[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
// close the handle for the events
|
|
|
|
if ( m_WaveInEvent != NULL )
|
|
|
|
{
|
|
|
|
CloseHandle ( m_WaveInEvent );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( m_WaveOutEvent != NULL )
|
|
|
|
{
|
|
|
|
CloseHandle ( m_WaveOutEvent );
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2007-12-19 21:16:50 +01:00
|
|
|
// ASIO callbacks -------------------------------------------------------------
|
|
|
|
ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime *timeInfo, long index, ASIOBool processNow )
|
|
|
|
{
|
2008-03-16 14:10:46 +01:00
|
|
|
bufferSwitch ( index, processNow );
|
|
|
|
return 0L;
|
|
|
|
}
|
2007-12-19 21:16:50 +01:00
|
|
|
|
2008-04-08 20:38:55 +02:00
|
|
|
void CSound::bufferSwitch ( long index, ASIOBool processNow )
|
2008-03-16 14:10:46 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
|
|
|
|
|
2008-03-16 14:10:46 +01:00
|
|
|
static long processedSamples = 0;
|
2007-12-19 21:16:50 +01:00
|
|
|
|
|
|
|
|
2008-03-16 14:10:46 +01:00
|
|
|
/*
|
2007-12-19 21:16:50 +01:00
|
|
|
// buffer size in samples
|
2008-03-16 14:10:46 +01:00
|
|
|
// long buffSize = asioDriverInfo.preferredSize;
|
2007-12-19 21:16:50 +01:00
|
|
|
|
|
|
|
// perform the processing
|
2008-03-16 14:10:46 +01:00
|
|
|
for ( int i = 0; i < asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers; i++ )
|
2007-12-19 21:16:50 +01:00
|
|
|
{
|
2008-03-16 14:10:46 +01:00
|
|
|
if ( asioDriverInfo.bufferInfos[i].isInput == false )
|
2007-12-19 21:16:50 +01:00
|
|
|
{
|
|
|
|
// OK do processing for the outputs only
|
2008-03-16 14:10:46 +01:00
|
|
|
switch ( asioDriverInfo.channelInfos[i].type )
|
2007-12-19 21:16:50 +01:00
|
|
|
{
|
|
|
|
case ASIOSTInt16LSB:
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 2 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
case ASIOSTInt24LSB: // used for 20 bits as well
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
case ASIOSTInt32LSB:
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 8 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
// these are used for 32 bit data buffer, with different alignment of the data inside
|
|
|
|
// 32 bit PCI bus systems can more easily used with these
|
|
|
|
case ASIOSTInt32LSB16: // 32 bit data with 18 bit alignment
|
|
|
|
case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
|
|
|
|
case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
|
|
|
|
case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case ASIOSTInt16MSB:
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 2 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
case ASIOSTInt24MSB: // used for 20 bits as well
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 3 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
case ASIOSTInt32MSB:
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 8 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
// these are used for 32 bit data buffer, with different alignment of the data inside
|
|
|
|
// 32 bit PCI bus systems can more easily used with these
|
|
|
|
case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment
|
|
|
|
case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
|
|
|
|
case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
|
|
|
|
case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
|
2008-03-16 14:10:46 +01:00
|
|
|
memset ( asioDriverInfo.bufferInfos[i].buffers[index], 0, buffSize * 4 );
|
2007-12-19 21:16:50 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// finally if the driver supports the ASIOOutputReady() optimization, do it here, all data are in place
|
2008-03-16 14:10:46 +01:00
|
|
|
if ( asioDriverInfo.postOutput )
|
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
ASIOOutputReady();
|
2008-03-16 14:10:46 +01:00
|
|
|
}
|
2007-12-19 21:16:50 +01:00
|
|
|
|
2008-03-16 14:10:46 +01:00
|
|
|
if ( processedSamples >= asioDriverInfo.sampleRate * TEST_RUN_TIME ) // roughly measured
|
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
asioDriverInfo.stopped = true;
|
2008-03-16 14:10:46 +01:00
|
|
|
}
|
2007-12-19 21:16:50 +01:00
|
|
|
else
|
2008-03-16 14:10:46 +01:00
|
|
|
{
|
2007-12-19 21:16:50 +01:00
|
|
|
processedSamples += buffSize;
|
2008-03-16 14:10:46 +01:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
2007-12-17 22:28:40 +01:00
|
|
|
#else // USE_ASIO_SND_INTERFACE
|
|
|
|
|
2006-12-18 15:39:33 +01:00
|
|
|
/******************************************************************************\
|
|
|
|
* Wave in *
|
|
|
|
\******************************************************************************/
|
2007-09-08 12:45:14 +02:00
|
|
|
bool CSound::Read ( CVector<short>& psData )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
int i;
|
|
|
|
bool bError;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
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 )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
OpenInDevice();
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// Reinit sound interface
|
|
|
|
InitRecording ( iBufferSizeIn, bBlockingRec );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// Reset flag
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamIn = false;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// wait until data is available
|
|
|
|
if ( ! ( m_WaveInHeader[iWhichBufferIn].dwFlags & WHDR_DONE ) )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
if ( bBlockingRec )
|
2007-09-08 12:45:14 +02:00
|
|
|
{
|
|
|
|
WaitForSingleObject ( m_WaveInEvent, INFINITE );
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
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
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// check if buffers got lost
|
2006-12-18 15:39:33 +01:00
|
|
|
int iNumInBufDone = 0;
|
2007-09-08 12:45:14 +02:00
|
|
|
for ( i = 0; i < iCurNumSndBufIn; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( m_WaveInHeader[i].dwFlags & WHDR_DONE )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
iNumInBufDone++;
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01: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
|
|
|
}
|
2006-12-18 15:39:33 +01: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
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// copy data from sound card in output buffer
|
|
|
|
for ( i = 0; i < iBufferSizeIn; i++ )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
psData[i] = psSoundcardBuffer[iWhichBufferIn][i];
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// add the buffer so that it can be filled with new samples
|
2006-12-18 15:39:33 +01:00
|
|
|
AddInBuffer();
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// in case more than one buffer was ready, reset event
|
|
|
|
ResetEvent ( m_WaveInEvent );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
|
|
|
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 ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// prepare buffers for sending to sound interface
|
|
|
|
PrepareInBuffer ( iWhichBufferIn );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
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 ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// toggle buffers
|
2006-12-18 15:39:33 +01:00
|
|
|
iWhichBufferIn++;
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( iWhichBufferIn == iCurNumSndBufIn )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
iWhichBufferIn = 0;
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::PrepareInBuffer ( int iBufNum )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
// set struct entries
|
|
|
|
m_WaveInHeader[iBufNum].lpData = (LPSTR) &psSoundcardBuffer[iBufNum][0];
|
2006-12-18 15:39:33 +01:00
|
|
|
m_WaveInHeader[iBufNum].dwBufferLength = iBufferSizeIn * BYTES_PER_SAMPLE;
|
2007-09-08 12:45:14 +02:00
|
|
|
m_WaveInHeader[iBufNum].dwFlags = 0;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// prepare wave-header
|
|
|
|
waveInPrepareHeader ( m_WaveIn, &m_WaveInHeader[iBufNum], sizeof ( WAVEHDR ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::InitRecording ( int iNewBufferSize, bool bNewBlocking )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
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 )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
OpenInDevice();
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// reset flag
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamIn = false;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// set internal parameter
|
2006-12-18 15:39:33 +01:00
|
|
|
iBufferSizeIn = iNewBufferSize;
|
2008-04-08 20:38:55 +02:00
|
|
|
bBlockingRec = bNewBlocking;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
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 );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
/* reset current buffer ID (it is important to do this BEFORE calling
|
2006-12-18 15:39:33 +01:00
|
|
|
"AddInBuffer()" */
|
|
|
|
iWhichBufferIn = 0;
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// create memory for sound card buffer
|
|
|
|
for ( int i = 0; i < iCurNumSndBufIn; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
/* 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 ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( psSoundcardBuffer[i] != NULL )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
delete[] psSoundcardBuffer[i];
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01: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 );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
|
|
|
AddInBuffer();
|
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// notify that sound capturing can start now
|
|
|
|
waveInStart ( m_WaveIn );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
|
|
|
/* This reset event is very important for initialization, otherwise we will
|
|
|
|
get errors! */
|
2007-09-08 12:45:14 +02:00
|
|
|
ResetEvent ( m_WaveInEvent );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
waveInReset ( m_WaveIn );
|
|
|
|
waveInClose ( m_WaveIn );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
MMRESULT result = waveInOpen ( &m_WaveIn, iCurInDev, &sWaveFormatEx,
|
|
|
|
(DWORD) m_WaveInEvent, NULL, CALLBACK_EVENT );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( result != MMSYSERR_NOERROR )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
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." );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::SetInDev ( int iNewDev )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
// set device to wave mapper if iNewDev is invalid
|
|
|
|
if ( ( iNewDev >= iNumDevs ) || ( iNewDev < 0 ) )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
iNewDev = WAVE_MAPPER;
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// change only in case new device id is not already active
|
|
|
|
if ( iNewDev != iCurInDev )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
iCurInDev = iNewDev;
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamIn = true;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::SetInNumBuf ( int iNewNum )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
// check new parameter
|
|
|
|
if ( ( iNewNum >= MAX_SND_BUF_IN ) || ( iNewNum < 1 ) )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
iNewNum = NUM_SOUND_BUFFERS_IN;
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// change only if parameter is different
|
|
|
|
if ( iNewNum != iCurNumSndBufIn )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
iCurNumSndBufIn = iNewNum;
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamIn = true;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************\
|
|
|
|
* Wave out *
|
|
|
|
\******************************************************************************/
|
2007-09-08 12:45:14 +02:00
|
|
|
bool CSound::Write ( CVector<short>& psData )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
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 )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
OpenOutDevice();
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// reinit sound interface
|
|
|
|
InitPlayback ( iBufferSizeOut, bBlockingPlay );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// reset flag
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamOut = false;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// get number of "done"-buffers and position of one of them
|
|
|
|
GetDoneBuffer ( iCntPrepBuf, iIndexDoneBuf );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// now check special cases (Buffer is full or empty)
|
|
|
|
if ( iCntPrepBuf == 0 )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
if ( bBlockingPlay )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
/* Blocking wave out routine. Always
|
2006-12-18 15:39:33 +01:00
|
|
|
ensure that the buffer is completely filled to avoid buffer
|
|
|
|
underruns */
|
2007-09-08 12:45:14 +02:00
|
|
|
while ( iCntPrepBuf == 0 )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
WaitForSingleObject ( m_WaveOutEvent, INFINITE );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
GetDoneBuffer ( iCntPrepBuf, iIndexDoneBuf );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-01-22 20:58:53 +01:00
|
|
|
// All buffers are filled, dump new block --------------------------
|
2006-12-18 15:39:33 +01:00
|
|
|
// 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
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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++ )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
psPlaybackBuffer[iIndexDoneBuf][i] = psData[i];
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// now, send the current block
|
|
|
|
AddOutBuffer ( iIndexDoneBuf );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
|
|
|
return bError;
|
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::GetDoneBuffer ( int& iCntPrepBuf, int& iIndexDoneBuf )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
// get number of "done"-buffers and position of one of them
|
2006-12-18 15:39:33 +01:00
|
|
|
iCntPrepBuf = 0;
|
2007-09-08 12:45:14 +02:00
|
|
|
for ( int i = 0; i < iCurNumSndBufOut; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( m_WaveOutHeader[i].dwFlags & WHDR_DONE )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
iCntPrepBuf++;
|
|
|
|
iIndexDoneBuf = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::AddOutBuffer ( int iBufNum )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
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 ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
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 );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
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 ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::PrepareOutBuffer ( int iBufNum )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
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];
|
2006-12-18 15:39:33 +01:00
|
|
|
m_WaveOutHeader[iBufNum].dwBufferLength = iBufferSizeOut * BYTES_PER_SAMPLE;
|
2007-09-08 12:45:14 +02:00
|
|
|
m_WaveOutHeader[iBufNum].dwFlags = 0;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
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 ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::InitPlayback ( int iNewBufferSize, bool bNewBlocking )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
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 )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
OpenOutDevice();
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// reset flag
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamOut = false;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// set internal parameters
|
2006-12-18 15:39:33 +01:00
|
|
|
iBufferSizeOut = iNewBufferSize;
|
2007-09-08 12:45:14 +02:00
|
|
|
bBlockingPlay = bNewBlocking;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// reset interface
|
|
|
|
waveOutReset ( m_WaveOut );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
for ( j = 0; j < iCurNumSndBufOut; j++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
/* 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 ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// create memory for playback buffer
|
|
|
|
if ( psPlaybackBuffer[j] != NULL )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
delete[] psPlaybackBuffer[j];
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
|
|
|
psPlaybackBuffer[j] = new short[iBufferSizeOut];
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// clear new buffer
|
|
|
|
for ( i = 0; i < iBufferSizeOut; i++ )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
psPlaybackBuffer[j][i] = 0;
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// prepare buffer for sending to the sound interface
|
|
|
|
PrepareOutBuffer ( j );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// initially, send all buffers to the interface
|
|
|
|
AddOutBuffer ( j );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSound::OpenOutDevice()
|
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( m_WaveOut != NULL )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
waveOutReset ( m_WaveOut );
|
|
|
|
waveOutClose ( m_WaveOut );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
MMRESULT result = waveOutOpen ( &m_WaveOut, iCurOutDev, &sWaveFormatEx,
|
|
|
|
(DWORD) m_WaveOutEvent, NULL, CALLBACK_EVENT );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( result != MMSYSERR_NOERROR )
|
|
|
|
{
|
|
|
|
throw CGenErr ( "Sound Interface Start, waveOutOpen() failed." );
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::SetOutDev ( int iNewDev )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
// set device to wave mapper if iNewDev is invalid
|
|
|
|
if ( ( iNewDev >= iNumDevs ) || ( iNewDev < 0 ) )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
iNewDev = WAVE_MAPPER;
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// change only in case new device id is not already active
|
|
|
|
if ( iNewDev != iCurOutDev )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
iCurOutDev = iNewDev;
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamOut = true;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
void CSound::SetOutNumBuf ( int iNewNum )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
// check new parameter
|
|
|
|
if ( ( iNewNum >= MAX_SND_BUF_OUT ) || ( iNewNum < 1 ) )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
iNewNum = NUM_SOUND_BUFFERS_OUT;
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// change only if parameter is different
|
|
|
|
if ( iNewNum != iCurNumSndBufOut )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
|
|
|
iCurNumSndBufOut = iNewNum;
|
2008-01-27 11:05:15 +01:00
|
|
|
bChangParamOut = true;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************\
|
|
|
|
* Common *
|
|
|
|
\******************************************************************************/
|
|
|
|
void CSound::Close()
|
|
|
|
{
|
2008-01-27 11:05:15 +01:00
|
|
|
int i;
|
|
|
|
MMRESULT result;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// reset audio driver
|
|
|
|
if ( m_WaveOut != NULL )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
result = waveOutReset ( m_WaveOut );
|
|
|
|
|
|
|
|
if ( result != MMSYSERR_NOERROR )
|
|
|
|
{
|
|
|
|
throw CGenErr ( "Sound Interface, waveOutReset() failed." );
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( m_WaveIn != NULL )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
result = waveInReset ( m_WaveIn );
|
|
|
|
|
|
|
|
if ( result != MMSYSERR_NOERROR )
|
|
|
|
{
|
|
|
|
throw CGenErr ( "Sound Interface, waveInReset() failed." );
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// set event to ensure that thread leaves the waiting function
|
|
|
|
if ( m_WaveInEvent != NULL )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
SetEvent(m_WaveInEvent);
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// wait for the thread to terminate
|
|
|
|
Sleep ( 500 );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// unprepare wave-headers
|
|
|
|
if ( m_WaveIn != NULL )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
for ( i = 0; i < iCurNumSndBufIn; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
result = waveInUnprepareHeader (
|
|
|
|
m_WaveIn, &m_WaveInHeader[i], sizeof ( WAVEHDR ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( result != MMSYSERR_NOERROR )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
throw CGenErr ( "Sound Interface, waveInUnprepareHeader()"
|
|
|
|
" failed." );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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." );
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( m_WaveOut != NULL )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
for ( i = 0; i < iCurNumSndBufOut; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
result = waveOutUnprepareHeader (
|
|
|
|
m_WaveOut, &m_WaveOutHeader[i], sizeof ( WAVEHDR ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( result != MMSYSERR_NOERROR )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
throw CGenErr ( "Sound Interface, waveOutUnprepareHeader()"
|
|
|
|
" failed." );
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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." );
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
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;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
CSound::CSound()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// init number of sound buffers
|
|
|
|
iCurNumSndBufIn = NUM_SOUND_BUFFERS_IN;
|
2006-12-18 15:39:33 +01:00
|
|
|
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;
|
2006-12-18 15:39:33 +01:00
|
|
|
m_WaveOutEvent = NULL;
|
2007-09-08 12:45:14 +02:00
|
|
|
m_WaveIn = NULL;
|
|
|
|
m_WaveOut = NULL;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// init buffer pointer to zero
|
|
|
|
for ( i = 0; i < MAX_SND_BUF_IN; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
memset ( &m_WaveInHeader[i], 0, sizeof ( WAVEHDR ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
psSoundcardBuffer[i] = NULL;
|
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
for ( i = 0; i < MAX_SND_BUF_OUT; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
memset ( &m_WaveOutHeader[i], 0, sizeof ( WAVEHDR ) );
|
2006-12-18 15:39:33 +01:00
|
|
|
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 *
|
2006-12-18 15:39:33 +01:00
|
|
|
sWaveFormatEx.wBitsPerSample / 8;
|
|
|
|
sWaveFormatEx.nAvgBytesPerSec = sWaveFormatEx.nBlockAlign *
|
|
|
|
sWaveFormatEx.nSamplesPerSec;
|
2008-01-27 11:05:15 +01:00
|
|
|
sWaveFormatEx.cbSize = 0;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// get the number of digital audio devices in this computer, check range
|
2006-12-18 15:39:33 +01:00
|
|
|
iNumDevs = waveInGetNumDevs();
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( iNumDevs > MAX_NUMBER_SOUND_CARDS )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
iNumDevs = MAX_NUMBER_SOUND_CARDS;
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01: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." );
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// get info about the devices and store the names
|
|
|
|
for ( i = 0; i < iNumDevs; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( !waveInGetDevCaps ( i, &m_WaveInDevCaps, sizeof ( WAVEINCAPS ) ) )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
pstrDevices[i] = m_WaveInDevCaps.szPname;
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01: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 );
|
2006-12-18 15:39:33 +01:00
|
|
|
|
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;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// default device number, "wave mapper"
|
|
|
|
iCurInDev = WAVE_MAPPER;
|
2006-12-18 15:39:33 +01:00
|
|
|
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;
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// blocking wave in is default
|
2008-01-27 11:05:15 +01:00
|
|
|
bBlockingRec = true;
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
CSound::~CSound()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// delete allocated memory
|
|
|
|
for ( i = 0; i < iCurNumSndBufIn; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( psSoundcardBuffer[i] != NULL )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
delete[] psSoundcardBuffer[i];
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
for ( i = 0; i < iCurNumSndBufOut; i++ )
|
2006-12-18 15:39:33 +01:00
|
|
|
{
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( psPlaybackBuffer[i] != NULL )
|
|
|
|
{
|
2006-12-18 15:39:33 +01:00
|
|
|
delete[] psPlaybackBuffer[i];
|
2007-09-08 12:45:14 +02:00
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
// close the handle for the events
|
|
|
|
if ( m_WaveInEvent != NULL )
|
|
|
|
{
|
|
|
|
CloseHandle ( m_WaveInEvent );
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
|
2007-09-08 12:45:14 +02:00
|
|
|
if ( m_WaveOutEvent != NULL )
|
|
|
|
{
|
|
|
|
CloseHandle ( m_WaveOutEvent );
|
|
|
|
}
|
2006-12-18 15:39:33 +01:00
|
|
|
}
|
2007-12-17 22:28:40 +01:00
|
|
|
|
|
|
|
#endif // USE_ASIO_SND_INTERFACE
|