From 75b1994257ed39244bd59c731f929d1ab34377af Mon Sep 17 00:00:00 2001 From: Volker Fischer Date: Wed, 3 Feb 2010 19:27:48 +0000 Subject: [PATCH] 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 --- src/buffer.cpp | 289 ++++++++++++++++++++--------------- src/buffer.h | 41 +++-- src/client.cpp | 201 +++++++++++++------------ src/client.h | 32 +++- src/global.h | 14 +- windows/sound.cpp | 376 +++++++++++++++++++++++++++++++++++++++++++--- windows/sound.h | 5 + 7 files changed, 694 insertions(+), 264 deletions(-) diff --git a/src/buffer.cpp b/src/buffer.cpp index 219ad6aa..9e4bda21 100755 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -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; // for network buffer +template class CBufferBase; // for sound card conversion buffer + +template +void CBufferBase::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::BS_EMPTY; +} + +template +bool CBufferBase::Put ( const CVector& 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::BS_FULL; + } + else + { + eBufState = CBufferBase::BS_OK; + } + + return true; // no error check in base class, alyways return ok +} + +template +bool CBufferBase::Get ( CVector& 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::BS_EMPTY; + } + else + { + eBufState = CBufferBase::BS_OK; + } + + return true; // no error check in base class, alyways return ok +} + +template +int CBufferBase::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 +int CBufferBase::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::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& 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::Put ( vecbyData, iInSize ); // update statistic ErrorRateStatistic.Update ( !bPutOK ); @@ -152,43 +275,9 @@ bool CNetBuf::Get ( CVector& 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::Get ( vecbyData ); // update statistic ErrorRateStatistic.Update ( !bGetOK ); @@ -196,48 +285,6 @@ bool CNetBuf::Get ( CVector& 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) diff --git a/src/buffer.h b/src/buffer.h index 5e3cefbd..4c24f360 100755 --- a/src/buffer.h +++ b/src/buffer.h @@ -36,13 +36,32 @@ /* Classes ********************************************************************/ -// Network buffer (jitter buffer) ---------------------------------------------- -class CNetBuf +// Buffer base class ----------------------------------------------------------- +template class CBufferBase { public: - CNetBuf() {} - virtual ~CNetBuf() {} + virtual void Init ( const int iNewMemSize ); + virtual bool Put ( const CVector& vecData, const int iInSize ); + virtual bool Get ( CVector& vecData ); + + virtual int GetAvailSpace() const; + virtual int GetAvailData() const; + +protected: + enum EBufState { BS_OK, BS_FULL, BS_EMPTY }; + + CVector vecMemory; + int iMemSize; + int iGetPos, iPutPos; + EBufState eBufState; +}; + + +// Network buffer (jitter buffer) ---------------------------------------------- +class CNetBuf : public CBufferBase +{ +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 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 CConvBuf { public: CConvBuf() { Init ( 0 ); } - virtual ~CConvBuf() {} void Init ( const int iNewMemSize ) { diff --git a/src/client.cpp b/src/client.cpp index 068e7b6b..3463b158 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -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 " + - QString().setNum ( iMonoBlockSizeSam ) + " samples 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: " + - 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." -#ifdef _WIN32 - "

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& psData, void* arg ) -{ - // get the pointer to the object - CClient* pMyClientObj = reinterpret_cast ( 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 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& psData, void* arg ) +{ + // get the pointer to the object + CClient* pMyClientObj = reinterpret_cast ( arg ); + + // process audio data + pMyClientObj->ProcessSndCrdAudioData ( psData ); +} + +void CClient::ProcessSndCrdAudioData ( CVector& 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& vecsStereoSndCrd ) +void CClient::ProcessAudioDataIntern ( CVector& 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 diff --git a/src/client.h b/src/client.h index 1261ead1..1ad217b6 100755 --- a/src/client.h +++ b/src/client.h @@ -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 vstrIPAddress; QString strName; @@ -162,8 +175,9 @@ protected: // callback function must be static, otherwise it does not work static void AudioCallback ( CVector& psData, void* arg ); - bool Init(); - void ProcessAudioData ( CVector& vecsStereoSndCrd ); + void Init(); + void ProcessSndCrdAudioData ( CVector& vecsStereoSndCrd ); + void ProcessAudioDataIntern ( CVector& 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 SndCrdConversionBufferIn; + CBufferBase SndCrdConversionBufferOut; + CVector vecDataConvBuf; + bool bFraSiFactPrefSupported; bool bFraSiFactDefSupported; bool bFraSiFactSafeSupported; @@ -200,7 +220,7 @@ protected: int iStereoBlockSizeSam; bool bOpenChatOnNewMessage; - EGUIDesign eGUIDesign; + EGUIDesign eGUIDesign; CVector vecsAudioSndCrdMono; CVector vecsAudioSndCrdStereo; diff --git a/src/global.h b/src/global.h index 0f7a81ab..5495a15d 100755 --- a/src/global.h +++ b/src/global.h @@ -145,17 +145,21 @@ #elif HAVE_INTTYPES_H # include #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 diff --git a/windows/sound.cpp b/windows/sound.cpp index c6f9a495..8ceef66b 100755 --- a/windows/sound.cpp +++ b/windows/sound.cpp @@ -46,7 +46,7 @@ ASIOCallbacks asioCallbacks; int iASIOBufferSizeMono; int iASIOBufferSizeStereo; -CVector vecsTmpAudioSndCrdStereo; +CVector 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& psData, void* arg ), void* arg ) : +CSound::CSound ( void (*fpNewCallback) ( CVector& 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 ( 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 ( iCurSam ); + static_cast ( 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 ( static_cast ( + 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 ( static_cast ( + 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 ( static_cast ( + 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 ( static_cast ( + 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 ( ( static_cast ( + 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 ( ( static_cast ( + 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 ( ( static_cast ( + 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 ( + 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 ( Flip32Bits ( static_cast ( + 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 ( 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 ( vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); + int32_t iCurSam = static_cast ( + 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 ( vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); - ( (int*) bufferInfos[i].buffers[index] )[iCurSample] = ( iCurSam << 16 ); + const int32_t iCurSam = static_cast ( + vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); + + static_cast ( 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 ( + vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); + + static_cast ( 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 ( + vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); + + static_cast ( 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 ( + vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); + + static_cast ( 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 ( + vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); + + static_cast ( 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 ( + vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); + + static_cast ( 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 ( + vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); + + static_cast ( 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 ( + vecsTmpAudioSndCrdStereo[2 * iCurSample + bufferInfos[i].channelNum] ); + + static_cast ( 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 ( 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 ( 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; +} diff --git a/windows/sound.h b/windows/sound.h index c0705e01..327a31b2 100755 --- a/windows/sound.h +++ b/windows/sound.h @@ -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 {