complete redesign of ASIO conversion buffers to have greater flexibility with different ASIO buffer sizes, not yet working correctly

This commit is contained in:
Volker Fischer 2008-07-15 22:09:48 +00:00
parent 33be3c0812
commit 2516cf62f9
2 changed files with 122 additions and 187 deletions

View file

@ -78,13 +78,13 @@ void CSettings::ReadIniFile ( const QString& sFileName )
} }
// sound card in number of buffers // sound card in number of buffers
if ( GetNumericIniSet ( IniXMLDocument, "client", "audinbuf", 0, AUD_SLIDER_LENGTH, iValue ) ) if ( GetNumericIniSet ( IniXMLDocument, "client", "audinbuf", 1, AUD_SLIDER_LENGTH, iValue ) )
{ {
pClient->GetSndInterface()->SetInNumBuf( iValue ); pClient->GetSndInterface()->SetInNumBuf( iValue );
} }
// sound card out number of buffers // sound card out number of buffers
if ( GetNumericIniSet ( IniXMLDocument, "client", "audoutbuf", 0, AUD_SLIDER_LENGTH, iValue ) ) if ( GetNumericIniSet ( IniXMLDocument, "client", "audoutbuf", 1, AUD_SLIDER_LENGTH, iValue ) )
{ {
pClient->GetSndInterface()->SetOutNumBuf ( iValue ); pClient->GetSndInterface()->SetOutNumBuf ( iValue );
} }

View file

@ -57,21 +57,14 @@ int iASIOBufferSizeMono;
HANDLE m_ASIOEvent; HANDLE m_ASIOEvent;
// wave in // wave in
int iInCurBlockToWrite; short* psCaptureBuffer;
short* psSoundcardBuffer[MAX_SND_BUF_IN]; int iBufferPosCapture;
bool bBufferOverrun; bool bCaptureBufferOverrun;
// wave out // wave out
int iOutCurBlockToWrite; short* psPlayBuffer;
short* psPlaybackBuffer[MAX_SND_BUF_OUT]; int iBufferPosPlay;
bool bBufferUnderrun; bool bPlayBufferUnderrun;
// ASIO buffer size conversion buffer
short* psCaptureConvBuf;
int iCurPosCaptConvBuf;
short* psPlayConvBuf;
int iCurPosPlayConvBuf;
int iASIOConfBufSize;
int iCurNumSndBufIn; int iCurNumSndBufIn;
int iCurNumSndBufOut; int iCurNumSndBufOut;
@ -79,8 +72,8 @@ int iNewNumSndBufIn;
int iNewNumSndBufOut; int iNewNumSndBufOut;
// we must implement these functions here to get access to global variables // we must implement these functions here to get access to global variables
int CSound::GetOutNumBuf() { return iCurNumSndBufOut; } int CSound::GetOutNumBuf() { return iNewNumSndBufOut; }
int CSound::GetInNumBuf() { return iCurNumSndBufIn; } int CSound::GetInNumBuf() { return iNewNumSndBufIn; }
/******************************************************************************\ /******************************************************************************\
@ -88,7 +81,7 @@ int CSound::GetInNumBuf() { return iCurNumSndBufIn; }
\******************************************************************************/ \******************************************************************************/
bool CSound::Read ( CVector<short>& psData ) bool CSound::Read ( CVector<short>& psData )
{ {
int i, j; int i;
bool bError; bool bError;
// check if device must be opened or reinitialized // check if device must be opened or reinitialized
@ -101,46 +94,44 @@ bool CSound::Read ( CVector<short>& psData )
bChangParamIn = false; bChangParamIn = false;
} }
// wait until data is available // wait until enough data is available
if ( iInCurBlockToWrite == 0 ) while ( iBufferPosCapture < iBufferSizeStereo )
{ {
if ( bBlockingRec ) if ( bBlockingRec && !bCaptureBufferOverrun )
{ {
WaitForSingleObject ( m_ASIOEvent, INFINITE ); WaitForSingleObject ( m_ASIOEvent, INFINITE );
} }
else else
{ {
return false; return true;
} }
} }
ASIOMutex.lock(); // get mutex lock ASIOMutex.lock(); // get mutex lock
{ {
// check for buffer overrun in ASIO thread // check for buffer overrun in ASIO thread
bError = bBufferOverrun; bError = bCaptureBufferOverrun;
if ( bBufferOverrun ) if ( bCaptureBufferOverrun )
{ {
// reset flag // reset flag
bBufferOverrun = false; bCaptureBufferOverrun = false;
} }
// copy data from sound card in output buffer // copy data from sound card capture buffer in function output buffer
for ( i = 0; i < iBufferSizeStereo; i++ ) for ( i = 0; i < iBufferSizeStereo; i++ )
{ {
psData[i] = psSoundcardBuffer[0][i]; psData[i] = psCaptureBuffer[i];
} }
// move all other data in buffer // move all other data in buffer
for ( j = 0; j < iInCurBlockToWrite - 1; j++ ) const int iLenCopyRegion = iBufferPosCapture - iBufferSizeStereo;
for ( i = 0; i < iBufferSizeStereo; i++ )
{ {
for ( i = 0; i < iBufferSizeStereo; i++ ) psCaptureBuffer[i] = psCaptureBuffer[iBufferSizeStereo + i];
{
psSoundcardBuffer[j][i] = psSoundcardBuffer[j + 1][i];
}
} }
// adjust "current block to write" pointer // adjust "current block to write" pointer
iInCurBlockToWrite--; iBufferPosCapture -= iBufferSizeStereo;
// in case more than one buffer was ready, reset event // in case more than one buffer was ready, reset event
ResetEvent ( m_ASIOEvent ); ResetEvent ( m_ASIOEvent );
@ -187,29 +178,31 @@ bool CSound::Write ( CVector<short>& psData )
ASIOMutex.lock(); // get mutex lock ASIOMutex.lock(); // get mutex lock
{ {
// check for buffer underrun in ASIO thread // check for buffer underrun in ASIO thread
bError = bBufferUnderrun; bError = bPlayBufferUnderrun;
if ( bBufferUnderrun ) if ( bPlayBufferUnderrun )
{ {
// reset flag // reset flag
bBufferUnderrun = false; bPlayBufferUnderrun = false;
} }
// first check if buffer is available // first check if enough data in buffer is available
if ( iOutCurBlockToWrite < iCurNumSndBufOut ) const int iPlayBufferLen = iCurNumSndBufOut * iBufferSizeStereo;
{
// copy stereo data from input in soundcard buffer
for ( int i = 0; i < iBufferSizeStereo; i++ )
{
psPlaybackBuffer[iOutCurBlockToWrite][i] = psData[i];
}
iOutCurBlockToWrite++; if ( iBufferPosPlay + iBufferSizeStereo > iPlayBufferLen )
}
else
{ {
// buffer overrun, return error // buffer overrun, return error
bError = true; bError = true;
} }
else
{
// copy stereo data from function input in soundcard play buffer
for ( int i = 0; i < iBufferSizeStereo; i++ )
{
psPlayBuffer[iBufferPosPlay + i] = psData[i];
}
iBufferPosPlay += iBufferSizeStereo;
}
} }
ASIOMutex.unlock(); ASIOMutex.unlock();
@ -238,7 +231,7 @@ void CSound::SetOutNumBuf ( int iNewNum )
\******************************************************************************/ \******************************************************************************/
void CSound::InitRecordingAndPlayback ( int iNewBufferSize ) void CSound::InitRecordingAndPlayback ( int iNewBufferSize )
{ {
int i, j; int i;
// first, stop audio and dispose ASIO buffers // first, stop audio and dispose ASIO buffers
ASIOStop(); ASIOStop();
@ -313,13 +306,18 @@ void CSound::InitRecordingAndPlayback ( int iNewBufferSize )
} }
} }
// TEST test if requested buffer size is supported by the audio hardware, if not, fire error
// TEST test if requested buffer size is supported by the audio hardware, if not, fire error
if ( iASIOBufferSizeMono != iBufferSizeMono ) if ( iASIOBufferSizeMono != iBufferSizeMono )
{ {
throw CGenErr ( QString ( "Required sound card buffer size of %1 samples " throw CGenErr ( QString ( "Required sound card buffer size of %1 samples "
"not supported by the audio hardware." ).arg(iBufferSizeMono) ); "not supported by the audio hardware." ).arg(iBufferSizeMono) );
} }
// TEST
//iASIOBufferSizeMono = 256;
// prepare input channels // prepare input channels
for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ ) for ( i = 0; i < NUM_IN_OUT_CHANNELS; i++ )
{ {
@ -363,63 +361,32 @@ if ( iASIOBufferSizeMono != iBufferSizeMono )
iCurNumSndBufOut = iNewNumSndBufOut; iCurNumSndBufOut = iNewNumSndBufOut;
// initialize write block pointer in and overrun flag // initialize write block pointer in and overrun flag
iInCurBlockToWrite = 0; iBufferPosCapture = 0;
bBufferOverrun = false; bCaptureBufferOverrun = false;
// create memory for sound card buffer // create memory for capture buffer
for ( i = 0; i < iCurNumSndBufIn; i++ ) if ( psCaptureBuffer != NULL )
{ {
if ( psSoundcardBuffer[i] != NULL ) delete[] psCaptureBuffer;
{ }
delete[] psSoundcardBuffer[i]; psCaptureBuffer = new short[iCurNumSndBufIn * iBufferSizeStereo];
}
psSoundcardBuffer[i] = new short[iBufferSizeStereo];
}
// initialize write block pointer out and underrun flag // initialize write block pointer out and underrun flag
iOutCurBlockToWrite = 0; iBufferPosPlay = 0;
bBufferUnderrun = false; bPlayBufferUnderrun = false;
// create memory for playback buffer // create memory for play buffer
for ( j = 0; j < iCurNumSndBufOut; j++ ) if ( psPlayBuffer != NULL )
{ {
if ( psPlaybackBuffer[j] != NULL ) delete[] psPlayBuffer;
{ }
delete[] psPlaybackBuffer[j]; psPlayBuffer = new short[iCurNumSndBufOut * iBufferSizeStereo];
}
psPlaybackBuffer[j] = new short[iBufferSizeStereo]; // clear new buffer
for ( i = 0; i < iCurNumSndBufOut * iBufferSizeStereo; i++ )
// clear new buffer {
for ( i = 0; i < iBufferSizeStereo; i++ ) psPlayBuffer[i] = 0;
{ }
psPlaybackBuffer[j][i] = 0;
}
}
// create memory for ASIO buffer conversion if required
if ( iASIOBufferSizeMono != iBufferSizeMono )
{
// required size: two times of one stereo buffer of the larger buffer
iASIOConfBufSize = max(4 * iASIOBufferSizeMono, 2 * iBufferSizeStereo);
if ( psCaptureConvBuf != NULL )
{
delete[] psCaptureConvBuf;
}
psCaptureConvBuf = new short[iASIOConfBufSize];
iCurPosCaptConvBuf = 0;
if ( psPlayConvBuf != NULL )
{
delete[] psPlayConvBuf;
}
psPlayConvBuf = new short[iASIOConfBufSize];
iCurPosPlayConvBuf = 0;
}
// reset event // reset event
ResetEvent ( m_ASIOEvent ); ResetEvent ( m_ASIOEvent );
@ -510,7 +477,6 @@ iNumDevs = 1;
pstrDevices[0] = driverInfo.name; pstrDevices[0] = driverInfo.name;
// check the number of available channels // check the number of available channels
long lNumInChan; long lNumInChan;
long lNumOutChan; long lNumOutChan;
@ -518,7 +484,8 @@ pstrDevices[0] = driverInfo.name;
if ( ( lNumInChan < NUM_IN_OUT_CHANNELS ) || if ( ( lNumInChan < NUM_IN_OUT_CHANNELS ) ||
( lNumOutChan < NUM_IN_OUT_CHANNELS ) ) ( lNumOutChan < NUM_IN_OUT_CHANNELS ) )
{ {
throw CGenErr ( "The audio device does not support required number of channels." ); throw CGenErr ( "The audio device does not support the "
"required number of channels." );
} }
// query the usable buffer sizes // query the usable buffer sizes
@ -534,7 +501,8 @@ pstrDevices[0] = driverInfo.name;
ASIOGetSampleRate ( &sampleRate ); ASIOGetSampleRate ( &sampleRate );
if ( sampleRate != SND_CRD_SAMPLE_RATE ) if ( sampleRate != SND_CRD_SAMPLE_RATE )
{ {
throw CGenErr ( "The audio device does not support required sample rate." ); throw CGenErr ( "The audio device does not support the "
"required sample rate." );
} }
// check wether the driver requires the ASIOOutputReady() optimization // check wether the driver requires the ASIOOutputReady() optimization
@ -548,19 +516,8 @@ pstrDevices[0] = driverInfo.name;
asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo; asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
// init buffer pointer to zero // init buffer pointer to zero
for ( i = 0; i < MAX_SND_BUF_IN; i++ ) psCaptureBuffer = NULL;
{ psPlayBuffer = NULL;
psSoundcardBuffer[i] = NULL;
}
for ( i = 0; i < MAX_SND_BUF_OUT; i++ )
{
psPlaybackBuffer[i] = NULL;
}
// init ASIO convertion buffers
psCaptureConvBuf = NULL;
psPlayConvBuf = NULL;
// we use an event controlled structure // we use an event controlled structure
// create event // create event
@ -573,8 +530,6 @@ pstrDevices[0] = driverInfo.name;
CSound::~CSound() CSound::~CSound()
{ {
int i;
// cleanup ASIO stuff // cleanup ASIO stuff
ASIOStop(); ASIOStop();
ASIODisposeBuffers(); ASIODisposeBuffers();
@ -582,21 +537,14 @@ CSound::~CSound()
asioDrivers->removeCurrentDriver(); asioDrivers->removeCurrentDriver();
// delete allocated memory // delete allocated memory
for ( i = 0; i < iCurNumSndBufIn; i++ ) if ( psCaptureBuffer != NULL )
{ {
if ( psSoundcardBuffer[i] != NULL ) delete[] psCaptureBuffer;
{ }
delete[] psSoundcardBuffer[i]; if ( psPlayBuffer != NULL )
} {
} delete[] psPlayBuffer;
}
for ( i = 0; i < iCurNumSndBufOut; i++ )
{
if ( psPlaybackBuffer[i] != NULL )
{
delete[] psPlaybackBuffer[i];
}
}
// close the handle for the event // close the handle for the event
if ( m_ASIOEvent != NULL ) if ( m_ASIOEvent != NULL )
@ -620,88 +568,75 @@ void CSound::bufferSwitch ( long index, ASIOBool processNow )
ASIOMutex.lock(); // get mutex lock ASIOMutex.lock(); // get mutex lock
{ {
// check if special treatment is required with buffer conversion // first check buffer state of capture and play buffers
const bool bConvBufNeeded = ( iASIOBufferSizeMono != iBufferSizeMono ); const int iCaptureBufferLen = iCurNumSndBufIn * iBufferSizeStereo;
bCaptureBufferOverrun =
( iBufferPosCapture + 2 * iASIOBufferSizeMono > iCaptureBufferLen );
// TODO implementation of ASIO conversion buffer bPlayBufferUnderrun = ( 2 * iASIOBufferSizeMono > iBufferPosPlay );
// psCaptureConvBuf
// iCurPosCaptConvBuf
// psPlayConvBuf
// iCurPosPlayConvBuf
// perform the processing for input and output
// perform the processing for input and output
for ( int i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ ) // stereo for ( int i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ ) // stereo
{ {
if ( bufferInfos[i].isInput == ASIOFalse ) if ( bufferInfos[i].isInput == ASIOTrue )
{ {
// CAPTURE -----------------------------------------------------
// first check if space in buffer is available
if ( !bCaptureBufferOverrun )
{
// copy new captured block in thread transfer buffer
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
// copy mono data interleaved in stereo buffer
psCaptureBuffer[iBufferPosCapture +
2 * iCurSample + bufferInfos[i].channelNum] =
((short*) bufferInfos[i].buffers[index])[iCurSample];
}
}
}
else
{
// PLAYBACK ---------------------------------------------------- // PLAYBACK ----------------------------------------------------
if ( iOutCurBlockToWrite > 0 ) if ( !bPlayBufferUnderrun )
{ {
// copy data from sound card in output buffer // copy data from sound card in output buffer
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{ {
// copy interleaved stereo data in mono sound card buffer // copy interleaved stereo data in mono sound card buffer
((short*) bufferInfos[i].buffers[index])[iCurSample] = ((short*) bufferInfos[i].buffers[index])[iCurSample] =
psPlaybackBuffer[0][2 * iCurSample + bufferInfos[i].channelNum]; psPlayBuffer[2 * iCurSample + bufferInfos[i].channelNum];
} }
} }
}
else
{
// CAPTURE -----------------------------------------------------
// first check if space in buffer is available
if ( iInCurBlockToWrite < iCurNumSndBufIn )
{
// copy new captured block in thread transfer buffer
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
// copy mono data interleaved in stereo buffer
psSoundcardBuffer[iInCurBlockToWrite][2 * iCurSample + bufferInfos[i].channelNum] =
((short*) bufferInfos[i].buffers[index])[iCurSample];
}
}
} }
} }
// Manage thread interface buffers for input and output ---------------- // Manage thread interface buffers for input and output ----------------
// playback // capture
if ( iOutCurBlockToWrite > 0 ) if ( !bCaptureBufferOverrun )
{ {
// move all other data in playback buffer iBufferPosCapture += 2 * iASIOBufferSizeMono;
for ( int j = 0; j < iOutCurBlockToWrite - 1; j++ ) }
// play
if ( !bPlayBufferUnderrun )
{
// move all other data in play buffer
const int iLenCopyRegion = iBufferPosPlay - 2 * iASIOBufferSizeMono;
for ( iCurSample = 0; iCurSample < iLenCopyRegion; iCurSample++ )
{ {
for ( iCurSample = 0; iCurSample < iBufferSizeStereo; iCurSample++ ) psPlayBuffer[iCurSample] =
{ psPlayBuffer[2 * iASIOBufferSizeMono + iCurSample];
psPlaybackBuffer[j][iCurSample] =
psPlaybackBuffer[j + 1][iCurSample];
}
} }
// adjust "current block to write" pointer // adjust "current block to write" pointer
iOutCurBlockToWrite--; iBufferPosPlay -= 2 * iASIOBufferSizeMono;
}
else
{
// set buffer underrun flag
bBufferUnderrun = true;
} }
// capture
if ( iInCurBlockToWrite < iCurNumSndBufIn )
{
iInCurBlockToWrite++;
}
else
{
// set buffer overrun flag
bBufferOverrun = true;
}
// finally if the driver supports the ASIOOutputReady() optimization, // finally if the driver supports the ASIOOutputReady() optimization,
// do it here, all data are in place // do it here, all data are in place -----------------------------------
if ( bASIOPostOutput ) if ( bASIOPostOutput )
{ {
ASIOOutputReady(); ASIOOutputReady();