added some more ASIO sample formats (not yet all implemented!), support for sound card conversion buffer to support sound card frame sizes which are different from the predefined ones
This commit is contained in:
parent
f06f38dc75
commit
75b1994257
7 changed files with 694 additions and 264 deletions
289
src/buffer.cpp
289
src/buffer.cpp
|
@ -29,15 +29,173 @@
|
||||||
|
|
||||||
|
|
||||||
/* Implementation *************************************************************/
|
/* Implementation *************************************************************/
|
||||||
|
// Buffer base class -----------------------------------------------------------
|
||||||
|
// explicit instantiation of the template used in this software (required to
|
||||||
|
// have the implementation in the cpp file and not in the header file)
|
||||||
|
template class CBufferBase<uint8_t>; // for network buffer
|
||||||
|
template class CBufferBase<int16_t>; // for sound card conversion buffer
|
||||||
|
|
||||||
|
template<class TData>
|
||||||
|
void CBufferBase<TData>::Init ( const int iNewMemSize )
|
||||||
|
{
|
||||||
|
// store total memory size value
|
||||||
|
iMemSize = iNewMemSize;
|
||||||
|
|
||||||
|
// allocate memory for actual data buffer
|
||||||
|
vecMemory.Init ( iNewMemSize );
|
||||||
|
|
||||||
|
// init buffer pointers and buffer state (empty buffer)
|
||||||
|
iGetPos = 0;
|
||||||
|
iPutPos = 0;
|
||||||
|
eBufState = CBufferBase<TData>::BS_EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData>
|
||||||
|
bool CBufferBase<TData>::Put ( const CVector<TData>& vecData,
|
||||||
|
const int iInSize )
|
||||||
|
{
|
||||||
|
// copy new data in internal buffer
|
||||||
|
int iCurPos = 0;
|
||||||
|
if ( iPutPos + iInSize > iMemSize )
|
||||||
|
{
|
||||||
|
// remaining space size for second block
|
||||||
|
const int iRemSpace = iPutPos + iInSize - iMemSize;
|
||||||
|
|
||||||
|
// data must be written in two steps because of wrap around
|
||||||
|
while ( iPutPos < iMemSize )
|
||||||
|
{
|
||||||
|
vecMemory[iPutPos++] = vecData[iCurPos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( iPutPos = 0; iPutPos < iRemSpace; iPutPos++ )
|
||||||
|
{
|
||||||
|
vecMemory[iPutPos] = vecData[iCurPos++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// data can be written in one step
|
||||||
|
const int iEnd = iPutPos + iInSize;
|
||||||
|
while ( iPutPos < iEnd )
|
||||||
|
{
|
||||||
|
vecMemory[iPutPos++] = vecData[iCurPos++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set buffer state flag
|
||||||
|
if ( iPutPos == iGetPos )
|
||||||
|
{
|
||||||
|
eBufState = CBufferBase<TData>::BS_FULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eBufState = CBufferBase<TData>::BS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // no error check in base class, alyways return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData>
|
||||||
|
bool CBufferBase<TData>::Get ( CVector<TData>& vecData )
|
||||||
|
{
|
||||||
|
// get size of data to be get from the buffer
|
||||||
|
const int iInSize = vecData.Size();
|
||||||
|
|
||||||
|
// copy data from internal buffer in output buffer
|
||||||
|
int iCurPos = 0;
|
||||||
|
if ( iGetPos + iInSize > iMemSize )
|
||||||
|
{
|
||||||
|
// remaining data size for second block
|
||||||
|
const int iRemData = iGetPos + iInSize - iMemSize;
|
||||||
|
|
||||||
|
// data must be read in two steps because of wrap around
|
||||||
|
while ( iGetPos < iMemSize )
|
||||||
|
{
|
||||||
|
vecData[iCurPos++] = vecMemory[iGetPos++];
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( iGetPos = 0; iGetPos < iRemData; iGetPos++ )
|
||||||
|
{
|
||||||
|
vecData[iCurPos++] = vecMemory[iGetPos];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// data can be read in one step
|
||||||
|
const int iEnd = iGetPos + iInSize;
|
||||||
|
while ( iGetPos < iEnd )
|
||||||
|
{
|
||||||
|
vecData[iCurPos++] = vecMemory[iGetPos++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set buffer state flag
|
||||||
|
if ( iPutPos == iGetPos )
|
||||||
|
{
|
||||||
|
eBufState = CBufferBase<TData>::BS_EMPTY;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
eBufState = CBufferBase<TData>::BS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true; // no error check in base class, alyways return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData>
|
||||||
|
int CBufferBase<TData>::GetAvailSpace() const
|
||||||
|
{
|
||||||
|
// calculate available space in buffer
|
||||||
|
int iAvSpace = iGetPos - iPutPos;
|
||||||
|
|
||||||
|
// check for special case and wrap around
|
||||||
|
if ( iAvSpace < 0 )
|
||||||
|
{
|
||||||
|
iAvSpace += iMemSize; // wrap around
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( ( iAvSpace == 0 ) && ( eBufState == BS_EMPTY ) )
|
||||||
|
{
|
||||||
|
iAvSpace = iMemSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iAvSpace;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class TData>
|
||||||
|
int CBufferBase<TData>::GetAvailData() const
|
||||||
|
{
|
||||||
|
// calculate available data in buffer
|
||||||
|
int iAvData = iPutPos - iGetPos;
|
||||||
|
|
||||||
|
// check for special case and wrap around
|
||||||
|
if ( iAvData < 0 )
|
||||||
|
{
|
||||||
|
iAvData += iMemSize; // wrap around
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( ( iAvData == 0 ) && ( eBufState == BS_FULL ) )
|
||||||
|
{
|
||||||
|
iAvData = iMemSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iAvData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Network buffer (jitter buffer) ----------------------------------------------
|
||||||
void CNetBuf::Init ( const int iNewBlockSize,
|
void CNetBuf::Init ( const int iNewBlockSize,
|
||||||
const int iNewNumBlocks )
|
const int iNewNumBlocks )
|
||||||
{
|
{
|
||||||
// total size -> size of one block times number of blocks
|
// store block size value
|
||||||
iBlockSize = iNewBlockSize;
|
iBlockSize = iNewBlockSize;
|
||||||
iMemSize = iNewBlockSize * iNewNumBlocks;
|
|
||||||
|
|
||||||
// allocate and clear memory for actual data buffer
|
// total size -> size of one block times number of blocks
|
||||||
vecbyMemory.Init ( iMemSize );
|
CBufferBase<uint8_t>::Init ( iNewBlockSize * iNewNumBlocks );
|
||||||
|
|
||||||
// use the "get" flag to make sure the buffer is cleared
|
// use the "get" flag to make sure the buffer is cleared
|
||||||
Clear ( CT_GET );
|
Clear ( CT_GET );
|
||||||
|
@ -68,43 +226,8 @@ bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy new data in internal buffer
|
// copy new data in internal buffer (implemented in base class)
|
||||||
int iCurPos = 0;
|
CBufferBase<uint8_t>::Put ( vecbyData, iInSize );
|
||||||
if ( iPutPos + iInSize > iMemSize )
|
|
||||||
{
|
|
||||||
// remaining space size for second block
|
|
||||||
const int iRemSpace = iPutPos + iInSize - iMemSize;
|
|
||||||
|
|
||||||
// data must be written in two steps because of wrap around
|
|
||||||
while ( iPutPos < iMemSize )
|
|
||||||
{
|
|
||||||
vecbyMemory[iPutPos++] = vecbyData[iCurPos++];
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( iPutPos = 0; iPutPos < iRemSpace; iPutPos++ )
|
|
||||||
{
|
|
||||||
vecbyMemory[iPutPos] = vecbyData[iCurPos++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// data can be written in one step
|
|
||||||
const int iEnd = iPutPos + iInSize;
|
|
||||||
while ( iPutPos < iEnd )
|
|
||||||
{
|
|
||||||
vecbyMemory[iPutPos++] = vecbyData[iCurPos++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set buffer state flag
|
|
||||||
if ( iPutPos == iGetPos )
|
|
||||||
{
|
|
||||||
eBufState = CNetBuf::BS_FULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
eBufState = CNetBuf::BS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update statistic
|
// update statistic
|
||||||
ErrorRateStatistic.Update ( !bPutOK );
|
ErrorRateStatistic.Update ( !bPutOK );
|
||||||
|
@ -152,43 +275,9 @@ bool CNetBuf::Get ( CVector<uint8_t>& vecbyData )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy data from internal buffer in output buffer
|
// copy data from internal buffer in output buffer (implemented in base
|
||||||
int iCurPos = 0;
|
// class)
|
||||||
if ( iGetPos + iInSize > iMemSize )
|
CBufferBase<uint8_t>::Get ( vecbyData );
|
||||||
{
|
|
||||||
// remaining data size for second block
|
|
||||||
const int iRemData = iGetPos + iInSize - iMemSize;
|
|
||||||
|
|
||||||
// data must be read in two steps because of wrap around
|
|
||||||
while ( iGetPos < iMemSize )
|
|
||||||
{
|
|
||||||
vecbyData[iCurPos++] = vecbyMemory[iGetPos++];
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( iGetPos = 0; iGetPos < iRemData; iGetPos++ )
|
|
||||||
{
|
|
||||||
vecbyData[iCurPos++] = vecbyMemory[iGetPos];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// data can be read in one step
|
|
||||||
const int iEnd = iGetPos + iInSize;
|
|
||||||
while ( iGetPos < iEnd )
|
|
||||||
{
|
|
||||||
vecbyData[iCurPos++] = vecbyMemory[iGetPos++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set buffer state flag
|
|
||||||
if ( iPutPos == iGetPos )
|
|
||||||
{
|
|
||||||
eBufState = CNetBuf::BS_EMPTY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
eBufState = CNetBuf::BS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// update statistic
|
// update statistic
|
||||||
ErrorRateStatistic.Update ( !bGetOK );
|
ErrorRateStatistic.Update ( !bGetOK );
|
||||||
|
@ -196,48 +285,6 @@ bool CNetBuf::Get ( CVector<uint8_t>& vecbyData )
|
||||||
return bGetOK;
|
return bGetOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int CNetBuf::GetAvailSpace() const
|
|
||||||
{
|
|
||||||
// calculate available space in buffer
|
|
||||||
int iAvSpace = iGetPos - iPutPos;
|
|
||||||
|
|
||||||
// check for special case and wrap around
|
|
||||||
if ( iAvSpace < 0 )
|
|
||||||
{
|
|
||||||
iAvSpace += iMemSize; // wrap around
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( ( iAvSpace == 0 ) && ( eBufState == BS_EMPTY ) )
|
|
||||||
{
|
|
||||||
iAvSpace = iMemSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return iAvSpace;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CNetBuf::GetAvailData() const
|
|
||||||
{
|
|
||||||
// calculate available data in buffer
|
|
||||||
int iAvData = iPutPos - iGetPos;
|
|
||||||
|
|
||||||
// check for special case and wrap around
|
|
||||||
if ( iAvData < 0 )
|
|
||||||
{
|
|
||||||
iAvData += iMemSize; // wrap around
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ( ( iAvData == 0 ) && ( eBufState == BS_FULL ) )
|
|
||||||
{
|
|
||||||
iAvData = iMemSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return iAvData;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CNetBuf::Clear ( const EClearType eClearType )
|
void CNetBuf::Clear ( const EClearType eClearType )
|
||||||
{
|
{
|
||||||
// Define the number of blocks bound for the "random offset" (1) algorithm.
|
// Define the number of blocks bound for the "random offset" (1) algorithm.
|
||||||
|
@ -301,7 +348,7 @@ void CNetBuf::Clear ( const EClearType eClearType )
|
||||||
if ( eClearType == CT_GET )
|
if ( eClearType == CT_GET )
|
||||||
{
|
{
|
||||||
// clear buffer since we had a buffer underrun
|
// clear buffer since we had a buffer underrun
|
||||||
vecbyMemory.Reset ( 0 );
|
vecMemory.Reset ( 0 );
|
||||||
|
|
||||||
// reset buffer pointers so that they are at maximum distance after
|
// reset buffer pointers so that they are at maximum distance after
|
||||||
// the get operation (assign new fill level value to the get pointer)
|
// the get operation (assign new fill level value to the get pointer)
|
||||||
|
|
41
src/buffer.h
41
src/buffer.h
|
@ -36,13 +36,32 @@
|
||||||
|
|
||||||
|
|
||||||
/* Classes ********************************************************************/
|
/* Classes ********************************************************************/
|
||||||
// Network buffer (jitter buffer) ----------------------------------------------
|
// Buffer base class -----------------------------------------------------------
|
||||||
class CNetBuf
|
template<class TData> class CBufferBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CNetBuf() {}
|
virtual void Init ( const int iNewMemSize );
|
||||||
virtual ~CNetBuf() {}
|
|
||||||
|
|
||||||
|
virtual bool Put ( const CVector<TData>& vecData, const int iInSize );
|
||||||
|
virtual bool Get ( CVector<TData>& vecData );
|
||||||
|
|
||||||
|
virtual int GetAvailSpace() const;
|
||||||
|
virtual int GetAvailData() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum EBufState { BS_OK, BS_FULL, BS_EMPTY };
|
||||||
|
|
||||||
|
CVector<TData> vecMemory;
|
||||||
|
int iMemSize;
|
||||||
|
int iGetPos, iPutPos;
|
||||||
|
EBufState eBufState;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Network buffer (jitter buffer) ----------------------------------------------
|
||||||
|
class CNetBuf : public CBufferBase<uint8_t>
|
||||||
|
{
|
||||||
|
public:
|
||||||
void Init ( const int iNewBlockSize, const int iNewNumBlocks );
|
void Init ( const int iNewBlockSize, const int iNewNumBlocks );
|
||||||
int GetSize() { return iMemSize / iBlockSize; }
|
int GetSize() { return iMemSize / iBlockSize; }
|
||||||
|
|
||||||
|
@ -52,18 +71,12 @@ public:
|
||||||
double GetErrorRate() { return ErrorRateStatistic.GetAverage(); }
|
double GetErrorRate() { return ErrorRateStatistic.GetAverage(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
enum EBufState { BS_OK, BS_FULL, BS_EMPTY };
|
|
||||||
enum EClearType { CT_PUT, CT_GET };
|
enum EClearType { CT_PUT, CT_GET };
|
||||||
void Clear ( const EClearType eClearType );
|
|
||||||
int GetAvailSpace() const;
|
|
||||||
int GetAvailData() const;
|
|
||||||
|
|
||||||
CVector<uint8_t> vecbyMemory;
|
void Clear ( const EClearType eClearType );
|
||||||
int iMemSize;
|
|
||||||
int iBlockSize;
|
int iBlockSize;
|
||||||
int iNumInvalidElements;
|
int iNumInvalidElements;
|
||||||
int iGetPos, iPutPos;
|
|
||||||
EBufState eBufState;
|
|
||||||
|
|
||||||
// statistic
|
// statistic
|
||||||
CErrorRate ErrorRateStatistic;
|
CErrorRate ErrorRateStatistic;
|
||||||
|
@ -71,11 +84,13 @@ protected:
|
||||||
|
|
||||||
|
|
||||||
// Conversion buffer (very simple buffer) --------------------------------------
|
// Conversion buffer (very simple buffer) --------------------------------------
|
||||||
|
// For this very simple buffer no wrap around mechanism is implemented. We
|
||||||
|
// assume here, that the applied buffers are an integer fraction of the total
|
||||||
|
// buffer size.
|
||||||
template<class TData> class CConvBuf
|
template<class TData> class CConvBuf
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CConvBuf() { Init ( 0 ); }
|
CConvBuf() { Init ( 0 ); }
|
||||||
virtual ~CConvBuf() {}
|
|
||||||
|
|
||||||
void Init ( const int iNewMemSize )
|
void Init ( const int iNewMemSize )
|
||||||
{
|
{
|
||||||
|
|
193
src/client.cpp
193
src/client.cpp
|
@ -43,7 +43,9 @@ CClient::CClient ( const quint16 iPortNumber ) :
|
||||||
bFraSiFactDefSupported ( false ),
|
bFraSiFactDefSupported ( false ),
|
||||||
bFraSiFactSafeSupported ( false ),
|
bFraSiFactSafeSupported ( false ),
|
||||||
iCeltNumCodedBytes ( CELT_NUM_BYTES_NORMAL_QUALITY ),
|
iCeltNumCodedBytes ( CELT_NUM_BYTES_NORMAL_QUALITY ),
|
||||||
bCeltDoHighQuality ( false )
|
bCeltDoHighQuality ( false ),
|
||||||
|
bSndCrdConversionBufferRequired ( false ),
|
||||||
|
iSndCardMonoBlockSizeSamConvBuff ( 0 )
|
||||||
{
|
{
|
||||||
// init audio endocder/decoder (mono)
|
// init audio endocder/decoder (mono)
|
||||||
CeltMode = celt_mode_create (
|
CeltMode = celt_mode_create (
|
||||||
|
@ -189,22 +191,12 @@ void CClient::SetSndCrdPrefFrameSizeFactor ( const int iNewFactor )
|
||||||
iSndCrdPrefFrameSizeFactor = iNewFactor;
|
iSndCrdPrefFrameSizeFactor = iNewFactor;
|
||||||
|
|
||||||
// init with new block size index parameter
|
// init with new block size index parameter
|
||||||
const bool bInitWasOk = Init();
|
Init();
|
||||||
|
|
||||||
if ( bWasRunning )
|
if ( bWasRunning )
|
||||||
{
|
{
|
||||||
if ( bInitWasOk )
|
// restart client
|
||||||
{
|
Sound.Start();
|
||||||
// init was ok, restart client
|
|
||||||
Sound.Start();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// init was not successful, do not restart client and
|
|
||||||
// inform main window of the stopped client
|
|
||||||
Stop();
|
|
||||||
emit Stopped();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,22 +237,12 @@ QString CClient::SetSndCrdDev ( const int iNewDev )
|
||||||
|
|
||||||
// init again because the sound card actual buffer size might
|
// init again because the sound card actual buffer size might
|
||||||
// be changed on new device
|
// be changed on new device
|
||||||
const bool bInitWasOk = Init();
|
Init();
|
||||||
|
|
||||||
if ( bWasRunning )
|
if ( bWasRunning )
|
||||||
{
|
{
|
||||||
if ( bInitWasOk )
|
// restart client
|
||||||
{
|
Sound.Start();
|
||||||
// init was ok, restart client
|
|
||||||
Sound.Start();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// init was not successful, do not restart client and
|
|
||||||
// inform main window of the stopped client
|
|
||||||
Stop();
|
|
||||||
emit Stopped();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return strReturn;
|
return strReturn;
|
||||||
|
@ -279,52 +261,19 @@ void CClient::OnSndCrdReinitRequest()
|
||||||
// reinit the driver (we use the currently selected driver) and
|
// reinit the driver (we use the currently selected driver) and
|
||||||
// init client object, too
|
// init client object, too
|
||||||
Sound.SetDev ( Sound.GetDev() );
|
Sound.SetDev ( Sound.GetDev() );
|
||||||
const bool bInitWasOk = Init();
|
Init();
|
||||||
|
|
||||||
if ( bWasRunning )
|
if ( bWasRunning )
|
||||||
{
|
{
|
||||||
if ( bInitWasOk )
|
// restart client
|
||||||
{
|
Sound.Start();
|
||||||
// init was ok, restart client
|
|
||||||
Sound.Start();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// init was not successful, do not restart client and
|
|
||||||
// inform main window of the stopped client
|
|
||||||
Stop();
|
|
||||||
emit Stopped();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::Start()
|
void CClient::Start()
|
||||||
{
|
{
|
||||||
// init object
|
// init object
|
||||||
if ( !Init() )
|
Init() ;
|
||||||
{
|
|
||||||
const QString strError = "The current sound card frame size of <b>" +
|
|
||||||
QString().setNum ( iMonoBlockSizeSam ) + " samples</b> is not supported "
|
|
||||||
"by this software. Please open your "
|
|
||||||
#ifdef _WIN32
|
|
||||||
"ASIO "
|
|
||||||
#else
|
|
||||||
"JACK "
|
|
||||||
#endif
|
|
||||||
"configuration panel and use one of the following frame sizes: <b>" +
|
|
||||||
QString().setNum ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) + ", " +
|
|
||||||
QString().setNum ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) + ", or " +
|
|
||||||
QString().setNum ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) +
|
|
||||||
" samples</b>."
|
|
||||||
#ifdef _WIN32
|
|
||||||
"<br><br>To open the ASIO configuration panel, you can open the Settings "
|
|
||||||
"Dialog in the llcon software (using the menu) and click on the \"ASIO "
|
|
||||||
"Setup\" button."
|
|
||||||
#endif
|
|
||||||
;
|
|
||||||
|
|
||||||
throw CGenErr ( strError );
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable channel
|
// enable channel
|
||||||
Channel.SetEnable ( true );
|
Channel.SetEnable ( true );
|
||||||
|
@ -367,16 +316,7 @@ void CClient::Stop()
|
||||||
PostWinMessage ( MS_RESET_ALL, 0 );
|
PostWinMessage ( MS_RESET_ALL, 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::AudioCallback ( CVector<int16_t>& psData, void* arg )
|
void CClient::Init()
|
||||||
{
|
|
||||||
// get the pointer to the object
|
|
||||||
CClient* pMyClientObj = reinterpret_cast<CClient*> ( arg );
|
|
||||||
|
|
||||||
// process audio data
|
|
||||||
pMyClientObj->ProcessAudioData ( psData );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CClient::Init()
|
|
||||||
{
|
{
|
||||||
// check if possible frame size factors are supported
|
// check if possible frame size factors are supported
|
||||||
const int iFraSizePreffered =
|
const int iFraSizePreffered =
|
||||||
|
@ -402,16 +342,60 @@ bool CClient::Init()
|
||||||
iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES;
|
iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES;
|
||||||
|
|
||||||
// get actual sound card buffer size using preferred size
|
// get actual sound card buffer size using preferred size
|
||||||
iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize );
|
iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize );
|
||||||
|
|
||||||
|
// Calculate the current sound card frame size factor. In case
|
||||||
|
// the current mono block size is not a multiple of the system
|
||||||
|
// frame size, we have to use a sound card conversion buffer.
|
||||||
|
if ( ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) ||
|
||||||
|
( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) ||
|
||||||
|
( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) )
|
||||||
|
{
|
||||||
|
// regular case: one of our predefined buffer sizes is available
|
||||||
|
iSndCrdFrameSizeFactor = iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES;
|
||||||
|
|
||||||
|
// no sound card conversion buffer required
|
||||||
|
iSndCardMonoBlockSizeSamConvBuff = 0; // this is important!
|
||||||
|
bSndCrdConversionBufferRequired = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// An unsupported sound card buffer size is currently used -> we have
|
||||||
|
// to use a conversion buffer. Per definition we use the smallest buffer
|
||||||
|
// size as llcon frame size
|
||||||
|
|
||||||
|
// store actual sound card buffer size (stereo)
|
||||||
|
iSndCardMonoBlockSizeSamConvBuff = iMonoBlockSizeSam;
|
||||||
|
const int iSndCardStereoBlockSizeSamConvBuff = 2 * iMonoBlockSizeSam;
|
||||||
|
|
||||||
|
// overwrite block size by smallest supported llcon buffer size
|
||||||
|
iSndCrdFrameSizeFactor = FRAME_SIZE_FACTOR_PREFERRED;
|
||||||
|
iMonoBlockSizeSam =
|
||||||
|
SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED;
|
||||||
|
|
||||||
|
iStereoBlockSizeSam = 2 * iMonoBlockSizeSam;
|
||||||
|
|
||||||
|
// inits for conversion buffer (the size of the conversion buffer must
|
||||||
|
// be the sum of input/output sizes which is the worst case fill level)
|
||||||
|
const int iConBufSize =
|
||||||
|
iStereoBlockSizeSam + iSndCardStereoBlockSizeSamConvBuff;
|
||||||
|
|
||||||
|
SndCrdConversionBufferIn.Init ( iConBufSize );
|
||||||
|
SndCrdConversionBufferOut.Init ( iConBufSize );
|
||||||
|
vecDataConvBuf.Init ( iStereoBlockSizeSam );
|
||||||
|
|
||||||
|
// the output conversion buffer must be filled with the inner
|
||||||
|
// block size for initialization (this is the latency which is
|
||||||
|
// introduced by the conversion buffer) to avoid buffer underruns
|
||||||
|
const CVector<int16_t> vZeros ( iStereoBlockSizeSam, 0 );
|
||||||
|
SndCrdConversionBufferOut.Put ( vZeros, vZeros.Size() );
|
||||||
|
|
||||||
|
bSndCrdConversionBufferRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate stereo (two channels) buffer size
|
||||||
iStereoBlockSizeSam = 2 * iMonoBlockSizeSam;
|
iStereoBlockSizeSam = 2 * iMonoBlockSizeSam;
|
||||||
|
|
||||||
|
|
||||||
// TEST (we assume here that "iMonoBlockSizeSam" is divisible by
|
|
||||||
// "SYSTEM_FRAME_SIZE_SAMPLES")
|
|
||||||
// calculate actual frame size factor
|
|
||||||
iSndCrdFrameSizeFactor = max ( 1, iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES );
|
|
||||||
|
|
||||||
|
|
||||||
vecsAudioSndCrdMono.Init ( iMonoBlockSizeSam );
|
vecsAudioSndCrdMono.Init ( iMonoBlockSizeSam );
|
||||||
vecsAudioSndCrdStereo.Init ( iStereoBlockSizeSam );
|
vecsAudioSndCrdStereo.Init ( iStereoBlockSizeSam );
|
||||||
vecdAudioStereo.Init ( iStereoBlockSizeSam );
|
vecdAudioStereo.Init ( iStereoBlockSizeSam );
|
||||||
|
@ -443,21 +427,49 @@ iSndCrdFrameSizeFactor = max ( 1, iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES
|
||||||
// set the channel network properties
|
// set the channel network properties
|
||||||
Channel.SetNetwFrameSizeAndFact ( iCeltNumCodedBytes,
|
Channel.SetNetwFrameSizeAndFact ( iCeltNumCodedBytes,
|
||||||
iSndCrdFrameSizeFactor );
|
iSndCrdFrameSizeFactor );
|
||||||
|
}
|
||||||
|
|
||||||
// check sound card buffer sizes, if not supported, return error flag
|
void CClient::AudioCallback ( CVector<int16_t>& psData, void* arg )
|
||||||
if ( ( iMonoBlockSizeSam != ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) &&
|
{
|
||||||
( iMonoBlockSizeSam != ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) &&
|
// get the pointer to the object
|
||||||
( iMonoBlockSizeSam != ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) )
|
CClient* pMyClientObj = reinterpret_cast<CClient*> ( arg );
|
||||||
|
|
||||||
|
// process audio data
|
||||||
|
pMyClientObj->ProcessSndCrdAudioData ( psData );
|
||||||
|
}
|
||||||
|
|
||||||
|
void CClient::ProcessSndCrdAudioData ( CVector<int16_t>& vecsStereoSndCrd )
|
||||||
|
{
|
||||||
|
// check if a conversion buffer is required or not
|
||||||
|
if ( bSndCrdConversionBufferRequired )
|
||||||
{
|
{
|
||||||
return false; // init was not successful
|
// add new sound card block in conversion buffer
|
||||||
|
SndCrdConversionBufferIn.Put ( vecsStereoSndCrd, vecsStereoSndCrd.Size() );
|
||||||
|
|
||||||
|
// process all available blocks of data
|
||||||
|
while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam )
|
||||||
|
{
|
||||||
|
// get one block of data for processing
|
||||||
|
SndCrdConversionBufferIn.Get ( vecDataConvBuf );
|
||||||
|
|
||||||
|
// process audio data
|
||||||
|
ProcessAudioDataIntern ( vecDataConvBuf );
|
||||||
|
|
||||||
|
SndCrdConversionBufferOut.Put ( vecDataConvBuf, vecDataConvBuf.Size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// get processed sound card block out of the conversion buffer
|
||||||
|
SndCrdConversionBufferOut.Get ( vecsStereoSndCrd );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return true; // ok
|
// regular case: no conversion buffer required
|
||||||
|
// process audio data
|
||||||
|
ProcessAudioDataIntern ( vecsStereoSndCrd );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClient::ProcessAudioData ( CVector<int16_t>& vecsStereoSndCrd )
|
void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
@ -628,7 +640,8 @@ void CClient::UpdateSocketBufferSize()
|
||||||
|
|
||||||
// calculate current buffer setting
|
// calculate current buffer setting
|
||||||
const double dAudioBufferDurationMs =
|
const double dAudioBufferDurationMs =
|
||||||
iMonoBlockSizeSam * 1000 / SYSTEM_SAMPLE_RATE;
|
( iMonoBlockSizeSam + iSndCardMonoBlockSizeSamConvBuff ) *
|
||||||
|
1000 / SYSTEM_SAMPLE_RATE;
|
||||||
|
|
||||||
// jitter introduced in the server by the timer implementation
|
// jitter introduced in the server by the timer implementation
|
||||||
const double dServerJitterMs = 0.666666; // ms
|
const double dServerJitterMs = 0.666666; // ms
|
||||||
|
|
28
src/client.h
28
src/client.h
|
@ -36,6 +36,7 @@
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "buffer.h"
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# include "../windows/sound.h"
|
# include "../windows/sound.h"
|
||||||
#else
|
#else
|
||||||
|
@ -134,7 +135,20 @@ public:
|
||||||
void SetSndCrdPrefFrameSizeFactor ( const int iNewFactor );
|
void SetSndCrdPrefFrameSizeFactor ( const int iNewFactor );
|
||||||
int GetSndCrdPrefFrameSizeFactor()
|
int GetSndCrdPrefFrameSizeFactor()
|
||||||
{ return iSndCrdPrefFrameSizeFactor; }
|
{ return iSndCrdPrefFrameSizeFactor; }
|
||||||
int GetSndCrdActualMonoBlSize() { return iMonoBlockSizeSam; }
|
|
||||||
|
int GetSndCrdActualMonoBlSize()
|
||||||
|
{
|
||||||
|
// the actual sound card mono block size depends on whether a
|
||||||
|
// sound card conversion buffer is used or not
|
||||||
|
if ( bSndCrdConversionBufferRequired )
|
||||||
|
{
|
||||||
|
return iSndCardMonoBlockSizeSamConvBuff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return iMonoBlockSizeSam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool GetFraSiFactPrefSupported() { return bFraSiFactPrefSupported; }
|
bool GetFraSiFactPrefSupported() { return bFraSiFactPrefSupported; }
|
||||||
bool GetFraSiFactDefSupported() { return bFraSiFactDefSupported; }
|
bool GetFraSiFactDefSupported() { return bFraSiFactDefSupported; }
|
||||||
|
@ -153,7 +167,6 @@ public:
|
||||||
|
|
||||||
CChannel* GetChannel() { return &Channel; }
|
CChannel* GetChannel() { return &Channel; }
|
||||||
|
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
CVector<QString> vstrIPAddress;
|
CVector<QString> vstrIPAddress;
|
||||||
QString strName;
|
QString strName;
|
||||||
|
@ -162,8 +175,9 @@ protected:
|
||||||
// callback function must be static, otherwise it does not work
|
// callback function must be static, otherwise it does not work
|
||||||
static void AudioCallback ( CVector<short>& psData, void* arg );
|
static void AudioCallback ( CVector<short>& psData, void* arg );
|
||||||
|
|
||||||
bool Init();
|
void Init();
|
||||||
void ProcessAudioData ( CVector<short>& vecsStereoSndCrd );
|
void ProcessSndCrdAudioData ( CVector<short>& vecsStereoSndCrd );
|
||||||
|
void ProcessAudioDataIntern ( CVector<short>& vecsStereoSndCrd );
|
||||||
void UpdateSocketBufferSize();
|
void UpdateSocketBufferSize();
|
||||||
|
|
||||||
// only one channel is needed for client application
|
// only one channel is needed for client application
|
||||||
|
@ -192,6 +206,12 @@ protected:
|
||||||
int iSndCrdPrefFrameSizeFactor;
|
int iSndCrdPrefFrameSizeFactor;
|
||||||
int iSndCrdFrameSizeFactor;
|
int iSndCrdFrameSizeFactor;
|
||||||
|
|
||||||
|
bool bSndCrdConversionBufferRequired;
|
||||||
|
int iSndCardMonoBlockSizeSamConvBuff;
|
||||||
|
CBufferBase<int16_t> SndCrdConversionBufferIn;
|
||||||
|
CBufferBase<int16_t> SndCrdConversionBufferOut;
|
||||||
|
CVector<int16_t> vecDataConvBuf;
|
||||||
|
|
||||||
bool bFraSiFactPrefSupported;
|
bool bFraSiFactPrefSupported;
|
||||||
bool bFraSiFactDefSupported;
|
bool bFraSiFactDefSupported;
|
||||||
bool bFraSiFactSafeSupported;
|
bool bFraSiFactSafeSupported;
|
||||||
|
|
14
src/global.h
14
src/global.h
|
@ -145,17 +145,21 @@
|
||||||
#elif HAVE_INTTYPES_H
|
#elif HAVE_INTTYPES_H
|
||||||
# include <inttypes.h>
|
# include <inttypes.h>
|
||||||
#elif defined ( _WIN32 )
|
#elif defined ( _WIN32 )
|
||||||
|
typedef __int64 int64_t;
|
||||||
typedef __int32 int32_t;
|
typedef __int32 int32_t;
|
||||||
typedef __int16 int16_t;
|
typedef __int16 int16_t;
|
||||||
|
typedef unsigned __int64 uint64_t;
|
||||||
typedef unsigned __int32 uint32_t;
|
typedef unsigned __int32 uint32_t;
|
||||||
typedef unsigned __int16 uint16_t;
|
typedef unsigned __int16 uint16_t;
|
||||||
typedef unsigned __int8 uint8_t;
|
typedef unsigned __int8 uint8_t;
|
||||||
#else
|
#else
|
||||||
typedef int int32_t;
|
typedef long int int64_t;
|
||||||
typedef short int16_t;
|
typedef int int32_t;
|
||||||
typedef unsigned int uint32_t;
|
typedef short int16_t;
|
||||||
typedef unsigned short uint16_t;
|
typedef unsigned long int uint64_t;
|
||||||
typedef unsigned char uint8_t;
|
typedef unsigned int uint32_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ ASIOCallbacks asioCallbacks;
|
||||||
int iASIOBufferSizeMono;
|
int iASIOBufferSizeMono;
|
||||||
int iASIOBufferSizeStereo;
|
int iASIOBufferSizeStereo;
|
||||||
|
|
||||||
CVector<short> vecsTmpAudioSndCrdStereo;
|
CVector<int16_t> vecsTmpAudioSndCrdStereo;
|
||||||
|
|
||||||
QMutex ASIOMutex;
|
QMutex ASIOMutex;
|
||||||
|
|
||||||
|
@ -275,14 +275,28 @@ QString CSound::CheckDeviceCapabilities()
|
||||||
}
|
}
|
||||||
ASIOGetChannelInfo ( &channelInfos[i] );
|
ASIOGetChannelInfo ( &channelInfos[i] );
|
||||||
|
|
||||||
// only 16/24/32 LSB is supported
|
// check supported sample formats
|
||||||
if ( ( channelInfos[i].type != ASIOSTInt16LSB ) &&
|
if ( ( channelInfos[i].type != ASIOSTInt16LSB ) &&
|
||||||
( channelInfos[i].type != ASIOSTInt24LSB ) &&
|
( channelInfos[i].type != ASIOSTInt24LSB ) &&
|
||||||
( channelInfos[i].type != ASIOSTInt32LSB ) )
|
( channelInfos[i].type != ASIOSTInt32LSB ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTFloat32LSB ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTFloat64LSB ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt32LSB16 ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt32LSB18 ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt32LSB20 ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt32LSB24 ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt16MSB ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt24MSB ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt32MSB ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTFloat32MSB ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTFloat64MSB ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt32MSB16 ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt32MSB18 ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt32MSB20 ) &&
|
||||||
|
( channelInfos[i].type != ASIOSTInt32MSB24 ) )
|
||||||
{
|
{
|
||||||
// return error string
|
// return error string
|
||||||
return tr ( "Required audio sample format not "
|
return tr ( "Required audio sample format not available." );
|
||||||
"available (16/24/32 bit LSB)." );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +442,7 @@ void CSound::Stop()
|
||||||
CSoundBase::Stop();
|
CSoundBase::Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
CSound::CSound ( void (*fpNewCallback) ( CVector<short>& psData, void* arg ), void* arg ) :
|
CSound::CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* arg ), void* arg ) :
|
||||||
CSoundBase ( true, fpNewCallback, arg )
|
CSoundBase ( true, fpNewCallback, arg )
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -496,9 +510,9 @@ CSound::~CSound()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ASIO callbacks -------------------------------------------------------------
|
// ASIO callbacks -------------------------------------------------------------
|
||||||
ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime *timeInfo,
|
ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime* timeInfo,
|
||||||
long index,
|
long index,
|
||||||
ASIOBool processNow )
|
ASIOBool processNow )
|
||||||
{
|
{
|
||||||
bufferSwitch ( index, processNow );
|
bufferSwitch ( index, processNow );
|
||||||
return 0L;
|
return 0L;
|
||||||
|
@ -521,37 +535,153 @@ void CSound::bufferSwitch ( long index, ASIOBool processNow )
|
||||||
switch ( channelInfos[i].type )
|
switch ( channelInfos[i].type )
|
||||||
{
|
{
|
||||||
case ASIOSTInt16LSB:
|
case ASIOSTInt16LSB:
|
||||||
|
// no type conversion required, just copy operation
|
||||||
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
{
|
{
|
||||||
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
( (short*) bufferInfos[i].buffers[index] )[iCurSample];
|
static_cast<int16_t*> ( bufferInfos[i].buffers[index] )[iCurSample];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ASIOSTInt24LSB:
|
case ASIOSTInt24LSB:
|
||||||
|
// NOT YET TESTED
|
||||||
// not yet tested, horrible things might happen with the following code ;-)
|
|
||||||
|
|
||||||
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
{
|
{
|
||||||
// convert current sample in 16 bit format
|
|
||||||
int iCurSam = 0;
|
int iCurSam = 0;
|
||||||
memcpy ( &iCurSam, ( (char*) bufferInfos[i].buffers[index] ) + iCurSample * 3, 3 );
|
memcpy ( &iCurSam, ( (char*) bufferInfos[i].buffers[index] ) + iCurSample * 3, 3 );
|
||||||
iCurSam >>= 8;
|
iCurSam >>= 8;
|
||||||
|
|
||||||
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
static_cast<short> ( iCurSam );
|
static_cast<int16_t> ( iCurSam );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ASIOSTInt32LSB:
|
case ASIOSTInt32LSB:
|
||||||
|
// NOT YET TESTED
|
||||||
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
{
|
{
|
||||||
// convert to 16 bit
|
|
||||||
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
(((int*) bufferInfos[i].buffers[index])[iCurSample] >> 16);
|
static_cast<int16_t> ( static_cast<int32_t*> (
|
||||||
|
bufferInfos[i].buffers[index] )[iCurSample] >> 16 );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
|
static_cast<int16_t> ( static_cast<float*> (
|
||||||
|
bufferInfos[i].buffers[index] )[iCurSample] * _MAXSHORT );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
|
static_cast<int16_t> ( static_cast<double*> (
|
||||||
|
bufferInfos[i].buffers[index] )[iCurSample] * _MAXSHORT );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
|
static_cast<int16_t> ( static_cast<int32_t*> (
|
||||||
|
bufferInfos[i].buffers[index] )[iCurSample] & 0xFFFF );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
|
static_cast<int16_t> ( ( static_cast<int32_t*> (
|
||||||
|
bufferInfos[i].buffers[index] )[iCurSample] & 0x3FFFF ) >> 2 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
|
static_cast<int16_t> ( ( static_cast<int32_t*> (
|
||||||
|
bufferInfos[i].buffers[index] )[iCurSample] & 0xFFFFF ) >> 4 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
|
static_cast<int16_t> ( ( static_cast<int32_t*> (
|
||||||
|
bufferInfos[i].buffers[index] )[iCurSample] & 0xFFFFFF ) >> 8 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt16MSB:
|
||||||
|
// NOT YET TESTED
|
||||||
|
// flip bits
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
|
Flip16Bits ( ( static_cast<int16_t*> (
|
||||||
|
bufferInfos[i].buffers[index] ) )[iCurSample] );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt24MSB:
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB:
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
// flip bits and convert to 16 bit
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
|
||||||
|
static_cast<int16_t> ( Flip32Bits ( static_cast<int32_t*> (
|
||||||
|
bufferInfos[i].buffers[index] )[iCurSample] ) >> 16 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -570,21 +700,22 @@ void CSound::bufferSwitch ( long index, ASIOBool processNow )
|
||||||
switch ( channelInfos[i].type )
|
switch ( channelInfos[i].type )
|
||||||
{
|
{
|
||||||
case ASIOSTInt16LSB:
|
case ASIOSTInt16LSB:
|
||||||
|
// no type conversion required, just copy operation
|
||||||
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
{
|
{
|
||||||
( (short*) bufferInfos[i].buffers[index] )[iCurSample] =
|
static_cast<int16_t*> ( bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum];
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ASIOSTInt24LSB:
|
case ASIOSTInt24LSB:
|
||||||
|
// NOT YET TESTED
|
||||||
// not yet tested, horrible things might happen with the following code ;-)
|
|
||||||
|
|
||||||
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
{
|
{
|
||||||
// convert current sample in 24 bit format
|
// convert current sample in 24 bit format
|
||||||
int iCurSam = static_cast<int> ( vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
int32_t iCurSam = static_cast<int32_t> (
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
|
||||||
iCurSam <<= 8;
|
iCurSam <<= 8;
|
||||||
|
|
||||||
memcpy ( ( (char*) bufferInfos[i].buffers[index] ) + iCurSample * 3, &iCurSam, 3 );
|
memcpy ( ( (char*) bufferInfos[i].buffers[index] ) + iCurSample * 3, &iCurSam, 3 );
|
||||||
|
@ -592,13 +723,151 @@ void CSound::bufferSwitch ( long index, ASIOBool processNow )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ASIOSTInt32LSB:
|
case ASIOSTInt32LSB:
|
||||||
|
// NOT YET TESTED
|
||||||
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
{
|
{
|
||||||
// convert to 32 bit
|
// convert to 32 bit
|
||||||
int iCurSam = static_cast<int> ( vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
const int32_t iCurSam = static_cast<int32_t> (
|
||||||
( (int*) bufferInfos[i].buffers[index] )[iCurSample] = ( iCurSam << 16 );
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
|
||||||
|
static_cast<int32_t*> ( bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
|
( iCurSam << 16 );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
const float fCurSam = static_cast<float> (
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
|
||||||
|
static_cast<float*> ( bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
|
fCurSam / _MAXSHORT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
const double fCurSam = static_cast<double> (
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
|
||||||
|
static_cast<double*> ( bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
|
fCurSam / _MAXSHORT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
// convert to 32 bit
|
||||||
|
const int32_t iCurSam = static_cast<int32_t> (
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
|
||||||
|
static_cast<int32_t*> ( bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
|
iCurSam;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
// convert to 32 bit
|
||||||
|
const int32_t iCurSam = static_cast<int32_t> (
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
|
||||||
|
static_cast<int32_t*> ( bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
|
( iCurSam << 2 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
// convert to 32 bit
|
||||||
|
const int32_t iCurSam = static_cast<int32_t> (
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
|
||||||
|
static_cast<int32_t*> ( bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
|
( iCurSam << 4 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
// convert to 32 bit
|
||||||
|
const int32_t iCurSam = static_cast<int32_t> (
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
|
||||||
|
static_cast<int32_t*> ( bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
|
( iCurSam << 8 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt16MSB:
|
||||||
|
// NOT YET TESTED
|
||||||
|
// flip bits
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
( (int16_t*) bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
|
Flip16Bits ( vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt24MSB:
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB:
|
||||||
|
// NOT YET TESTED
|
||||||
|
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
|
||||||
|
{
|
||||||
|
// convert to 32 bit and flip bits
|
||||||
|
int iCurSam = static_cast<int32_t> (
|
||||||
|
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
|
||||||
|
|
||||||
|
static_cast<int32_t*> ( bufferInfos[i].buffers[index] )[iCurSample] =
|
||||||
|
Flip32Bits ( iCurSam << 16 );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment
|
||||||
|
// NOT YET TESTED
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -613,7 +882,10 @@ void CSound::bufferSwitch ( long index, ASIOBool processNow )
|
||||||
ASIOMutex.unlock();
|
ASIOMutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
long CSound::asioMessages ( long selector, long value, void* message, double* opt )
|
long CSound::asioMessages ( long selector,
|
||||||
|
long value,
|
||||||
|
void* message,
|
||||||
|
double* opt )
|
||||||
{
|
{
|
||||||
long ret = 0;
|
long ret = 0;
|
||||||
switch ( selector )
|
switch ( selector )
|
||||||
|
@ -632,3 +904,57 @@ long CSound::asioMessages ( long selector, long value, void* message, double* op
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t CSound::Flip16Bits ( const int16_t iIn )
|
||||||
|
{
|
||||||
|
uint16_t iMask = ( 1 << 15 );
|
||||||
|
int16_t iOut = 0;
|
||||||
|
|
||||||
|
for ( unsigned int i = 0; i < 16; i++ )
|
||||||
|
{
|
||||||
|
// copy current bit to correct position
|
||||||
|
iOut |= ( iIn & iMask ) ? 1 : 0;
|
||||||
|
|
||||||
|
// shift out value and mask by one bit
|
||||||
|
iOut <<= 1;
|
||||||
|
iMask >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t CSound::Flip32Bits ( const int32_t iIn )
|
||||||
|
{
|
||||||
|
uint32_t iMask = ( static_cast<uint32_t> ( 1 ) << 31 );
|
||||||
|
int32_t iOut = 0;
|
||||||
|
|
||||||
|
for ( unsigned int i = 0; i < 32; i++ )
|
||||||
|
{
|
||||||
|
// copy current bit to correct position
|
||||||
|
iOut |= ( iIn & iMask ) ? 1 : 0;
|
||||||
|
|
||||||
|
// shift out value and mask by one bit
|
||||||
|
iOut <<= 1;
|
||||||
|
iMask >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t CSound::Flip64Bits ( const int64_t iIn )
|
||||||
|
{
|
||||||
|
uint64_t iMask = ( static_cast<uint64_t> ( 1 ) << 63 );
|
||||||
|
int64_t iOut = 0;
|
||||||
|
|
||||||
|
for ( unsigned int i = 0; i < 64; i++ )
|
||||||
|
{
|
||||||
|
// copy current bit to correct position
|
||||||
|
iOut |= ( iIn & iMask ) ? 1 : 0;
|
||||||
|
|
||||||
|
// shift out value and mask by one bit
|
||||||
|
iOut <<= 1;
|
||||||
|
iMask >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iOut;
|
||||||
|
}
|
||||||
|
|
|
@ -75,6 +75,11 @@ protected:
|
||||||
int GetActualBufferSize ( const int iDesiredBufferSizeMono );
|
int GetActualBufferSize ( const int iDesiredBufferSizeMono );
|
||||||
QString CheckDeviceCapabilities();
|
QString CheckDeviceCapabilities();
|
||||||
|
|
||||||
|
// utility functions
|
||||||
|
static int16_t Flip16Bits ( const int16_t iIn );
|
||||||
|
static int32_t Flip32Bits ( const int32_t iIn );
|
||||||
|
static int64_t Flip64Bits ( const int64_t iIn );
|
||||||
|
|
||||||
// audio hardware buffer info
|
// audio hardware buffer info
|
||||||
struct sHWBufferInfo
|
struct sHWBufferInfo
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue