some more ASIO implementation, audio in is nearly ready

This commit is contained in:
Volker Fischer 2008-07-12 11:48:51 +00:00
parent 3e3deac163
commit 5eb8694134
3 changed files with 187 additions and 319 deletions

View file

@ -33,7 +33,7 @@ void CNetBuf::Init ( const int iNewBlockSize, const int iNewNumBlocks )
{ {
// total size -> size of one block times number of blocks // total size -> size of one block times number of blocks
iBlockSize = iNewBlockSize; iBlockSize = iNewBlockSize;
iMemSize = iNewBlockSize * iNewNumBlocks; iMemSize = iNewBlockSize * iNewNumBlocks;
// fade in first block added to the buffer // fade in first block added to the buffer
bFadeInNewPutData = true; bFadeInNewPutData = true;
@ -159,7 +159,7 @@ bool CNetBuf::Get ( CVector<double>& vecdData )
// set flag to fade in next new block in buffer and fade out last // set flag to fade in next new block in buffer and fade out last
// block by extrapolation to avoid clicks // block by extrapolation to avoid clicks
bFadeInNewPutData = true; bFadeInNewPutData = true;
bFadeOutExtrap = true; bFadeOutExtrap = true;
bGetOK = false; // return error flag bGetOK = false; // return error flag
} }

View file

@ -30,11 +30,19 @@
/* Implementation *************************************************************/ /* Implementation *************************************************************/
#ifdef USE_ASIO_SND_INTERFACE #ifdef USE_ASIO_SND_INTERFACE
#include <qmutex.h>
// external references // external references
extern AsioDrivers* asioDrivers; extern AsioDrivers* asioDrivers;
bool loadAsioDriver ( char *name ); 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 :-(((
// ASIO stuff // ASIO stuff
ASIODriverInfo driverInfo; ASIODriverInfo driverInfo;
ASIOBufferInfo bufferInfos[2 * NUM_IN_OUT_CHANNELS]; // for input and output buffers -> "2 *" ASIOBufferInfo bufferInfos[2 * NUM_IN_OUT_CHANNELS]; // for input and output buffers -> "2 *"
@ -43,14 +51,31 @@ bool bASIOPostOutput;
ASIOCallbacks asioCallbacks; ASIOCallbacks asioCallbacks;
int iBufferSize; int iBufferSize;
// event
HANDLE m_ASIOEvent;
// wave in
int iCurBlockToWrite;
short* psSoundcardBuffer[MAX_SND_BUF_IN];
// wave out
short* psPlaybackBuffer[MAX_SND_BUF_OUT];
int iCurNumSndBufIn;
int iCurNumSndBufOut;
// we must implement these functions here to get access to global variables
int CSound::GetOutNumBuf() { return iCurNumSndBufOut; }
int CSound::GetInNumBuf() { return iCurNumSndBufIn; }
/******************************************************************************\ /******************************************************************************\
* Wave in * * Wave in *
\******************************************************************************/ \******************************************************************************/
bool CSound::Read ( CVector<short>& psData ) bool CSound::Read ( CVector<short>& psData )
{ {
int i; int i, j;
bool bError = false; bool bError = false;
// check if device must be opened or reinitialized // check if device must be opened or reinitialized
if ( bChangParamIn ) if ( bChangParamIn )
@ -62,100 +87,51 @@ bool CSound::Read ( CVector<short>& psData )
bChangParamIn = false; bChangParamIn = false;
} }
/*
// wait until data is available // wait until data is available
if ( ! ( m_WaveInHeader[iWhichBufferIn].dwFlags & WHDR_DONE ) ) if ( iCurBlockToWrite == 0 )
{ {
if ( bBlockingRec ) if ( bBlockingRec )
{ {
WaitForSingleObject ( m_WaveInEvent, INFINITE ); WaitForSingleObject ( m_ASIOEvent, INFINITE );
} }
else else
{ {
return false; return false;
} }
} }
*/
/*
// 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 // If the number of done buffers equals the total number of buffers, it is
// very likely that a buffer got lost -> set error flag // very likely that a buffer got lost -> set error flag
if ( iNumInBufDone == iCurNumSndBufIn ) bError = ( iCurBlockToWrite == iCurNumSndBufIn );
{
bError = true;
}
else
{
bError = false;
}
*/
ASIOMutex.lock(); // get mutex lock
{
// copy data from sound card in output buffer
for ( i = 0; i < iBufferSize; i++ )
{
psData[i] = psSoundcardBuffer[0][i];
}
/* // move all other data in buffer
// copy data from sound card in output buffer for ( j = 0; j < iCurBlockToWrite - 1; j++ )
for ( i = 0; i < iBufferSize; i++ ) {
{ for ( i = 0; i < iBufferSize; i++ )
psData[i] = psSoundcardBuffer[iWhichBufferIn][i]; {
} psSoundcardBuffer[j][i] = psSoundcardBuffer[j + 1][i];
}
}
// add the buffer so that it can be filled with new samples // adjust "current block to write" pointer
AddInBuffer(); iCurBlockToWrite--;
}
ASIOMutex.unlock();
// in case more than one buffer was ready, reset event // in case more than one buffer was ready, reset event
ResetEvent ( m_WaveInEvent ); ResetEvent ( m_ASIOEvent );
*/
return bError; 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 ) void CSound::SetInNumBuf ( int iNewNum )
{ {
// check new parameter // check new parameter
@ -264,50 +240,6 @@ return true;
return bError; 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 ) );
*/
}
void CSound::SetOutNumBuf ( int iNewNum ) void CSound::SetOutNumBuf ( int iNewNum )
{ {
// check new parameter // check new parameter
@ -335,147 +267,108 @@ void CSound::InitRecordingAndPlayback ( int iNewBufferSize )
// first, stop audio // first, stop audio
ASIOStop(); ASIOStop();
// calculate "nearest" buffer size and set internal parameter accordingly ASIOMutex.lock(); // get mutex lock
// first check minimum and maximum values
if ( iNewBufferSize < HWBufferInfo.lMinSize )
{ {
iBufferSize = HWBufferInfo.lMinSize; // calculate "nearest" buffer size and set internal parameter accordingly
} // first check minimum and maximum values
else if ( iNewBufferSize < HWBufferInfo.lMinSize )
{
if ( iNewBufferSize > HWBufferInfo.lMaxSize )
{ {
iBufferSize = HWBufferInfo.lMaxSize; iBufferSize = HWBufferInfo.lMinSize;
} }
else else
{ {
// initialization if ( iNewBufferSize > HWBufferInfo.lMaxSize )
int iTrialBufSize = HWBufferInfo.lMinSize;
int iLastTrialBufSize = HWBufferInfo.lMinSize;
bool bSizeFound = false;
// test loop
while ( ( iTrialBufSize <= HWBufferInfo.lMaxSize ) && ( !bSizeFound ) )
{ {
if ( iTrialBufSize > iNewBufferSize ) iBufferSize = HWBufferInfo.lMaxSize;
}
else
{
// initialization
int iTrialBufSize = HWBufferInfo.lMinSize;
int iLastTrialBufSize = HWBufferInfo.lMinSize;
bool bSizeFound = false;
// test loop
while ( ( iTrialBufSize <= HWBufferInfo.lMaxSize ) && ( !bSizeFound ) )
{ {
// test which buffer size fits better: the old one or the if ( iTrialBufSize > iNewBufferSize )
// current one
if ( ( iTrialBufSize - iNewBufferSize ) < ( iNewBufferSize - iLastTrialBufSize ) )
{ {
iBufferSize = iTrialBufSize; // 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 else
{ {
iBufferSize = iLastTrialBufSize; iTrialBufSize += HWBufferInfo.lGranularity;
} }
// 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;
} }
} }
} }
}
// create and activate buffers // create and activate ASIO 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] );
// only 16 bit is supported
if ( channelInfos[i].type != ASIOSTInt16LSB )
{
throw CGenErr ( "Required audio sample format not available (16 bit LSB)." );
}
}
int test2; // our buffer management -----------------------------------------------
int test = ASE_OK; // initialize write block pointer
int test1 = ASIOCreateBuffers ( bufferInfos, 2 * NUM_IN_OUT_CHANNELS, iCurBlockToWrite = 0;
iBufferSize * BYTES_PER_SAMPLE, &asioCallbacks );
// now set all the buffer details // create memory for sound card buffer
for ( i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ ) for ( i = 0; i < iCurNumSndBufIn; i++ )
{
channelInfos[i].channel = NUM_IN_OUT_CHANNELS;
channelInfos[i].isInput = bufferInfos[i].isInput;
ASIOGetChannelInfo ( &channelInfos[i] );
// only 16 bit is supported
if ( channelInfos[i].type != ASIOSTInt16LSB )
{ {
// TODO fire error if ( psSoundcardBuffer[i] != NULL )
{
delete[] psSoundcardBuffer[i];
}
psSoundcardBuffer[i] = new short[iBufferSize];
} }
}
/*
// reset interface so that all buffers are returned from the interface
waveInReset ( m_WaveIn );
waveInStop ( m_WaveIn );
*/
// 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++ )
{
/*
// 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 ) );
*/
if ( psSoundcardBuffer[i] != NULL )
{
delete[] psSoundcardBuffer[i];
}
psSoundcardBuffer[i] = new short[iBufferSize];
/* Send all buffers to driver for filling the queue ----------------- */
// prepare buffers before sending them to the sound interface
PrepareInBuffer ( i );
AddInBuffer();
}
/*
// notify that sound capturing can start now
waveInStart ( m_WaveIn );
*/
// This reset event is very important for initialization, otherwise we will
// get errors!
ResetEvent ( m_WaveInEvent );
/* /*
// reset interface // reset interface
waveOutReset ( m_WaveOut ); waveOutReset ( m_WaveOut );
*/ */
/*
for ( j = 0; j < iCurNumSndBufOut; j++ ) for ( j = 0; j < iCurNumSndBufOut; j++ )
{ {
/*
// 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 ) );
*/
// create memory for playback buffer // create memory for playback buffer
if ( psPlaybackBuffer[j] != NULL ) if ( psPlaybackBuffer[j] != NULL )
{ {
@ -496,7 +389,12 @@ int test = ASE_OK;
// initially, send all buffers to the interface // initially, send all buffers to the interface
AddOutBuffer ( j ); AddOutBuffer ( j );
} }
*/
// reset event
ResetEvent ( m_ASIOEvent );
}
ASIOMutex.unlock();
// initialization is done, (re)start audio // initialization is done, (re)start audio
ASIOStart(); ASIOStart();
@ -505,9 +403,9 @@ int test = ASE_OK;
void CSound::Close() void CSound::Close()
{ {
// set event to ensure that thread leaves the waiting function // set event to ensure that thread leaves the waiting function
if ( m_WaveInEvent != NULL ) if ( m_ASIOEvent != NULL )
{ {
SetEvent(m_WaveInEvent); SetEvent(m_ASIOEvent);
} }
// wait for the thread to terminate // wait for the thread to terminate
@ -527,8 +425,7 @@ CSound::CSound()
iCurNumSndBufOut = NUM_SOUND_BUFFERS_OUT; iCurNumSndBufOut = NUM_SOUND_BUFFERS_OUT;
// should be initialized because an error can occur during init // should be initialized because an error can occur during init
m_WaveInEvent = NULL; m_ASIOEvent = NULL;
m_WaveOutEvent = NULL;
// get available ASIO driver names in system // get available ASIO driver names in system
char* cDriverNames[MAX_NUMBER_SOUND_CARDS]; char* cDriverNames[MAX_NUMBER_SOUND_CARDS];
@ -641,10 +538,9 @@ pstrDevices[0] = driverInfo.name;
psPlaybackBuffer[i] = NULL; psPlaybackBuffer[i] = NULL;
} }
// we use an event controlled wave-in (wave-out) structure // we use an event controlled structure
// create events // create event
m_WaveInEvent = CreateEvent ( NULL, FALSE, FALSE, NULL ); m_ASIOEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
m_WaveOutEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
// set flag to open devices // set flag to open devices
bChangParamIn = true; bChangParamIn = true;
@ -678,18 +574,11 @@ CSound::~CSound()
} }
} }
/* // close the handle for the event
// close the handle for the events if ( m_ASIOEvent != NULL )
if ( m_WaveInEvent != NULL )
{ {
CloseHandle ( m_WaveInEvent ); CloseHandle ( m_ASIOEvent );
} }
if ( m_WaveOutEvent != NULL )
{
CloseHandle ( m_WaveOutEvent );
}
*/
} }
// ASIO callbacks ------------------------------------------------------------- // ASIO callbacks -------------------------------------------------------------
@ -701,59 +590,51 @@ ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime *timeInfo, long index, ASIOBoo
void CSound::bufferSwitch ( long index, ASIOBool processNow ) void CSound::bufferSwitch ( long index, ASIOBool processNow )
{ {
static long processedSamples = 0; ASIOMutex.lock(); // get mutex lock
// buffer size in samples
long buffSize = iBufferSize * BYTES_PER_SAMPLE;
// perform the processing for input and output
for ( int i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ )
{
if ( bufferInfos[i].isInput == false )
{
// PLAYBACK --------------------------------------------------------
// TODO the following is just a test code
//memset ( bufferInfos[i].buffers[index], 0, buffSize /** 2*/ );
}
else
{
// CAPTURE ---------------------------------------------------------
/*
// TEST
static FILE* pFile = fopen ( "test.dat", "w" );
for ( int iIdx = 0; iIdx < buffSize * 2; iIdx++ )
{
fprintf ( pFile, "%d\n", ((short*) bufferInfos[i].buffers[index])[iIdx] );
}
fflush ( pFile );
*/
// TEST
channelInfos[i];
short* test;
test = (short*) bufferInfos[i].buffers[index];
/*
// TEST
static FILE* pFile = fopen ( "test.dat", "w" );
for ( int iIdx = 0; iIdx < buffSize; iIdx++ )
{
fprintf ( pFile, "%d\n", test[iIdx] );
}
fflush ( pFile );
*/
int test2 = 0;
// TODO
}
}
// finally if the driver supports the ASIOOutputReady() optimization,
// do it here, all data are in place
if ( bASIOPostOutput )
{ {
ASIOOutputReady(); // perform the processing for input and output
for ( int i = 0; i < 2 * NUM_IN_OUT_CHANNELS; i++ )
{
if ( bufferInfos[i].isInput == false )
{
// PLAYBACK --------------------------------------------------------
// TODO the following is just a test code
//memset ( bufferInfos[i].buffers[index], 0, buffSize /** 2*/ );
}
else
{
// CAPTURE ---------------------------------------------------------
// first check if buffer is available
if ( iCurBlockToWrite < iCurNumSndBufIn )
{
// copy new captured block in thread transfer buffer
for ( int iCurSample = 0; iCurSample < iBufferSize; iCurSample++ )
{
psSoundcardBuffer[iCurBlockToWrite][iCurSample] =
((short*) bufferInfos[i].buffers[index])[iCurSample];
}
iCurBlockToWrite++;
}
else
{
// TODO
// buffer overrun, inform user somehow...?
}
}
}
// finally if the driver supports the ASIOOutputReady() optimization,
// do it here, all data are in place
if ( bASIOPostOutput )
{
ASIOOutputReady();
}
// set event
SetEvent ( m_ASIOEvent );
} }
ASIOMutex.unlock();
} }
long CSound::asioMessages ( long selector, long value, void* message, double* opt ) long CSound::asioMessages ( long selector, long value, void* message, double* opt )

View file

@ -71,8 +71,8 @@ public:
CSound(); CSound();
virtual ~CSound(); virtual ~CSound();
void InitRecording ( int iNewBufferSize, bool bNewBlocking = true ) { InitRecordingAndPlayback ( iNewBufferSize ); } void InitRecording ( int iNewBufferSize, bool bNewBlocking = true ) { bBlockingRec = bNewBlocking; InitRecordingAndPlayback ( iNewBufferSize ); }
void InitPlayback ( int iNewBufferSize, bool bNewBlocking = false ) { InitRecordingAndPlayback ( iNewBufferSize ); } void InitPlayback ( int iNewBufferSize, bool bNewBlocking = false ) { bBlockingPlay = bNewBlocking; InitRecordingAndPlayback ( iNewBufferSize ); }
bool Read ( CVector<short>& psData ); bool Read ( CVector<short>& psData );
bool Write ( CVector<short>& psData ); bool Write ( CVector<short>& psData );
@ -80,22 +80,17 @@ public:
std::string GetDeviceName ( int iDiD ) { return pstrDevices[iDiD]; } std::string GetDeviceName ( int iDiD ) { return pstrDevices[iDiD]; }
void SetOutDev ( int iNewDev ) {} // not supported void SetOutDev ( int iNewDev ) {} // not supported
int GetOutDev() { return 0; } // not supported int GetOutDev() { return 0; } // not supported
void SetInDev ( int iNewDev ) {} // not supported void SetInDev ( int iNewDev ) {} // not supported
int GetInDev() { return 0; } // not supported int GetInDev() { return 0; } // not supported
void SetOutNumBuf ( int iNewNum ); void SetOutNumBuf ( int iNewNum );
int GetOutNumBuf() { return iCurNumSndBufOut; } int GetOutNumBuf();
void SetInNumBuf ( int iNewNum ); void SetInNumBuf ( int iNewNum );
int GetInNumBuf() { return iCurNumSndBufIn; } int GetInNumBuf();
void Close(); void Close();
protected: protected:
void InitRecordingAndPlayback ( int iNewBufferSize ); void InitRecordingAndPlayback ( int iNewBufferSize );
void PrepareInBuffer ( int iBufNum );
void PrepareOutBuffer ( int iBufNum );
void AddInBuffer();
void AddOutBuffer ( int iBufNum );
void GetDoneBuffer ( int& iCntPrepBuf, int& iIndexDoneBuf );
// audio hardware buffer info // audio hardware buffer info
struct sHWBufferInfo struct sHWBufferInfo
@ -116,17 +111,9 @@ protected:
std::string pstrDevices[MAX_NUMBER_SOUND_CARDS]; std::string pstrDevices[MAX_NUMBER_SOUND_CARDS];
bool bChangParamIn; bool bChangParamIn;
bool bChangParamOut; bool bChangParamOut;
int iCurNumSndBufIn;
int iCurNumSndBufOut;
// wave in bool bBlockingRec;
HANDLE m_WaveInEvent; bool bBlockingPlay;
int iWhichBufferIn;
short* psSoundcardBuffer[MAX_SND_BUF_IN];
// wave out
HANDLE m_WaveOutEvent;
short* psPlaybackBuffer[MAX_SND_BUF_OUT];
}; };
#else // USE_ASIO_SND_INTERFACE #else // USE_ASIO_SND_INTERFACE