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:
Volker Fischer 2010-02-03 19:27:48 +00:00
parent f06f38dc75
commit 75b1994257
7 changed files with 694 additions and 264 deletions

View file

@ -29,15 +29,173 @@
/* 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,
const int iNewNumBlocks )
{
// total size -> size of one block times number of blocks
// store block size value
iBlockSize = iNewBlockSize;
iMemSize = iNewBlockSize * iNewNumBlocks;
// allocate and clear memory for actual data buffer
vecbyMemory.Init ( iMemSize );
// total size -> size of one block times number of blocks
CBufferBase<uint8_t>::Init ( iNewBlockSize * iNewNumBlocks );
// use the "get" flag to make sure the buffer is cleared
Clear ( CT_GET );
@ -68,43 +226,8 @@ bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData,
}
}
// 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 )
{
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;
}
// copy new data in internal buffer (implemented in base class)
CBufferBase<uint8_t>::Put ( vecbyData, iInSize );
// update statistic
ErrorRateStatistic.Update ( !bPutOK );
@ -152,43 +275,9 @@ bool CNetBuf::Get ( CVector<uint8_t>& vecbyData )
}
}
// 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 )
{
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;
}
// copy data from internal buffer in output buffer (implemented in base
// class)
CBufferBase<uint8_t>::Get ( vecbyData );
// update statistic
ErrorRateStatistic.Update ( !bGetOK );
@ -196,48 +285,6 @@ bool CNetBuf::Get ( CVector<uint8_t>& vecbyData )
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 )
{
// 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 )
{
// 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
// the get operation (assign new fill level value to the get pointer)

View file

@ -36,13 +36,32 @@
/* Classes ********************************************************************/
// Network buffer (jitter buffer) ----------------------------------------------
class CNetBuf
// Buffer base class -----------------------------------------------------------
template<class TData> class CBufferBase
{
public:
CNetBuf() {}
virtual ~CNetBuf() {}
virtual void Init ( const int iNewMemSize );
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 );
int GetSize() { return iMemSize / iBlockSize; }
@ -52,18 +71,12 @@ public:
double GetErrorRate() { return ErrorRateStatistic.GetAverage(); }
protected:
enum EBufState { BS_OK, BS_FULL, BS_EMPTY };
enum EClearType { CT_PUT, CT_GET };
void Clear ( const EClearType eClearType );
int GetAvailSpace() const;
int GetAvailData() const;
CVector<uint8_t> vecbyMemory;
int iMemSize;
void Clear ( const EClearType eClearType );
int iBlockSize;
int iNumInvalidElements;
int iGetPos, iPutPos;
EBufState eBufState;
// statistic
CErrorRate ErrorRateStatistic;
@ -71,11 +84,13 @@ protected:
// 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
{
public:
CConvBuf() { Init ( 0 ); }
virtual ~CConvBuf() {}
void Init ( const int iNewMemSize )
{

View file

@ -8,16 +8,16 @@
*
* 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
* 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
* 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
* 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.,
* this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
\******************************************************************************/
@ -43,7 +43,9 @@ CClient::CClient ( const quint16 iPortNumber ) :
bFraSiFactDefSupported ( false ),
bFraSiFactSafeSupported ( false ),
iCeltNumCodedBytes ( CELT_NUM_BYTES_NORMAL_QUALITY ),
bCeltDoHighQuality ( false )
bCeltDoHighQuality ( false ),
bSndCrdConversionBufferRequired ( false ),
iSndCardMonoBlockSizeSamConvBuff ( 0 )
{
// init audio endocder/decoder (mono)
CeltMode = celt_mode_create (
@ -189,22 +191,12 @@ void CClient::SetSndCrdPrefFrameSizeFactor ( const int iNewFactor )
iSndCrdPrefFrameSizeFactor = iNewFactor;
// init with new block size index parameter
const bool bInitWasOk = Init();
Init();
if ( bWasRunning )
{
if ( bInitWasOk )
{
// 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();
}
// restart client
Sound.Start();
}
}
}
@ -245,22 +237,12 @@ QString CClient::SetSndCrdDev ( const int iNewDev )
// init again because the sound card actual buffer size might
// be changed on new device
const bool bInitWasOk = Init();
Init();
if ( bWasRunning )
{
if ( bInitWasOk )
{
// 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();
}
// restart client
Sound.Start();
}
return strReturn;
@ -279,52 +261,19 @@ void CClient::OnSndCrdReinitRequest()
// reinit the driver (we use the currently selected driver) and
// init client object, too
Sound.SetDev ( Sound.GetDev() );
const bool bInitWasOk = Init();
Init();
if ( bWasRunning )
{
if ( bInitWasOk )
{
// 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();
}
// restart client
Sound.Start();
}
}
void CClient::Start()
{
// init object
if ( !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 );
}
Init() ;
// enable channel
Channel.SetEnable ( true );
@ -367,16 +316,7 @@ void CClient::Stop()
PostWinMessage ( MS_RESET_ALL, 0 );
}
void CClient::AudioCallback ( CVector<int16_t>& psData, void* arg )
{
// get the pointer to the object
CClient* pMyClientObj = reinterpret_cast<CClient*> ( arg );
// process audio data
pMyClientObj->ProcessAudioData ( psData );
}
bool CClient::Init()
void CClient::Init()
{
// check if possible frame size factors are supported
const int iFraSizePreffered =
@ -402,16 +342,60 @@ bool CClient::Init()
iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES;
// 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;
// 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 );
vecsAudioSndCrdStereo.Init ( iStereoBlockSizeSam );
vecdAudioStereo.Init ( iStereoBlockSizeSam );
@ -443,21 +427,49 @@ iSndCrdFrameSizeFactor = max ( 1, iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES
// set the channel network properties
Channel.SetNetwFrameSizeAndFact ( iCeltNumCodedBytes,
iSndCrdFrameSizeFactor );
}
// check sound card buffer sizes, if not supported, return error flag
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 ) ) )
void CClient::AudioCallback ( CVector<int16_t>& psData, void* arg )
{
// get the pointer to the object
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
{
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;
@ -628,7 +640,8 @@ void CClient::UpdateSocketBufferSize()
// calculate current buffer setting
const double dAudioBufferDurationMs =
iMonoBlockSizeSam * 1000 / SYSTEM_SAMPLE_RATE;
( iMonoBlockSizeSam + iSndCardMonoBlockSizeSamConvBuff ) *
1000 / SYSTEM_SAMPLE_RATE;
// jitter introduced in the server by the timer implementation
const double dServerJitterMs = 0.666666; // ms

View file

@ -36,6 +36,7 @@
#include "socket.h"
#include "channel.h"
#include "util.h"
#include "buffer.h"
#ifdef _WIN32
# include "../windows/sound.h"
#else
@ -113,7 +114,7 @@ public:
// set the new socket size (number of frames)
if ( !Channel.SetSockBufNumFrames ( iNumBlocks ) )
{
// if setting of socket buffer size was successful,
// if setting of socket buffer size was successful,
// tell the server that size has changed
Channel.CreateJitBufMes ( iNumBlocks );
}
@ -134,7 +135,20 @@ public:
void SetSndCrdPrefFrameSizeFactor ( const int iNewFactor );
int GetSndCrdPrefFrameSizeFactor()
{ 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 GetFraSiFactDefSupported() { return bFraSiFactDefSupported; }
@ -153,7 +167,6 @@ public:
CChannel* GetChannel() { return &Channel; }
// settings
CVector<QString> vstrIPAddress;
QString strName;
@ -162,8 +175,9 @@ protected:
// callback function must be static, otherwise it does not work
static void AudioCallback ( CVector<short>& psData, void* arg );
bool Init();
void ProcessAudioData ( CVector<short>& vecsStereoSndCrd );
void Init();
void ProcessSndCrdAudioData ( CVector<short>& vecsStereoSndCrd );
void ProcessAudioDataIntern ( CVector<short>& vecsStereoSndCrd );
void UpdateSocketBufferSize();
// only one channel is needed for client application
@ -192,6 +206,12 @@ protected:
int iSndCrdPrefFrameSizeFactor;
int iSndCrdFrameSizeFactor;
bool bSndCrdConversionBufferRequired;
int iSndCardMonoBlockSizeSamConvBuff;
CBufferBase<int16_t> SndCrdConversionBufferIn;
CBufferBase<int16_t> SndCrdConversionBufferOut;
CVector<int16_t> vecDataConvBuf;
bool bFraSiFactPrefSupported;
bool bFraSiFactDefSupported;
bool bFraSiFactSafeSupported;
@ -200,7 +220,7 @@ protected:
int iStereoBlockSizeSam;
bool bOpenChatOnNewMessage;
EGUIDesign eGUIDesign;
EGUIDesign eGUIDesign;
CVector<int16_t> vecsAudioSndCrdMono;
CVector<int16_t> vecsAudioSndCrdStereo;

View file

@ -145,17 +145,21 @@
#elif HAVE_INTTYPES_H
# include <inttypes.h>
#elif defined ( _WIN32 )
typedef __int64 int64_t;
typedef __int32 int32_t;
typedef __int16 int16_t;
typedef unsigned __int64 uint64_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int8 uint8_t;
#else
typedef int int32_t;
typedef short int16_t;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
typedef long int int64_t;
typedef int int32_t;
typedef short int16_t;
typedef unsigned long int uint64_t;
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
#endif

View file

@ -46,7 +46,7 @@ ASIOCallbacks asioCallbacks;
int iASIOBufferSizeMono;
int iASIOBufferSizeStereo;
CVector<short> vecsTmpAudioSndCrdStereo;
CVector<int16_t> vecsTmpAudioSndCrdStereo;
QMutex ASIOMutex;
@ -275,14 +275,28 @@ QString CSound::CheckDeviceCapabilities()
}
ASIOGetChannelInfo ( &channelInfos[i] );
// only 16/24/32 LSB is supported
// check supported sample formats
if ( ( channelInfos[i].type != ASIOSTInt16LSB ) &&
( 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 tr ( "Required audio sample format not "
"available (16/24/32 bit LSB)." );
return tr ( "Required audio sample format not available." );
}
}
@ -428,7 +442,7 @@ void CSound::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 )
{
int i;
@ -496,9 +510,9 @@ CSound::~CSound()
}
// ASIO callbacks -------------------------------------------------------------
ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime *timeInfo,
long index,
ASIOBool processNow )
ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime* timeInfo,
long index,
ASIOBool processNow )
{
bufferSwitch ( index, processNow );
return 0L;
@ -521,37 +535,153 @@ void CSound::bufferSwitch ( long index, ASIOBool processNow )
switch ( channelInfos[i].type )
{
case ASIOSTInt16LSB:
// no type conversion required, just copy operation
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
( (short*) bufferInfos[i].buffers[index] )[iCurSample];
static_cast<int16_t*> ( bufferInfos[i].buffers[index] )[iCurSample];
}
break;
case ASIOSTInt24LSB:
// not yet tested, horrible things might happen with the following code ;-)
// NOT YET TESTED
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
// convert current sample in 16 bit format
int iCurSam = 0;
memcpy ( &iCurSam, ( (char*) bufferInfos[i].buffers[index] ) + iCurSample * 3, 3 );
iCurSam >>= 8;
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] =
static_cast<short> ( iCurSam );
static_cast<int16_t> ( iCurSam );
}
break;
case ASIOSTInt32LSB:
// NOT YET TESTED
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
// convert to 16 bit
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;
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 )
{
case ASIOSTInt16LSB:
// no type conversion required, just copy operation
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];
}
break;
case ASIOSTInt24LSB:
// not yet tested, horrible things might happen with the following code ;-)
// NOT YET TESTED
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
// 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;
memcpy ( ( (char*) bufferInfos[i].buffers[index] ) + iCurSample * 3, &iCurSam, 3 );
@ -592,13 +723,151 @@ void CSound::bufferSwitch ( long index, ASIOBool processNow )
break;
case ASIOSTInt32LSB:
// NOT YET TESTED
for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ )
{
// convert to 32 bit
int iCurSam = static_cast<int> ( vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
( (int*) bufferInfos[i].buffers[index] )[iCurSample] = ( iCurSam << 16 );
const int32_t iCurSam = static_cast<int32_t> (
vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] );
static_cast<int32_t*> ( bufferInfos[i].buffers[index] )[iCurSample] =
( iCurSam << 16 );
}
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();
}
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;
switch ( selector )
@ -632,3 +904,57 @@ long CSound::asioMessages ( long selector, long value, void* message, double* op
}
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;
}

View file

@ -75,6 +75,11 @@ protected:
int GetActualBufferSize ( const int iDesiredBufferSizeMono );
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
struct sHWBufferInfo
{