From c0685e9b782c783e808daac140291e6b667e8e8b Mon Sep 17 00:00:00 2001 From: Volker Fischer Date: Sat, 24 Oct 2009 12:24:44 +0000 Subject: [PATCH] fix unix2dos issue with some files --- src/client.h | 446 +++---- src/clientsettingsdlg.cpp | 946 +++++++-------- src/clientsettingsdlg.h | 190 +-- src/protocol.cpp | 2314 ++++++++++++++++++------------------- src/protocol.h | 6 +- src/server.cpp | 1744 ++++++++++++++-------------- src/serverlogging.cpp | 904 +++++++-------- src/settings.cpp | 704 +++++------ src/util.h | 1294 ++++++++++----------- 9 files changed, 4274 insertions(+), 4274 deletions(-) diff --git a/src/client.h b/src/client.h index 775a6b07..fb124e59 100755 --- a/src/client.h +++ b/src/client.h @@ -1,223 +1,223 @@ -/******************************************************************************\ - * Copyright (c) 2004-2009 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * 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 - * version. - * - * 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 - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#if !defined ( CLIENT_HOIHGE76GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) -#define CLIENT_HOIHGE76GEKJH98_3_43445KJIUHF1912__INCLUDED_ - -#include -#include -#include -#include -#include -#include -#include "celt.h" -#include "global.h" -#include "socket.h" -#include "channel.h" -#include "util.h" -#ifdef _WIN32 -# include "../windows/sound.h" -#else -# include "../linux/sound.h" -# include -# include -# include -#endif - - -/* Definitions ****************************************************************/ -// audio in fader range -#define AUD_FADER_IN_MIN 0 -#define AUD_FADER_IN_MAX 100 -#define AUD_FADER_IN_MIDDLE ( AUD_FADER_IN_MAX / 2 ) - -// audio reverberation range -#define AUD_REVERB_MAX 100 - -// CELT number of coded bytes per audio packet -// 24: low/normal quality 156 kbsp (128) / 114 kbps (256) -// 44: high quality 216 kbps (128) / 174 kbps (256) -#define CELT_NUM_BYTES_NORMAL_QUALITY 24 -#define CELT_NUM_BYTES_HIGH_QUALITY 44 - - -/* Classes ********************************************************************/ -class CClient : public QObject -{ - Q_OBJECT - -public: - CClient ( const quint16 iPortNumber ); - virtual ~CClient() {} - - void Start(); - void Stop(); - bool IsRunning() { return Sound.IsRunning(); } - bool SetServerAddr ( QString strNAddr ); - double MicLevelL() { return SignalLevelMeter.MicLevelLeft(); } - double MicLevelR() { return SignalLevelMeter.MicLevelRight(); } - bool IsConnected() { return Channel.IsConnected(); } - - double GetTimingStdDev() { return CycleTimeVariance.GetStdDev(); } - - bool GetOpenChatOnNewMessage() const { return bOpenChatOnNewMessage; } - void SetOpenChatOnNewMessage ( const bool bNV ) { bOpenChatOnNewMessage = bNV; } - - EGUIDesign GetGUIDesign() const { return eGUIDesign; } - void SetGUIDesign ( const EGUIDesign bNGD ) { eGUIDesign = bNGD; } - - bool GetCELTHighQuality() const { return bCeltDoHighQuality; } - void SetCELTHighQuality ( const bool bNCeltHighQualityFlag ); - - int GetAudioInFader() const { return iAudioInFader; } - void SetAudioInFader ( const int iNV ) { iAudioInFader = iNV; } - - int GetReverbLevel() const { return iReverbLevel; } - void SetReverbLevel ( const int iNL ) { iReverbLevel = iNL; } - - bool IsReverbOnLeftChan() const { return bReverbOnLeftChan; } - void SetReverbOnLeftChan ( const bool bIL ) - { - bReverbOnLeftChan = bIL; - AudioReverb.Clear(); - } - - void SetDoAutoSockBufSize ( const bool bValue ) { bDoAutoSockBufSize = bValue; } - bool GetDoAutoSockBufSize() const { return bDoAutoSockBufSize; } - void SetSockBufNumFrames ( const int iNumBlocks ) - { - // only change parameter if new parameter is different from current one - if ( Channel.GetSockBufNumFrames() != iNumBlocks ) - { - // set the new socket size (number of frames) - if ( !Channel.SetSockBufNumFrames ( iNumBlocks ) ) - { - // if setting of socket buffer size was successful, - // tell the server that size has changed - Channel.CreateJitBufMes ( iNumBlocks ); - } - } - } - int GetSockBufNumFrames() { return Channel.GetSockBufNumFrames(); } - - int GetUploadRateKbps() { return Channel.GetUploadRateKbps(); } - - int GetSndCrdNumDev() { return Sound.GetNumDev(); } - std::string GetSndCrdDeviceName ( const int iDiD ) - { return Sound.GetDeviceName ( iDiD ); } - - QString SetSndCrdDev ( const int iNewDev ); - int GetSndCrdDev() { return Sound.GetDev(); } - void OpenSndCrdDriverSetup() { Sound.OpenDriverSetup(); } - - void SetSndCrdPrefFrameSizeFactor ( const int iNewFactor ); - int GetSndCrdPrefFrameSizeFactor() - { return iSndCrdPrefFrameSizeFactor; } - int GetSndCrdActualMonoBlSize() { return iMonoBlockSizeSam; } - - void SetRemoteChanGain ( const int iId, const double dGain ) - { Channel.SetRemoteChanGain ( iId, dGain ); } - - void SetRemoteName() { Channel.SetRemoteName ( strName ); } - - void SendTextMess ( const QString& strChatText ) - { Channel.CreateChatTextMes ( strChatText ); } - - void SendPingMess() - { Channel.CreatePingMes ( PreciseTime.elapsed() ); }; - - CChannel* GetChannel() { return &Channel; } - - - // settings - CVector vstrIPAddress; - QString strName; - -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 UpdateSocketBufferSize(); - - // only one channel is needed for client application - CChannel Channel; - bool bDoAutoSockBufSize; - - // audio encoder/decoder - CELTMode* CeltMode; - CELTEncoder* CeltEncoder; - CELTDecoder* CeltDecoder; - int iCeltNumCodedBytes; - bool bCeltDoHighQuality; - CVector vecCeltData; - - CSocket Socket; - CSound Sound; - CStereoSignalLevelMeter SignalLevelMeter; - - CVector vecbyNetwData; - - int iAudioInFader; - bool bReverbOnLeftChan; - int iReverbLevel; - CAudioReverb AudioReverb; - - int iSndCrdPrefFrameSizeFactor; - int iSndCrdFrameSizeFactor; - - int iMonoBlockSizeSam; - int iStereoBlockSizeSam; - - bool bOpenChatOnNewMessage; - EGUIDesign eGUIDesign; - - CVector vecsAudioSndCrdMono; - CVector vecsAudioSndCrdStereo; - CVector vecdAudioStereo; - CVector vecsNetwork; - - // for ping measurement - CPreciseTime PreciseTime; - - CCycleTimeVariance CycleTimeVariance; - -public slots: - void OnSendProtMessage ( CVector vecMessage ); - void OnReqJittBufSize() { Channel.CreateJitBufMes ( Channel.GetSockBufNumFrames() ); } - void OnReqChanName() { Channel.SetRemoteName ( strName ); } - void OnNewConnection(); - void OnReceivePingMessage ( int iMs ); - void OnSndCrdReinitRequest(); - -signals: - void ConClientListMesReceived ( CVector vecChanInfo ); - void ChatTextReceived ( QString strChatText ); - void PingTimeReceived ( int iPingTime ); - void Disconnected(); - void Stopped(); -}; - -#endif /* !defined ( CLIENT_HOIHGE76GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) */ +/******************************************************************************\ + * Copyright (c) 2004-2009 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * 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 + * version. + * + * 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 + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#if !defined ( CLIENT_HOIHGE76GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) +#define CLIENT_HOIHGE76GEKJH98_3_43445KJIUHF1912__INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include "celt.h" +#include "global.h" +#include "socket.h" +#include "channel.h" +#include "util.h" +#ifdef _WIN32 +# include "../windows/sound.h" +#else +# include "../linux/sound.h" +# include +# include +# include +#endif + + +/* Definitions ****************************************************************/ +// audio in fader range +#define AUD_FADER_IN_MIN 0 +#define AUD_FADER_IN_MAX 100 +#define AUD_FADER_IN_MIDDLE ( AUD_FADER_IN_MAX / 2 ) + +// audio reverberation range +#define AUD_REVERB_MAX 100 + +// CELT number of coded bytes per audio packet +// 24: low/normal quality 156 kbsp (128) / 114 kbps (256) +// 44: high quality 216 kbps (128) / 174 kbps (256) +#define CELT_NUM_BYTES_NORMAL_QUALITY 24 +#define CELT_NUM_BYTES_HIGH_QUALITY 44 + + +/* Classes ********************************************************************/ +class CClient : public QObject +{ + Q_OBJECT + +public: + CClient ( const quint16 iPortNumber ); + virtual ~CClient() {} + + void Start(); + void Stop(); + bool IsRunning() { return Sound.IsRunning(); } + bool SetServerAddr ( QString strNAddr ); + double MicLevelL() { return SignalLevelMeter.MicLevelLeft(); } + double MicLevelR() { return SignalLevelMeter.MicLevelRight(); } + bool IsConnected() { return Channel.IsConnected(); } + + double GetTimingStdDev() { return CycleTimeVariance.GetStdDev(); } + + bool GetOpenChatOnNewMessage() const { return bOpenChatOnNewMessage; } + void SetOpenChatOnNewMessage ( const bool bNV ) { bOpenChatOnNewMessage = bNV; } + + EGUIDesign GetGUIDesign() const { return eGUIDesign; } + void SetGUIDesign ( const EGUIDesign bNGD ) { eGUIDesign = bNGD; } + + bool GetCELTHighQuality() const { return bCeltDoHighQuality; } + void SetCELTHighQuality ( const bool bNCeltHighQualityFlag ); + + int GetAudioInFader() const { return iAudioInFader; } + void SetAudioInFader ( const int iNV ) { iAudioInFader = iNV; } + + int GetReverbLevel() const { return iReverbLevel; } + void SetReverbLevel ( const int iNL ) { iReverbLevel = iNL; } + + bool IsReverbOnLeftChan() const { return bReverbOnLeftChan; } + void SetReverbOnLeftChan ( const bool bIL ) + { + bReverbOnLeftChan = bIL; + AudioReverb.Clear(); + } + + void SetDoAutoSockBufSize ( const bool bValue ) { bDoAutoSockBufSize = bValue; } + bool GetDoAutoSockBufSize() const { return bDoAutoSockBufSize; } + void SetSockBufNumFrames ( const int iNumBlocks ) + { + // only change parameter if new parameter is different from current one + if ( Channel.GetSockBufNumFrames() != iNumBlocks ) + { + // set the new socket size (number of frames) + if ( !Channel.SetSockBufNumFrames ( iNumBlocks ) ) + { + // if setting of socket buffer size was successful, + // tell the server that size has changed + Channel.CreateJitBufMes ( iNumBlocks ); + } + } + } + int GetSockBufNumFrames() { return Channel.GetSockBufNumFrames(); } + + int GetUploadRateKbps() { return Channel.GetUploadRateKbps(); } + + int GetSndCrdNumDev() { return Sound.GetNumDev(); } + std::string GetSndCrdDeviceName ( const int iDiD ) + { return Sound.GetDeviceName ( iDiD ); } + + QString SetSndCrdDev ( const int iNewDev ); + int GetSndCrdDev() { return Sound.GetDev(); } + void OpenSndCrdDriverSetup() { Sound.OpenDriverSetup(); } + + void SetSndCrdPrefFrameSizeFactor ( const int iNewFactor ); + int GetSndCrdPrefFrameSizeFactor() + { return iSndCrdPrefFrameSizeFactor; } + int GetSndCrdActualMonoBlSize() { return iMonoBlockSizeSam; } + + void SetRemoteChanGain ( const int iId, const double dGain ) + { Channel.SetRemoteChanGain ( iId, dGain ); } + + void SetRemoteName() { Channel.SetRemoteName ( strName ); } + + void SendTextMess ( const QString& strChatText ) + { Channel.CreateChatTextMes ( strChatText ); } + + void SendPingMess() + { Channel.CreatePingMes ( PreciseTime.elapsed() ); }; + + CChannel* GetChannel() { return &Channel; } + + + // settings + CVector vstrIPAddress; + QString strName; + +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 UpdateSocketBufferSize(); + + // only one channel is needed for client application + CChannel Channel; + bool bDoAutoSockBufSize; + + // audio encoder/decoder + CELTMode* CeltMode; + CELTEncoder* CeltEncoder; + CELTDecoder* CeltDecoder; + int iCeltNumCodedBytes; + bool bCeltDoHighQuality; + CVector vecCeltData; + + CSocket Socket; + CSound Sound; + CStereoSignalLevelMeter SignalLevelMeter; + + CVector vecbyNetwData; + + int iAudioInFader; + bool bReverbOnLeftChan; + int iReverbLevel; + CAudioReverb AudioReverb; + + int iSndCrdPrefFrameSizeFactor; + int iSndCrdFrameSizeFactor; + + int iMonoBlockSizeSam; + int iStereoBlockSizeSam; + + bool bOpenChatOnNewMessage; + EGUIDesign eGUIDesign; + + CVector vecsAudioSndCrdMono; + CVector vecsAudioSndCrdStereo; + CVector vecdAudioStereo; + CVector vecsNetwork; + + // for ping measurement + CPreciseTime PreciseTime; + + CCycleTimeVariance CycleTimeVariance; + +public slots: + void OnSendProtMessage ( CVector vecMessage ); + void OnReqJittBufSize() { Channel.CreateJitBufMes ( Channel.GetSockBufNumFrames() ); } + void OnReqChanName() { Channel.SetRemoteName ( strName ); } + void OnNewConnection(); + void OnReceivePingMessage ( int iMs ); + void OnSndCrdReinitRequest(); + +signals: + void ConClientListMesReceived ( CVector vecChanInfo ); + void ChatTextReceived ( QString strChatText ); + void PingTimeReceived ( int iPingTime ); + void Disconnected(); + void Stopped(); +}; + +#endif /* !defined ( CLIENT_HOIHGE76GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) */ diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index d8f42f92..f77712eb 100755 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -1,473 +1,473 @@ -/******************************************************************************\ - * Copyright (c) 2004-2009 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * 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 - * version. - * - * 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 - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "clientsettingsdlg.h" - - -/* Implementation *************************************************************/ -CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, - Qt::WindowFlags f ) : pClient ( pNCliP ), QDialog ( parent, f ) -{ - setupUi ( this ); - - // add help text to controls - QString strJitterBufferSize = tr ( "Jitter Buffer Size: The size of " - "the network buffer (jitter buffer). The jitter buffer compensates for " - "the network jitter. The larger this buffer is, the more robust the " - "connection is against network jitter but the higher is the latency. " - "This setting is therefore a trade-off between audio drop outs and " - "overall audio delay.
By changing this setting, both, the client " - "and the server jitter buffer is set to the same value." ); - SliderNetBuf->setWhatsThis ( strJitterBufferSize ); - TextNetBuf->setWhatsThis ( strJitterBufferSize ); - GroupBoxJitterBuffer->setWhatsThis ( strJitterBufferSize ); - - // init driver button -#ifdef _WIN32 - ButtonDriverSetup->setText ( "ASIO Setup" ); -#else - // no use for this button for Linux right now, maybe later used - // for Jack - ButtonDriverSetup->hide(); -#endif - - // init delay and other information controls - CLEDOverallDelay->SetUpdateTime ( 2 * PING_UPDATE_TIME ); - CLEDOverallDelay->Reset(); - TextLabelPingTime->setText ( "" ); - TextLabelOverallDelay->setText ( "" ); - TextUpstreamValue->setText ( "" ); - - - // init slider controls --- - // network buffer - SliderNetBuf->setRange ( MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL ); - UpdateJitterBufferFrame(); - - // init combo box containing all available sound cards in the system - cbSoundcard->clear(); - for ( int iSndDevIdx = 0; iSndDevIdx < pClient->GetSndCrdNumDev(); iSndDevIdx++ ) - { - cbSoundcard->addItem ( pClient->GetSndCrdDeviceName ( iSndDevIdx ).c_str() ); - } - cbSoundcard->setCurrentIndex ( pClient->GetSndCrdDev() ); - - // "OpenChatOnNewMessage" check box - if ( pClient->GetOpenChatOnNewMessage() ) - { - cbOpenChatOnNewMessage->setCheckState ( Qt::Checked ); - } - else - { - cbOpenChatOnNewMessage->setCheckState ( Qt::Unchecked ); - } - - // fancy GUI design check box - if ( pClient->GetGUIDesign() == GD_STANDARD ) - { - cbGUIDesignFancy->setCheckState ( Qt::Unchecked ); - } - else - { - cbGUIDesignFancy->setCheckState ( Qt::Checked ); - } - - -// TODO we disable the fancy GUI switch because the new design -// is not yet finished -cbGUIDesignFancy->setVisible ( false ); - - - // "High Quality Audio" check box - if ( pClient->GetCELTHighQuality() ) - { - cbUseHighQualityAudio->setCheckState ( Qt::Checked ); - } - else - { - cbUseHighQualityAudio->setCheckState ( Qt::Unchecked ); - } - - // set text for sound card buffer delay radio buttons - rButBufferDelayPreferred->setText ( GenSndCrdBufferDelayString ( - FRAME_SIZE_FACTOR_PREFERRED * SYSTEM_FRAME_SIZE_SAMPLES, - ", preferred" ) ); - - rButBufferDelayDefault->setText ( GenSndCrdBufferDelayString ( - FRAME_SIZE_FACTOR_DEFAULT * SYSTEM_FRAME_SIZE_SAMPLES, - ", default" ) ); - - rButBufferDelaySafe->setText ( GenSndCrdBufferDelayString ( - FRAME_SIZE_FACTOR_SAFE * SYSTEM_FRAME_SIZE_SAMPLES ) ); - - // sound card buffer delay inits - SndCrdBufferDelayButtonGroup.addButton ( rButBufferDelayPreferred ); - SndCrdBufferDelayButtonGroup.addButton ( rButBufferDelayDefault ); - SndCrdBufferDelayButtonGroup.addButton ( rButBufferDelaySafe ); - - UpdateSoundCardFrame(); - - - // Connections ------------------------------------------------------------- - // timers - QObject::connect ( &TimerStatus, SIGNAL ( timeout() ), - this, SLOT ( OnTimerStatus() ) ); - - QObject::connect ( &TimerPing, SIGNAL ( timeout() ), - this, SLOT ( OnTimerPing() ) ); - - // slider controls - QObject::connect ( SliderNetBuf, SIGNAL ( valueChanged ( int ) ), - this, SLOT ( OnSliderNetBuf ( int ) ) ); - - // check boxes - QObject::connect ( cbOpenChatOnNewMessage, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnOpenChatOnNewMessageStateChanged ( int ) ) ); - - QObject::connect ( cbGUIDesignFancy, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnGUIDesignFancyStateChanged ( int ) ) ); - - QObject::connect ( cbUseHighQualityAudio, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnUseHighQualityAudioStateChanged ( int ) ) ); - - QObject::connect ( cbAutoJitBuf, SIGNAL ( stateChanged ( int ) ), - this, SLOT ( OnAutoJitBuf ( int ) ) ); - - // combo boxes - QObject::connect ( cbSoundcard, SIGNAL ( activated ( int ) ), - this, SLOT ( OnSoundCrdSelection ( int ) ) ); - - // buttons - QObject::connect ( ButtonDriverSetup, SIGNAL ( clicked() ), - this, SLOT ( OnDriverSetupBut() ) ); - - // misc - QObject::connect ( pClient, SIGNAL ( PingTimeReceived ( int ) ), - this, SLOT ( OnPingTimeResult ( int ) ) ); - - QObject::connect ( &SndCrdBufferDelayButtonGroup, - SIGNAL ( buttonClicked ( QAbstractButton* ) ), this, - SLOT ( OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* ) ) ); - - - // Timers ------------------------------------------------------------------ - // start timer for status bar - TimerStatus.start ( DISPLAY_UPDATE_TIME ); -} - -void CClientSettingsDlg::UpdateJitterBufferFrame() -{ - // update slider value and text - const int iCurNumNetBuf = pClient->GetSockBufNumFrames(); - SliderNetBuf->setValue ( iCurNumNetBuf ); - TextNetBuf->setText ( "Size: " + QString().setNum ( iCurNumNetBuf ) ); - - // if auto setting is enabled, disable slider control - cbAutoJitBuf->setChecked ( pClient->GetDoAutoSockBufSize() ); - SliderNetBuf->setEnabled ( !pClient->GetDoAutoSockBufSize() ); - TextNetBuf->setEnabled ( !pClient->GetDoAutoSockBufSize() ); -} - -QString CClientSettingsDlg::GenSndCrdBufferDelayString ( const int iFrameSize, - const QString strAddText ) -{ - // use two times the buffer delay for the entire delay since - // we have input and output - return QString().setNum ( (double) iFrameSize * 2 * - 1000 / SYSTEM_SAMPLE_RATE, 'f', 2 ) + " ms (" + - QString().setNum ( iFrameSize ) + strAddText + ")"; -} - -void CClientSettingsDlg::UpdateSoundCardFrame() -{ - // update slider value and text - const int iCurPrefFrameSizeFactor = - pClient->GetSndCrdPrefFrameSizeFactor(); - - const int iCurActualBufSize = - pClient->GetSndCrdActualMonoBlSize(); - - // update radio buttons - switch ( iCurPrefFrameSizeFactor ) - { - case FRAME_SIZE_FACTOR_PREFERRED: - rButBufferDelayPreferred->setChecked ( true ); - break; - - case FRAME_SIZE_FACTOR_DEFAULT: - rButBufferDelayDefault->setChecked ( true ); - break; - - case FRAME_SIZE_FACTOR_SAFE: - rButBufferDelaySafe->setChecked ( true ); - break; - } - - // preferred size - const int iPrefBufSize = - iCurPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; - - // actual size (use yellow color if different from preferred size) - const QString strActSizeValues = - GenSndCrdBufferDelayString ( iCurActualBufSize ); - - if ( iPrefBufSize != iCurActualBufSize ) - { - // yellow color if actual buffer size is not the selected one - // but a valid one, red color if actual buffer size is not the - // selected one and is not a vaild one - if ( ( iCurActualBufSize != ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) && - ( iCurActualBufSize != ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) && - ( iCurActualBufSize != ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) - { - TextLabelActualSndCrdBufDelay->setText ( "" + - strActSizeValues + "" ); - } - else - { - TextLabelActualSndCrdBufDelay->setText ( "" + - strActSizeValues + "" ); - } - } - else - { - TextLabelActualSndCrdBufDelay->setText ( strActSizeValues ); - } -} - -void CClientSettingsDlg::showEvent ( QShowEvent* showEvent ) -{ - // only activate ping timer if window is actually shown - TimerPing.start ( PING_UPDATE_TIME ); - - UpdateDisplay(); -} - -void CClientSettingsDlg::hideEvent ( QHideEvent* hideEvent ) -{ - // if window is closed, stop timer for ping - TimerPing.stop(); -} - -void CClientSettingsDlg::OnDriverSetupBut() -{ - pClient->OpenSndCrdDriverSetup(); -} - -void CClientSettingsDlg::OnSliderNetBuf ( int value ) -{ - pClient->SetSockBufNumFrames ( value ); - UpdateJitterBufferFrame(); -} - -void CClientSettingsDlg::OnSliderSndCrdBufferDelay ( int value ) -{ - pClient->SetSndCrdPrefFrameSizeFactor ( value ); - UpdateDisplay(); -} - -void CClientSettingsDlg::OnSoundCrdSelection ( int iSndDevIdx ) -{ - const QString strError = pClient->SetSndCrdDev ( iSndDevIdx ); - - if ( !strError.isEmpty() ) - { - QMessageBox::critical ( 0, APP_NAME, - QString ( "The selected audio device could not be used because " - "of the following error: " ) + strError + - QString ( " The previous driver will be selected." ), "Ok", 0 ); - - // recover old selection - cbSoundcard->setCurrentIndex ( pClient->GetSndCrdDev() ); - } - UpdateDisplay(); -} - -void CClientSettingsDlg::OnAutoJitBuf ( int value ) -{ - pClient->SetDoAutoSockBufSize ( value == Qt::Checked ); - UpdateJitterBufferFrame(); -} - -void CClientSettingsDlg::OnOpenChatOnNewMessageStateChanged ( int value ) -{ - pClient->SetOpenChatOnNewMessage ( value == Qt::Checked ); - UpdateDisplay(); -} - -void CClientSettingsDlg::OnGUIDesignFancyStateChanged ( int value ) -{ - if ( value == Qt::Unchecked ) - { - pClient->SetGUIDesign ( GD_STANDARD ); - } - else - { - pClient->SetGUIDesign ( GD_ORIGINAL ); - } - emit GUIDesignChanged(); - UpdateDisplay(); -} - -void CClientSettingsDlg::OnUseHighQualityAudioStateChanged ( int value ) -{ - pClient->SetCELTHighQuality ( value == Qt::Checked ); - UpdateDisplay(); -} - -void CClientSettingsDlg::OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* button ) -{ - if ( button == rButBufferDelayPreferred ) - { - pClient->SetSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ); - } - - if ( button == rButBufferDelayDefault ) - { - pClient->SetSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_DEFAULT ); - } - - if ( button == rButBufferDelaySafe ) - { - pClient->SetSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_SAFE ); - } - - UpdateDisplay(); -} - -void CClientSettingsDlg::OnTimerPing() -{ - // send ping message to server - pClient->SendPingMess(); -} - -void CClientSettingsDlg::OnPingTimeResult ( int iPingTime ) -{ -/* - For estimating the overall delay, use the following assumptions: - - the mean delay of a cyclic buffer is half the buffer size (since - for the average it is assumed that the buffer is half filled) - - consider the jitter buffer on the server side, too -*/ - // 2 times buffers at client and server divided by 2 (half the buffer - // for the delay) is simply the total socket buffer size - const double dTotalJitterBufferDelayMS = - SYSTEM_BLOCK_DURATION_MS_FLOAT * pClient->GetSockBufNumFrames(); - - // we assume that we have two period sizes for the input and one for the - // output, therefore we have "3 *" instead of "2 *" (for input and output) - // the actual sound card buffer size - const double dTotalSoundCardDelayMS = - 3 * pClient->GetSndCrdActualMonoBlSize() * - 1000 / SYSTEM_SAMPLE_RATE; - - // network packets are of the same size as the audio packets per definition - const double dDelayToFillNetworkPackets = - pClient->GetSndCrdActualMonoBlSize() * - 1000 / SYSTEM_SAMPLE_RATE; - - // CELT additional delay at small frame sizes is half a frame size - const double dAdditionalAudioCodecDelay = - SYSTEM_BLOCK_DURATION_MS_FLOAT / 2; - - const double dTotalBufferDelay = - dDelayToFillNetworkPackets + - dTotalJitterBufferDelayMS + - dTotalSoundCardDelayMS + - dAdditionalAudioCodecDelay; - - const int iOverallDelay = - LlconMath::round ( dTotalBufferDelay + iPingTime ); - - // apply values to GUI labels, take special care if ping time exceeds - // a certain value - if ( iPingTime > 500 ) - { - const QString sErrorText = - ">500 ms"; - - TextLabelPingTime->setText ( sErrorText ); - TextLabelOverallDelay->setText ( sErrorText ); - } - else - { - TextLabelPingTime->setText ( QString().setNum ( iPingTime ) + " ms" ); - TextLabelOverallDelay->setText ( - QString().setNum ( iOverallDelay ) + " ms" ); - } - - // color definition: < 40 ms green, < 65 ms yellow, otherwise red - if ( iOverallDelay <= 40 ) - { - CLEDOverallDelay->SetLight ( MUL_COL_LED_GREEN ); - } - else - { - if ( iOverallDelay <= 65 ) - { - CLEDOverallDelay->SetLight ( MUL_COL_LED_YELLOW ); - } - else - { - CLEDOverallDelay->SetLight ( MUL_COL_LED_RED ); - } - } -} - -void CClientSettingsDlg::UpdateDisplay() -{ - // update slider controls (settings might have been changed) - UpdateJitterBufferFrame(); - UpdateSoundCardFrame(); - - if ( !pClient->IsRunning() ) - { - // clear text labels with client parameters - TextLabelPingTime->setText ( "" ); - TextLabelOverallDelay->setText ( "" ); - TextUpstreamValue->setText ( "" ); - } - else - { - // update upstream rate information label (only if client is running) - TextUpstreamValue->setText ( - QString().setNum ( pClient->GetUploadRateKbps() ) + " kbps" ); - } -} - -void CClientSettingsDlg::SetStatus ( const int iMessType, const int iStatus ) -{ - switch ( iMessType ) - { - case MS_JIT_BUF_PUT: - case MS_JIT_BUF_GET: - // network LED shows combined status of put and get - CLEDNetw->SetLight ( iStatus ); - break; - - case MS_RESET_ALL: - CLEDNetw->Reset(); - break; - } -} +/******************************************************************************\ + * Copyright (c) 2004-2009 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * 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 + * version. + * + * 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 + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "clientsettingsdlg.h" + + +/* Implementation *************************************************************/ +CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent, + Qt::WindowFlags f ) : pClient ( pNCliP ), QDialog ( parent, f ) +{ + setupUi ( this ); + + // add help text to controls + QString strJitterBufferSize = tr ( "Jitter Buffer Size: The size of " + "the network buffer (jitter buffer). The jitter buffer compensates for " + "the network jitter. The larger this buffer is, the more robust the " + "connection is against network jitter but the higher is the latency. " + "This setting is therefore a trade-off between audio drop outs and " + "overall audio delay.
By changing this setting, both, the client " + "and the server jitter buffer is set to the same value." ); + SliderNetBuf->setWhatsThis ( strJitterBufferSize ); + TextNetBuf->setWhatsThis ( strJitterBufferSize ); + GroupBoxJitterBuffer->setWhatsThis ( strJitterBufferSize ); + + // init driver button +#ifdef _WIN32 + ButtonDriverSetup->setText ( "ASIO Setup" ); +#else + // no use for this button for Linux right now, maybe later used + // for Jack + ButtonDriverSetup->hide(); +#endif + + // init delay and other information controls + CLEDOverallDelay->SetUpdateTime ( 2 * PING_UPDATE_TIME ); + CLEDOverallDelay->Reset(); + TextLabelPingTime->setText ( "" ); + TextLabelOverallDelay->setText ( "" ); + TextUpstreamValue->setText ( "" ); + + + // init slider controls --- + // network buffer + SliderNetBuf->setRange ( MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL ); + UpdateJitterBufferFrame(); + + // init combo box containing all available sound cards in the system + cbSoundcard->clear(); + for ( int iSndDevIdx = 0; iSndDevIdx < pClient->GetSndCrdNumDev(); iSndDevIdx++ ) + { + cbSoundcard->addItem ( pClient->GetSndCrdDeviceName ( iSndDevIdx ).c_str() ); + } + cbSoundcard->setCurrentIndex ( pClient->GetSndCrdDev() ); + + // "OpenChatOnNewMessage" check box + if ( pClient->GetOpenChatOnNewMessage() ) + { + cbOpenChatOnNewMessage->setCheckState ( Qt::Checked ); + } + else + { + cbOpenChatOnNewMessage->setCheckState ( Qt::Unchecked ); + } + + // fancy GUI design check box + if ( pClient->GetGUIDesign() == GD_STANDARD ) + { + cbGUIDesignFancy->setCheckState ( Qt::Unchecked ); + } + else + { + cbGUIDesignFancy->setCheckState ( Qt::Checked ); + } + + +// TODO we disable the fancy GUI switch because the new design +// is not yet finished +cbGUIDesignFancy->setVisible ( false ); + + + // "High Quality Audio" check box + if ( pClient->GetCELTHighQuality() ) + { + cbUseHighQualityAudio->setCheckState ( Qt::Checked ); + } + else + { + cbUseHighQualityAudio->setCheckState ( Qt::Unchecked ); + } + + // set text for sound card buffer delay radio buttons + rButBufferDelayPreferred->setText ( GenSndCrdBufferDelayString ( + FRAME_SIZE_FACTOR_PREFERRED * SYSTEM_FRAME_SIZE_SAMPLES, + ", preferred" ) ); + + rButBufferDelayDefault->setText ( GenSndCrdBufferDelayString ( + FRAME_SIZE_FACTOR_DEFAULT * SYSTEM_FRAME_SIZE_SAMPLES, + ", default" ) ); + + rButBufferDelaySafe->setText ( GenSndCrdBufferDelayString ( + FRAME_SIZE_FACTOR_SAFE * SYSTEM_FRAME_SIZE_SAMPLES ) ); + + // sound card buffer delay inits + SndCrdBufferDelayButtonGroup.addButton ( rButBufferDelayPreferred ); + SndCrdBufferDelayButtonGroup.addButton ( rButBufferDelayDefault ); + SndCrdBufferDelayButtonGroup.addButton ( rButBufferDelaySafe ); + + UpdateSoundCardFrame(); + + + // Connections ------------------------------------------------------------- + // timers + QObject::connect ( &TimerStatus, SIGNAL ( timeout() ), + this, SLOT ( OnTimerStatus() ) ); + + QObject::connect ( &TimerPing, SIGNAL ( timeout() ), + this, SLOT ( OnTimerPing() ) ); + + // slider controls + QObject::connect ( SliderNetBuf, SIGNAL ( valueChanged ( int ) ), + this, SLOT ( OnSliderNetBuf ( int ) ) ); + + // check boxes + QObject::connect ( cbOpenChatOnNewMessage, SIGNAL ( stateChanged ( int ) ), + this, SLOT ( OnOpenChatOnNewMessageStateChanged ( int ) ) ); + + QObject::connect ( cbGUIDesignFancy, SIGNAL ( stateChanged ( int ) ), + this, SLOT ( OnGUIDesignFancyStateChanged ( int ) ) ); + + QObject::connect ( cbUseHighQualityAudio, SIGNAL ( stateChanged ( int ) ), + this, SLOT ( OnUseHighQualityAudioStateChanged ( int ) ) ); + + QObject::connect ( cbAutoJitBuf, SIGNAL ( stateChanged ( int ) ), + this, SLOT ( OnAutoJitBuf ( int ) ) ); + + // combo boxes + QObject::connect ( cbSoundcard, SIGNAL ( activated ( int ) ), + this, SLOT ( OnSoundCrdSelection ( int ) ) ); + + // buttons + QObject::connect ( ButtonDriverSetup, SIGNAL ( clicked() ), + this, SLOT ( OnDriverSetupBut() ) ); + + // misc + QObject::connect ( pClient, SIGNAL ( PingTimeReceived ( int ) ), + this, SLOT ( OnPingTimeResult ( int ) ) ); + + QObject::connect ( &SndCrdBufferDelayButtonGroup, + SIGNAL ( buttonClicked ( QAbstractButton* ) ), this, + SLOT ( OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* ) ) ); + + + // Timers ------------------------------------------------------------------ + // start timer for status bar + TimerStatus.start ( DISPLAY_UPDATE_TIME ); +} + +void CClientSettingsDlg::UpdateJitterBufferFrame() +{ + // update slider value and text + const int iCurNumNetBuf = pClient->GetSockBufNumFrames(); + SliderNetBuf->setValue ( iCurNumNetBuf ); + TextNetBuf->setText ( "Size: " + QString().setNum ( iCurNumNetBuf ) ); + + // if auto setting is enabled, disable slider control + cbAutoJitBuf->setChecked ( pClient->GetDoAutoSockBufSize() ); + SliderNetBuf->setEnabled ( !pClient->GetDoAutoSockBufSize() ); + TextNetBuf->setEnabled ( !pClient->GetDoAutoSockBufSize() ); +} + +QString CClientSettingsDlg::GenSndCrdBufferDelayString ( const int iFrameSize, + const QString strAddText ) +{ + // use two times the buffer delay for the entire delay since + // we have input and output + return QString().setNum ( (double) iFrameSize * 2 * + 1000 / SYSTEM_SAMPLE_RATE, 'f', 2 ) + " ms (" + + QString().setNum ( iFrameSize ) + strAddText + ")"; +} + +void CClientSettingsDlg::UpdateSoundCardFrame() +{ + // update slider value and text + const int iCurPrefFrameSizeFactor = + pClient->GetSndCrdPrefFrameSizeFactor(); + + const int iCurActualBufSize = + pClient->GetSndCrdActualMonoBlSize(); + + // update radio buttons + switch ( iCurPrefFrameSizeFactor ) + { + case FRAME_SIZE_FACTOR_PREFERRED: + rButBufferDelayPreferred->setChecked ( true ); + break; + + case FRAME_SIZE_FACTOR_DEFAULT: + rButBufferDelayDefault->setChecked ( true ); + break; + + case FRAME_SIZE_FACTOR_SAFE: + rButBufferDelaySafe->setChecked ( true ); + break; + } + + // preferred size + const int iPrefBufSize = + iCurPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; + + // actual size (use yellow color if different from preferred size) + const QString strActSizeValues = + GenSndCrdBufferDelayString ( iCurActualBufSize ); + + if ( iPrefBufSize != iCurActualBufSize ) + { + // yellow color if actual buffer size is not the selected one + // but a valid one, red color if actual buffer size is not the + // selected one and is not a vaild one + if ( ( iCurActualBufSize != ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) && + ( iCurActualBufSize != ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) && + ( iCurActualBufSize != ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) + { + TextLabelActualSndCrdBufDelay->setText ( "" + + strActSizeValues + "" ); + } + else + { + TextLabelActualSndCrdBufDelay->setText ( "" + + strActSizeValues + "" ); + } + } + else + { + TextLabelActualSndCrdBufDelay->setText ( strActSizeValues ); + } +} + +void CClientSettingsDlg::showEvent ( QShowEvent* showEvent ) +{ + // only activate ping timer if window is actually shown + TimerPing.start ( PING_UPDATE_TIME ); + + UpdateDisplay(); +} + +void CClientSettingsDlg::hideEvent ( QHideEvent* hideEvent ) +{ + // if window is closed, stop timer for ping + TimerPing.stop(); +} + +void CClientSettingsDlg::OnDriverSetupBut() +{ + pClient->OpenSndCrdDriverSetup(); +} + +void CClientSettingsDlg::OnSliderNetBuf ( int value ) +{ + pClient->SetSockBufNumFrames ( value ); + UpdateJitterBufferFrame(); +} + +void CClientSettingsDlg::OnSliderSndCrdBufferDelay ( int value ) +{ + pClient->SetSndCrdPrefFrameSizeFactor ( value ); + UpdateDisplay(); +} + +void CClientSettingsDlg::OnSoundCrdSelection ( int iSndDevIdx ) +{ + const QString strError = pClient->SetSndCrdDev ( iSndDevIdx ); + + if ( !strError.isEmpty() ) + { + QMessageBox::critical ( 0, APP_NAME, + QString ( "The selected audio device could not be used because " + "of the following error: " ) + strError + + QString ( " The previous driver will be selected." ), "Ok", 0 ); + + // recover old selection + cbSoundcard->setCurrentIndex ( pClient->GetSndCrdDev() ); + } + UpdateDisplay(); +} + +void CClientSettingsDlg::OnAutoJitBuf ( int value ) +{ + pClient->SetDoAutoSockBufSize ( value == Qt::Checked ); + UpdateJitterBufferFrame(); +} + +void CClientSettingsDlg::OnOpenChatOnNewMessageStateChanged ( int value ) +{ + pClient->SetOpenChatOnNewMessage ( value == Qt::Checked ); + UpdateDisplay(); +} + +void CClientSettingsDlg::OnGUIDesignFancyStateChanged ( int value ) +{ + if ( value == Qt::Unchecked ) + { + pClient->SetGUIDesign ( GD_STANDARD ); + } + else + { + pClient->SetGUIDesign ( GD_ORIGINAL ); + } + emit GUIDesignChanged(); + UpdateDisplay(); +} + +void CClientSettingsDlg::OnUseHighQualityAudioStateChanged ( int value ) +{ + pClient->SetCELTHighQuality ( value == Qt::Checked ); + UpdateDisplay(); +} + +void CClientSettingsDlg::OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* button ) +{ + if ( button == rButBufferDelayPreferred ) + { + pClient->SetSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_PREFERRED ); + } + + if ( button == rButBufferDelayDefault ) + { + pClient->SetSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_DEFAULT ); + } + + if ( button == rButBufferDelaySafe ) + { + pClient->SetSndCrdPrefFrameSizeFactor ( FRAME_SIZE_FACTOR_SAFE ); + } + + UpdateDisplay(); +} + +void CClientSettingsDlg::OnTimerPing() +{ + // send ping message to server + pClient->SendPingMess(); +} + +void CClientSettingsDlg::OnPingTimeResult ( int iPingTime ) +{ +/* + For estimating the overall delay, use the following assumptions: + - the mean delay of a cyclic buffer is half the buffer size (since + for the average it is assumed that the buffer is half filled) + - consider the jitter buffer on the server side, too +*/ + // 2 times buffers at client and server divided by 2 (half the buffer + // for the delay) is simply the total socket buffer size + const double dTotalJitterBufferDelayMS = + SYSTEM_BLOCK_DURATION_MS_FLOAT * pClient->GetSockBufNumFrames(); + + // we assume that we have two period sizes for the input and one for the + // output, therefore we have "3 *" instead of "2 *" (for input and output) + // the actual sound card buffer size + const double dTotalSoundCardDelayMS = + 3 * pClient->GetSndCrdActualMonoBlSize() * + 1000 / SYSTEM_SAMPLE_RATE; + + // network packets are of the same size as the audio packets per definition + const double dDelayToFillNetworkPackets = + pClient->GetSndCrdActualMonoBlSize() * + 1000 / SYSTEM_SAMPLE_RATE; + + // CELT additional delay at small frame sizes is half a frame size + const double dAdditionalAudioCodecDelay = + SYSTEM_BLOCK_DURATION_MS_FLOAT / 2; + + const double dTotalBufferDelay = + dDelayToFillNetworkPackets + + dTotalJitterBufferDelayMS + + dTotalSoundCardDelayMS + + dAdditionalAudioCodecDelay; + + const int iOverallDelay = + LlconMath::round ( dTotalBufferDelay + iPingTime ); + + // apply values to GUI labels, take special care if ping time exceeds + // a certain value + if ( iPingTime > 500 ) + { + const QString sErrorText = + ">500 ms"; + + TextLabelPingTime->setText ( sErrorText ); + TextLabelOverallDelay->setText ( sErrorText ); + } + else + { + TextLabelPingTime->setText ( QString().setNum ( iPingTime ) + " ms" ); + TextLabelOverallDelay->setText ( + QString().setNum ( iOverallDelay ) + " ms" ); + } + + // color definition: < 40 ms green, < 65 ms yellow, otherwise red + if ( iOverallDelay <= 40 ) + { + CLEDOverallDelay->SetLight ( MUL_COL_LED_GREEN ); + } + else + { + if ( iOverallDelay <= 65 ) + { + CLEDOverallDelay->SetLight ( MUL_COL_LED_YELLOW ); + } + else + { + CLEDOverallDelay->SetLight ( MUL_COL_LED_RED ); + } + } +} + +void CClientSettingsDlg::UpdateDisplay() +{ + // update slider controls (settings might have been changed) + UpdateJitterBufferFrame(); + UpdateSoundCardFrame(); + + if ( !pClient->IsRunning() ) + { + // clear text labels with client parameters + TextLabelPingTime->setText ( "" ); + TextLabelOverallDelay->setText ( "" ); + TextUpstreamValue->setText ( "" ); + } + else + { + // update upstream rate information label (only if client is running) + TextUpstreamValue->setText ( + QString().setNum ( pClient->GetUploadRateKbps() ) + " kbps" ); + } +} + +void CClientSettingsDlg::SetStatus ( const int iMessType, const int iStatus ) +{ + switch ( iMessType ) + { + case MS_JIT_BUF_PUT: + case MS_JIT_BUF_GET: + // network LED shows combined status of put and get + CLEDNetw->SetLight ( iStatus ); + break; + + case MS_RESET_ALL: + CLEDNetw->Reset(); + break; + } +} diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 1c309740..45fbf4f9 100755 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -1,95 +1,95 @@ -/******************************************************************************\ - * Copyright (c) 2004-2009 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * 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 - * version. - * - * 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 - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "global.h" -#include "client.h" -#include "multicolorled.h" -#ifdef _WIN32 -# include "../windows/moc/clientsettingsdlgbase.h" -#else -# include "moc/clientsettingsdlgbase.h" -#endif - - -/* Definitions ****************************************************************/ -// update time for GUI controls -#define DISPLAY_UPDATE_TIME 1000 // ms -#define PING_UPDATE_TIME 500 // ms - - -/* Classes ********************************************************************/ -class CClientSettingsDlg : public QDialog, private Ui_CClientSettingsDlgBase -{ - Q_OBJECT - -public: - CClientSettingsDlg ( CClient* pNCliP, QWidget* parent = 0, - Qt::WindowFlags f = 0 ); - - void SetStatus ( const int iMessType, const int iStatus ); - -protected: - void UpdateJitterBufferFrame(); - void UpdateSoundCardFrame(); - QString GenSndCrdBufferDelayString ( const int iFrameSize, - const QString strAddText = "" ); - - virtual void showEvent ( QShowEvent* showEvent ); - virtual void hideEvent ( QHideEvent* hideEvent ); - - CClient* pClient; - QTimer TimerStatus; - QTimer TimerPing; - QButtonGroup SndCrdBufferDelayButtonGroup; - void UpdateDisplay(); - - public slots: - void OnTimerStatus() { UpdateDisplay(); } - void OnTimerPing(); - void OnSliderNetBuf ( int value ); - void OnSliderSndCrdBufferDelay ( int value ); - void OnAutoJitBuf ( int value ); - void OnOpenChatOnNewMessageStateChanged ( int value ); - void OnGUIDesignFancyStateChanged ( int value ); - void OnUseHighQualityAudioStateChanged ( int value ); - void OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* button ); - void OnPingTimeResult ( int iPingTime ); - void OnSoundCrdSelection ( int iSndDevIdx ); - void OnDriverSetupBut(); - -signals: - void GUIDesignChanged(); -}; +/******************************************************************************\ + * Copyright (c) 2004-2009 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * 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 + * version. + * + * 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 + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "global.h" +#include "client.h" +#include "multicolorled.h" +#ifdef _WIN32 +# include "../windows/moc/clientsettingsdlgbase.h" +#else +# include "moc/clientsettingsdlgbase.h" +#endif + + +/* Definitions ****************************************************************/ +// update time for GUI controls +#define DISPLAY_UPDATE_TIME 1000 // ms +#define PING_UPDATE_TIME 500 // ms + + +/* Classes ********************************************************************/ +class CClientSettingsDlg : public QDialog, private Ui_CClientSettingsDlgBase +{ + Q_OBJECT + +public: + CClientSettingsDlg ( CClient* pNCliP, QWidget* parent = 0, + Qt::WindowFlags f = 0 ); + + void SetStatus ( const int iMessType, const int iStatus ); + +protected: + void UpdateJitterBufferFrame(); + void UpdateSoundCardFrame(); + QString GenSndCrdBufferDelayString ( const int iFrameSize, + const QString strAddText = "" ); + + virtual void showEvent ( QShowEvent* showEvent ); + virtual void hideEvent ( QHideEvent* hideEvent ); + + CClient* pClient; + QTimer TimerStatus; + QTimer TimerPing; + QButtonGroup SndCrdBufferDelayButtonGroup; + void UpdateDisplay(); + + public slots: + void OnTimerStatus() { UpdateDisplay(); } + void OnTimerPing(); + void OnSliderNetBuf ( int value ); + void OnSliderSndCrdBufferDelay ( int value ); + void OnAutoJitBuf ( int value ); + void OnOpenChatOnNewMessageStateChanged ( int value ); + void OnGUIDesignFancyStateChanged ( int value ); + void OnUseHighQualityAudioStateChanged ( int value ); + void OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton* button ); + void OnPingTimeResult ( int iPingTime ); + void OnSoundCrdSelection ( int iSndDevIdx ); + void OnDriverSetupBut(); + +signals: + void GUIDesignChanged(); +}; diff --git a/src/protocol.cpp b/src/protocol.cpp index e79f7a1c..9b01f4ed 100755 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1,1158 +1,1158 @@ -/******************************************************************************\ - * Copyright (c) 2004-2009 - * - * Author(s): - * Volker Fischer - * - -Protocol message definition ---------------------------- - -- All messages received need to be acknowledged by an acknowledge packet - - - -MAIN FRAME ----------- - - +-------------+------------+------------+------------------+ ... - | 2 bytes TAG | 2 bytes ID | 1 byte cnt | 2 bytes length n | ... - +-------------+------------+------------+------------------+ ... - ... --------------+-------------+ - ... n bytes data | 2 bytes CRC | - ... --------------+-------------+ - -- TAG is an all zero bit word to identify protocol messages -- message ID defined by the defines PROTMESSID_x -- cnt: counter which is increment for each message and wraps around at 255 -- length n in bytes of the data -- actual data, dependent on message type -- 16 bits CRC, calculating over the entire message, is transmitted inverted - Generator polynom: G_16(x) = x^16 + x^12 + x^5 + 1, initial state: all ones - - -MESSAGES --------- - -- Acknowledgement message: PROTMESSID_ACKN - - +-----------------------------------+ - | 2 bytes ID of message to be ackn. | - +-----------------------------------+ - - note: the cnt value is the same as of the message to be acknowledged - - -- Jitter buffer size: PROTMESSID_JITT_BUF_SIZE - - +--------------------------+ - | 2 bytes number of blocks | - +--------------------------+ - - -- Request jitter buffer size: PROTMESSID_REQ_JITT_BUF_SIZE - - note: does not have any data -> n = 0 - - -- Server full message: PROTMESSID_SERVER_FULL - - note: does not have any data -> n = 0 - - -- Gain of channel: PROTMESSID_CHANNEL_GAIN - - +-------------------+--------------+ - | 1 byte channel ID | 2 bytes gain | - +-------------------+--------------+ - - -- IP number and name of connected clients: PROTMESSID_CONN_CLIENTS_LIST - - for each connected client append following data: - - +-------------------+--------------------+------------------+ ... - | 1 byte channel ID | 4 bytes IP address | 2 bytes number n | ... - +-------------------+--------------------+------------------+ ... - ... ----------------------+ - ... n bytes UTF-8 string | - ... ----------------------+ - -- Request connected clients list: PROTMESSID_REQ_CONN_CLIENTS_LIST - - note: does not have any data -> n = 0 - - -- Name of channel: PROTMESSID_CHANNEL_NAME - - for each connected client append following data: - - +------------------+----------------------+ - | 2 bytes number n | n bytes UTF-8 string | - +------------------+----------------------+ - -- Request name of channel: PROTMESSID_REQ_CHANNEL_NAME - -- Chat text: PROTMESSID_CHAT_TEXT - - +------------------+----------------------+ - | 2 bytes number n | n bytes UTF-8 string | - +------------------+----------------------+ - - -- Ping message (for measuring the ping time): PROTMESSID_PING_MS - - +-----------------------------+ - | 4 bytes transmit time in ms | - +-----------------------------+ - - -- Properties for network transport: PROTMESSID_NETW_TRANSPORT_PROPS - - +------------------------+-------------------------+-----------------+ ... - | 4 bytes base netw size | 2 bytes block size fact | 1 byte num chan | ... - +------------------------+-------------------------+-----------------+ ... - ... ------------------+-----------------------+ ... - ... 4 bytes sam rate | 2 bytes audiocod type | ... - ... ------------------+-----------------------+ ... - ... -----------------+----------------------+ - ... 2 bytes version | 4 bytes audiocod arg | - ... -----------------+----------------------+ - - - "base netw size": length of the base network packet (frame) in bytes - - "block size fact": block size factor - - "num chan": number of channels of the audio signal, e.g. "2" is stereo - - "sam rate": sample rate of the audio stream - - "audiocod type": audio coding type, the following types are supported: - - 0: none, no audio coding applied - - 1: CELT - - "version": version of the audio coder, if not used this value shall be set to 0 - - "audiocod arg": argument for the audio coder, if not used this value shall be set to 0 - - -- Request properties for network transport: PROTMESSID_REQ_NETW_TRANSPORT_PROPS - - note: does not have any data -> n = 0 - - -- Disconnect message: PROTMESSID_DISCONNECTION - - note: does not have any data -> n = 0 - - - ****************************************************************************** - * - * 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 - * version. - * - * 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 - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "protocol.h" - - -/* Implementation *************************************************************/ -CProtocol::CProtocol() -{ - Reset(); - - // connections - QObject::connect ( &TimerSendMess, SIGNAL ( timeout() ), - this, SLOT ( OnTimerSendMess() ) ); -} - -void CProtocol::Reset() -{ - QMutexLocker locker ( &Mutex ); - - // prepare internal variables for initial protocol transfer - iCounter = 0; - iOldRecID = PROTMESSID_ILLEGAL; - iOldRecCnt = 0; - - // delete complete "send message queue" - SendMessQueue.clear(); -} - -void CProtocol::EnqueueMessage ( CVector& vecMessage, - const int iCnt, - const int iID ) -{ - bool bListWasEmpty; - - Mutex.lock(); - { - // check if list is empty so that we have to initiate a send process - bListWasEmpty = SendMessQueue.empty(); - - // create send message object for the queue - CSendMessage SendMessageObj ( vecMessage, iCnt, iID ); - - // we want to have a FIFO: we add at the end and take from the beginning - SendMessQueue.push_back ( SendMessageObj ); - } - Mutex.unlock(); - - // if list was empty, initiate send process - if ( bListWasEmpty ) - { - SendMessage(); - } -} - -void CProtocol::SendMessage() -{ - CVector vecMessage; - bool bSendMess = false; - - Mutex.lock(); - { - // we have to check that list is not empty, since in another thread the - // last element of the list might have been erased - if ( !SendMessQueue.empty() ) - { - vecMessage.Init ( SendMessQueue.front().vecMessage.Size() ); - vecMessage = SendMessQueue.front().vecMessage; - - bSendMess = true; - } - } - Mutex.unlock(); - - if ( bSendMess ) - { - // send message - emit MessReadyForSending ( vecMessage ); - - // start time-out timer if not active - if ( !TimerSendMess.isActive() ) - { - TimerSendMess.start ( SEND_MESS_TIMEOUT_MS ); - } - } - else - { - // no message to send, stop timer - TimerSendMess.stop(); - } -} - -void CProtocol::CreateAndSendMessage ( const int iID, - const CVector& vecData ) -{ - CVector vecNewMessage; - int iCurCounter; - - Mutex.lock(); - { - // store current counter value - iCurCounter = iCounter; - - // increase counter (wraps around automatically) - iCounter++; - } - Mutex.unlock(); - - // build complete message - GenMessageFrame ( vecNewMessage, iCurCounter, iID, vecData ); - - // enqueue message - EnqueueMessage ( vecNewMessage, iCurCounter, iID ); -} - -void CProtocol::CreateAndImmSendAcknMess ( const int& iID, - const int& iCnt ) -{ - CVector vecAcknMessage; - CVector vecData ( 2 ); // 2 bytes of data - unsigned int iPos = 0; // init position pointer - - // build data vector - PutValOnStream ( vecData, iPos, static_cast ( iID ), 2 ); - - // build complete message - GenMessageFrame ( vecAcknMessage, iCnt, PROTMESSID_ACKN, vecData ); - - // immediately send acknowledge message - emit MessReadyForSending ( vecAcknMessage ); -} - -bool CProtocol::IsProtocolMessage ( const CVector& vecbyData, - const int iNumBytes ) -{ -/* - just check if this is a protocol message but do not act on message -*/ - int iRecCounter, iRecID; - CVector vecData; - - return !ParseMessageFrame ( vecbyData, iNumBytes, iRecCounter, iRecID, vecData ); -} - -bool CProtocol::ParseMessage ( const CVector& vecbyData, - const int iNumBytes ) -{ -/* - return code: false -> ok; true -> error -*/ - bool bRet = false; - bool bSendNextMess; - int iRecCounter, iRecID; - CVector vecData; - - if ( !ParseMessageFrame ( vecbyData, iNumBytes, iRecCounter, iRecID, vecData ) ) - { - -/* -// TEST channel implementation: randomly delete protocol messages (50 % loss) -if ( rand() < ( RAND_MAX / 2 ) ) return false; -*/ - - // In case we received a message and returned an answer but our answer - // did not make it to the receiver, he will resend his message. We check - // here if the message is the same as the old one, and if this is the - // case, just resend our old answer again - if ( ( iOldRecID == iRecID ) && ( iOldRecCnt == iRecCounter ) ) - { - // acknowledgments are not acknowledged - if ( iRecID != PROTMESSID_ACKN ) - { - // resend acknowledgement - CreateAndImmSendAcknMess ( iRecID, iRecCounter ); - } - } - else - { - // special treatment for acknowledge messages - if ( iRecID == PROTMESSID_ACKN ) - { - // extract data from stream and emit signal for received value - unsigned int iPos = 0; - const int iData = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - Mutex.lock(); - { - // check if this is the correct acknowledgment - bSendNextMess = false; - if ( !SendMessQueue.empty() ) - { - if ( ( SendMessQueue.front().iCnt == iRecCounter ) && - ( SendMessQueue.front().iID == iData ) ) - { - // message acknowledged, remove from queue - SendMessQueue.pop_front(); - - // send next message in queue - bSendNextMess = true; - } - } - } - Mutex.unlock(); - - if ( bSendNextMess ) - { - SendMessage(); - } - } - else - { - // check which type of message we received and do action - switch ( iRecID ) - { - case PROTMESSID_JITT_BUF_SIZE: - bRet = EvaluateJitBufMes ( vecData ); - break; - - case PROTMESSID_REQ_JITT_BUF_SIZE: - bRet = EvaluateReqJitBufMes ( vecData ); - break; - - case PROTMESSID_SERVER_FULL: - bRet = EvaluateServerFullMes ( vecData ); - break; - - case PROTMESSID_CHANNEL_GAIN: - bRet = EvaluateChanGainMes ( vecData ); - break; - - case PROTMESSID_CONN_CLIENTS_LIST: - bRet = EvaluateConClientListMes ( vecData ); - break; - - case PROTMESSID_REQ_CONN_CLIENTS_LIST: - bRet = EvaluateReqConnClientsList ( vecData ); - break; - - case PROTMESSID_CHANNEL_NAME: - bRet = EvaluateChanNameMes ( vecData ); - break; - - case PROTMESSID_REQ_CHANNEL_NAME: - bRet = EvaluateReqChanNameMes ( vecData ); - break; - - case PROTMESSID_CHAT_TEXT: - bRet = EvaluateChatTextMes ( vecData ); - break; - - case PROTMESSID_PING_MS: - bRet = EvaluatePingMes ( vecData ); - break; - - case PROTMESSID_NETW_TRANSPORT_PROPS: - bRet = EvaluateNetwTranspPropsMes ( vecData ); - break; - - case PROTMESSID_REQ_NETW_TRANSPORT_PROPS: - bRet = EvaluateReqNetwTranspPropsMes ( vecData ); - break; - - case PROTMESSID_DISCONNECTION: - bRet = EvaluateDisconnectionMes ( vecData ); - break; - } - - // immediately send acknowledge message - CreateAndImmSendAcknMess ( iRecID, iRecCounter ); - - // save current message ID and counter to find out if message - // was resent - iOldRecID = iRecID; - iOldRecCnt = iRecCounter; - } - } - } - else - { - bRet = true; // return error code - } - - return bRet; -} - - -// Access-functions for creating and parsing messages -------------------------- -void CProtocol::CreateJitBufMes ( const int iJitBufSize ) -{ - CVector vecData ( 2 ); // 2 bytes of data - unsigned int iPos = 0; // init position pointer - - // build data vector - PutValOnStream ( vecData, iPos, static_cast ( iJitBufSize ), 2 ); - - CreateAndSendMessage ( PROTMESSID_JITT_BUF_SIZE, vecData ); -} - -bool CProtocol::EvaluateJitBufMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size - if ( vecData.Size() != 2 ) - { - return true; - } - - // extract jitter buffer size - const int iData = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - +/******************************************************************************\ + * Copyright (c) 2004-2009 + * + * Author(s): + * Volker Fischer + * + +Protocol message definition +--------------------------- + +- All messages received need to be acknowledged by an acknowledge packet + + + +MAIN FRAME +---------- + + +-------------+------------+------------+------------------+ ... + | 2 bytes TAG | 2 bytes ID | 1 byte cnt | 2 bytes length n | ... + +-------------+------------+------------+------------------+ ... + ... --------------+-------------+ + ... n bytes data | 2 bytes CRC | + ... --------------+-------------+ + +- TAG is an all zero bit word to identify protocol messages +- message ID defined by the defines PROTMESSID_x +- cnt: counter which is increment for each message and wraps around at 255 +- length n in bytes of the data +- actual data, dependent on message type +- 16 bits CRC, calculating over the entire message, is transmitted inverted + Generator polynom: G_16(x) = x^16 + x^12 + x^5 + 1, initial state: all ones + + +MESSAGES +-------- + +- Acknowledgement message: PROTMESSID_ACKN + + +-----------------------------------+ + | 2 bytes ID of message to be ackn. | + +-----------------------------------+ + + note: the cnt value is the same as of the message to be acknowledged + + +- Jitter buffer size: PROTMESSID_JITT_BUF_SIZE + + +--------------------------+ + | 2 bytes number of blocks | + +--------------------------+ + + +- Request jitter buffer size: PROTMESSID_REQ_JITT_BUF_SIZE + + note: does not have any data -> n = 0 + + +- Server full message: PROTMESSID_SERVER_FULL + + note: does not have any data -> n = 0 + + +- Gain of channel: PROTMESSID_CHANNEL_GAIN + + +-------------------+--------------+ + | 1 byte channel ID | 2 bytes gain | + +-------------------+--------------+ + + +- IP number and name of connected clients: PROTMESSID_CONN_CLIENTS_LIST + + for each connected client append following data: + + +-------------------+--------------------+------------------+ ... + | 1 byte channel ID | 4 bytes IP address | 2 bytes number n | ... + +-------------------+--------------------+------------------+ ... + ... ----------------------+ + ... n bytes UTF-8 string | + ... ----------------------+ + +- Request connected clients list: PROTMESSID_REQ_CONN_CLIENTS_LIST + + note: does not have any data -> n = 0 + + +- Name of channel: PROTMESSID_CHANNEL_NAME + + for each connected client append following data: + + +------------------+----------------------+ + | 2 bytes number n | n bytes UTF-8 string | + +------------------+----------------------+ + +- Request name of channel: PROTMESSID_REQ_CHANNEL_NAME + +- Chat text: PROTMESSID_CHAT_TEXT + + +------------------+----------------------+ + | 2 bytes number n | n bytes UTF-8 string | + +------------------+----------------------+ + + +- Ping message (for measuring the ping time): PROTMESSID_PING_MS + + +-----------------------------+ + | 4 bytes transmit time in ms | + +-----------------------------+ + + +- Properties for network transport: PROTMESSID_NETW_TRANSPORT_PROPS + + +------------------------+-------------------------+-----------------+ ... + | 4 bytes base netw size | 2 bytes block size fact | 1 byte num chan | ... + +------------------------+-------------------------+-----------------+ ... + ... ------------------+-----------------------+ ... + ... 4 bytes sam rate | 2 bytes audiocod type | ... + ... ------------------+-----------------------+ ... + ... -----------------+----------------------+ + ... 2 bytes version | 4 bytes audiocod arg | + ... -----------------+----------------------+ + + - "base netw size": length of the base network packet (frame) in bytes + - "block size fact": block size factor + - "num chan": number of channels of the audio signal, e.g. "2" is stereo + - "sam rate": sample rate of the audio stream + - "audiocod type": audio coding type, the following types are supported: + - 0: none, no audio coding applied + - 1: CELT + - "version": version of the audio coder, if not used this value shall be set to 0 + - "audiocod arg": argument for the audio coder, if not used this value shall be set to 0 + + +- Request properties for network transport: PROTMESSID_REQ_NETW_TRANSPORT_PROPS + + note: does not have any data -> n = 0 + + +- Disconnect message: PROTMESSID_DISCONNECTION + + note: does not have any data -> n = 0 + + + ****************************************************************************** + * + * 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 + * version. + * + * 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 + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "protocol.h" + + +/* Implementation *************************************************************/ +CProtocol::CProtocol() +{ + Reset(); + + // connections + QObject::connect ( &TimerSendMess, SIGNAL ( timeout() ), + this, SLOT ( OnTimerSendMess() ) ); +} + +void CProtocol::Reset() +{ + QMutexLocker locker ( &Mutex ); + + // prepare internal variables for initial protocol transfer + iCounter = 0; + iOldRecID = PROTMESSID_ILLEGAL; + iOldRecCnt = 0; + + // delete complete "send message queue" + SendMessQueue.clear(); +} + +void CProtocol::EnqueueMessage ( CVector& vecMessage, + const int iCnt, + const int iID ) +{ + bool bListWasEmpty; + + Mutex.lock(); + { + // check if list is empty so that we have to initiate a send process + bListWasEmpty = SendMessQueue.empty(); + + // create send message object for the queue + CSendMessage SendMessageObj ( vecMessage, iCnt, iID ); + + // we want to have a FIFO: we add at the end and take from the beginning + SendMessQueue.push_back ( SendMessageObj ); + } + Mutex.unlock(); + + // if list was empty, initiate send process + if ( bListWasEmpty ) + { + SendMessage(); + } +} + +void CProtocol::SendMessage() +{ + CVector vecMessage; + bool bSendMess = false; + + Mutex.lock(); + { + // we have to check that list is not empty, since in another thread the + // last element of the list might have been erased + if ( !SendMessQueue.empty() ) + { + vecMessage.Init ( SendMessQueue.front().vecMessage.Size() ); + vecMessage = SendMessQueue.front().vecMessage; + + bSendMess = true; + } + } + Mutex.unlock(); + + if ( bSendMess ) + { + // send message + emit MessReadyForSending ( vecMessage ); + + // start time-out timer if not active + if ( !TimerSendMess.isActive() ) + { + TimerSendMess.start ( SEND_MESS_TIMEOUT_MS ); + } + } + else + { + // no message to send, stop timer + TimerSendMess.stop(); + } +} + +void CProtocol::CreateAndSendMessage ( const int iID, + const CVector& vecData ) +{ + CVector vecNewMessage; + int iCurCounter; + + Mutex.lock(); + { + // store current counter value + iCurCounter = iCounter; + + // increase counter (wraps around automatically) + iCounter++; + } + Mutex.unlock(); + + // build complete message + GenMessageFrame ( vecNewMessage, iCurCounter, iID, vecData ); + + // enqueue message + EnqueueMessage ( vecNewMessage, iCurCounter, iID ); +} + +void CProtocol::CreateAndImmSendAcknMess ( const int& iID, + const int& iCnt ) +{ + CVector vecAcknMessage; + CVector vecData ( 2 ); // 2 bytes of data + unsigned int iPos = 0; // init position pointer + + // build data vector + PutValOnStream ( vecData, iPos, static_cast ( iID ), 2 ); + + // build complete message + GenMessageFrame ( vecAcknMessage, iCnt, PROTMESSID_ACKN, vecData ); + + // immediately send acknowledge message + emit MessReadyForSending ( vecAcknMessage ); +} + +bool CProtocol::IsProtocolMessage ( const CVector& vecbyData, + const int iNumBytes ) +{ +/* + just check if this is a protocol message but do not act on message +*/ + int iRecCounter, iRecID; + CVector vecData; + + return !ParseMessageFrame ( vecbyData, iNumBytes, iRecCounter, iRecID, vecData ); +} + +bool CProtocol::ParseMessage ( const CVector& vecbyData, + const int iNumBytes ) +{ +/* + return code: false -> ok; true -> error +*/ + bool bRet = false; + bool bSendNextMess; + int iRecCounter, iRecID; + CVector vecData; + + if ( !ParseMessageFrame ( vecbyData, iNumBytes, iRecCounter, iRecID, vecData ) ) + { + +/* +// TEST channel implementation: randomly delete protocol messages (50 % loss) +if ( rand() < ( RAND_MAX / 2 ) ) return false; +*/ + + // In case we received a message and returned an answer but our answer + // did not make it to the receiver, he will resend his message. We check + // here if the message is the same as the old one, and if this is the + // case, just resend our old answer again + if ( ( iOldRecID == iRecID ) && ( iOldRecCnt == iRecCounter ) ) + { + // acknowledgments are not acknowledged + if ( iRecID != PROTMESSID_ACKN ) + { + // resend acknowledgement + CreateAndImmSendAcknMess ( iRecID, iRecCounter ); + } + } + else + { + // special treatment for acknowledge messages + if ( iRecID == PROTMESSID_ACKN ) + { + // extract data from stream and emit signal for received value + unsigned int iPos = 0; + const int iData = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + Mutex.lock(); + { + // check if this is the correct acknowledgment + bSendNextMess = false; + if ( !SendMessQueue.empty() ) + { + if ( ( SendMessQueue.front().iCnt == iRecCounter ) && + ( SendMessQueue.front().iID == iData ) ) + { + // message acknowledged, remove from queue + SendMessQueue.pop_front(); + + // send next message in queue + bSendNextMess = true; + } + } + } + Mutex.unlock(); + + if ( bSendNextMess ) + { + SendMessage(); + } + } + else + { + // check which type of message we received and do action + switch ( iRecID ) + { + case PROTMESSID_JITT_BUF_SIZE: + bRet = EvaluateJitBufMes ( vecData ); + break; + + case PROTMESSID_REQ_JITT_BUF_SIZE: + bRet = EvaluateReqJitBufMes ( vecData ); + break; + + case PROTMESSID_SERVER_FULL: + bRet = EvaluateServerFullMes ( vecData ); + break; + + case PROTMESSID_CHANNEL_GAIN: + bRet = EvaluateChanGainMes ( vecData ); + break; + + case PROTMESSID_CONN_CLIENTS_LIST: + bRet = EvaluateConClientListMes ( vecData ); + break; + + case PROTMESSID_REQ_CONN_CLIENTS_LIST: + bRet = EvaluateReqConnClientsList ( vecData ); + break; + + case PROTMESSID_CHANNEL_NAME: + bRet = EvaluateChanNameMes ( vecData ); + break; + + case PROTMESSID_REQ_CHANNEL_NAME: + bRet = EvaluateReqChanNameMes ( vecData ); + break; + + case PROTMESSID_CHAT_TEXT: + bRet = EvaluateChatTextMes ( vecData ); + break; + + case PROTMESSID_PING_MS: + bRet = EvaluatePingMes ( vecData ); + break; + + case PROTMESSID_NETW_TRANSPORT_PROPS: + bRet = EvaluateNetwTranspPropsMes ( vecData ); + break; + + case PROTMESSID_REQ_NETW_TRANSPORT_PROPS: + bRet = EvaluateReqNetwTranspPropsMes ( vecData ); + break; + + case PROTMESSID_DISCONNECTION: + bRet = EvaluateDisconnectionMes ( vecData ); + break; + } + + // immediately send acknowledge message + CreateAndImmSendAcknMess ( iRecID, iRecCounter ); + + // save current message ID and counter to find out if message + // was resent + iOldRecID = iRecID; + iOldRecCnt = iRecCounter; + } + } + } + else + { + bRet = true; // return error code + } + + return bRet; +} + + +// Access-functions for creating and parsing messages -------------------------- +void CProtocol::CreateJitBufMes ( const int iJitBufSize ) +{ + CVector vecData ( 2 ); // 2 bytes of data + unsigned int iPos = 0; // init position pointer + + // build data vector + PutValOnStream ( vecData, iPos, static_cast ( iJitBufSize ), 2 ); + + CreateAndSendMessage ( PROTMESSID_JITT_BUF_SIZE, vecData ); +} + +bool CProtocol::EvaluateJitBufMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size + if ( vecData.Size() != 2 ) + { + return true; + } + + // extract jitter buffer size + const int iData = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + if ( ( iData < MIN_NET_BUF_SIZE_NUM_BL ) || - ( iData > MAX_NET_BUF_SIZE_NUM_BL ) ) - { - return true; - } - - // invoke message action - emit ChangeJittBufSize ( iData ); - - return false; // no error -} - -void CProtocol::CreateReqJitBufMes() -{ - CreateAndSendMessage ( PROTMESSID_REQ_JITT_BUF_SIZE, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateReqJitBufMes ( const CVector& vecData ) -{ - // invoke message action - emit ReqJittBufSize(); - - return false; // no error -} - -void CProtocol::CreateServerFullMes() -{ - CreateAndSendMessage ( PROTMESSID_SERVER_FULL, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateServerFullMes ( const CVector& vecData ) -{ - // invoke message action - emit ServerFull(); - - return false; // no error -} - -void CProtocol::CreateChanGainMes ( const int iChanID, const double dGain ) -{ - CVector vecData ( 3 ); // 3 bytes of data - unsigned int iPos = 0; // init position pointer - - // build data vector - // channel ID - PutValOnStream ( vecData, iPos, static_cast ( iChanID ), 1 ); - - // actual gain, we convert from double with range 0..1 to integer - const int iCurGain = static_cast ( dGain * ( 1 << 15 ) ); - PutValOnStream ( vecData, iPos, static_cast ( iCurGain ), 2 ); - - CreateAndSendMessage ( PROTMESSID_CHANNEL_GAIN, vecData ); -} - -bool CProtocol::EvaluateChanGainMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size - if ( vecData.Size() != 3 ) - { - return true; - } - - // channel ID - const int iCurID = - static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - - // actual gain, we convert from integer to double with range 0..1 - const int iData = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - const double dNewGain = static_cast ( iData ) / ( 1 << 15 ); - - // invoke message action - emit ChangeChanGain ( iCurID, dNewGain ); - - return false; // no error -} - -void CProtocol::CreateConClientListMes ( const CVector& vecChanInfo ) -{ - const int iNumClients = vecChanInfo.Size(); - - // build data vector - CVector vecData ( 0 ); - unsigned int iPos = 0; // init position pointer - - for ( int i = 0; i < iNumClients; i++ ) - { - // current string size - const int iCurStrLen = vecChanInfo[i].strName.size(); - - // size of current list entry - const int iCurListEntrLen = - 1 /* chan ID */ + 4 /* IP addr. */ + 2 /* str. size */ + iCurStrLen; - - // make space for new data - vecData.Enlarge ( iCurListEntrLen ); - - // channel ID - PutValOnStream ( vecData, iPos, - static_cast ( vecChanInfo[i].iChanID ), 1 ); - - // IP address (4 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( vecChanInfo[i].iIpAddr ), 4 ); - - // number of bytes for name string (2 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( iCurStrLen ), 2 ); - - // name string (n bytes) - for ( int j = 0; j < iCurStrLen; j++ ) - { - // byte-by-byte copying of the string data - PutValOnStream ( vecData, iPos, - static_cast ( vecChanInfo[i].strName[j].toAscii() ), 1 ); - } - } - - CreateAndSendMessage ( PROTMESSID_CONN_CLIENTS_LIST, vecData ); -} - -bool CProtocol::EvaluateConClientListMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - int iData; - const unsigned int iDataLen = vecData.Size(); - CVector vecChanInfo ( 0 ); - - while ( iPos < iDataLen ) - { - // check size (the first 7 bytes) - if ( iDataLen - iPos < 7 ) - { - return true; - } - - // channel ID (1 byte) - const int iChanID = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - - // IP address (4 bytes) - const int iIpAddr = static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); - - // number of bytes for name string (2 bytes) - const unsigned int iStringLen = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // check size - if ( iDataLen - iPos < iStringLen ) - { - return true; - } - - // name string (n bytes) - QString strCurStr = ""; - for ( unsigned int j = 0; j < iStringLen; j++ ) - { - // byte-by-byte copying of the string data - iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - strCurStr += QString ( (char*) &iData ); - } - - // add channel information to vector - vecChanInfo.Add ( CChannelShortInfo ( iChanID, iIpAddr, strCurStr ) ); - } - - // invoke message action - emit ConClientListMesReceived ( vecChanInfo ); - - return false; // no error -} - -void CProtocol::CreateReqConnClientsList() -{ - CreateAndSendMessage ( PROTMESSID_REQ_CONN_CLIENTS_LIST, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateReqConnClientsList ( const CVector& vecData ) -{ - // invoke message action - emit ReqConnClientsList(); - - return false; // no error -} - -void CProtocol::CreateChanNameMes ( const QString strName ) -{ - unsigned int iPos = 0; // init position pointer - const int iStrLen = strName.size(); // get string size - - // size of current list entry - const int iEntrLen = 2 /* string size */ + iStrLen; - - // build data vector - CVector vecData ( iEntrLen ); - - // number of bytes for name string (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( iStrLen ), 2 ); - - // name string (n bytes) - for ( int j = 0; j < iStrLen; j++ ) - { - // byte-by-byte copying of the string data - PutValOnStream ( vecData, iPos, - static_cast ( strName[j].toAscii() ), 1 ); - } - - CreateAndSendMessage ( PROTMESSID_CHANNEL_NAME, vecData ); -} - -bool CProtocol::EvaluateChanNameMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size (the first 2 bytes) - if ( vecData.Size() < 2 ) - { - return true; - } - - // number of bytes for name string (2 bytes) - const int iStrLen = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // check size - if ( vecData.Size() - 2 != iStrLen ) - { - return true; - } - - // name string (n bytes) - QString strName = ""; - for ( int j = 0; j < iStrLen; j++ ) - { - // byte-by-byte copying of the string data - int iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - strName += QString ( (char*) &iData ); - } - - // invoke message action - emit ChangeChanName ( strName ); - - return false; // no error -} - -void CProtocol::CreateReqChanNameMes() -{ - CreateAndSendMessage ( PROTMESSID_REQ_CHANNEL_NAME, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateReqChanNameMes ( const CVector& vecData ) -{ - // invoke message action - emit ReqChanName(); - - return false; // no error -} - -void CProtocol::CreateChatTextMes ( const QString strChatText ) -{ - unsigned int iPos = 0; // init position pointer - const int iStrLen = strChatText.size(); // get string size - - // size of message body - const int iEntrLen = 2 /* string size */ + iStrLen; - - // build data vector - CVector vecData ( iEntrLen ); - - // number of bytes for name string (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( iStrLen ), 2 ); - - // name string (n bytes) - for ( int j = 0; j < iStrLen; j++ ) - { - // byte-by-byte copying of the string data - PutValOnStream ( vecData, iPos, - static_cast ( strChatText[j].toAscii() ), 1 ); - } - - CreateAndSendMessage ( PROTMESSID_CHAT_TEXT, vecData ); -} - -bool CProtocol::EvaluateChatTextMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size (the first 2 bytes) - if ( vecData.Size() < 2 ) - { - return true; - } - - // number of bytes for name string (2 bytes) - const int iStrLen = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // check size - if ( vecData.Size() - 2 != iStrLen ) - { - return true; - } - - // name string (n bytes) - QString strChatText = ""; - for ( int j = 0; j < iStrLen; j++ ) - { - // byte-by-byte copying of the string data - int iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - strChatText += QString ( (char*) &iData ); - } - - // invoke message action - emit ChatTextReceived ( strChatText ); - - return false; // no error -} - -void CProtocol::CreatePingMes ( const int iMs ) -{ - unsigned int iPos = 0; // init position pointer - - // build data vector (4 bytes long) - CVector vecData ( 4 ); - - // byte-by-byte copying of the string data - PutValOnStream ( vecData, iPos, static_cast ( iMs ), 4 ); - - CreateAndSendMessage ( PROTMESSID_PING_MS, vecData ); -} - -bool CProtocol::EvaluatePingMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size - if ( vecData.Size() != 4 ) - { - return true; - } - - emit PingReceived ( static_cast ( GetValFromStream ( vecData, iPos, 4 ) ) ); - - return false; // no error -} - -void CProtocol::CreateNetwTranspPropsMes ( const CNetworkTransportProps& NetTrProps ) -{ - unsigned int iPos = 0; // init position pointer - - // size of current message body - const int iEntrLen = 4 /* netw size */ + 2 /* block size fact */ + - 1 /* num chan */ + 4 /* sam rate */ + 2 /* audiocod type */ + - 2 /* version */ + 4 /* audiocod arg */; - - // build data vector - CVector vecData ( iEntrLen ); - - // length of the base network packet (frame) in bytes (4 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iBaseNetworkPacketSize ), 4 ); - - // block size factor (2 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iBlockSizeFact ), 2 ); - - // number of channels of the audio signal, e.g. "2" is stereo (1 byte) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iNumAudioChannels ), 1 ); - - // sample rate of the audio stream (4 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iSampleRate ), 4 ); - - // audio coding type (2 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.eAudioCodingType ), 2 ); - - // version (2 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iVersion ), 2 ); - - // argument for the audio coder (4 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iAudioCodingArg ), 4 ); - - CreateAndSendMessage ( PROTMESSID_NETW_TRANSPORT_PROPS, vecData ); -} - -bool CProtocol::EvaluateNetwTranspPropsMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - CNetworkTransportProps ReceivedNetwTranspProps; - - // size of current message body - const int iEntrLen = 4 /* netw size */ + 2 /* block size fact */ + - 1 /* num chan */ + 4 /* sam rate */ + 2 /* audiocod type */ + - 2 /* version */ + 4 /* audiocod arg */; - - // check size - if ( vecData.Size() != iEntrLen ) - { - return true; - } - - // length of the base network packet (frame) in bytes (4 bytes) - ReceivedNetwTranspProps.iBaseNetworkPacketSize = - static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); - - if ( ( ReceivedNetwTranspProps.iBaseNetworkPacketSize < 1 ) || - ( ReceivedNetwTranspProps.iBaseNetworkPacketSize > MAX_SIZE_BYTES_NETW_BUF ) ) - { - return true; - } - - // block size factor (2 bytes) - ReceivedNetwTranspProps.iBlockSizeFact = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - if ( ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_PREFERRED ) && - ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_DEFAULT ) && - ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_SAFE ) ) - { - return true; - } - - // number of channels of the audio signal, e.g. "2" is stereo (1 byte) - ReceivedNetwTranspProps.iNumAudioChannels = - static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - - if ( ( ReceivedNetwTranspProps.iNumAudioChannels != 1 ) && - ( ReceivedNetwTranspProps.iNumAudioChannels != 2 ) ) - { - return true; - } - - // sample rate of the audio stream (4 bytes) - ReceivedNetwTranspProps.iSampleRate = - static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); - - // audio coding type (2 bytes) with error check - const int iRecCodingType = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - if ( ( iRecCodingType != CT_NONE ) && - ( iRecCodingType != CT_CELT ) ) - { - return true; - } - - ReceivedNetwTranspProps.eAudioCodingType = - static_cast ( iRecCodingType ); - - // version (2 bytes) - ReceivedNetwTranspProps.iVersion = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // argument for the audio coder (4 bytes) - ReceivedNetwTranspProps.iAudioCodingArg = - static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); - - // invoke message action - emit NetTranspPropsReceived ( ReceivedNetwTranspProps ); - - return false; // no error -} - -void CProtocol::CreateReqNetwTranspPropsMes() -{ - CreateAndSendMessage ( PROTMESSID_REQ_NETW_TRANSPORT_PROPS, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateReqNetwTranspPropsMes ( const CVector& vecData ) -{ - // invoke message action - emit ReqNetTranspProps(); - - return false; // no error -} - -void CProtocol::CreateAndImmSendDisconnectionMes() -{ - CVector vecDisconMessage; - int iCurCounter; - - Mutex.lock(); - { - // store current counter value - iCurCounter = iCounter; - - // increase counter (wraps around automatically) - iCounter++; - } - Mutex.unlock(); - - // build complete message - GenMessageFrame ( vecDisconMessage, iCurCounter, - PROTMESSID_DISCONNECTION, CVector ( 0 ) ); - - // immediately send acknowledge message - emit MessReadyForSending ( vecDisconMessage ); -} - -bool CProtocol::EvaluateDisconnectionMes ( const CVector& vecData ) -{ - // invoke message action - emit Disconnection(); - - return false; // no error -} - - -/******************************************************************************\ -* Message generation (parsing) * -\******************************************************************************/ -bool CProtocol::ParseMessageFrame ( const CVector& vecIn, - const int iNumBytesIn, - int& iCnt, - int& iID, - CVector& vecData ) -{ - int iLenBy, i; - unsigned int iCurPos; - - // vector must be at least "MESS_LEN_WITHOUT_DATA_BYTE" bytes long - if ( iNumBytesIn < MESS_LEN_WITHOUT_DATA_BYTE ) - { - return true; // return error code - } - - - // decode header ----- - iCurPos = 0; // start from beginning - - // 2 bytes TAG - const int iTag = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); - - // check if tag is correct - if ( iTag != 0 ) - { - return true; // return error code - } - - /* 2 bytes ID */ - iID = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); - - /* 1 byte cnt */ - iCnt = static_cast ( GetValFromStream ( vecIn, iCurPos, 1 ) ); - - /* 2 bytes length */ - iLenBy = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); - - // make sure the length is correct - if ( iLenBy != iNumBytesIn - MESS_LEN_WITHOUT_DATA_BYTE ) - { - return true; // return error code - } - - - // now check CRC ----- - CCRC CRCObj; - const int iLenCRCCalc = MESS_HEADER_LENGTH_BYTE + iLenBy; - - iCurPos = 0; // start from beginning - for ( i = 0; i < iLenCRCCalc; i++ ) - { - CRCObj.AddByte ( static_cast ( - GetValFromStream ( vecIn, iCurPos, 1 ) ) ); - } - - if ( CRCObj.GetCRC () != GetValFromStream ( vecIn, iCurPos, 2 ) ) - { - return true; // return error code - } - - - // extract actual data ----- - vecData.Init ( iLenBy ); - iCurPos = MESS_HEADER_LENGTH_BYTE; // start from beginning of data - for ( i = 0; i < iLenBy; i++ ) - { - vecData[i] = static_cast ( - GetValFromStream ( vecIn, iCurPos, 1 ) ); - } - - return false; // everything was ok -} - -uint32_t CProtocol::GetValFromStream ( const CVector& vecIn, - unsigned int& iPos, - const unsigned int iNumOfBytes ) -{ -/* - note: iPos is automatically incremented in this function -*/ - // 4 bytes maximum since we return uint32 - Q_ASSERT ( ( iNumOfBytes > 0 ) && ( iNumOfBytes <= 4 ) ); - Q_ASSERT ( static_cast ( vecIn.Size() ) >= iPos + iNumOfBytes ); - - uint32_t iRet = 0; - for ( unsigned int i = 0; i < iNumOfBytes; i++ ) - { - iRet |= vecIn[iPos] << ( i * 8 /* size of byte */ ); - iPos++; - } - - return iRet; -} - -void CProtocol::GenMessageFrame ( CVector& vecOut, - const int iCnt, - const int iID, - const CVector& vecData ) -{ - int i; - - // query length of data vector - const int iDataLenByte = vecData.Size(); - - // total length of message - const int iTotLenByte = MESS_LEN_WITHOUT_DATA_BYTE + iDataLenByte; - - // init message vector - vecOut.Init ( iTotLenByte ); - - // encode header ----- - unsigned int iCurPos = 0; // init position pointer - - // 2 bytes TAG (all zero bits) - PutValOnStream ( vecOut, iCurPos, - static_cast ( 0 ), 2 ); - - // 2 bytes ID - PutValOnStream ( vecOut, iCurPos, - static_cast ( iID ), 2 ); - - // 1 byte cnt - PutValOnStream ( vecOut, iCurPos, - static_cast ( iCnt ), 1 ); - - // 2 bytes length - PutValOnStream ( vecOut, iCurPos, - static_cast ( iDataLenByte ), 2 ); - - // encode data ----- - for ( i = 0; i < iDataLenByte; i++ ) - { - PutValOnStream ( vecOut, iCurPos, - static_cast ( vecData[i] ), 1 ); - } - - // encode CRC ----- - CCRC CRCObj; - iCurPos = 0; // start from beginning - - const int iLenCRCCalc = MESS_HEADER_LENGTH_BYTE + iDataLenByte; - for ( i = 0; i < iLenCRCCalc; i++ ) - { - CRCObj.AddByte ( static_cast ( - GetValFromStream ( vecOut, iCurPos, 1 ) ) ); - } - - PutValOnStream ( vecOut, iCurPos, - static_cast ( CRCObj.GetCRC() ), 2 ); -} - -void CProtocol::PutValOnStream ( CVector& vecIn, - unsigned int& iPos, - const uint32_t iVal, - const unsigned int iNumOfBytes ) -{ -/* - note: iPos is automatically incremented in this function -*/ - // 4 bytes maximum since we use uint32 - Q_ASSERT ( ( iNumOfBytes > 0 ) && ( iNumOfBytes <= 4 ) ); - Q_ASSERT ( static_cast ( vecIn.Size() ) >= iPos + iNumOfBytes ); - - for ( unsigned int i = 0; i < iNumOfBytes; i++ ) - { - vecIn[iPos] = - ( iVal >> ( i * 8 /* size of byte */ ) ) & 255 /* 11111111 */; - - iPos++; - } -} + ( iData > MAX_NET_BUF_SIZE_NUM_BL ) ) + { + return true; + } + + // invoke message action + emit ChangeJittBufSize ( iData ); + + return false; // no error +} + +void CProtocol::CreateReqJitBufMes() +{ + CreateAndSendMessage ( PROTMESSID_REQ_JITT_BUF_SIZE, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateReqJitBufMes ( const CVector& vecData ) +{ + // invoke message action + emit ReqJittBufSize(); + + return false; // no error +} + +void CProtocol::CreateServerFullMes() +{ + CreateAndSendMessage ( PROTMESSID_SERVER_FULL, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateServerFullMes ( const CVector& vecData ) +{ + // invoke message action + emit ServerFull(); + + return false; // no error +} + +void CProtocol::CreateChanGainMes ( const int iChanID, const double dGain ) +{ + CVector vecData ( 3 ); // 3 bytes of data + unsigned int iPos = 0; // init position pointer + + // build data vector + // channel ID + PutValOnStream ( vecData, iPos, static_cast ( iChanID ), 1 ); + + // actual gain, we convert from double with range 0..1 to integer + const int iCurGain = static_cast ( dGain * ( 1 << 15 ) ); + PutValOnStream ( vecData, iPos, static_cast ( iCurGain ), 2 ); + + CreateAndSendMessage ( PROTMESSID_CHANNEL_GAIN, vecData ); +} + +bool CProtocol::EvaluateChanGainMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size + if ( vecData.Size() != 3 ) + { + return true; + } + + // channel ID + const int iCurID = + static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + // actual gain, we convert from integer to double with range 0..1 + const int iData = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + const double dNewGain = static_cast ( iData ) / ( 1 << 15 ); + + // invoke message action + emit ChangeChanGain ( iCurID, dNewGain ); + + return false; // no error +} + +void CProtocol::CreateConClientListMes ( const CVector& vecChanInfo ) +{ + const int iNumClients = vecChanInfo.Size(); + + // build data vector + CVector vecData ( 0 ); + unsigned int iPos = 0; // init position pointer + + for ( int i = 0; i < iNumClients; i++ ) + { + // current string size + const int iCurStrLen = vecChanInfo[i].strName.size(); + + // size of current list entry + const int iCurListEntrLen = + 1 /* chan ID */ + 4 /* IP addr. */ + 2 /* str. size */ + iCurStrLen; + + // make space for new data + vecData.Enlarge ( iCurListEntrLen ); + + // channel ID + PutValOnStream ( vecData, iPos, + static_cast ( vecChanInfo[i].iChanID ), 1 ); + + // IP address (4 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( vecChanInfo[i].iIpAddr ), 4 ); + + // number of bytes for name string (2 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( iCurStrLen ), 2 ); + + // name string (n bytes) + for ( int j = 0; j < iCurStrLen; j++ ) + { + // byte-by-byte copying of the string data + PutValOnStream ( vecData, iPos, + static_cast ( vecChanInfo[i].strName[j].toAscii() ), 1 ); + } + } + + CreateAndSendMessage ( PROTMESSID_CONN_CLIENTS_LIST, vecData ); +} + +bool CProtocol::EvaluateConClientListMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + int iData; + const unsigned int iDataLen = vecData.Size(); + CVector vecChanInfo ( 0 ); + + while ( iPos < iDataLen ) + { + // check size (the first 7 bytes) + if ( iDataLen - iPos < 7 ) + { + return true; + } + + // channel ID (1 byte) + const int iChanID = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + // IP address (4 bytes) + const int iIpAddr = static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); + + // number of bytes for name string (2 bytes) + const unsigned int iStringLen = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // check size + if ( iDataLen - iPos < iStringLen ) + { + return true; + } + + // name string (n bytes) + QString strCurStr = ""; + for ( unsigned int j = 0; j < iStringLen; j++ ) + { + // byte-by-byte copying of the string data + iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + strCurStr += QString ( (char*) &iData ); + } + + // add channel information to vector + vecChanInfo.Add ( CChannelShortInfo ( iChanID, iIpAddr, strCurStr ) ); + } + + // invoke message action + emit ConClientListMesReceived ( vecChanInfo ); + + return false; // no error +} + +void CProtocol::CreateReqConnClientsList() +{ + CreateAndSendMessage ( PROTMESSID_REQ_CONN_CLIENTS_LIST, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateReqConnClientsList ( const CVector& vecData ) +{ + // invoke message action + emit ReqConnClientsList(); + + return false; // no error +} + +void CProtocol::CreateChanNameMes ( const QString strName ) +{ + unsigned int iPos = 0; // init position pointer + const int iStrLen = strName.size(); // get string size + + // size of current list entry + const int iEntrLen = 2 /* string size */ + iStrLen; + + // build data vector + CVector vecData ( iEntrLen ); + + // number of bytes for name string (2 bytes) + PutValOnStream ( vecData, iPos, static_cast ( iStrLen ), 2 ); + + // name string (n bytes) + for ( int j = 0; j < iStrLen; j++ ) + { + // byte-by-byte copying of the string data + PutValOnStream ( vecData, iPos, + static_cast ( strName[j].toAscii() ), 1 ); + } + + CreateAndSendMessage ( PROTMESSID_CHANNEL_NAME, vecData ); +} + +bool CProtocol::EvaluateChanNameMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size (the first 2 bytes) + if ( vecData.Size() < 2 ) + { + return true; + } + + // number of bytes for name string (2 bytes) + const int iStrLen = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // check size + if ( vecData.Size() - 2 != iStrLen ) + { + return true; + } + + // name string (n bytes) + QString strName = ""; + for ( int j = 0; j < iStrLen; j++ ) + { + // byte-by-byte copying of the string data + int iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + strName += QString ( (char*) &iData ); + } + + // invoke message action + emit ChangeChanName ( strName ); + + return false; // no error +} + +void CProtocol::CreateReqChanNameMes() +{ + CreateAndSendMessage ( PROTMESSID_REQ_CHANNEL_NAME, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateReqChanNameMes ( const CVector& vecData ) +{ + // invoke message action + emit ReqChanName(); + + return false; // no error +} + +void CProtocol::CreateChatTextMes ( const QString strChatText ) +{ + unsigned int iPos = 0; // init position pointer + const int iStrLen = strChatText.size(); // get string size + + // size of message body + const int iEntrLen = 2 /* string size */ + iStrLen; + + // build data vector + CVector vecData ( iEntrLen ); + + // number of bytes for name string (2 bytes) + PutValOnStream ( vecData, iPos, static_cast ( iStrLen ), 2 ); + + // name string (n bytes) + for ( int j = 0; j < iStrLen; j++ ) + { + // byte-by-byte copying of the string data + PutValOnStream ( vecData, iPos, + static_cast ( strChatText[j].toAscii() ), 1 ); + } + + CreateAndSendMessage ( PROTMESSID_CHAT_TEXT, vecData ); +} + +bool CProtocol::EvaluateChatTextMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size (the first 2 bytes) + if ( vecData.Size() < 2 ) + { + return true; + } + + // number of bytes for name string (2 bytes) + const int iStrLen = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // check size + if ( vecData.Size() - 2 != iStrLen ) + { + return true; + } + + // name string (n bytes) + QString strChatText = ""; + for ( int j = 0; j < iStrLen; j++ ) + { + // byte-by-byte copying of the string data + int iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + strChatText += QString ( (char*) &iData ); + } + + // invoke message action + emit ChatTextReceived ( strChatText ); + + return false; // no error +} + +void CProtocol::CreatePingMes ( const int iMs ) +{ + unsigned int iPos = 0; // init position pointer + + // build data vector (4 bytes long) + CVector vecData ( 4 ); + + // byte-by-byte copying of the string data + PutValOnStream ( vecData, iPos, static_cast ( iMs ), 4 ); + + CreateAndSendMessage ( PROTMESSID_PING_MS, vecData ); +} + +bool CProtocol::EvaluatePingMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size + if ( vecData.Size() != 4 ) + { + return true; + } + + emit PingReceived ( static_cast ( GetValFromStream ( vecData, iPos, 4 ) ) ); + + return false; // no error +} + +void CProtocol::CreateNetwTranspPropsMes ( const CNetworkTransportProps& NetTrProps ) +{ + unsigned int iPos = 0; // init position pointer + + // size of current message body + const int iEntrLen = 4 /* netw size */ + 2 /* block size fact */ + + 1 /* num chan */ + 4 /* sam rate */ + 2 /* audiocod type */ + + 2 /* version */ + 4 /* audiocod arg */; + + // build data vector + CVector vecData ( iEntrLen ); + + // length of the base network packet (frame) in bytes (4 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iBaseNetworkPacketSize ), 4 ); + + // block size factor (2 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iBlockSizeFact ), 2 ); + + // number of channels of the audio signal, e.g. "2" is stereo (1 byte) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iNumAudioChannels ), 1 ); + + // sample rate of the audio stream (4 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iSampleRate ), 4 ); + + // audio coding type (2 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.eAudioCodingType ), 2 ); + + // version (2 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iVersion ), 2 ); + + // argument for the audio coder (4 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iAudioCodingArg ), 4 ); + + CreateAndSendMessage ( PROTMESSID_NETW_TRANSPORT_PROPS, vecData ); +} + +bool CProtocol::EvaluateNetwTranspPropsMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + CNetworkTransportProps ReceivedNetwTranspProps; + + // size of current message body + const int iEntrLen = 4 /* netw size */ + 2 /* block size fact */ + + 1 /* num chan */ + 4 /* sam rate */ + 2 /* audiocod type */ + + 2 /* version */ + 4 /* audiocod arg */; + + // check size + if ( vecData.Size() != iEntrLen ) + { + return true; + } + + // length of the base network packet (frame) in bytes (4 bytes) + ReceivedNetwTranspProps.iBaseNetworkPacketSize = + static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); + + if ( ( ReceivedNetwTranspProps.iBaseNetworkPacketSize < 1 ) || + ( ReceivedNetwTranspProps.iBaseNetworkPacketSize > MAX_SIZE_BYTES_NETW_BUF ) ) + { + return true; + } + + // block size factor (2 bytes) + ReceivedNetwTranspProps.iBlockSizeFact = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + if ( ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_PREFERRED ) && + ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_DEFAULT ) && + ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_SAFE ) ) + { + return true; + } + + // number of channels of the audio signal, e.g. "2" is stereo (1 byte) + ReceivedNetwTranspProps.iNumAudioChannels = + static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + if ( ( ReceivedNetwTranspProps.iNumAudioChannels != 1 ) && + ( ReceivedNetwTranspProps.iNumAudioChannels != 2 ) ) + { + return true; + } + + // sample rate of the audio stream (4 bytes) + ReceivedNetwTranspProps.iSampleRate = + static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); + + // audio coding type (2 bytes) with error check + const int iRecCodingType = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + if ( ( iRecCodingType != CT_NONE ) && + ( iRecCodingType != CT_CELT ) ) + { + return true; + } + + ReceivedNetwTranspProps.eAudioCodingType = + static_cast ( iRecCodingType ); + + // version (2 bytes) + ReceivedNetwTranspProps.iVersion = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // argument for the audio coder (4 bytes) + ReceivedNetwTranspProps.iAudioCodingArg = + static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); + + // invoke message action + emit NetTranspPropsReceived ( ReceivedNetwTranspProps ); + + return false; // no error +} + +void CProtocol::CreateReqNetwTranspPropsMes() +{ + CreateAndSendMessage ( PROTMESSID_REQ_NETW_TRANSPORT_PROPS, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateReqNetwTranspPropsMes ( const CVector& vecData ) +{ + // invoke message action + emit ReqNetTranspProps(); + + return false; // no error +} + +void CProtocol::CreateAndImmSendDisconnectionMes() +{ + CVector vecDisconMessage; + int iCurCounter; + + Mutex.lock(); + { + // store current counter value + iCurCounter = iCounter; + + // increase counter (wraps around automatically) + iCounter++; + } + Mutex.unlock(); + + // build complete message + GenMessageFrame ( vecDisconMessage, iCurCounter, + PROTMESSID_DISCONNECTION, CVector ( 0 ) ); + + // immediately send acknowledge message + emit MessReadyForSending ( vecDisconMessage ); +} + +bool CProtocol::EvaluateDisconnectionMes ( const CVector& vecData ) +{ + // invoke message action + emit Disconnection(); + + return false; // no error +} + + +/******************************************************************************\ +* Message generation (parsing) * +\******************************************************************************/ +bool CProtocol::ParseMessageFrame ( const CVector& vecIn, + const int iNumBytesIn, + int& iCnt, + int& iID, + CVector& vecData ) +{ + int iLenBy, i; + unsigned int iCurPos; + + // vector must be at least "MESS_LEN_WITHOUT_DATA_BYTE" bytes long + if ( iNumBytesIn < MESS_LEN_WITHOUT_DATA_BYTE ) + { + return true; // return error code + } + + + // decode header ----- + iCurPos = 0; // start from beginning + + // 2 bytes TAG + const int iTag = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); + + // check if tag is correct + if ( iTag != 0 ) + { + return true; // return error code + } + + /* 2 bytes ID */ + iID = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); + + /* 1 byte cnt */ + iCnt = static_cast ( GetValFromStream ( vecIn, iCurPos, 1 ) ); + + /* 2 bytes length */ + iLenBy = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); + + // make sure the length is correct + if ( iLenBy != iNumBytesIn - MESS_LEN_WITHOUT_DATA_BYTE ) + { + return true; // return error code + } + + + // now check CRC ----- + CCRC CRCObj; + const int iLenCRCCalc = MESS_HEADER_LENGTH_BYTE + iLenBy; + + iCurPos = 0; // start from beginning + for ( i = 0; i < iLenCRCCalc; i++ ) + { + CRCObj.AddByte ( static_cast ( + GetValFromStream ( vecIn, iCurPos, 1 ) ) ); + } + + if ( CRCObj.GetCRC () != GetValFromStream ( vecIn, iCurPos, 2 ) ) + { + return true; // return error code + } + + + // extract actual data ----- + vecData.Init ( iLenBy ); + iCurPos = MESS_HEADER_LENGTH_BYTE; // start from beginning of data + for ( i = 0; i < iLenBy; i++ ) + { + vecData[i] = static_cast ( + GetValFromStream ( vecIn, iCurPos, 1 ) ); + } + + return false; // everything was ok +} + +uint32_t CProtocol::GetValFromStream ( const CVector& vecIn, + unsigned int& iPos, + const unsigned int iNumOfBytes ) +{ +/* + note: iPos is automatically incremented in this function +*/ + // 4 bytes maximum since we return uint32 + Q_ASSERT ( ( iNumOfBytes > 0 ) && ( iNumOfBytes <= 4 ) ); + Q_ASSERT ( static_cast ( vecIn.Size() ) >= iPos + iNumOfBytes ); + + uint32_t iRet = 0; + for ( unsigned int i = 0; i < iNumOfBytes; i++ ) + { + iRet |= vecIn[iPos] << ( i * 8 /* size of byte */ ); + iPos++; + } + + return iRet; +} + +void CProtocol::GenMessageFrame ( CVector& vecOut, + const int iCnt, + const int iID, + const CVector& vecData ) +{ + int i; + + // query length of data vector + const int iDataLenByte = vecData.Size(); + + // total length of message + const int iTotLenByte = MESS_LEN_WITHOUT_DATA_BYTE + iDataLenByte; + + // init message vector + vecOut.Init ( iTotLenByte ); + + // encode header ----- + unsigned int iCurPos = 0; // init position pointer + + // 2 bytes TAG (all zero bits) + PutValOnStream ( vecOut, iCurPos, + static_cast ( 0 ), 2 ); + + // 2 bytes ID + PutValOnStream ( vecOut, iCurPos, + static_cast ( iID ), 2 ); + + // 1 byte cnt + PutValOnStream ( vecOut, iCurPos, + static_cast ( iCnt ), 1 ); + + // 2 bytes length + PutValOnStream ( vecOut, iCurPos, + static_cast ( iDataLenByte ), 2 ); + + // encode data ----- + for ( i = 0; i < iDataLenByte; i++ ) + { + PutValOnStream ( vecOut, iCurPos, + static_cast ( vecData[i] ), 1 ); + } + + // encode CRC ----- + CCRC CRCObj; + iCurPos = 0; // start from beginning + + const int iLenCRCCalc = MESS_HEADER_LENGTH_BYTE + iDataLenByte; + for ( i = 0; i < iLenCRCCalc; i++ ) + { + CRCObj.AddByte ( static_cast ( + GetValFromStream ( vecOut, iCurPos, 1 ) ) ); + } + + PutValOnStream ( vecOut, iCurPos, + static_cast ( CRCObj.GetCRC() ), 2 ); +} + +void CProtocol::PutValOnStream ( CVector& vecIn, + unsigned int& iPos, + const uint32_t iVal, + const unsigned int iNumOfBytes ) +{ +/* + note: iPos is automatically incremented in this function +*/ + // 4 bytes maximum since we use uint32 + Q_ASSERT ( ( iNumOfBytes > 0 ) && ( iNumOfBytes <= 4 ) ); + Q_ASSERT ( static_cast ( vecIn.Size() ) >= iPos + iNumOfBytes ); + + for ( unsigned int i = 0; i < iNumOfBytes; i++ ) + { + vecIn[iPos] = + ( iVal >> ( i * 8 /* size of byte */ ) ) & 255 /* 11111111 */; + + iPos++; + } +} diff --git a/src/protocol.h b/src/protocol.h index 2344f81c..583c7d9a 100755 --- a/src/protocol.h +++ b/src/protocol.h @@ -88,9 +88,9 @@ public: void CreateAndImmSendAcknMess ( const int& iID, const int& iCnt ); bool ParseMessage ( const CVector& vecbyData, - const int iNumBytes ); - - bool IsProtocolMessage ( const CVector& vecbyData, + const int iNumBytes ); + + bool IsProtocolMessage ( const CVector& vecbyData, const int iNumBytes ); protected: diff --git a/src/server.cpp b/src/server.cpp index a49d8afc..8c25042f 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -1,872 +1,872 @@ -/******************************************************************************\ - * Copyright (c) 2004-2009 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * 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 - * version. - * - * 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 - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "server.h" - - -// CHighPrecisionTimer implementation ****************************************** -CHighPrecisionTimer::CHighPrecisionTimer() -{ - // add some error checking, the high precision timer implementation only - // supports 128 samples frame size at 48 kHz sampling rate -#if ( SYSTEM_FRAME_SIZE_SAMPLES != 128 ) -# error "Only system frame size of 128 samples is supported by this module" -#endif -#if SYSTEM_SAMPLE_RATE != 48000 -# error "Only a system sample rate of 48 kHz is supported by this module" -#endif - - // Since QT only supports a minimum timer resolution of 1 ms but for our - // llcon server we require a timer interval of 2.333 ms for 128 samples - // frame size at 48 kHz sampling rate. - // To support this interval, we use a timer with 2 ms - // resolution and fire the actual frame timer if the error to the actual - // required interval is minimum. - veciTimeOutIntervals.Init ( 3 ); - - // for 128 sample frame size at 48 kHz sampling rate: - // actual intervals: 0.0 2.666 5.333 8.0 - // quantized to 2 ms: 0 2 6 8 (0) - veciTimeOutIntervals[0] = 0; - veciTimeOutIntervals[1] = 1; - veciTimeOutIntervals[2] = 0; - - // connect timer timeout signal - QObject::connect ( &Timer, SIGNAL ( timeout() ), - this, SLOT ( OnTimer() ) ); -} - -void CHighPrecisionTimer::start() -{ - // reset position pointer and counter - iCurPosInVector = 0; - iIntervalCounter = 0; - - // start internal timer with 2 ms resolution - Timer.start ( 2 ); -} - -void CHighPrecisionTimer::stop() -{ - // stop timer - Timer.stop(); -} - -void CHighPrecisionTimer::OnTimer() -{ - // check if maximum number of high precision timer intervals are - // finished - if ( veciTimeOutIntervals[iCurPosInVector] == iIntervalCounter ) - { - // reset interval counter - iIntervalCounter = 0; - - // go to next position in vector, take care of wrap around - iCurPosInVector++; - if ( iCurPosInVector == veciTimeOutIntervals.Size() ) - { - iCurPosInVector = 0; - } - - // minimum time error to actual required timer interval is reached, - // emit signal for server - emit timeout(); - } - else - { - // next high precision timer interval - iIntervalCounter++; - } -} - - -// CServer implementation ****************************************************** -CServer::CServer ( const QString& strLoggingFileName, - const quint16 iPortNumber, - const QString& strHTMLStatusFileName, - const QString& strHistoryFileName, - const QString& strServerNameForHTMLStatusFile ) : - Socket ( this, iPortNumber ), - bWriteStatusHTMLFile ( false ) -{ - int i; - - // create CELT encoder/decoder for each channel (must be done before - // enabling the channels) - for ( i = 0; i < USED_NUM_CHANNELS; i++ ) - { - // init audio endocder/decoder (mono) - CeltMode[i] = celt_mode_create ( - SYSTEM_SAMPLE_RATE, 1, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); - - CeltEncoder[i] = celt_encoder_create ( CeltMode[i] ); - CeltDecoder[i] = celt_decoder_create ( CeltMode[i] ); - -#ifdef USE_LOW_COMPLEXITY_CELT_ENC - // set encoder low complexity - celt_encoder_ctl(CeltEncoder[i], - CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); -#endif - } - - // define colors for chat window identifiers - vstrChatColors.Init ( 6 ); - vstrChatColors[0] = "mediumblue"; - vstrChatColors[1] = "red"; - vstrChatColors[2] = "darkorchid"; - vstrChatColors[3] = "green"; - vstrChatColors[4] = "maroon"; - vstrChatColors[5] = "coral"; - - vecsSendData.Init ( SYSTEM_FRAME_SIZE_SAMPLES ); - - // init moving average buffer for response time evaluation - CycleTimeVariance.Init ( SYSTEM_FRAME_SIZE_SAMPLES, - SYSTEM_SAMPLE_RATE, TIME_MOV_AV_RESPONSE ); - - // enable history graph (if requested) - if ( !strHistoryFileName.isEmpty() ) - { - Logging.EnableHistory ( strHistoryFileName ); - } - - // enable logging (if requested) - if ( !strLoggingFileName.isEmpty() ) - { - // in case the history is enabled and a logging file name is - // given, parse the logging file for old entries which are then - // added in the history on software startup - if ( !strHistoryFileName.isEmpty() ) - { - Logging.ParseLogFile ( strLoggingFileName ); - } - - Logging.Start ( strLoggingFileName ); - } - - // HTML status file writing - if ( !strHTMLStatusFileName.isEmpty() ) - { - QString strCurServerNameForHTMLStatusFile = strServerNameForHTMLStatusFile; - - // if server name is empty, substitude a default name - if ( strCurServerNameForHTMLStatusFile.isEmpty() ) - { - strCurServerNameForHTMLStatusFile = "[server address]"; - } - - // (the static cast to integer of the port number is required so that it - // works correctly under Linux) - StartStatusHTMLFileWriting ( strHTMLStatusFileName, - strCurServerNameForHTMLStatusFile + ":" + - QString().number( static_cast ( iPortNumber ) ) ); - } - - // enable all channels (for the server all channel must be enabled the - // entire life time of the software) - for ( i = 0; i < USED_NUM_CHANNELS; i++ ) - { - vecChannels[i].SetEnable ( true ); - } - - - // connections ------------------------------------------------------------- - // connect timer timeout signal - QObject::connect ( &HighPrecisionTimer, SIGNAL ( timeout() ), - this, SLOT ( OnTimer() ) ); - - // CODE TAG: MAX_NUM_CHANNELS_TAG - // make sure we have MAX_NUM_CHANNELS connections!!! - // send message - QObject::connect ( &vecChannels[0], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh0 ( CVector ) ) ); - QObject::connect ( &vecChannels[1], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh1 ( CVector ) ) ); - QObject::connect ( &vecChannels[2], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh2 ( CVector ) ) ); - QObject::connect ( &vecChannels[3], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh3 ( CVector ) ) ); - QObject::connect ( &vecChannels[4], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh4 ( CVector ) ) ); - QObject::connect ( &vecChannels[5], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh5 ( CVector ) ) ); - QObject::connect ( &vecChannels[6], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh6 ( CVector ) ) ); - QObject::connect ( &vecChannels[7], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh7 ( CVector ) ) ); - QObject::connect ( &vecChannels[8], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh8 ( CVector ) ) ); - QObject::connect ( &vecChannels[9], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh9 ( CVector ) ) ); - - // request jitter buffer size - QObject::connect ( &vecChannels[0], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh0() ) ); - QObject::connect ( &vecChannels[1], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh1() ) ); - QObject::connect ( &vecChannels[2], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh2() ) ); - QObject::connect ( &vecChannels[3], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh3() ) ); - QObject::connect ( &vecChannels[4], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh4() ) ); - QObject::connect ( &vecChannels[5], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh5() ) ); - QObject::connect ( &vecChannels[6], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh6() ) ); - QObject::connect ( &vecChannels[7], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh7() ) ); - QObject::connect ( &vecChannels[8], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh8() ) ); - QObject::connect ( &vecChannels[9], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh9() ) ); - - // request connected clients list - QObject::connect ( &vecChannels[0], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh0() ) ); - QObject::connect ( &vecChannels[1], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh1() ) ); - QObject::connect ( &vecChannels[2], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh2() ) ); - QObject::connect ( &vecChannels[3], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh3() ) ); - QObject::connect ( &vecChannels[4], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh4() ) ); - QObject::connect ( &vecChannels[5], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh5() ) ); - QObject::connect ( &vecChannels[6], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh6() ) ); - QObject::connect ( &vecChannels[7], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh7() ) ); - QObject::connect ( &vecChannels[8], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh8() ) ); - QObject::connect ( &vecChannels[9], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh9() ) ); - - // channel name has changed - QObject::connect ( &vecChannels[0], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh0() ) ); - QObject::connect ( &vecChannels[1], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh1() ) ); - QObject::connect ( &vecChannels[2], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh2() ) ); - QObject::connect ( &vecChannels[3], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh3() ) ); - QObject::connect ( &vecChannels[4], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh4() ) ); - QObject::connect ( &vecChannels[5], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh5() ) ); - QObject::connect ( &vecChannels[6], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh6() ) ); - QObject::connect ( &vecChannels[7], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh7() ) ); - QObject::connect ( &vecChannels[8], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh8() ) ); - QObject::connect ( &vecChannels[9], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh9() ) ); - - // chat text received - QObject::connect ( &vecChannels[0], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh0 ( QString ) ) ); - QObject::connect ( &vecChannels[1], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh1 ( QString ) ) ); - QObject::connect ( &vecChannels[2], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh2 ( QString ) ) ); - QObject::connect ( &vecChannels[3], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh3 ( QString ) ) ); - QObject::connect ( &vecChannels[4], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh4 ( QString ) ) ); - QObject::connect ( &vecChannels[5], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh5 ( QString ) ) ); - QObject::connect ( &vecChannels[6], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh6 ( QString ) ) ); - QObject::connect ( &vecChannels[7], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh7 ( QString ) ) ); - QObject::connect ( &vecChannels[8], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh8 ( QString ) ) ); - QObject::connect ( &vecChannels[9], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh9 ( QString ) ) ); - - // ping message received - QObject::connect ( &vecChannels[0], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh0 ( int ) ) ); - QObject::connect ( &vecChannels[1], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh1 ( int ) ) ); - QObject::connect ( &vecChannels[2], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh2 ( int ) ) ); - QObject::connect ( &vecChannels[3], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh3 ( int ) ) ); - QObject::connect ( &vecChannels[4], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh4 ( int ) ) ); - QObject::connect ( &vecChannels[5], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh5 ( int ) ) ); - QObject::connect ( &vecChannels[6], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh6 ( int ) ) ); - QObject::connect ( &vecChannels[7], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh7 ( int ) ) ); - QObject::connect ( &vecChannels[8], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh8 ( int ) ) ); - QObject::connect ( &vecChannels[9], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh9 ( int ) ) ); -} - -void CServer::OnSendProtMessage ( int iChID, CVector vecMessage ) -{ - // the protocol queries me to call the function to send the message - // send it through the network - Socket.SendPacket ( vecMessage, vecChannels[iChID].GetAddress() ); -} - -void CServer::Start() -{ - // only start if not already running - if ( !IsRunning() ) - { - // start timer - HighPrecisionTimer.start(); - - // init time for response time evaluation - CycleTimeVariance.Reset(); - } -} - -void CServer::Stop() -{ - // stop timer - HighPrecisionTimer.stop(); - - // logging (add "server stopped" logging entry) - Logging.AddServerStopped(); -} - -void CServer::OnTimer() -{ - int i, j; - - CVector vecChanID; - CVector > vecvecdGains; - CVector > vecvecsData; - - // Get data from all connected clients ------------------------------------- - bool bChannelIsNowDisconnected = false; - - // Make put and get calls thread safe. Do not forget to unlock mutex - // afterwards! - Mutex.lock(); - { - // first, get number and IDs of connected channels - vecChanID.Init ( 0 ); - for ( i = 0; i < USED_NUM_CHANNELS; i++ ) - { - if ( vecChannels[i].IsConnected() ) - { - // add ID and data - vecChanID.Add ( i ); - } - } - - // process connected channels - const int iNumCurConnChan = vecChanID.Size(); - - // init temporary vectors - vecvecdGains.Init ( iNumCurConnChan ); - vecvecsData.Init ( iNumCurConnChan ); - - for ( i = 0; i < iNumCurConnChan; i++ ) - { - // get actual ID of current channel - const int iCurChanID = vecChanID[i]; - - // init vectors storing information of all channels - vecvecdGains[i].Init ( iNumCurConnChan ); - vecvecsData[i].Init ( SYSTEM_FRAME_SIZE_SAMPLES ); - - // get gains of all connected channels - for ( j = 0; j < iNumCurConnChan; j++ ) - { - // The second index of "vecvecdGains" does not represent - // the channel ID! Therefore we have to use "vecChanID" to - // query the IDs of the currently connected channels - vecvecdGains[i][j] = - vecChannels[iCurChanID].GetGain( vecChanID[j] ); - } - - // get current number of CELT coded bytes - const int iCeltNumCodedBytes = - vecChannels[iCurChanID].GetNetwFrameSize(); - - // init temporal data vector and clear input buffers - CVector vecbyData ( iCeltNumCodedBytes ); - - // get data - const EGetDataStat eGetStat = - vecChannels[iCurChanID].GetData ( vecbyData ); - - // if channel was just disconnected, set flag that connected - // client list is sent to all other clients - if ( eGetStat == GS_CHAN_NOW_DISCONNECTED ) - { - bChannelIsNowDisconnected = true; - } - - // CELT decode received data stream - if ( eGetStat == GS_BUFFER_OK ) - { - celt_decode ( CeltDecoder[iCurChanID], - &vecbyData[0], - iCeltNumCodedBytes, - &vecvecsData[i][0] ); - } - else - { - // lost packet - celt_decode ( CeltDecoder[iCurChanID], - NULL, - 0, - &vecvecsData[i][0] ); - } - - // send message for get status (for GUI) - if ( eGetStat == GS_BUFFER_OK ) - { - PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN, iCurChanID ); - } - else - { - PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED, iCurChanID ); - } - } - - // a channel is now disconnected, take action on it - if ( bChannelIsNowDisconnected ) - { - // update channel list for all currently connected clients - CreateAndSendChanListForAllConChannels(); - } - } - Mutex.unlock(); // release mutex - - - // Process data ------------------------------------------------------------ - const int iNumClients = vecChanID.Size(); - - // Check if at least one client is connected. If not, stop server until - // one client is connected. - if ( iNumClients != 0 ) - { - for ( int i = 0; i < iNumClients; i++ ) - { - // get actual ID of current channel - const int iCurChanID = vecChanID[i]; - - // generate a sparate mix for each channel - // actual processing of audio data -> mix - vecsSendData = ProcessData ( vecvecsData, vecvecdGains[i] ); - - // get current number of CELT coded bytes - const int iCeltNumCodedBytes = - vecChannels[iCurChanID].GetNetwFrameSize(); - - // CELT encoding - CVector vecCeltData ( iCeltNumCodedBytes ); - - celt_encode ( CeltEncoder[iCurChanID], - &vecsSendData[0], - NULL, - &vecCeltData[0], - iCeltNumCodedBytes ); - - // send separate mix to current clients - Socket.SendPacket ( - vecChannels[iCurChanID].PrepSendPacket ( vecCeltData ), - vecChannels[iCurChanID].GetAddress() ); - } - } - else - { - // Disable server if no clients are connected. In this case the server - // does not consume any significant CPU when no client is connected. - Stop(); - } - - // update response time measurement - CycleTimeVariance.Update(); -} - -CVector CServer::ProcessData ( CVector >& vecvecsData, - CVector& vecdGains ) -{ - int i; - - // init return vector with zeros since we mix all channels on that vector - -// TODO speed optimization: avoid using the zero vector, use the first valid -// data vector for initialization instead (take care of gain of this data, too!) - - CVector vecsOutData ( SYSTEM_FRAME_SIZE_SAMPLES, 0 ); - - const int iNumClients = vecvecsData.Size(); - - // mix all audio data from all clients together - for ( int j = 0; j < iNumClients; j++ ) - { - // if channel gain is 1, avoid multiplication for speed optimization - if ( vecdGains[j] == static_cast ( 1.0 ) ) - { - for ( i = 0; i < SYSTEM_FRAME_SIZE_SAMPLES; i++ ) - { - vecsOutData[i] = - Double2Short ( vecsOutData[i] + vecvecsData[j][i] ); - } - } - else - { - for ( i = 0; i < SYSTEM_FRAME_SIZE_SAMPLES; i++ ) - { - vecsOutData[i] = - Double2Short ( vecsOutData[i] + - vecvecsData[j][i] * vecdGains[j] ); - } - } - } - - return vecsOutData; -} - -CVector CServer::CreateChannelList() -{ - CVector vecChanInfo ( 0 ); - - // look for free channels - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) - { - if ( vecChannels[i].IsConnected() ) - { - // append channel ID, IP address and channel name to storing vectors - vecChanInfo.Add ( CChannelShortInfo ( - i, // ID - vecChannels[i].GetAddress().InetAddr.toIPv4Address(), // IP address - vecChannels[i].GetName() /* name */ ) ); - } - } - - return vecChanInfo; -} - -void CServer::CreateAndSendChanListForAllConChannels() -{ - // create channel list - CVector vecChanInfo ( CreateChannelList() ); - - // now send connected channels list to all connected clients - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) - { - if ( vecChannels[i].IsConnected() ) - { - // send message - vecChannels[i].CreateConClientListMes ( vecChanInfo ); - } - } - - // create status HTML file if enabled - if ( bWriteStatusHTMLFile ) - { - WriteHTMLChannelList(); - } -} - -void CServer::CreateAndSendChanListForThisChan ( const int iCurChanID ) -{ - // create channel list - CVector vecChanInfo ( CreateChannelList() ); - - // now send connected channels list to the channel with the ID "iCurChanID" - vecChannels[iCurChanID].CreateConClientListMes ( vecChanInfo ); -} - -void CServer::CreateAndSendChatTextForAllConChannels ( const int iCurChanID, - const QString& strChatText ) -{ - // create message which is sent to all connected clients ------------------- - // get client name, if name is empty, use IP address instead - QString ChanName = vecChannels[iCurChanID].GetName(); - if ( ChanName.isEmpty() ) - { - // convert IP address to text and show it - ChanName = - vecChannels[iCurChanID].GetAddress().GetIpAddressStringNoLastByte(); - } - - // add time and name of the client at the beginning of the message text and - // use different colors - QString sCurColor = vstrChatColors[iCurChanID % vstrChatColors.Size()]; - const QString strActualMessageText = - "(" + - QTime::currentTime().toString ( "hh:mm:ss AP" ) + ") " + ChanName + - " " + strChatText; - - - // send chat text to all connected clients --------------------------------- - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) - { - if ( vecChannels[i].IsConnected() ) - { - // send message - vecChannels[i].CreateChatTextMes ( strActualMessageText ); - } - } -} - -int CServer::GetFreeChan() -{ - // look for a free channel - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) - { - if ( !vecChannels[i].IsConnected() ) - { - return i; - } - } - - // no free channel found, return invalid ID - return INVALID_CHANNEL_ID; -} - -int CServer::CheckAddr ( const CHostAddress& Addr ) -{ - CHostAddress InetAddr; - - // check for all possible channels if IP is already in use - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) - { - if ( vecChannels[i].IsConnected() ) - { - if ( vecChannels[i].GetAddress ( InetAddr ) ) - { - // IP found, return channel number - if ( InetAddr == Addr ) - { - return i; - } - } - } - } - - // IP not found, return invalid ID - return INVALID_CHANNEL_ID; -} - -bool CServer::PutData ( const CVector& vecbyRecBuf, - const int iNumBytesRead, - const CHostAddress& HostAdr ) -{ - bool bChanOK = true; // init with ok, might be overwritten - bool bNewChannelReserved = false; - bool bIsNotEvaluatedProtocolMessage = false; - - Mutex.lock(); - { - // Get channel ID ------------------------------------------------------ - // check address - int iCurChanID = CheckAddr ( HostAdr ); - - if ( iCurChanID == INVALID_CHANNEL_ID ) - { - // a new client is calling, look for free channel - iCurChanID = GetFreeChan(); - - if ( iCurChanID != INVALID_CHANNEL_ID ) - { - // initialize current channel by storing the calling host - // address - vecChannels[iCurChanID].SetAddress ( HostAdr ); - - // reset channel name - vecChannels[iCurChanID].ResetName(); - - // reset the channel gains of current channel, at the same - // time reset gains of this channel ID for all other channels - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) - { - vecChannels[iCurChanID].SetGain ( i, (double) 1.0 ); - - // other channels (we do not distinguish the case if - // i == iCurChanID for simplicity) - vecChannels[i].SetGain ( iCurChanID, (double) 1.0 ); - } - - // set flag for new reserved channel - bNewChannelReserved = true; - } - else - { - // no free channel available - bChanOK = false; - - // create and send "server full" message - -// TODO problem: if channel is not officially connected, we cannot send messages - - } - } - - - // Put received data in jitter buffer ---------------------------------- - if ( bChanOK ) - { - // put packet in socket buffer - switch ( vecChannels[iCurChanID].PutData ( vecbyRecBuf, iNumBytesRead ) ) - { - case PS_AUDIO_OK: - PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_GREEN, iCurChanID ); - break; - - case PS_AUDIO_ERR: - PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_RED, iCurChanID ); - break; - - case PS_PROT_ERR: - PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_YELLOW, iCurChanID ); - break; - - case PS_PROT_OK_MESS_NOT_EVALUATED: - bIsNotEvaluatedProtocolMessage = true; // set flag - break; - } - } - - // act on new channel connection - if ( bNewChannelReserved && ( !bIsNotEvaluatedProtocolMessage ) ) - { - // logging of new connected channel - Logging.AddNewConnection ( HostAdr.InetAddr ); - - // A new client connected to the server, the channel list - // at all clients have to be updated. This is done by sending - // a channel name request to the client which causes a channel - // name message to be transmitted to the server. If the server - // receives this message, the channel list will be automatically - // updated (implicitely). - // To make sure the protocol message is transmitted, the channel - // first has to be marked as connected. - // - // Usually it is not required to send the channel list to the - // client currently connecting since it automatically requests - // the channel list on a new connection (as a result, he will - // usually get the list twice which has no impact on functionality - // but will only increase the network load a tiny little bit). But - // in case the client thinks he is still connected but the server - // was restartet, it is important that we send the channel list - // at this place. - vecChannels[iCurChanID].ResetTimeOutCounter(); - vecChannels[iCurChanID].CreateReqChanNameMes(); - -// COMPATIBILITY ISSUE -// since old versions of the llcon software did not implement the channel name -// request message, we have to explicitely send the channel list here -CreateAndSendChanListForAllConChannels(); - } - } - Mutex.unlock(); - - // we do not want the server to be started on a protocol message but only on - // an audio packet -> consider "bIsNotEvaluatedProtocolMessage", too - return bChanOK && ( !bIsNotEvaluatedProtocolMessage ); -} - -void CServer::GetConCliParam ( CVector& vecHostAddresses, - CVector& vecsName, - CVector& veciJitBufNumFrames, - CVector& veciNetwFrameSizeFact ) -{ - CHostAddress InetAddr; - - // init return values - vecHostAddresses.Init ( USED_NUM_CHANNELS ); - vecsName.Init ( USED_NUM_CHANNELS ); - veciJitBufNumFrames.Init ( USED_NUM_CHANNELS ); - veciNetwFrameSizeFact.Init ( USED_NUM_CHANNELS ); - - // check all possible channels - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) - { - if ( vecChannels[i].GetAddress ( InetAddr ) ) - { - // get requested data - vecHostAddresses[i] = InetAddr; - vecsName[i] = vecChannels[i].GetName(); - veciJitBufNumFrames[i] = vecChannels[i].GetSockBufNumFrames(); - veciNetwFrameSizeFact[i] = vecChannels[i].GetNetwFrameSizeFact(); - } - } -} - -void CServer::StartStatusHTMLFileWriting ( const QString& strNewFileName, - const QString& strNewServerNameWithPort ) -{ - // set important parameters - strServerHTMLFileListName = strNewFileName; - strServerNameWithPort = strNewServerNameWithPort; - - // set flag - bWriteStatusHTMLFile = true; - - // write initial file - WriteHTMLChannelList(); -} - -void CServer::WriteHTMLChannelList() -{ - // create channel list - CVector vecChanInfo ( CreateChannelList() ); - - // prepare file and stream - QFile serverFileListFile ( strServerHTMLFileListName ); - if ( !serverFileListFile.open ( QIODevice::WriteOnly | QIODevice::Text ) ) - { - return; - } - - QTextStream streamFileOut ( &serverFileListFile ); - streamFileOut << strServerNameWithPort << endl << "
    " << endl; - - // get the number of connected clients - int iNumConnClients = 0; - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) - { - if ( vecChannels[i].IsConnected() ) - { - iNumConnClients++; - } - } - - // depending on number of connected clients write list - if ( iNumConnClients == 0 ) - { - // no clients are connected -> empty server - streamFileOut << " No client connected" << endl; - } - else - { - // write entry for each connected client - for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) - { - if ( vecChannels[i].IsConnected() ) - { - QString strCurChanName = vecChannels[i].GetName(); - - // if text is empty, show IP address instead - if ( strCurChanName.isEmpty() ) - { - // convert IP address to text and show it, remove last - // digits - strCurChanName = - vecChannels[i].GetAddress().GetIpAddressStringNoLastByte(); - } - - streamFileOut << "
  • " << strCurChanName << "
  • " << endl; - } - } - } - - // finish list - streamFileOut << "
" << endl; -} - -bool CServer::GetTimingStdDev ( double& dCurTiStdDev ) -{ - dCurTiStdDev = 0.0; // init return value - - // only return value if server is active and the actual measurement is - // updated - if ( IsRunning() ) - { - // return the standard deviation - dCurTiStdDev = CycleTimeVariance.GetStdDev(); - - return true; - } - else - { - return false; - } -} - -void CServer::customEvent ( QEvent* Event ) -{ - if ( Event->type() == QEvent::User + 11 ) - { - const int iMessType = ( ( CLlconEvent* ) Event )->iMessType; - - switch ( iMessType ) - { - case MS_PACKET_RECEIVED: - // wake up the server if a packet was received - // if the server is still running, the call to Start() will have - // no effect - Start(); - break; - } - } -} +/******************************************************************************\ + * Copyright (c) 2004-2009 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * 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 + * version. + * + * 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 + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "server.h" + + +// CHighPrecisionTimer implementation ****************************************** +CHighPrecisionTimer::CHighPrecisionTimer() +{ + // add some error checking, the high precision timer implementation only + // supports 128 samples frame size at 48 kHz sampling rate +#if ( SYSTEM_FRAME_SIZE_SAMPLES != 128 ) +# error "Only system frame size of 128 samples is supported by this module" +#endif +#if SYSTEM_SAMPLE_RATE != 48000 +# error "Only a system sample rate of 48 kHz is supported by this module" +#endif + + // Since QT only supports a minimum timer resolution of 1 ms but for our + // llcon server we require a timer interval of 2.333 ms for 128 samples + // frame size at 48 kHz sampling rate. + // To support this interval, we use a timer with 2 ms + // resolution and fire the actual frame timer if the error to the actual + // required interval is minimum. + veciTimeOutIntervals.Init ( 3 ); + + // for 128 sample frame size at 48 kHz sampling rate: + // actual intervals: 0.0 2.666 5.333 8.0 + // quantized to 2 ms: 0 2 6 8 (0) + veciTimeOutIntervals[0] = 0; + veciTimeOutIntervals[1] = 1; + veciTimeOutIntervals[2] = 0; + + // connect timer timeout signal + QObject::connect ( &Timer, SIGNAL ( timeout() ), + this, SLOT ( OnTimer() ) ); +} + +void CHighPrecisionTimer::start() +{ + // reset position pointer and counter + iCurPosInVector = 0; + iIntervalCounter = 0; + + // start internal timer with 2 ms resolution + Timer.start ( 2 ); +} + +void CHighPrecisionTimer::stop() +{ + // stop timer + Timer.stop(); +} + +void CHighPrecisionTimer::OnTimer() +{ + // check if maximum number of high precision timer intervals are + // finished + if ( veciTimeOutIntervals[iCurPosInVector] == iIntervalCounter ) + { + // reset interval counter + iIntervalCounter = 0; + + // go to next position in vector, take care of wrap around + iCurPosInVector++; + if ( iCurPosInVector == veciTimeOutIntervals.Size() ) + { + iCurPosInVector = 0; + } + + // minimum time error to actual required timer interval is reached, + // emit signal for server + emit timeout(); + } + else + { + // next high precision timer interval + iIntervalCounter++; + } +} + + +// CServer implementation ****************************************************** +CServer::CServer ( const QString& strLoggingFileName, + const quint16 iPortNumber, + const QString& strHTMLStatusFileName, + const QString& strHistoryFileName, + const QString& strServerNameForHTMLStatusFile ) : + Socket ( this, iPortNumber ), + bWriteStatusHTMLFile ( false ) +{ + int i; + + // create CELT encoder/decoder for each channel (must be done before + // enabling the channels) + for ( i = 0; i < USED_NUM_CHANNELS; i++ ) + { + // init audio endocder/decoder (mono) + CeltMode[i] = celt_mode_create ( + SYSTEM_SAMPLE_RATE, 1, SYSTEM_FRAME_SIZE_SAMPLES, NULL ); + + CeltEncoder[i] = celt_encoder_create ( CeltMode[i] ); + CeltDecoder[i] = celt_decoder_create ( CeltMode[i] ); + +#ifdef USE_LOW_COMPLEXITY_CELT_ENC + // set encoder low complexity + celt_encoder_ctl(CeltEncoder[i], + CELT_SET_COMPLEXITY_REQUEST, celt_int32_t ( 1 ) ); +#endif + } + + // define colors for chat window identifiers + vstrChatColors.Init ( 6 ); + vstrChatColors[0] = "mediumblue"; + vstrChatColors[1] = "red"; + vstrChatColors[2] = "darkorchid"; + vstrChatColors[3] = "green"; + vstrChatColors[4] = "maroon"; + vstrChatColors[5] = "coral"; + + vecsSendData.Init ( SYSTEM_FRAME_SIZE_SAMPLES ); + + // init moving average buffer for response time evaluation + CycleTimeVariance.Init ( SYSTEM_FRAME_SIZE_SAMPLES, + SYSTEM_SAMPLE_RATE, TIME_MOV_AV_RESPONSE ); + + // enable history graph (if requested) + if ( !strHistoryFileName.isEmpty() ) + { + Logging.EnableHistory ( strHistoryFileName ); + } + + // enable logging (if requested) + if ( !strLoggingFileName.isEmpty() ) + { + // in case the history is enabled and a logging file name is + // given, parse the logging file for old entries which are then + // added in the history on software startup + if ( !strHistoryFileName.isEmpty() ) + { + Logging.ParseLogFile ( strLoggingFileName ); + } + + Logging.Start ( strLoggingFileName ); + } + + // HTML status file writing + if ( !strHTMLStatusFileName.isEmpty() ) + { + QString strCurServerNameForHTMLStatusFile = strServerNameForHTMLStatusFile; + + // if server name is empty, substitude a default name + if ( strCurServerNameForHTMLStatusFile.isEmpty() ) + { + strCurServerNameForHTMLStatusFile = "[server address]"; + } + + // (the static cast to integer of the port number is required so that it + // works correctly under Linux) + StartStatusHTMLFileWriting ( strHTMLStatusFileName, + strCurServerNameForHTMLStatusFile + ":" + + QString().number( static_cast ( iPortNumber ) ) ); + } + + // enable all channels (for the server all channel must be enabled the + // entire life time of the software) + for ( i = 0; i < USED_NUM_CHANNELS; i++ ) + { + vecChannels[i].SetEnable ( true ); + } + + + // connections ------------------------------------------------------------- + // connect timer timeout signal + QObject::connect ( &HighPrecisionTimer, SIGNAL ( timeout() ), + this, SLOT ( OnTimer() ) ); + + // CODE TAG: MAX_NUM_CHANNELS_TAG + // make sure we have MAX_NUM_CHANNELS connections!!! + // send message + QObject::connect ( &vecChannels[0], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh0 ( CVector ) ) ); + QObject::connect ( &vecChannels[1], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh1 ( CVector ) ) ); + QObject::connect ( &vecChannels[2], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh2 ( CVector ) ) ); + QObject::connect ( &vecChannels[3], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh3 ( CVector ) ) ); + QObject::connect ( &vecChannels[4], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh4 ( CVector ) ) ); + QObject::connect ( &vecChannels[5], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh5 ( CVector ) ) ); + QObject::connect ( &vecChannels[6], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh6 ( CVector ) ) ); + QObject::connect ( &vecChannels[7], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh7 ( CVector ) ) ); + QObject::connect ( &vecChannels[8], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh8 ( CVector ) ) ); + QObject::connect ( &vecChannels[9], SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessCh9 ( CVector ) ) ); + + // request jitter buffer size + QObject::connect ( &vecChannels[0], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh0() ) ); + QObject::connect ( &vecChannels[1], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh1() ) ); + QObject::connect ( &vecChannels[2], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh2() ) ); + QObject::connect ( &vecChannels[3], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh3() ) ); + QObject::connect ( &vecChannels[4], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh4() ) ); + QObject::connect ( &vecChannels[5], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh5() ) ); + QObject::connect ( &vecChannels[6], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh6() ) ); + QObject::connect ( &vecChannels[7], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh7() ) ); + QObject::connect ( &vecChannels[8], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh8() ) ); + QObject::connect ( &vecChannels[9], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh9() ) ); + + // request connected clients list + QObject::connect ( &vecChannels[0], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh0() ) ); + QObject::connect ( &vecChannels[1], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh1() ) ); + QObject::connect ( &vecChannels[2], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh2() ) ); + QObject::connect ( &vecChannels[3], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh3() ) ); + QObject::connect ( &vecChannels[4], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh4() ) ); + QObject::connect ( &vecChannels[5], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh5() ) ); + QObject::connect ( &vecChannels[6], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh6() ) ); + QObject::connect ( &vecChannels[7], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh7() ) ); + QObject::connect ( &vecChannels[8], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh8() ) ); + QObject::connect ( &vecChannels[9], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh9() ) ); + + // channel name has changed + QObject::connect ( &vecChannels[0], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh0() ) ); + QObject::connect ( &vecChannels[1], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh1() ) ); + QObject::connect ( &vecChannels[2], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh2() ) ); + QObject::connect ( &vecChannels[3], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh3() ) ); + QObject::connect ( &vecChannels[4], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh4() ) ); + QObject::connect ( &vecChannels[5], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh5() ) ); + QObject::connect ( &vecChannels[6], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh6() ) ); + QObject::connect ( &vecChannels[7], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh7() ) ); + QObject::connect ( &vecChannels[8], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh8() ) ); + QObject::connect ( &vecChannels[9], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh9() ) ); + + // chat text received + QObject::connect ( &vecChannels[0], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh0 ( QString ) ) ); + QObject::connect ( &vecChannels[1], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh1 ( QString ) ) ); + QObject::connect ( &vecChannels[2], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh2 ( QString ) ) ); + QObject::connect ( &vecChannels[3], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh3 ( QString ) ) ); + QObject::connect ( &vecChannels[4], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh4 ( QString ) ) ); + QObject::connect ( &vecChannels[5], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh5 ( QString ) ) ); + QObject::connect ( &vecChannels[6], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh6 ( QString ) ) ); + QObject::connect ( &vecChannels[7], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh7 ( QString ) ) ); + QObject::connect ( &vecChannels[8], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh8 ( QString ) ) ); + QObject::connect ( &vecChannels[9], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh9 ( QString ) ) ); + + // ping message received + QObject::connect ( &vecChannels[0], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh0 ( int ) ) ); + QObject::connect ( &vecChannels[1], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh1 ( int ) ) ); + QObject::connect ( &vecChannels[2], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh2 ( int ) ) ); + QObject::connect ( &vecChannels[3], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh3 ( int ) ) ); + QObject::connect ( &vecChannels[4], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh4 ( int ) ) ); + QObject::connect ( &vecChannels[5], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh5 ( int ) ) ); + QObject::connect ( &vecChannels[6], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh6 ( int ) ) ); + QObject::connect ( &vecChannels[7], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh7 ( int ) ) ); + QObject::connect ( &vecChannels[8], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh8 ( int ) ) ); + QObject::connect ( &vecChannels[9], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh9 ( int ) ) ); +} + +void CServer::OnSendProtMessage ( int iChID, CVector vecMessage ) +{ + // the protocol queries me to call the function to send the message + // send it through the network + Socket.SendPacket ( vecMessage, vecChannels[iChID].GetAddress() ); +} + +void CServer::Start() +{ + // only start if not already running + if ( !IsRunning() ) + { + // start timer + HighPrecisionTimer.start(); + + // init time for response time evaluation + CycleTimeVariance.Reset(); + } +} + +void CServer::Stop() +{ + // stop timer + HighPrecisionTimer.stop(); + + // logging (add "server stopped" logging entry) + Logging.AddServerStopped(); +} + +void CServer::OnTimer() +{ + int i, j; + + CVector vecChanID; + CVector > vecvecdGains; + CVector > vecvecsData; + + // Get data from all connected clients ------------------------------------- + bool bChannelIsNowDisconnected = false; + + // Make put and get calls thread safe. Do not forget to unlock mutex + // afterwards! + Mutex.lock(); + { + // first, get number and IDs of connected channels + vecChanID.Init ( 0 ); + for ( i = 0; i < USED_NUM_CHANNELS; i++ ) + { + if ( vecChannels[i].IsConnected() ) + { + // add ID and data + vecChanID.Add ( i ); + } + } + + // process connected channels + const int iNumCurConnChan = vecChanID.Size(); + + // init temporary vectors + vecvecdGains.Init ( iNumCurConnChan ); + vecvecsData.Init ( iNumCurConnChan ); + + for ( i = 0; i < iNumCurConnChan; i++ ) + { + // get actual ID of current channel + const int iCurChanID = vecChanID[i]; + + // init vectors storing information of all channels + vecvecdGains[i].Init ( iNumCurConnChan ); + vecvecsData[i].Init ( SYSTEM_FRAME_SIZE_SAMPLES ); + + // get gains of all connected channels + for ( j = 0; j < iNumCurConnChan; j++ ) + { + // The second index of "vecvecdGains" does not represent + // the channel ID! Therefore we have to use "vecChanID" to + // query the IDs of the currently connected channels + vecvecdGains[i][j] = + vecChannels[iCurChanID].GetGain( vecChanID[j] ); + } + + // get current number of CELT coded bytes + const int iCeltNumCodedBytes = + vecChannels[iCurChanID].GetNetwFrameSize(); + + // init temporal data vector and clear input buffers + CVector vecbyData ( iCeltNumCodedBytes ); + + // get data + const EGetDataStat eGetStat = + vecChannels[iCurChanID].GetData ( vecbyData ); + + // if channel was just disconnected, set flag that connected + // client list is sent to all other clients + if ( eGetStat == GS_CHAN_NOW_DISCONNECTED ) + { + bChannelIsNowDisconnected = true; + } + + // CELT decode received data stream + if ( eGetStat == GS_BUFFER_OK ) + { + celt_decode ( CeltDecoder[iCurChanID], + &vecbyData[0], + iCeltNumCodedBytes, + &vecvecsData[i][0] ); + } + else + { + // lost packet + celt_decode ( CeltDecoder[iCurChanID], + NULL, + 0, + &vecvecsData[i][0] ); + } + + // send message for get status (for GUI) + if ( eGetStat == GS_BUFFER_OK ) + { + PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN, iCurChanID ); + } + else + { + PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED, iCurChanID ); + } + } + + // a channel is now disconnected, take action on it + if ( bChannelIsNowDisconnected ) + { + // update channel list for all currently connected clients + CreateAndSendChanListForAllConChannels(); + } + } + Mutex.unlock(); // release mutex + + + // Process data ------------------------------------------------------------ + const int iNumClients = vecChanID.Size(); + + // Check if at least one client is connected. If not, stop server until + // one client is connected. + if ( iNumClients != 0 ) + { + for ( int i = 0; i < iNumClients; i++ ) + { + // get actual ID of current channel + const int iCurChanID = vecChanID[i]; + + // generate a sparate mix for each channel + // actual processing of audio data -> mix + vecsSendData = ProcessData ( vecvecsData, vecvecdGains[i] ); + + // get current number of CELT coded bytes + const int iCeltNumCodedBytes = + vecChannels[iCurChanID].GetNetwFrameSize(); + + // CELT encoding + CVector vecCeltData ( iCeltNumCodedBytes ); + + celt_encode ( CeltEncoder[iCurChanID], + &vecsSendData[0], + NULL, + &vecCeltData[0], + iCeltNumCodedBytes ); + + // send separate mix to current clients + Socket.SendPacket ( + vecChannels[iCurChanID].PrepSendPacket ( vecCeltData ), + vecChannels[iCurChanID].GetAddress() ); + } + } + else + { + // Disable server if no clients are connected. In this case the server + // does not consume any significant CPU when no client is connected. + Stop(); + } + + // update response time measurement + CycleTimeVariance.Update(); +} + +CVector CServer::ProcessData ( CVector >& vecvecsData, + CVector& vecdGains ) +{ + int i; + + // init return vector with zeros since we mix all channels on that vector + +// TODO speed optimization: avoid using the zero vector, use the first valid +// data vector for initialization instead (take care of gain of this data, too!) + + CVector vecsOutData ( SYSTEM_FRAME_SIZE_SAMPLES, 0 ); + + const int iNumClients = vecvecsData.Size(); + + // mix all audio data from all clients together + for ( int j = 0; j < iNumClients; j++ ) + { + // if channel gain is 1, avoid multiplication for speed optimization + if ( vecdGains[j] == static_cast ( 1.0 ) ) + { + for ( i = 0; i < SYSTEM_FRAME_SIZE_SAMPLES; i++ ) + { + vecsOutData[i] = + Double2Short ( vecsOutData[i] + vecvecsData[j][i] ); + } + } + else + { + for ( i = 0; i < SYSTEM_FRAME_SIZE_SAMPLES; i++ ) + { + vecsOutData[i] = + Double2Short ( vecsOutData[i] + + vecvecsData[j][i] * vecdGains[j] ); + } + } + } + + return vecsOutData; +} + +CVector CServer::CreateChannelList() +{ + CVector vecChanInfo ( 0 ); + + // look for free channels + for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + { + if ( vecChannels[i].IsConnected() ) + { + // append channel ID, IP address and channel name to storing vectors + vecChanInfo.Add ( CChannelShortInfo ( + i, // ID + vecChannels[i].GetAddress().InetAddr.toIPv4Address(), // IP address + vecChannels[i].GetName() /* name */ ) ); + } + } + + return vecChanInfo; +} + +void CServer::CreateAndSendChanListForAllConChannels() +{ + // create channel list + CVector vecChanInfo ( CreateChannelList() ); + + // now send connected channels list to all connected clients + for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + { + if ( vecChannels[i].IsConnected() ) + { + // send message + vecChannels[i].CreateConClientListMes ( vecChanInfo ); + } + } + + // create status HTML file if enabled + if ( bWriteStatusHTMLFile ) + { + WriteHTMLChannelList(); + } +} + +void CServer::CreateAndSendChanListForThisChan ( const int iCurChanID ) +{ + // create channel list + CVector vecChanInfo ( CreateChannelList() ); + + // now send connected channels list to the channel with the ID "iCurChanID" + vecChannels[iCurChanID].CreateConClientListMes ( vecChanInfo ); +} + +void CServer::CreateAndSendChatTextForAllConChannels ( const int iCurChanID, + const QString& strChatText ) +{ + // create message which is sent to all connected clients ------------------- + // get client name, if name is empty, use IP address instead + QString ChanName = vecChannels[iCurChanID].GetName(); + if ( ChanName.isEmpty() ) + { + // convert IP address to text and show it + ChanName = + vecChannels[iCurChanID].GetAddress().GetIpAddressStringNoLastByte(); + } + + // add time and name of the client at the beginning of the message text and + // use different colors + QString sCurColor = vstrChatColors[iCurChanID % vstrChatColors.Size()]; + const QString strActualMessageText = + "(" + + QTime::currentTime().toString ( "hh:mm:ss AP" ) + ") " + ChanName + + " " + strChatText; + + + // send chat text to all connected clients --------------------------------- + for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + { + if ( vecChannels[i].IsConnected() ) + { + // send message + vecChannels[i].CreateChatTextMes ( strActualMessageText ); + } + } +} + +int CServer::GetFreeChan() +{ + // look for a free channel + for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + { + if ( !vecChannels[i].IsConnected() ) + { + return i; + } + } + + // no free channel found, return invalid ID + return INVALID_CHANNEL_ID; +} + +int CServer::CheckAddr ( const CHostAddress& Addr ) +{ + CHostAddress InetAddr; + + // check for all possible channels if IP is already in use + for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + { + if ( vecChannels[i].IsConnected() ) + { + if ( vecChannels[i].GetAddress ( InetAddr ) ) + { + // IP found, return channel number + if ( InetAddr == Addr ) + { + return i; + } + } + } + } + + // IP not found, return invalid ID + return INVALID_CHANNEL_ID; +} + +bool CServer::PutData ( const CVector& vecbyRecBuf, + const int iNumBytesRead, + const CHostAddress& HostAdr ) +{ + bool bChanOK = true; // init with ok, might be overwritten + bool bNewChannelReserved = false; + bool bIsNotEvaluatedProtocolMessage = false; + + Mutex.lock(); + { + // Get channel ID ------------------------------------------------------ + // check address + int iCurChanID = CheckAddr ( HostAdr ); + + if ( iCurChanID == INVALID_CHANNEL_ID ) + { + // a new client is calling, look for free channel + iCurChanID = GetFreeChan(); + + if ( iCurChanID != INVALID_CHANNEL_ID ) + { + // initialize current channel by storing the calling host + // address + vecChannels[iCurChanID].SetAddress ( HostAdr ); + + // reset channel name + vecChannels[iCurChanID].ResetName(); + + // reset the channel gains of current channel, at the same + // time reset gains of this channel ID for all other channels + for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + { + vecChannels[iCurChanID].SetGain ( i, (double) 1.0 ); + + // other channels (we do not distinguish the case if + // i == iCurChanID for simplicity) + vecChannels[i].SetGain ( iCurChanID, (double) 1.0 ); + } + + // set flag for new reserved channel + bNewChannelReserved = true; + } + else + { + // no free channel available + bChanOK = false; + + // create and send "server full" message + +// TODO problem: if channel is not officially connected, we cannot send messages + + } + } + + + // Put received data in jitter buffer ---------------------------------- + if ( bChanOK ) + { + // put packet in socket buffer + switch ( vecChannels[iCurChanID].PutData ( vecbyRecBuf, iNumBytesRead ) ) + { + case PS_AUDIO_OK: + PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_GREEN, iCurChanID ); + break; + + case PS_AUDIO_ERR: + PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_RED, iCurChanID ); + break; + + case PS_PROT_ERR: + PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_YELLOW, iCurChanID ); + break; + + case PS_PROT_OK_MESS_NOT_EVALUATED: + bIsNotEvaluatedProtocolMessage = true; // set flag + break; + } + } + + // act on new channel connection + if ( bNewChannelReserved && ( !bIsNotEvaluatedProtocolMessage ) ) + { + // logging of new connected channel + Logging.AddNewConnection ( HostAdr.InetAddr ); + + // A new client connected to the server, the channel list + // at all clients have to be updated. This is done by sending + // a channel name request to the client which causes a channel + // name message to be transmitted to the server. If the server + // receives this message, the channel list will be automatically + // updated (implicitely). + // To make sure the protocol message is transmitted, the channel + // first has to be marked as connected. + // + // Usually it is not required to send the channel list to the + // client currently connecting since it automatically requests + // the channel list on a new connection (as a result, he will + // usually get the list twice which has no impact on functionality + // but will only increase the network load a tiny little bit). But + // in case the client thinks he is still connected but the server + // was restartet, it is important that we send the channel list + // at this place. + vecChannels[iCurChanID].ResetTimeOutCounter(); + vecChannels[iCurChanID].CreateReqChanNameMes(); + +// COMPATIBILITY ISSUE +// since old versions of the llcon software did not implement the channel name +// request message, we have to explicitely send the channel list here +CreateAndSendChanListForAllConChannels(); + } + } + Mutex.unlock(); + + // we do not want the server to be started on a protocol message but only on + // an audio packet -> consider "bIsNotEvaluatedProtocolMessage", too + return bChanOK && ( !bIsNotEvaluatedProtocolMessage ); +} + +void CServer::GetConCliParam ( CVector& vecHostAddresses, + CVector& vecsName, + CVector& veciJitBufNumFrames, + CVector& veciNetwFrameSizeFact ) +{ + CHostAddress InetAddr; + + // init return values + vecHostAddresses.Init ( USED_NUM_CHANNELS ); + vecsName.Init ( USED_NUM_CHANNELS ); + veciJitBufNumFrames.Init ( USED_NUM_CHANNELS ); + veciNetwFrameSizeFact.Init ( USED_NUM_CHANNELS ); + + // check all possible channels + for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + { + if ( vecChannels[i].GetAddress ( InetAddr ) ) + { + // get requested data + vecHostAddresses[i] = InetAddr; + vecsName[i] = vecChannels[i].GetName(); + veciJitBufNumFrames[i] = vecChannels[i].GetSockBufNumFrames(); + veciNetwFrameSizeFact[i] = vecChannels[i].GetNetwFrameSizeFact(); + } + } +} + +void CServer::StartStatusHTMLFileWriting ( const QString& strNewFileName, + const QString& strNewServerNameWithPort ) +{ + // set important parameters + strServerHTMLFileListName = strNewFileName; + strServerNameWithPort = strNewServerNameWithPort; + + // set flag + bWriteStatusHTMLFile = true; + + // write initial file + WriteHTMLChannelList(); +} + +void CServer::WriteHTMLChannelList() +{ + // create channel list + CVector vecChanInfo ( CreateChannelList() ); + + // prepare file and stream + QFile serverFileListFile ( strServerHTMLFileListName ); + if ( !serverFileListFile.open ( QIODevice::WriteOnly | QIODevice::Text ) ) + { + return; + } + + QTextStream streamFileOut ( &serverFileListFile ); + streamFileOut << strServerNameWithPort << endl << "
    " << endl; + + // get the number of connected clients + int iNumConnClients = 0; + for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + { + if ( vecChannels[i].IsConnected() ) + { + iNumConnClients++; + } + } + + // depending on number of connected clients write list + if ( iNumConnClients == 0 ) + { + // no clients are connected -> empty server + streamFileOut << " No client connected" << endl; + } + else + { + // write entry for each connected client + for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) + { + if ( vecChannels[i].IsConnected() ) + { + QString strCurChanName = vecChannels[i].GetName(); + + // if text is empty, show IP address instead + if ( strCurChanName.isEmpty() ) + { + // convert IP address to text and show it, remove last + // digits + strCurChanName = + vecChannels[i].GetAddress().GetIpAddressStringNoLastByte(); + } + + streamFileOut << "
  • " << strCurChanName << "
  • " << endl; + } + } + } + + // finish list + streamFileOut << "
" << endl; +} + +bool CServer::GetTimingStdDev ( double& dCurTiStdDev ) +{ + dCurTiStdDev = 0.0; // init return value + + // only return value if server is active and the actual measurement is + // updated + if ( IsRunning() ) + { + // return the standard deviation + dCurTiStdDev = CycleTimeVariance.GetStdDev(); + + return true; + } + else + { + return false; + } +} + +void CServer::customEvent ( QEvent* Event ) +{ + if ( Event->type() == QEvent::User + 11 ) + { + const int iMessType = ( ( CLlconEvent* ) Event )->iMessType; + + switch ( iMessType ) + { + case MS_PACKET_RECEIVED: + // wake up the server if a packet was received + // if the server is still running, the call to Start() will have + // no effect + Start(); + break; + } + } +} diff --git a/src/serverlogging.cpp b/src/serverlogging.cpp index 6dc3225b..a9efeb72 100755 --- a/src/serverlogging.cpp +++ b/src/serverlogging.cpp @@ -1,452 +1,452 @@ -/******************************************************************************\ -* Copyright (c) 2004-2009 -* -* Author(s): -* Volker Fischer -* -****************************************************************************** -* -* 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 -* version. -* -* 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 -* 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., -* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -\******************************************************************************/ - -#include "serverlogging.h" - - -/* Implementation *************************************************************/ -CHistoryGraph::CHistoryGraph() : - sFileName ( "" ), - bDoHistory ( false ), - vHistoryDataFifo ( NUM_ITEMS_HISTORY ), - PlotCanvasRect ( 0, 0, 600, 450 ), // defines total size of graph - iNumTicksX ( 0 ), // just an initialization value, will be overwritten - iYAxisStart ( 0 ), - iYAxisEnd ( 24 ), - iNumTicksY ( 5 ), - iGridFrameOffset ( 10 ), - iGridWidthWeekend ( 3 ), // should be odd value - iTextOffsetToGrid ( 3 ), - iXAxisTextHeight ( 22 ), - iMarkerSizeNewCon ( 11 ), - iMarkerSizeServSt ( 9 ), - AxisFont ( "Arial", 12 ), - iTextOffsetX ( 18 ), - PlotBackgroundColor ( Qt::white ), // background - PlotFrameColor ( Qt::black ), // frame - PlotGridColor ( Qt::gray ), // grid - PlotTextColor ( Qt::black ), // text - PlotMarkerNewColor ( Qt::darkCyan ), // marker for new connection - PlotMarkerNewLocalColor ( Qt::blue ), // marker for new local connection - PlotMarkerStopColor ( Qt::red ), // marker for server stop - PlotPixmap ( 1, 1, QImage::Format_RGB32 ) -{ - // generate plot grid frame rectangle - PlotGridFrame.setRect ( PlotCanvasRect.x() + iGridFrameOffset, - PlotCanvasRect.y() + iGridFrameOffset, - PlotCanvasRect.width() - 2 * iGridFrameOffset, - PlotCanvasRect.height() - 2 * iGridFrameOffset - iXAxisTextHeight ); - - // scale pixmap to correct size - PlotPixmap = PlotPixmap.scaled ( - PlotCanvasRect.width(), PlotCanvasRect.height() ); - - // connections - QObject::connect ( &TimerDailyUpdate, SIGNAL ( timeout() ), - this, SLOT ( OnTimerDailyUpdate() ) ); -} - -void CHistoryGraph::Start ( const QString& sNewFileName ) -{ - if ( !sNewFileName.isEmpty() ) - { - // save file name - sFileName = sNewFileName; - - // set enable flag - bDoHistory = true; - - // enable timer (update once a day) - TimerDailyUpdate.start ( 3600000 * 24 ); - - // initial update (empty graph) - Update(); - } -} - -void CHistoryGraph::DrawFrame ( const int iNewNumTicksX ) -{ - int i; - - // store number of x-axis ticks (number of days we want to draw - // the history for - iNumTicksX = iNewNumTicksX; - - // clear base pixmap for new plotting - PlotPixmap.fill ( PlotBackgroundColor.rgb() ); // fill background - - // create painter - QPainter PlotPainter ( &PlotPixmap ); - - - // create actual plot region (grid frame) ---------------------------------- - PlotPainter.setPen ( PlotFrameColor ); - PlotPainter.drawRect ( PlotGridFrame ); - - // calculate step for x-axis ticks so that we get the desired number of - // ticks -> 5 ticks - -// TODO the following equation does not work as expected but results are acceptable - - // we want to have "floor ( iNumTicksX / 5 )" which is the same as - // "iNumTicksX / 5" since "iNumTicksX" is an integer variable - const int iXAxisTickStep = iNumTicksX / 5 + 1; - - // grid (ticks) for x-axis - dXSpace = static_cast ( PlotGridFrame.width() ) / ( iNumTicksX + 1 ); - for ( i = 0; i < iNumTicksX; i++ ) - { - int iBottomExtraTickLen = 0; - const int iCurX = PlotGridFrame.x() + static_cast ( dXSpace * ( i + 1 ) ); - const QDate curXAxisDate = curDate.addDays ( i - iNumTicksX + 1 ); - - // text (print only every "iXAxisTickStep" tick) - if ( !( i % iXAxisTickStep ) ) - { - PlotPainter.setPen ( PlotTextColor ); - PlotPainter.setFont ( AxisFont ); - PlotPainter.drawText ( - QPoint ( iCurX - iTextOffsetX, - PlotGridFrame.bottom() + iXAxisTextHeight + iTextOffsetToGrid ), - curXAxisDate.toString ( "dd.MM." ) ); - - iBottomExtraTickLen = 5; - } - - // regular grid - PlotPainter.setPen ( PlotGridColor ); - PlotPainter.drawLine ( iCurX, 1 + PlotGridFrame.y(), - iCurX, PlotGridFrame.bottom() + iBottomExtraTickLen ); - - // different grid width for weekends (overwrite regular grid) - if ( ( curXAxisDate.dayOfWeek() == 6 ) || // check for Saturday - ( curXAxisDate.dayOfWeek() == 7 ) ) // check for Sunday - { - const int iGridWidthWeekendHalf = iGridWidthWeekend / 2; - - PlotPainter.setPen ( QPen ( PlotGridColor, iGridWidthWeekend ) ); - PlotPainter.drawLine ( iCurX, 1 + PlotGridFrame.y() + iGridWidthWeekendHalf, - iCurX, PlotGridFrame.bottom() - iGridWidthWeekendHalf ); - } - } - - // grid (ticks) for y-axis, draw iNumTicksY - 2 grid lines and - // iNumTicksY - 1 text labels (the lowest grid line is the grid frame) - iYSpace = PlotGridFrame.height() / ( iNumTicksY - 1 ); - for ( i = 0; i < ( iNumTicksY - 1 ); i++ ) - { - const int iCurY = PlotGridFrame.y() + iYSpace * ( i + 1 ); - - // text - PlotPainter.setPen ( PlotTextColor ); - PlotPainter.setFont ( AxisFont ); - PlotPainter.drawText ( QPoint ( - PlotGridFrame.x() + iTextOffsetToGrid, - iCurY - iTextOffsetToGrid ), - QString ( "%1:00" ).arg ( - ( iYAxisEnd - iYAxisStart ) / ( iNumTicksY - 1 ) * - ( ( iNumTicksY - 2 ) - i ) ) ); - - // grid (do not overwrite frame) - if ( i < ( iNumTicksY - 2 ) ) - { - PlotPainter.setPen ( PlotGridColor ); - PlotPainter.drawLine ( PlotGridFrame.x(), iCurY, - PlotGridFrame.right(), iCurY ); - } - } -} - -void CHistoryGraph::AddMarker ( const SHistoryData& curHistoryData ) -{ - // calculate x-axis offset (difference of days compared to - // current date) - const int iXAxisOffs = - curDate.daysTo ( curHistoryData.DateTime.date() ); - - // check range, if out of range, do not plot anything - if ( -iXAxisOffs > ( iNumTicksX - 1 ) ) - { - return; - } - - // calculate y-axis offset (consider hours and minutes) - const double dYAxisOffs = 24 - curHistoryData.DateTime.time().hour() - - static_cast ( curHistoryData.DateTime.time().minute() ) / 60; - - // calculate the actual point in the graph (in pixels) - const QPoint curPoint ( - PlotGridFrame.x() + static_cast ( dXSpace * ( iNumTicksX + iXAxisOffs ) ), - PlotGridFrame.y() + static_cast ( static_cast ( - PlotGridFrame.height() ) / ( iYAxisEnd - iYAxisStart ) * dYAxisOffs ) ); - - // create painter for plot - QPainter PlotPainter ( &PlotPixmap ); - - // we use different markers for new connection and server stop items - switch ( curHistoryData.Type ) - { - case HIT_SERVER_STOP: - // filled circle marker - PlotPainter.setPen ( QPen ( QBrush ( PlotMarkerStopColor ), - iMarkerSizeServSt, Qt::SolidLine, Qt::RoundCap ) ); - break; - - case HIT_LOCAL_CONNECTION: - // filled square marker - PlotPainter.setPen ( QPen ( QBrush ( PlotMarkerNewLocalColor ), - iMarkerSizeNewCon ) ); - break; - - case HIT_REMOTE_CONNECTION: - // filled square marker - PlotPainter.setPen ( QPen ( QBrush ( PlotMarkerNewColor ), - iMarkerSizeNewCon ) ); - break; - } - PlotPainter.drawPoint ( curPoint ); -} - -void CHistoryGraph::Save ( const QString sFileName ) -{ - // save plot as a file - PlotPixmap.save ( sFileName, "JPG", 90 ); -} - -void CHistoryGraph::Add ( const QDateTime& newDateTime, - const QHostAddress ClientInetAddr ) -{ - if ( bDoHistory ) - { - // add element to history, distinguish between a local connection - // and a remote connection - if ( ( ClientInetAddr == QHostAddress ( "127.0.0.1" ) ) || - ( ClientInetAddr.toString().left ( 7 ).compare ( "192.168" ) == 0 ) ) - { - // local connection - Add ( newDateTime, CHistoryGraph::HIT_LOCAL_CONNECTION ); - } - else - { - // remote connection - Add ( newDateTime, CHistoryGraph::HIT_REMOTE_CONNECTION ); - } - } -} - -void CHistoryGraph::Add ( const QDateTime& newDateTime, - const EHistoryItemType curType ) -{ - if ( bDoHistory ) - { - // create and add new element in FIFO - SHistoryData curHistoryData; - curHistoryData.DateTime = newDateTime; - curHistoryData.Type = curType; - - vHistoryDataFifo.Add ( curHistoryData ); - } -} - -void CHistoryGraph::Update() -{ - if ( bDoHistory ) - { - int i; - - // store current date for reference - curDate = QDate::currentDate(); - - // get oldest date in history - QDate oldestDate = curDate.addDays ( 1 ); // one day in the future - const int iNumItemsForHistory = vHistoryDataFifo.Size(); - for ( i = 0; i < iNumItemsForHistory; i++ ) - { - // only use valid dates - if ( vHistoryDataFifo[i].DateTime.date().isValid() ) - { - if ( vHistoryDataFifo[i].DateTime.date() < oldestDate ) - { - oldestDate = vHistoryDataFifo[i].DateTime.date(); - } - } - } - const int iNumDaysInHistory = -curDate.daysTo ( oldestDate ) + 1; - - // draw frame of the graph - DrawFrame ( iNumDaysInHistory ); - - // add markers - for ( i = 0; i < iNumItemsForHistory; i++ ) - { - AddMarker ( vHistoryDataFifo[i] ); - } - - // save graph as picture in file - Save ( sFileName ); - } -} - - -// Server logging -------------------------------------------------------------- -CServerLogging::~CServerLogging() -{ - // close logging file of open - if ( File.isOpen() ) - { - File.close(); - } -} - -void CServerLogging::Start ( const QString& strLoggingFileName ) -{ - // open file - File.setFileName ( strLoggingFileName ); - if ( File.open ( QIODevice::Append | QIODevice::Text ) ) - { - bDoLogging = true; - } -} - -void CServerLogging::EnableHistory ( const QString& strHistoryFileName ) -{ - HistoryGraph.Start ( strHistoryFileName ); -} - -void CServerLogging::AddNewConnection ( const QHostAddress& ClientInetAddr ) -{ - // logging of new connected channel - const QString strLogStr = CurTimeDatetoLogString() + ", " + - ClientInetAddr.toString() + ", connected"; - -#ifndef _WIN32 - QTextStream tsConsoloeStream ( stdout ); - tsConsoloeStream << strLogStr << endl; // on console -#endif - *this << strLogStr; // in log file - - // add element to history - HistoryGraph.Add ( QDateTime::currentDateTime(), ClientInetAddr ); -} - -void CServerLogging::AddServerStopped() -{ - const QString strLogStr = CurTimeDatetoLogString() + ",, server stopped " - "-------------------------------------"; - -#ifndef _WIN32 - QTextStream tsConsoloeStream ( stdout ); - tsConsoloeStream << strLogStr << endl; // on console -#endif - *this << strLogStr; // in log file - - // add element to history and update on server stop - HistoryGraph.Add ( QDateTime::currentDateTime(), - CHistoryGraph::HIT_SERVER_STOP ); - - HistoryGraph.Update(); -} - -void CServerLogging::operator<< ( const QString& sNewStr ) -{ - if ( bDoLogging ) - { - // append new line in logging file - QTextStream out ( &File ); - out << sNewStr << endl; - File.flush(); - } -} - -void CServerLogging::ParseLogFile ( const QString& strFileName ) -{ - // open file for reading - QFile LogFile ( strFileName ); - LogFile.open ( QIODevice::ReadOnly | QIODevice::Text ); - - QTextStream inStream ( &LogFile ); - - // read all content from file - while ( !inStream.atEnd() ) - { - // get a new line from log file - QString strCurLine = inStream.readLine(); - - // parse log file line - QStringList strlistCurLine = strCurLine.split( "," ); - - // check number of separated strings condition - if ( strlistCurLine.size() == 4 ) - { - // first entry - QDate curDate = - QDate::fromString ( strlistCurLine.at ( 0 ).trimmed(), - "d.M.yyyy" ); - - // second entry - QTime curTime = - QTime::fromString ( strlistCurLine.at ( 1 ).trimmed(), - "hh:mm:ss" ); - - if ( curDate.isValid() && curTime.isValid() ) - { - QDateTime curDateTime ( curDate, curTime ); - - // check if server stop or new client connection - QString strAddress = strlistCurLine.at ( 2 ).trimmed(); - if ( strAddress.isEmpty() ) - { - // server stop - HistoryGraph.Add ( curDateTime, - CHistoryGraph::HIT_SERVER_STOP ); - } - else - { - QHostAddress curAddress; - - // third entry is IP address - if ( curAddress.setAddress ( strlistCurLine.at ( 2 ).trimmed() ) ) - { - // new client connection - HistoryGraph.Add ( curDateTime, curAddress ); - } - } - } - } - } - - HistoryGraph.Update(); -} - -QString CServerLogging::CurTimeDatetoLogString() -{ - // time and date to string conversion - const QDateTime curDateTime = QDateTime::currentDateTime(); - - // format date and time output according to "3.9.2006, 11:38:08" - return QString().setNum ( curDateTime.date().day() ) + "." + - QString().setNum ( curDateTime.date().month() ) + "." + - QString().setNum ( curDateTime.date().year() ) + ", " + - curDateTime.time().toString(); -} +/******************************************************************************\ +* Copyright (c) 2004-2009 +* +* Author(s): +* Volker Fischer +* +****************************************************************************** +* +* 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 +* version. +* +* 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 +* 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., +* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +\******************************************************************************/ + +#include "serverlogging.h" + + +/* Implementation *************************************************************/ +CHistoryGraph::CHistoryGraph() : + sFileName ( "" ), + bDoHistory ( false ), + vHistoryDataFifo ( NUM_ITEMS_HISTORY ), + PlotCanvasRect ( 0, 0, 600, 450 ), // defines total size of graph + iNumTicksX ( 0 ), // just an initialization value, will be overwritten + iYAxisStart ( 0 ), + iYAxisEnd ( 24 ), + iNumTicksY ( 5 ), + iGridFrameOffset ( 10 ), + iGridWidthWeekend ( 3 ), // should be odd value + iTextOffsetToGrid ( 3 ), + iXAxisTextHeight ( 22 ), + iMarkerSizeNewCon ( 11 ), + iMarkerSizeServSt ( 9 ), + AxisFont ( "Arial", 12 ), + iTextOffsetX ( 18 ), + PlotBackgroundColor ( Qt::white ), // background + PlotFrameColor ( Qt::black ), // frame + PlotGridColor ( Qt::gray ), // grid + PlotTextColor ( Qt::black ), // text + PlotMarkerNewColor ( Qt::darkCyan ), // marker for new connection + PlotMarkerNewLocalColor ( Qt::blue ), // marker for new local connection + PlotMarkerStopColor ( Qt::red ), // marker for server stop + PlotPixmap ( 1, 1, QImage::Format_RGB32 ) +{ + // generate plot grid frame rectangle + PlotGridFrame.setRect ( PlotCanvasRect.x() + iGridFrameOffset, + PlotCanvasRect.y() + iGridFrameOffset, + PlotCanvasRect.width() - 2 * iGridFrameOffset, + PlotCanvasRect.height() - 2 * iGridFrameOffset - iXAxisTextHeight ); + + // scale pixmap to correct size + PlotPixmap = PlotPixmap.scaled ( + PlotCanvasRect.width(), PlotCanvasRect.height() ); + + // connections + QObject::connect ( &TimerDailyUpdate, SIGNAL ( timeout() ), + this, SLOT ( OnTimerDailyUpdate() ) ); +} + +void CHistoryGraph::Start ( const QString& sNewFileName ) +{ + if ( !sNewFileName.isEmpty() ) + { + // save file name + sFileName = sNewFileName; + + // set enable flag + bDoHistory = true; + + // enable timer (update once a day) + TimerDailyUpdate.start ( 3600000 * 24 ); + + // initial update (empty graph) + Update(); + } +} + +void CHistoryGraph::DrawFrame ( const int iNewNumTicksX ) +{ + int i; + + // store number of x-axis ticks (number of days we want to draw + // the history for + iNumTicksX = iNewNumTicksX; + + // clear base pixmap for new plotting + PlotPixmap.fill ( PlotBackgroundColor.rgb() ); // fill background + + // create painter + QPainter PlotPainter ( &PlotPixmap ); + + + // create actual plot region (grid frame) ---------------------------------- + PlotPainter.setPen ( PlotFrameColor ); + PlotPainter.drawRect ( PlotGridFrame ); + + // calculate step for x-axis ticks so that we get the desired number of + // ticks -> 5 ticks + +// TODO the following equation does not work as expected but results are acceptable + + // we want to have "floor ( iNumTicksX / 5 )" which is the same as + // "iNumTicksX / 5" since "iNumTicksX" is an integer variable + const int iXAxisTickStep = iNumTicksX / 5 + 1; + + // grid (ticks) for x-axis + dXSpace = static_cast ( PlotGridFrame.width() ) / ( iNumTicksX + 1 ); + for ( i = 0; i < iNumTicksX; i++ ) + { + int iBottomExtraTickLen = 0; + const int iCurX = PlotGridFrame.x() + static_cast ( dXSpace * ( i + 1 ) ); + const QDate curXAxisDate = curDate.addDays ( i - iNumTicksX + 1 ); + + // text (print only every "iXAxisTickStep" tick) + if ( !( i % iXAxisTickStep ) ) + { + PlotPainter.setPen ( PlotTextColor ); + PlotPainter.setFont ( AxisFont ); + PlotPainter.drawText ( + QPoint ( iCurX - iTextOffsetX, + PlotGridFrame.bottom() + iXAxisTextHeight + iTextOffsetToGrid ), + curXAxisDate.toString ( "dd.MM." ) ); + + iBottomExtraTickLen = 5; + } + + // regular grid + PlotPainter.setPen ( PlotGridColor ); + PlotPainter.drawLine ( iCurX, 1 + PlotGridFrame.y(), + iCurX, PlotGridFrame.bottom() + iBottomExtraTickLen ); + + // different grid width for weekends (overwrite regular grid) + if ( ( curXAxisDate.dayOfWeek() == 6 ) || // check for Saturday + ( curXAxisDate.dayOfWeek() == 7 ) ) // check for Sunday + { + const int iGridWidthWeekendHalf = iGridWidthWeekend / 2; + + PlotPainter.setPen ( QPen ( PlotGridColor, iGridWidthWeekend ) ); + PlotPainter.drawLine ( iCurX, 1 + PlotGridFrame.y() + iGridWidthWeekendHalf, + iCurX, PlotGridFrame.bottom() - iGridWidthWeekendHalf ); + } + } + + // grid (ticks) for y-axis, draw iNumTicksY - 2 grid lines and + // iNumTicksY - 1 text labels (the lowest grid line is the grid frame) + iYSpace = PlotGridFrame.height() / ( iNumTicksY - 1 ); + for ( i = 0; i < ( iNumTicksY - 1 ); i++ ) + { + const int iCurY = PlotGridFrame.y() + iYSpace * ( i + 1 ); + + // text + PlotPainter.setPen ( PlotTextColor ); + PlotPainter.setFont ( AxisFont ); + PlotPainter.drawText ( QPoint ( + PlotGridFrame.x() + iTextOffsetToGrid, + iCurY - iTextOffsetToGrid ), + QString ( "%1:00" ).arg ( + ( iYAxisEnd - iYAxisStart ) / ( iNumTicksY - 1 ) * + ( ( iNumTicksY - 2 ) - i ) ) ); + + // grid (do not overwrite frame) + if ( i < ( iNumTicksY - 2 ) ) + { + PlotPainter.setPen ( PlotGridColor ); + PlotPainter.drawLine ( PlotGridFrame.x(), iCurY, + PlotGridFrame.right(), iCurY ); + } + } +} + +void CHistoryGraph::AddMarker ( const SHistoryData& curHistoryData ) +{ + // calculate x-axis offset (difference of days compared to + // current date) + const int iXAxisOffs = + curDate.daysTo ( curHistoryData.DateTime.date() ); + + // check range, if out of range, do not plot anything + if ( -iXAxisOffs > ( iNumTicksX - 1 ) ) + { + return; + } + + // calculate y-axis offset (consider hours and minutes) + const double dYAxisOffs = 24 - curHistoryData.DateTime.time().hour() - + static_cast ( curHistoryData.DateTime.time().minute() ) / 60; + + // calculate the actual point in the graph (in pixels) + const QPoint curPoint ( + PlotGridFrame.x() + static_cast ( dXSpace * ( iNumTicksX + iXAxisOffs ) ), + PlotGridFrame.y() + static_cast ( static_cast ( + PlotGridFrame.height() ) / ( iYAxisEnd - iYAxisStart ) * dYAxisOffs ) ); + + // create painter for plot + QPainter PlotPainter ( &PlotPixmap ); + + // we use different markers for new connection and server stop items + switch ( curHistoryData.Type ) + { + case HIT_SERVER_STOP: + // filled circle marker + PlotPainter.setPen ( QPen ( QBrush ( PlotMarkerStopColor ), + iMarkerSizeServSt, Qt::SolidLine, Qt::RoundCap ) ); + break; + + case HIT_LOCAL_CONNECTION: + // filled square marker + PlotPainter.setPen ( QPen ( QBrush ( PlotMarkerNewLocalColor ), + iMarkerSizeNewCon ) ); + break; + + case HIT_REMOTE_CONNECTION: + // filled square marker + PlotPainter.setPen ( QPen ( QBrush ( PlotMarkerNewColor ), + iMarkerSizeNewCon ) ); + break; + } + PlotPainter.drawPoint ( curPoint ); +} + +void CHistoryGraph::Save ( const QString sFileName ) +{ + // save plot as a file + PlotPixmap.save ( sFileName, "JPG", 90 ); +} + +void CHistoryGraph::Add ( const QDateTime& newDateTime, + const QHostAddress ClientInetAddr ) +{ + if ( bDoHistory ) + { + // add element to history, distinguish between a local connection + // and a remote connection + if ( ( ClientInetAddr == QHostAddress ( "127.0.0.1" ) ) || + ( ClientInetAddr.toString().left ( 7 ).compare ( "192.168" ) == 0 ) ) + { + // local connection + Add ( newDateTime, CHistoryGraph::HIT_LOCAL_CONNECTION ); + } + else + { + // remote connection + Add ( newDateTime, CHistoryGraph::HIT_REMOTE_CONNECTION ); + } + } +} + +void CHistoryGraph::Add ( const QDateTime& newDateTime, + const EHistoryItemType curType ) +{ + if ( bDoHistory ) + { + // create and add new element in FIFO + SHistoryData curHistoryData; + curHistoryData.DateTime = newDateTime; + curHistoryData.Type = curType; + + vHistoryDataFifo.Add ( curHistoryData ); + } +} + +void CHistoryGraph::Update() +{ + if ( bDoHistory ) + { + int i; + + // store current date for reference + curDate = QDate::currentDate(); + + // get oldest date in history + QDate oldestDate = curDate.addDays ( 1 ); // one day in the future + const int iNumItemsForHistory = vHistoryDataFifo.Size(); + for ( i = 0; i < iNumItemsForHistory; i++ ) + { + // only use valid dates + if ( vHistoryDataFifo[i].DateTime.date().isValid() ) + { + if ( vHistoryDataFifo[i].DateTime.date() < oldestDate ) + { + oldestDate = vHistoryDataFifo[i].DateTime.date(); + } + } + } + const int iNumDaysInHistory = -curDate.daysTo ( oldestDate ) + 1; + + // draw frame of the graph + DrawFrame ( iNumDaysInHistory ); + + // add markers + for ( i = 0; i < iNumItemsForHistory; i++ ) + { + AddMarker ( vHistoryDataFifo[i] ); + } + + // save graph as picture in file + Save ( sFileName ); + } +} + + +// Server logging -------------------------------------------------------------- +CServerLogging::~CServerLogging() +{ + // close logging file of open + if ( File.isOpen() ) + { + File.close(); + } +} + +void CServerLogging::Start ( const QString& strLoggingFileName ) +{ + // open file + File.setFileName ( strLoggingFileName ); + if ( File.open ( QIODevice::Append | QIODevice::Text ) ) + { + bDoLogging = true; + } +} + +void CServerLogging::EnableHistory ( const QString& strHistoryFileName ) +{ + HistoryGraph.Start ( strHistoryFileName ); +} + +void CServerLogging::AddNewConnection ( const QHostAddress& ClientInetAddr ) +{ + // logging of new connected channel + const QString strLogStr = CurTimeDatetoLogString() + ", " + + ClientInetAddr.toString() + ", connected"; + +#ifndef _WIN32 + QTextStream tsConsoloeStream ( stdout ); + tsConsoloeStream << strLogStr << endl; // on console +#endif + *this << strLogStr; // in log file + + // add element to history + HistoryGraph.Add ( QDateTime::currentDateTime(), ClientInetAddr ); +} + +void CServerLogging::AddServerStopped() +{ + const QString strLogStr = CurTimeDatetoLogString() + ",, server stopped " + "-------------------------------------"; + +#ifndef _WIN32 + QTextStream tsConsoloeStream ( stdout ); + tsConsoloeStream << strLogStr << endl; // on console +#endif + *this << strLogStr; // in log file + + // add element to history and update on server stop + HistoryGraph.Add ( QDateTime::currentDateTime(), + CHistoryGraph::HIT_SERVER_STOP ); + + HistoryGraph.Update(); +} + +void CServerLogging::operator<< ( const QString& sNewStr ) +{ + if ( bDoLogging ) + { + // append new line in logging file + QTextStream out ( &File ); + out << sNewStr << endl; + File.flush(); + } +} + +void CServerLogging::ParseLogFile ( const QString& strFileName ) +{ + // open file for reading + QFile LogFile ( strFileName ); + LogFile.open ( QIODevice::ReadOnly | QIODevice::Text ); + + QTextStream inStream ( &LogFile ); + + // read all content from file + while ( !inStream.atEnd() ) + { + // get a new line from log file + QString strCurLine = inStream.readLine(); + + // parse log file line + QStringList strlistCurLine = strCurLine.split( "," ); + + // check number of separated strings condition + if ( strlistCurLine.size() == 4 ) + { + // first entry + QDate curDate = + QDate::fromString ( strlistCurLine.at ( 0 ).trimmed(), + "d.M.yyyy" ); + + // second entry + QTime curTime = + QTime::fromString ( strlistCurLine.at ( 1 ).trimmed(), + "hh:mm:ss" ); + + if ( curDate.isValid() && curTime.isValid() ) + { + QDateTime curDateTime ( curDate, curTime ); + + // check if server stop or new client connection + QString strAddress = strlistCurLine.at ( 2 ).trimmed(); + if ( strAddress.isEmpty() ) + { + // server stop + HistoryGraph.Add ( curDateTime, + CHistoryGraph::HIT_SERVER_STOP ); + } + else + { + QHostAddress curAddress; + + // third entry is IP address + if ( curAddress.setAddress ( strlistCurLine.at ( 2 ).trimmed() ) ) + { + // new client connection + HistoryGraph.Add ( curDateTime, curAddress ); + } + } + } + } + } + + HistoryGraph.Update(); +} + +QString CServerLogging::CurTimeDatetoLogString() +{ + // time and date to string conversion + const QDateTime curDateTime = QDateTime::currentDateTime(); + + // format date and time output according to "3.9.2006, 11:38:08" + return QString().setNum ( curDateTime.date().day() ) + "." + + QString().setNum ( curDateTime.date().month() ) + "." + + QString().setNum ( curDateTime.date().year() ) + ", " + + curDateTime.time().toString(); +} diff --git a/src/settings.cpp b/src/settings.cpp index cb5151c8..cfcd8db8 100755 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1,352 +1,352 @@ -/******************************************************************************\ - * Copyright (c) 2004-2009 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * 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 - * version. - * - * 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 - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#include "settings.h" - - -/* Implementation *************************************************************/ -void CSettings::ReadIniFile ( const QString& sFileName ) -{ - int iValue; - bool bValue; - QDomDocument IniXMLDocument; - - // load data from init-file - // prepare file name for loading initialization data from XML file - QString sCurFileName = sFileName; - if ( sCurFileName.isEmpty() ) - { - // if no file name is available, use default file name - sCurFileName = LLCON_INIT_FILE_NAME; - } - - // read data from file if possible - QFile file ( sCurFileName ); - if ( file.open ( QIODevice::ReadOnly ) ) - { - QTextStream in ( &file ); - IniXMLDocument.setContent ( in.readAll(), false ); - - file.close(); - } - - - // actual settings data --------------------------------------------------- - // IP addresses - for ( int iIPAddrIdx = 0; iIPAddrIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIPAddrIdx++ ) - { - QString sDefaultIP = ""; - - // use default only for first entry - if ( iIPAddrIdx == 0 ) - { - sDefaultIP = DEFAULT_SERVER_ADDRESS; - } - - pClient->vstrIPAddress[iIPAddrIdx] = - GetIniSetting ( IniXMLDocument, "client", - QString ( "ipaddress%1" ).arg ( iIPAddrIdx ), sDefaultIP ); - } - - // name - pClient->strName = GetIniSetting ( IniXMLDocument, "client", "name" ); - - // audio fader - if ( GetNumericIniSet ( IniXMLDocument, "client", "audfad", - AUD_FADER_IN_MIN, AUD_FADER_IN_MAX, iValue ) ) - { - pClient->SetAudioInFader ( iValue ); - } - - // reverberation level - if ( GetNumericIniSet ( IniXMLDocument, "client", "revlev", - 0, AUD_REVERB_MAX, iValue ) ) - { - pClient->SetReverbLevel ( iValue ); - } - - // reverberation channel assignment - if ( GetFlagIniSet ( IniXMLDocument, "client", "reverblchan", bValue ) ) - { - pClient->SetReverbOnLeftChan ( bValue ); - } - - // sound card selection - // special case with this setting: the sound card initialization depends on this setting - // call, therefore, if no setting file parameter could be retrieved, the sound card is - // initialized with a default setting defined here - if ( GetNumericIniSet ( IniXMLDocument, "client", "auddevidx", - 1, MAX_NUMBER_SOUND_CARDS, iValue ) ) - { - pClient->SetSndCrdDev ( iValue ); - } - else - { - // use "INVALID_SNC_CARD_DEVICE" to tell the sound card driver that no - // device selection was done previously - pClient->SetSndCrdDev ( INVALID_SNC_CARD_DEVICE ); - } - - // sound card preferred buffer size index - if ( GetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", - FRAME_SIZE_FACTOR_PREFERRED, FRAME_SIZE_FACTOR_SAFE, iValue ) ) - { - // additional check required since only a subset of factors are - // defined - if ( ( iValue == FRAME_SIZE_FACTOR_PREFERRED ) || - ( iValue == FRAME_SIZE_FACTOR_DEFAULT ) || - ( iValue == FRAME_SIZE_FACTOR_SAFE ) ) - { - pClient->SetSndCrdPrefFrameSizeFactor ( iValue ); - } - } - - // automatic network jitter buffer size setting - if ( GetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", bValue ) ) - { - pClient->SetDoAutoSockBufSize ( bValue ); - } - - // network jitter buffer size - if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbuf", - MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) - { - pClient->SetSockBufNumFrames ( iValue ); - } - - // flag whether the chat window shall be opened on a new chat message - if ( GetFlagIniSet ( IniXMLDocument, "client", "openchatonnewmessage", bValue ) ) - { - pClient->SetOpenChatOnNewMessage ( bValue ); - } - - // GUI design - if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", - 0, 1 /* GD_ORIGINAL */, iValue ) ) - { - pClient->SetGUIDesign ( static_cast ( iValue ) ); - } - - // flag whether using high quality audio or not - if ( GetFlagIniSet ( IniXMLDocument, "client", "highqualityaudio", bValue ) ) - { - pClient->SetCELTHighQuality ( bValue ); - } -} - -void CSettings::WriteIniFile ( const QString& sFileName ) -{ - // create XML document for storing initialization parameters - QDomDocument IniXMLDocument; - - - // actual settings data --------------------------------------------------- - // IP addresses - for ( int iIPAddrIdx = 0; iIPAddrIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIPAddrIdx++ ) - { - PutIniSetting ( IniXMLDocument, "client", - QString ( "ipaddress%1" ).arg ( iIPAddrIdx ), - pClient->vstrIPAddress[iIPAddrIdx] ); - } - - // name - PutIniSetting ( IniXMLDocument, "client", "name", - pClient->strName ); - - // audio fader - SetNumericIniSet ( IniXMLDocument, "client", "audfad", - pClient->GetAudioInFader() ); - - // reverberation level - SetNumericIniSet ( IniXMLDocument, "client", "revlev", - pClient->GetReverbLevel() ); - - // reverberation channel assignment - SetFlagIniSet ( IniXMLDocument, "client", "reverblchan", - pClient->IsReverbOnLeftChan() ); - - // sound card selection - SetNumericIniSet ( IniXMLDocument, "client", "auddevidx", - pClient->GetSndCrdDev() ); - - // sound card preferred buffer size index - SetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", - pClient->GetSndCrdPrefFrameSizeFactor() ); - - // automatic network jitter buffer size setting - SetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", - pClient->GetDoAutoSockBufSize() ); - - // network jitter buffer size - SetNumericIniSet ( IniXMLDocument, "client", "jitbuf", - pClient->GetSockBufNumFrames() ); - - // flag whether the chat window shall be opened on a new chat message - SetFlagIniSet ( IniXMLDocument, "client", "openchatonnewmessage", - pClient->GetOpenChatOnNewMessage() ); - - // GUI design - SetNumericIniSet ( IniXMLDocument, "client", "guidesign", - static_cast ( pClient->GetGUIDesign() ) ); - - // flag whether using high quality audio or not - SetFlagIniSet ( IniXMLDocument, "client", "highqualityaudio", - pClient->GetCELTHighQuality() ); - - // prepare file name for storing initialization data in XML file - QString sCurFileName = sFileName; - if ( sCurFileName.isEmpty() ) - { - // if no file name is available, use default file name - sCurFileName = LLCON_INIT_FILE_NAME; - } - - // store XML data in file - QFile file ( sCurFileName ); - if ( file.open ( QIODevice::WriteOnly ) ) - { - QTextStream out ( &file ); - out << IniXMLDocument.toString(); - } -} - -void CSettings::SetNumericIniSet ( QDomDocument& xmlFile, const QString& strSection, - const QString& strKey, const int iValue ) -{ - // convert input parameter which is an integer to string and store - PutIniSetting ( xmlFile, strSection, strKey, QString("%1").arg(iValue) ); -} - -bool CSettings::GetNumericIniSet ( const QDomDocument& xmlFile, const QString& strSection, - const QString& strKey, const int iRangeStart, - const int iRangeStop, int& iValue ) -{ - // init return value - bool bReturn = false; - - const QString strGetIni = GetIniSetting ( xmlFile, strSection, strKey ); - - // check if it is a valid parameter - if ( !strGetIni.isEmpty() ) - { - // convert string from init file to integer - iValue = strGetIni.toInt(); - - // check range - if ( ( iValue >= iRangeStart ) && ( iValue <= iRangeStop ) ) - { - bReturn = true; - } - } - - return bReturn; -} - -void CSettings::SetFlagIniSet ( QDomDocument& xmlFile, const QString& strSection, - const QString& strKey, const bool bValue ) -{ - // we encode true -> "1" and false -> "0" - if ( bValue == true ) - { - PutIniSetting ( xmlFile, strSection, strKey, "1" ); - } - else - { - PutIniSetting ( xmlFile, strSection, strKey, "0" ); - } -} - -bool CSettings::GetFlagIniSet ( const QDomDocument& xmlFile, const QString& strSection, - const QString& strKey, bool& bValue ) -{ - // init return value - bool bReturn = false; - - const QString strGetIni = GetIniSetting ( xmlFile, strSection, strKey ); - - if ( !strGetIni.isEmpty() ) - { - if ( strGetIni.toInt() ) - { - bValue = true; - } - else - { - bValue = false; - } - - bReturn = true; - } - - return bReturn; -} - - -// Init-file routines using XML *********************************************** -QString CSettings::GetIniSetting ( const QDomDocument& xmlFile, const QString& sSection, - const QString& sKey, const QString& sDefaultVal ) -{ - // init return parameter with default value - QString sResult ( sDefaultVal ); - - // get section - QDomElement xmlSection = xmlFile.firstChildElement ( sSection ); - if ( !xmlSection.isNull() ) - { - // get key - QDomElement xmlKey = xmlSection.firstChildElement ( sKey ); - if ( !xmlKey.isNull() ) - { - // get value - sResult = xmlKey.text(); - } - } - - return sResult; -} - -void CSettings::PutIniSetting ( QDomDocument& xmlFile, const QString& sSection, - const QString& sKey, const QString& sValue ) -{ - // check if section is already there, if not then create it - QDomElement xmlSection = xmlFile.firstChildElement ( sSection ); - if ( xmlSection.isNull() ) - { - // create new root element and add to document - xmlSection = xmlFile.createElement ( sSection ); - xmlFile.appendChild ( xmlSection ); - } - - // check if key is already there, if not then create it - QDomElement xmlKey = xmlSection.firstChildElement ( sKey ); - if ( xmlKey.isNull() ) - { - xmlKey = xmlFile.createElement ( sKey ); - xmlSection.appendChild ( xmlKey ); - } - - // add actual data to the key - QDomText currentValue = xmlFile.createTextNode ( sValue ); - xmlKey.appendChild ( currentValue ); -} +/******************************************************************************\ + * Copyright (c) 2004-2009 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * 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 + * version. + * + * 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 + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#include "settings.h" + + +/* Implementation *************************************************************/ +void CSettings::ReadIniFile ( const QString& sFileName ) +{ + int iValue; + bool bValue; + QDomDocument IniXMLDocument; + + // load data from init-file + // prepare file name for loading initialization data from XML file + QString sCurFileName = sFileName; + if ( sCurFileName.isEmpty() ) + { + // if no file name is available, use default file name + sCurFileName = LLCON_INIT_FILE_NAME; + } + + // read data from file if possible + QFile file ( sCurFileName ); + if ( file.open ( QIODevice::ReadOnly ) ) + { + QTextStream in ( &file ); + IniXMLDocument.setContent ( in.readAll(), false ); + + file.close(); + } + + + // actual settings data --------------------------------------------------- + // IP addresses + for ( int iIPAddrIdx = 0; iIPAddrIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIPAddrIdx++ ) + { + QString sDefaultIP = ""; + + // use default only for first entry + if ( iIPAddrIdx == 0 ) + { + sDefaultIP = DEFAULT_SERVER_ADDRESS; + } + + pClient->vstrIPAddress[iIPAddrIdx] = + GetIniSetting ( IniXMLDocument, "client", + QString ( "ipaddress%1" ).arg ( iIPAddrIdx ), sDefaultIP ); + } + + // name + pClient->strName = GetIniSetting ( IniXMLDocument, "client", "name" ); + + // audio fader + if ( GetNumericIniSet ( IniXMLDocument, "client", "audfad", + AUD_FADER_IN_MIN, AUD_FADER_IN_MAX, iValue ) ) + { + pClient->SetAudioInFader ( iValue ); + } + + // reverberation level + if ( GetNumericIniSet ( IniXMLDocument, "client", "revlev", + 0, AUD_REVERB_MAX, iValue ) ) + { + pClient->SetReverbLevel ( iValue ); + } + + // reverberation channel assignment + if ( GetFlagIniSet ( IniXMLDocument, "client", "reverblchan", bValue ) ) + { + pClient->SetReverbOnLeftChan ( bValue ); + } + + // sound card selection + // special case with this setting: the sound card initialization depends on this setting + // call, therefore, if no setting file parameter could be retrieved, the sound card is + // initialized with a default setting defined here + if ( GetNumericIniSet ( IniXMLDocument, "client", "auddevidx", + 1, MAX_NUMBER_SOUND_CARDS, iValue ) ) + { + pClient->SetSndCrdDev ( iValue ); + } + else + { + // use "INVALID_SNC_CARD_DEVICE" to tell the sound card driver that no + // device selection was done previously + pClient->SetSndCrdDev ( INVALID_SNC_CARD_DEVICE ); + } + + // sound card preferred buffer size index + if ( GetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", + FRAME_SIZE_FACTOR_PREFERRED, FRAME_SIZE_FACTOR_SAFE, iValue ) ) + { + // additional check required since only a subset of factors are + // defined + if ( ( iValue == FRAME_SIZE_FACTOR_PREFERRED ) || + ( iValue == FRAME_SIZE_FACTOR_DEFAULT ) || + ( iValue == FRAME_SIZE_FACTOR_SAFE ) ) + { + pClient->SetSndCrdPrefFrameSizeFactor ( iValue ); + } + } + + // automatic network jitter buffer size setting + if ( GetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", bValue ) ) + { + pClient->SetDoAutoSockBufSize ( bValue ); + } + + // network jitter buffer size + if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbuf", + MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) + { + pClient->SetSockBufNumFrames ( iValue ); + } + + // flag whether the chat window shall be opened on a new chat message + if ( GetFlagIniSet ( IniXMLDocument, "client", "openchatonnewmessage", bValue ) ) + { + pClient->SetOpenChatOnNewMessage ( bValue ); + } + + // GUI design + if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", + 0, 1 /* GD_ORIGINAL */, iValue ) ) + { + pClient->SetGUIDesign ( static_cast ( iValue ) ); + } + + // flag whether using high quality audio or not + if ( GetFlagIniSet ( IniXMLDocument, "client", "highqualityaudio", bValue ) ) + { + pClient->SetCELTHighQuality ( bValue ); + } +} + +void CSettings::WriteIniFile ( const QString& sFileName ) +{ + // create XML document for storing initialization parameters + QDomDocument IniXMLDocument; + + + // actual settings data --------------------------------------------------- + // IP addresses + for ( int iIPAddrIdx = 0; iIPAddrIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIPAddrIdx++ ) + { + PutIniSetting ( IniXMLDocument, "client", + QString ( "ipaddress%1" ).arg ( iIPAddrIdx ), + pClient->vstrIPAddress[iIPAddrIdx] ); + } + + // name + PutIniSetting ( IniXMLDocument, "client", "name", + pClient->strName ); + + // audio fader + SetNumericIniSet ( IniXMLDocument, "client", "audfad", + pClient->GetAudioInFader() ); + + // reverberation level + SetNumericIniSet ( IniXMLDocument, "client", "revlev", + pClient->GetReverbLevel() ); + + // reverberation channel assignment + SetFlagIniSet ( IniXMLDocument, "client", "reverblchan", + pClient->IsReverbOnLeftChan() ); + + // sound card selection + SetNumericIniSet ( IniXMLDocument, "client", "auddevidx", + pClient->GetSndCrdDev() ); + + // sound card preferred buffer size index + SetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", + pClient->GetSndCrdPrefFrameSizeFactor() ); + + // automatic network jitter buffer size setting + SetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", + pClient->GetDoAutoSockBufSize() ); + + // network jitter buffer size + SetNumericIniSet ( IniXMLDocument, "client", "jitbuf", + pClient->GetSockBufNumFrames() ); + + // flag whether the chat window shall be opened on a new chat message + SetFlagIniSet ( IniXMLDocument, "client", "openchatonnewmessage", + pClient->GetOpenChatOnNewMessage() ); + + // GUI design + SetNumericIniSet ( IniXMLDocument, "client", "guidesign", + static_cast ( pClient->GetGUIDesign() ) ); + + // flag whether using high quality audio or not + SetFlagIniSet ( IniXMLDocument, "client", "highqualityaudio", + pClient->GetCELTHighQuality() ); + + // prepare file name for storing initialization data in XML file + QString sCurFileName = sFileName; + if ( sCurFileName.isEmpty() ) + { + // if no file name is available, use default file name + sCurFileName = LLCON_INIT_FILE_NAME; + } + + // store XML data in file + QFile file ( sCurFileName ); + if ( file.open ( QIODevice::WriteOnly ) ) + { + QTextStream out ( &file ); + out << IniXMLDocument.toString(); + } +} + +void CSettings::SetNumericIniSet ( QDomDocument& xmlFile, const QString& strSection, + const QString& strKey, const int iValue ) +{ + // convert input parameter which is an integer to string and store + PutIniSetting ( xmlFile, strSection, strKey, QString("%1").arg(iValue) ); +} + +bool CSettings::GetNumericIniSet ( const QDomDocument& xmlFile, const QString& strSection, + const QString& strKey, const int iRangeStart, + const int iRangeStop, int& iValue ) +{ + // init return value + bool bReturn = false; + + const QString strGetIni = GetIniSetting ( xmlFile, strSection, strKey ); + + // check if it is a valid parameter + if ( !strGetIni.isEmpty() ) + { + // convert string from init file to integer + iValue = strGetIni.toInt(); + + // check range + if ( ( iValue >= iRangeStart ) && ( iValue <= iRangeStop ) ) + { + bReturn = true; + } + } + + return bReturn; +} + +void CSettings::SetFlagIniSet ( QDomDocument& xmlFile, const QString& strSection, + const QString& strKey, const bool bValue ) +{ + // we encode true -> "1" and false -> "0" + if ( bValue == true ) + { + PutIniSetting ( xmlFile, strSection, strKey, "1" ); + } + else + { + PutIniSetting ( xmlFile, strSection, strKey, "0" ); + } +} + +bool CSettings::GetFlagIniSet ( const QDomDocument& xmlFile, const QString& strSection, + const QString& strKey, bool& bValue ) +{ + // init return value + bool bReturn = false; + + const QString strGetIni = GetIniSetting ( xmlFile, strSection, strKey ); + + if ( !strGetIni.isEmpty() ) + { + if ( strGetIni.toInt() ) + { + bValue = true; + } + else + { + bValue = false; + } + + bReturn = true; + } + + return bReturn; +} + + +// Init-file routines using XML *********************************************** +QString CSettings::GetIniSetting ( const QDomDocument& xmlFile, const QString& sSection, + const QString& sKey, const QString& sDefaultVal ) +{ + // init return parameter with default value + QString sResult ( sDefaultVal ); + + // get section + QDomElement xmlSection = xmlFile.firstChildElement ( sSection ); + if ( !xmlSection.isNull() ) + { + // get key + QDomElement xmlKey = xmlSection.firstChildElement ( sKey ); + if ( !xmlKey.isNull() ) + { + // get value + sResult = xmlKey.text(); + } + } + + return sResult; +} + +void CSettings::PutIniSetting ( QDomDocument& xmlFile, const QString& sSection, + const QString& sKey, const QString& sValue ) +{ + // check if section is already there, if not then create it + QDomElement xmlSection = xmlFile.firstChildElement ( sSection ); + if ( xmlSection.isNull() ) + { + // create new root element and add to document + xmlSection = xmlFile.createElement ( sSection ); + xmlFile.appendChild ( xmlSection ); + } + + // check if key is already there, if not then create it + QDomElement xmlKey = xmlSection.firstChildElement ( sKey ); + if ( xmlKey.isNull() ) + { + xmlKey = xmlFile.createElement ( sKey ); + xmlSection.appendChild ( xmlKey ); + } + + // add actual data to the key + QDomText currentValue = xmlFile.createTextNode ( sValue ); + xmlKey.appendChild ( currentValue ); +} diff --git a/src/util.h b/src/util.h index 9733106e..c7ee3eb7 100755 --- a/src/util.h +++ b/src/util.h @@ -1,647 +1,647 @@ -/******************************************************************************\ - * Copyright (c) 2004-2009 - * - * Author(s): - * Volker Fischer - * - ****************************************************************************** - * - * 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 - * version. - * - * 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 - * 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -\******************************************************************************/ - -#if !defined ( UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) -#define UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "global.h" -using namespace std; // because of the library: "vector" -#ifdef _WIN32 -# include "../windows/moc/aboutdlgbase.h" -# include -# include -#else -# include "moc/aboutdlgbase.h" -#endif - - -/* Definitions ****************************************************************/ -#define METER_FLY_BACK 2 - - -/* Global functions ***********************************************************/ -// converting double to short -inline short Double2Short ( const double dInput ) -{ - // lower bound - if ( dInput < _MINSHORT ) - { - return _MINSHORT; - } - - // upper bound - if ( dInput > _MAXSHORT ) - { - return _MAXSHORT; - } - - return (short) dInput; -} - -// debug error handling -void DebugError ( const QString& pchErDescr, const QString& pchPar1Descr, - const double dPar1, const QString& pchPar2Descr, - const double dPar2 ); - - -/******************************************************************************\ -* CVector base class * -\******************************************************************************/ -template class CVector : public std::vector -{ -public: - CVector() : iVectorSize ( 0 ) { pData = this->begin(); } - CVector ( const int iNeSi ) { Init(iNeSi); } - CVector ( const int iNeSi, const TData tInVa ) { Init ( iNeSi, tInVa ); } - virtual ~CVector() {} - - /* Copy constructor: The order of the initialization list must not be - changed. First, the base class must be initialized, then the pData - pointer must be set to the new data source. The bit access is, by - default, reset */ - CVector ( const CVector& vecI ) : - std::vector ( static_cast&> ( vecI ) ), - iVectorSize ( vecI.Size() ) { pData = this->begin(); } - - void Init ( const int iNewSize ); - - // use this init to give all elements a defined value - void Init ( const int iNewSize, const TData tIniVal ); - void Reset ( const TData tResetVal ); - - void Enlarge ( const int iAddedSize ); - void Add ( const TData& tI ) { Enlarge ( 1 ); pData[iVectorSize - 1] = tI; } - - inline int Size() const { return iVectorSize; } - - /* This operator allows for a l-value assignment of this object: - CVector[x] = y is possible */ - inline TData& operator[] ( const int iPos ) { -#ifdef _DEBUG_ - if ( ( iPos < 0 ) || ( iPos > iVectorSize - 1 ) ) - { - DebugError ( "Writing vector out of bounds", "Vector size", - iVectorSize, "New parameter", iPos ); - } -#endif - return pData[iPos]; } - - inline TData operator[] ( const int iPos ) const { -#ifdef _DEBUG_ - if ( ( iPos < 0 ) || ( iPos > iVectorSize - 1 ) ) - { - DebugError ( "Reading vector out of bounds", "Vector size", - iVectorSize, "New parameter", iPos ); - } -#endif - return pData[iPos]; } - - inline CVector& operator= ( const CVector& vecI ) { -#ifdef _DEBUG_ - /* Vectors which shall be copied MUST have same size! (If this is - satisfied, the parameter "iVectorSize" must not be adjusted as - a side effect) */ - if ( vecI.Size() != iVectorSize ) - { - DebugError ( "Vector operator=() different size", "Vector size", - iVectorSize, "New parameter", vecI.Size() ); - } -#endif - vector::operator= ( vecI ); - - /* Reset my data pointer in case, the operator=() of the base class - did change the actual memory */ - pData = this->begin(); - - return *this; - } - -protected: - typename std::vector::iterator pData; - int iVectorSize; -}; - - -/* Implementation *************************************************************/ -template void CVector::Init ( const int iNewSize ) -{ - iVectorSize = iNewSize; - - /* Clear old buffer and reserve memory for new buffer, get iterator - for pointer operations */ - this->clear(); - this->resize ( iNewSize ); - pData = this->begin(); -} - -template void CVector::Init ( const int iNewSize, - const TData tIniVal ) -{ - // call actual init routine - Init ( iNewSize ); - - // set values - Reset ( tIniVal ); -} - -template void CVector::Enlarge ( const int iAddedSize ) -{ - iVectorSize += iAddedSize; - this->resize ( iVectorSize ); - - /* We have to reset the pointer since it could be that the vector size was - zero before enlarging the vector */ - pData = this->begin(); -} - -template void CVector::Reset ( const TData tResetVal ) -{ - // set all values to reset value - for ( int i = 0; i < iVectorSize; i++ ) - { - pData[i] = tResetVal; - } -} - - -/******************************************************************************\ -* CFIFO class (first in, first out) * -\******************************************************************************/ -template class CFIFO : public CVector -{ -public: - CFIFO() : CVector(), iCurIdx ( 0 ) {} - CFIFO ( const int iNeSi ) : CVector(iNeSi), iCurIdx ( 0 ) {} - CFIFO ( const int iNeSi, const TData tInVa ) : - CVector ( iNeSi, tInVa ), iCurIdx ( 0 ) {} - - void Add ( const TData tNewD ); - inline TData Get() { return this->pData[iCurIdx]; } - - virtual void Init ( const int iNewSize ); - virtual void Init ( const int iNewSize, const TData tIniVal ); - -protected: - int iCurIdx; -}; - -template void CFIFO::Init ( const int iNewSize ) -{ - iCurIdx = 0; - CVector::Init ( iNewSize ); -} - -template void CFIFO::Init ( const int iNewSize, - const TData tIniVal ) -{ - iCurIdx = 0; - CVector::Init ( iNewSize, tIniVal ); -} - -template void CFIFO::Add ( const TData tNewD ) -{ - this->pData[iCurIdx] = tNewD; - - // increment index - iCurIdx++; - if ( iCurIdx >= this->iVectorSize ) - { - iCurIdx = 0; - } -} - - -/******************************************************************************\ -* CMovingAv class (moving average) * -\******************************************************************************/ -template class CMovingAv : public CVector -{ -public: - CMovingAv() : CVector(), iCurIdx ( 0 ), iNorm ( 0 ), - tCurAvResult ( TData ( 0 ) ) {} - CMovingAv ( const int iNeSi ) : CVector ( iNeSi ), iCurIdx ( 0 ), - iNorm ( 0 ), tCurAvResult ( TData ( 0 ) ) {} - CMovingAv ( const int iNeSi, const TData tInVa ) : - CVector ( iNeSi, tInVa ), iCurIdx ( 0 ), iNorm ( 0 ), - tCurAvResult ( TData ( 0 ) ) {} - - void Add ( const TData tNewD ); - inline TData GetAverage() - { - if ( this->iNorm == 0 ) - { - return TData ( 0 ); - } - else - { - return tCurAvResult / this->iNorm; - } - } - - virtual void Init ( const int iNewSize ); - void InitVec ( const int iNewSize, const int iNewVecSize ); - void Reset(); - bool IsInitialized() { return ( this->iNorm == this->iVectorSize ); } - -protected: - int iCurIdx; - int iNorm; - TData tCurAvResult; -}; - -template void CMovingAv::Init ( const int iNewSize ) -{ - iNorm = 0; - iCurIdx = 0; - tCurAvResult = TData ( 0 ); // only for scalars! - CVector::Init ( iNewSize ); -} - -template void CMovingAv::Reset() -{ - iNorm = 0; - iCurIdx = 0; - tCurAvResult = TData ( 0 ); // only for scalars! - CVector::Reset ( TData ( 0 ) ); -} - -template void CMovingAv::Add ( const TData tNewD ) -{ -/* - Optimized calculation of the moving average. We only add a new value and - subtract the old value from the result. We only need one addition and a - history buffer -*/ - // subtract oldest value - tCurAvResult -= this->pData[iCurIdx]; - - // add new value and write in memory - tCurAvResult += tNewD; - this->pData[iCurIdx] = tNewD; - - // increase position pointer and test if wrap - iCurIdx++; - if ( iCurIdx >= this->iVectorSize ) - { - iCurIdx = 0; - } - - // take care of norm - if ( this->iNorm < this->iVectorSize ) - { - this->iNorm++; - } -} - - -/******************************************************************************\ -* GUI utilities * -\******************************************************************************/ -// About dialog ---------------------------------------------------------------- -class CAboutDlg : public QDialog, private Ui_CAboutDlgBase -{ - Q_OBJECT - -public: - CAboutDlg ( QWidget* parent = 0 ); - - static QString GetVersionAndNameStr ( const bool bWithHtml = true ); -}; - - -// Help menu ------------------------------------------------------------------- -class CLlconHelpMenu : public QMenu -{ - Q_OBJECT - -public: - CLlconHelpMenu ( QWidget* parent = 0 ); - -protected: - CAboutDlg AboutDlg; - -public slots: - void OnHelpWhatsThis() { QWhatsThis::enterWhatsThisMode(); } - void OnHelpAbout() { AboutDlg.exec(); } -}; - - -/* Other Classes **************************************************************/ -// Stereo Signal Level Meter --------------------------------------------------- -class CStereoSignalLevelMeter -{ -public: - CStereoSignalLevelMeter() { Reset(); } - virtual ~CStereoSignalLevelMeter() {} - - void Update ( CVector& vecsAudio ); - double MicLevelLeft() { return CalcLogResult ( dCurLevelL ); } - double MicLevelRight() { return CalcLogResult ( dCurLevelR ); } - void Reset() { dCurLevelL = 0.0; dCurLevelR = 0.0; } - -protected: - double CalcLogResult ( const double& dLinearLevel ); - double UpdateCurLevel ( double dCurLevel, const short& sMax ); - - double dCurLevelL; - double dCurLevelR; -}; - -class CHostAddress -{ -public: - CHostAddress() : - InetAddr ( (quint32) 0 ), - iPort ( 0 ) {} - - CHostAddress ( const QHostAddress NInetAddr, - const quint16 iNPort ) : - InetAddr ( NInetAddr ), - iPort ( iNPort ) {} - - CHostAddress ( const CHostAddress& NHAddr ) : - InetAddr ( NHAddr.InetAddr ), - iPort ( NHAddr.iPort ) {} - - // copy and compare operators - CHostAddress& operator= ( const CHostAddress& NHAddr ) - { InetAddr = NHAddr.InetAddr; iPort = NHAddr.iPort; return *this; } - - bool operator== ( const CHostAddress& CompAddr ) // compare operator - { return ( ( CompAddr.InetAddr == InetAddr ) && ( CompAddr.iPort == iPort ) ); } - - QString GetIpAddressStringNoLastByte() const - { - // remove the last byte of the IP address - return InetAddr.toString().section ( ".", 0, 2 ) + ".x"; - } - - QHostAddress InetAddr; - quint16 iPort; -}; - -class CChannelShortInfo -{ -public: - CChannelShortInfo() : - iChanID ( 0 ), - iIpAddr ( 0 ), - strName ( "" ) {} - - CChannelShortInfo ( const int iNID, - const quint32 nIP, - const QString nN ) : - iChanID ( iNID ), - iIpAddr ( nIP ), - strName ( nN ) {} - - int iChanID; - quint32 iIpAddr; - QString strName; -}; - -enum EAudComprType -{ - CT_NONE = 0, - CT_CELT = 1 -}; - -enum EGetDataStat -{ - GS_BUFFER_OK, - GS_BUFFER_UNDERRUN, - GS_CHAN_NOW_DISCONNECTED, - GS_CHAN_NOT_CONNECTED -}; - -enum EGUIDesign -{ - GD_STANDARD = 0, - GD_ORIGINAL = 1 -}; - -class CNetworkTransportProps -{ -public: - CNetworkTransportProps() : - iBaseNetworkPacketSize ( 0 ), - iBlockSizeFact ( 0 ), - iNumAudioChannels ( 0 ), - iSampleRate ( 0 ), - eAudioCodingType ( CT_NONE ), - iAudioCodingArg ( 0 ) {} - - CNetworkTransportProps ( const uint32_t iNBNPS, - const uint16_t iNBSF, - const uint32_t iNNACH, - const uint32_t iNSR, - const EAudComprType eNACT, - const uint32_t iNVers, - const int32_t iNACA ) : - iBaseNetworkPacketSize ( iNBNPS ), - iBlockSizeFact ( iNBSF ), - iNumAudioChannels ( iNNACH ), - iSampleRate ( iNSR ), - eAudioCodingType ( eNACT ), - iVersion ( iNVers ), - iAudioCodingArg ( iNACA ) {} - - uint32_t iBaseNetworkPacketSize; - uint16_t iBlockSizeFact; - uint32_t iNumAudioChannels; - uint32_t iSampleRate; - EAudComprType eAudioCodingType; - uint32_t iVersion; - int32_t iAudioCodingArg; -}; - - -// Audio Reverbration ---------------------------------------------------------- -class CAudioReverb -{ -public: - CAudioReverb() {} - - void Init ( const int iSampleRate, const double rT60 = (double) 5.0 ); - void Clear(); - double ProcessSample ( const double input ); - -protected: - void setT60 ( const double rT60, const int iSampleRate ); - bool isPrime ( const int number ); - - CFIFO allpassDelays_[3]; - CFIFO combDelays_[4]; - double allpassCoefficient_; - double combCoefficient_[4]; -}; - - -// CRC ------------------------------------------------------------------------- -class CCRC -{ -public: - CCRC() : iPoly ( ( 1 << 5 ) | ( 1 << 12 ) ), iBitOutMask ( 1 << 16 ) - { Reset(); } - virtual ~CCRC() {} - - void Reset(); - void AddByte ( const uint8_t byNewInput ); - bool CheckCRC ( const uint32_t iCRC ) { return iCRC == GetCRC(); } - uint32_t GetCRC(); - -protected: - uint32_t iBitOutMask; - uint32_t iPoly; - uint32_t iStateShiftReg; -}; - - -// Mathematics utilities ------------------------------------------------------- -class LlconMath -{ -public: - static int round ( double x ) - { - return (int) ( ( x - floor ( x ) ) >= 0.5 ) ? ceil(x) : floor(x); - } -}; - - -// Precise time ---------------------------------------------------------------- -// needed for ping measurement -class CPreciseTime -{ -public: -#ifdef _WIN32 - // for the Windows version we have to define a minimum timer precision - // -> set it to 1 ms - CPreciseTime() { timeBeginPeriod ( 1 ); } - virtual ~CPreciseTime() { timeEndPeriod ( 1 ); } -#endif - - // precise time (on Windows the QTime is not precise enough) - int elapsed() - { -#ifdef _WIN32 - return timeGetTime(); -#else - return QTime().elapsed(); -#endif - } -}; - - -/******************************************************************************\ -* Cycle Time Variance Measurement * -\******************************************************************************/ -// use for, e.g., measuring the variance of a timer -class CCycleTimeVariance -{ -public: - CCycleTimeVariance() : iBlockLengthAtSystemSampleRate ( 0 ), - dIntervalTime ( 0.0 ), iNewValueBoundFactor ( 0 ) {} - virtual ~CCycleTimeVariance() {} - - void Init ( const int iNewBlockLengthAtSystemSampleRate, - const int iNewSystemSampleRate, - const int iHistoryLengthTime, - const int iNewNewValueBoundFactor = 4 ) - { - // store block size and new value bound factor - iBlockLengthAtSystemSampleRate = iNewBlockLengthAtSystemSampleRate; - iNewValueBoundFactor = iNewNewValueBoundFactor; - - // calculate interval time - dIntervalTime = static_cast ( - iBlockLengthAtSystemSampleRate ) * 1000 / iNewSystemSampleRate; - - // calculate actual moving average length and initialize buffer - RespTimeMoAvBuf.Init ( iHistoryLengthTime * - iNewSystemSampleRate / iNewBlockLengthAtSystemSampleRate ); - } - - int GetBlockLength() { return iBlockLengthAtSystemSampleRate; } - - void Reset() - { - TimeLastBlock = PreciseTime.elapsed(); - RespTimeMoAvBuf.Reset(); - } - - double Update() - { - // add time difference - const int CurTime = PreciseTime.elapsed(); - - // we want to calculate the standard deviation (we assume that the mean - // is correct at the block period time) - const double dCurAddVal = - static_cast ( CurTime - TimeLastBlock ) - dIntervalTime; - - if ( iNewValueBoundFactor > 0 ) - { - // check if new value is in range - if ( fabs ( dCurAddVal ) < ( iNewValueBoundFactor * dIntervalTime ) ) - { - // add squared value - RespTimeMoAvBuf.Add ( dCurAddVal * dCurAddVal ); - } - } - else - { - // new value bound is not used, add new value (add squared value) - RespTimeMoAvBuf.Add ( dCurAddVal * dCurAddVal ); - } - - // store old time value - TimeLastBlock = CurTime; - - return dCurAddVal; - } - - // return the standard deviation, for that we need to calculate - // the sqaure root - double GetStdDev() { return sqrt ( RespTimeMoAvBuf.GetAverage() ); } - - bool IsInitialized() { return RespTimeMoAvBuf.IsInitialized(); } - -protected: - CPreciseTime PreciseTime; - CMovingAv RespTimeMoAvBuf; - int TimeLastBlock; - int iBlockLengthAtSystemSampleRate; - double dIntervalTime; - int iNewValueBoundFactor; -}; - -#endif /* !defined ( UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) */ +/******************************************************************************\ + * Copyright (c) 2004-2009 + * + * Author(s): + * Volker Fischer + * + ****************************************************************************** + * + * 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 + * version. + * + * 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 + * 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., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +\******************************************************************************/ + +#if !defined ( UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) +#define UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "global.h" +using namespace std; // because of the library: "vector" +#ifdef _WIN32 +# include "../windows/moc/aboutdlgbase.h" +# include +# include +#else +# include "moc/aboutdlgbase.h" +#endif + + +/* Definitions ****************************************************************/ +#define METER_FLY_BACK 2 + + +/* Global functions ***********************************************************/ +// converting double to short +inline short Double2Short ( const double dInput ) +{ + // lower bound + if ( dInput < _MINSHORT ) + { + return _MINSHORT; + } + + // upper bound + if ( dInput > _MAXSHORT ) + { + return _MAXSHORT; + } + + return (short) dInput; +} + +// debug error handling +void DebugError ( const QString& pchErDescr, const QString& pchPar1Descr, + const double dPar1, const QString& pchPar2Descr, + const double dPar2 ); + + +/******************************************************************************\ +* CVector base class * +\******************************************************************************/ +template class CVector : public std::vector +{ +public: + CVector() : iVectorSize ( 0 ) { pData = this->begin(); } + CVector ( const int iNeSi ) { Init(iNeSi); } + CVector ( const int iNeSi, const TData tInVa ) { Init ( iNeSi, tInVa ); } + virtual ~CVector() {} + + /* Copy constructor: The order of the initialization list must not be + changed. First, the base class must be initialized, then the pData + pointer must be set to the new data source. The bit access is, by + default, reset */ + CVector ( const CVector& vecI ) : + std::vector ( static_cast&> ( vecI ) ), + iVectorSize ( vecI.Size() ) { pData = this->begin(); } + + void Init ( const int iNewSize ); + + // use this init to give all elements a defined value + void Init ( const int iNewSize, const TData tIniVal ); + void Reset ( const TData tResetVal ); + + void Enlarge ( const int iAddedSize ); + void Add ( const TData& tI ) { Enlarge ( 1 ); pData[iVectorSize - 1] = tI; } + + inline int Size() const { return iVectorSize; } + + /* This operator allows for a l-value assignment of this object: + CVector[x] = y is possible */ + inline TData& operator[] ( const int iPos ) { +#ifdef _DEBUG_ + if ( ( iPos < 0 ) || ( iPos > iVectorSize - 1 ) ) + { + DebugError ( "Writing vector out of bounds", "Vector size", + iVectorSize, "New parameter", iPos ); + } +#endif + return pData[iPos]; } + + inline TData operator[] ( const int iPos ) const { +#ifdef _DEBUG_ + if ( ( iPos < 0 ) || ( iPos > iVectorSize - 1 ) ) + { + DebugError ( "Reading vector out of bounds", "Vector size", + iVectorSize, "New parameter", iPos ); + } +#endif + return pData[iPos]; } + + inline CVector& operator= ( const CVector& vecI ) { +#ifdef _DEBUG_ + /* Vectors which shall be copied MUST have same size! (If this is + satisfied, the parameter "iVectorSize" must not be adjusted as + a side effect) */ + if ( vecI.Size() != iVectorSize ) + { + DebugError ( "Vector operator=() different size", "Vector size", + iVectorSize, "New parameter", vecI.Size() ); + } +#endif + vector::operator= ( vecI ); + + /* Reset my data pointer in case, the operator=() of the base class + did change the actual memory */ + pData = this->begin(); + + return *this; + } + +protected: + typename std::vector::iterator pData; + int iVectorSize; +}; + + +/* Implementation *************************************************************/ +template void CVector::Init ( const int iNewSize ) +{ + iVectorSize = iNewSize; + + /* Clear old buffer and reserve memory for new buffer, get iterator + for pointer operations */ + this->clear(); + this->resize ( iNewSize ); + pData = this->begin(); +} + +template void CVector::Init ( const int iNewSize, + const TData tIniVal ) +{ + // call actual init routine + Init ( iNewSize ); + + // set values + Reset ( tIniVal ); +} + +template void CVector::Enlarge ( const int iAddedSize ) +{ + iVectorSize += iAddedSize; + this->resize ( iVectorSize ); + + /* We have to reset the pointer since it could be that the vector size was + zero before enlarging the vector */ + pData = this->begin(); +} + +template void CVector::Reset ( const TData tResetVal ) +{ + // set all values to reset value + for ( int i = 0; i < iVectorSize; i++ ) + { + pData[i] = tResetVal; + } +} + + +/******************************************************************************\ +* CFIFO class (first in, first out) * +\******************************************************************************/ +template class CFIFO : public CVector +{ +public: + CFIFO() : CVector(), iCurIdx ( 0 ) {} + CFIFO ( const int iNeSi ) : CVector(iNeSi), iCurIdx ( 0 ) {} + CFIFO ( const int iNeSi, const TData tInVa ) : + CVector ( iNeSi, tInVa ), iCurIdx ( 0 ) {} + + void Add ( const TData tNewD ); + inline TData Get() { return this->pData[iCurIdx]; } + + virtual void Init ( const int iNewSize ); + virtual void Init ( const int iNewSize, const TData tIniVal ); + +protected: + int iCurIdx; +}; + +template void CFIFO::Init ( const int iNewSize ) +{ + iCurIdx = 0; + CVector::Init ( iNewSize ); +} + +template void CFIFO::Init ( const int iNewSize, + const TData tIniVal ) +{ + iCurIdx = 0; + CVector::Init ( iNewSize, tIniVal ); +} + +template void CFIFO::Add ( const TData tNewD ) +{ + this->pData[iCurIdx] = tNewD; + + // increment index + iCurIdx++; + if ( iCurIdx >= this->iVectorSize ) + { + iCurIdx = 0; + } +} + + +/******************************************************************************\ +* CMovingAv class (moving average) * +\******************************************************************************/ +template class CMovingAv : public CVector +{ +public: + CMovingAv() : CVector(), iCurIdx ( 0 ), iNorm ( 0 ), + tCurAvResult ( TData ( 0 ) ) {} + CMovingAv ( const int iNeSi ) : CVector ( iNeSi ), iCurIdx ( 0 ), + iNorm ( 0 ), tCurAvResult ( TData ( 0 ) ) {} + CMovingAv ( const int iNeSi, const TData tInVa ) : + CVector ( iNeSi, tInVa ), iCurIdx ( 0 ), iNorm ( 0 ), + tCurAvResult ( TData ( 0 ) ) {} + + void Add ( const TData tNewD ); + inline TData GetAverage() + { + if ( this->iNorm == 0 ) + { + return TData ( 0 ); + } + else + { + return tCurAvResult / this->iNorm; + } + } + + virtual void Init ( const int iNewSize ); + void InitVec ( const int iNewSize, const int iNewVecSize ); + void Reset(); + bool IsInitialized() { return ( this->iNorm == this->iVectorSize ); } + +protected: + int iCurIdx; + int iNorm; + TData tCurAvResult; +}; + +template void CMovingAv::Init ( const int iNewSize ) +{ + iNorm = 0; + iCurIdx = 0; + tCurAvResult = TData ( 0 ); // only for scalars! + CVector::Init ( iNewSize ); +} + +template void CMovingAv::Reset() +{ + iNorm = 0; + iCurIdx = 0; + tCurAvResult = TData ( 0 ); // only for scalars! + CVector::Reset ( TData ( 0 ) ); +} + +template void CMovingAv::Add ( const TData tNewD ) +{ +/* + Optimized calculation of the moving average. We only add a new value and + subtract the old value from the result. We only need one addition and a + history buffer +*/ + // subtract oldest value + tCurAvResult -= this->pData[iCurIdx]; + + // add new value and write in memory + tCurAvResult += tNewD; + this->pData[iCurIdx] = tNewD; + + // increase position pointer and test if wrap + iCurIdx++; + if ( iCurIdx >= this->iVectorSize ) + { + iCurIdx = 0; + } + + // take care of norm + if ( this->iNorm < this->iVectorSize ) + { + this->iNorm++; + } +} + + +/******************************************************************************\ +* GUI utilities * +\******************************************************************************/ +// About dialog ---------------------------------------------------------------- +class CAboutDlg : public QDialog, private Ui_CAboutDlgBase +{ + Q_OBJECT + +public: + CAboutDlg ( QWidget* parent = 0 ); + + static QString GetVersionAndNameStr ( const bool bWithHtml = true ); +}; + + +// Help menu ------------------------------------------------------------------- +class CLlconHelpMenu : public QMenu +{ + Q_OBJECT + +public: + CLlconHelpMenu ( QWidget* parent = 0 ); + +protected: + CAboutDlg AboutDlg; + +public slots: + void OnHelpWhatsThis() { QWhatsThis::enterWhatsThisMode(); } + void OnHelpAbout() { AboutDlg.exec(); } +}; + + +/* Other Classes **************************************************************/ +// Stereo Signal Level Meter --------------------------------------------------- +class CStereoSignalLevelMeter +{ +public: + CStereoSignalLevelMeter() { Reset(); } + virtual ~CStereoSignalLevelMeter() {} + + void Update ( CVector& vecsAudio ); + double MicLevelLeft() { return CalcLogResult ( dCurLevelL ); } + double MicLevelRight() { return CalcLogResult ( dCurLevelR ); } + void Reset() { dCurLevelL = 0.0; dCurLevelR = 0.0; } + +protected: + double CalcLogResult ( const double& dLinearLevel ); + double UpdateCurLevel ( double dCurLevel, const short& sMax ); + + double dCurLevelL; + double dCurLevelR; +}; + +class CHostAddress +{ +public: + CHostAddress() : + InetAddr ( (quint32) 0 ), + iPort ( 0 ) {} + + CHostAddress ( const QHostAddress NInetAddr, + const quint16 iNPort ) : + InetAddr ( NInetAddr ), + iPort ( iNPort ) {} + + CHostAddress ( const CHostAddress& NHAddr ) : + InetAddr ( NHAddr.InetAddr ), + iPort ( NHAddr.iPort ) {} + + // copy and compare operators + CHostAddress& operator= ( const CHostAddress& NHAddr ) + { InetAddr = NHAddr.InetAddr; iPort = NHAddr.iPort; return *this; } + + bool operator== ( const CHostAddress& CompAddr ) // compare operator + { return ( ( CompAddr.InetAddr == InetAddr ) && ( CompAddr.iPort == iPort ) ); } + + QString GetIpAddressStringNoLastByte() const + { + // remove the last byte of the IP address + return InetAddr.toString().section ( ".", 0, 2 ) + ".x"; + } + + QHostAddress InetAddr; + quint16 iPort; +}; + +class CChannelShortInfo +{ +public: + CChannelShortInfo() : + iChanID ( 0 ), + iIpAddr ( 0 ), + strName ( "" ) {} + + CChannelShortInfo ( const int iNID, + const quint32 nIP, + const QString nN ) : + iChanID ( iNID ), + iIpAddr ( nIP ), + strName ( nN ) {} + + int iChanID; + quint32 iIpAddr; + QString strName; +}; + +enum EAudComprType +{ + CT_NONE = 0, + CT_CELT = 1 +}; + +enum EGetDataStat +{ + GS_BUFFER_OK, + GS_BUFFER_UNDERRUN, + GS_CHAN_NOW_DISCONNECTED, + GS_CHAN_NOT_CONNECTED +}; + +enum EGUIDesign +{ + GD_STANDARD = 0, + GD_ORIGINAL = 1 +}; + +class CNetworkTransportProps +{ +public: + CNetworkTransportProps() : + iBaseNetworkPacketSize ( 0 ), + iBlockSizeFact ( 0 ), + iNumAudioChannels ( 0 ), + iSampleRate ( 0 ), + eAudioCodingType ( CT_NONE ), + iAudioCodingArg ( 0 ) {} + + CNetworkTransportProps ( const uint32_t iNBNPS, + const uint16_t iNBSF, + const uint32_t iNNACH, + const uint32_t iNSR, + const EAudComprType eNACT, + const uint32_t iNVers, + const int32_t iNACA ) : + iBaseNetworkPacketSize ( iNBNPS ), + iBlockSizeFact ( iNBSF ), + iNumAudioChannels ( iNNACH ), + iSampleRate ( iNSR ), + eAudioCodingType ( eNACT ), + iVersion ( iNVers ), + iAudioCodingArg ( iNACA ) {} + + uint32_t iBaseNetworkPacketSize; + uint16_t iBlockSizeFact; + uint32_t iNumAudioChannels; + uint32_t iSampleRate; + EAudComprType eAudioCodingType; + uint32_t iVersion; + int32_t iAudioCodingArg; +}; + + +// Audio Reverbration ---------------------------------------------------------- +class CAudioReverb +{ +public: + CAudioReverb() {} + + void Init ( const int iSampleRate, const double rT60 = (double) 5.0 ); + void Clear(); + double ProcessSample ( const double input ); + +protected: + void setT60 ( const double rT60, const int iSampleRate ); + bool isPrime ( const int number ); + + CFIFO allpassDelays_[3]; + CFIFO combDelays_[4]; + double allpassCoefficient_; + double combCoefficient_[4]; +}; + + +// CRC ------------------------------------------------------------------------- +class CCRC +{ +public: + CCRC() : iPoly ( ( 1 << 5 ) | ( 1 << 12 ) ), iBitOutMask ( 1 << 16 ) + { Reset(); } + virtual ~CCRC() {} + + void Reset(); + void AddByte ( const uint8_t byNewInput ); + bool CheckCRC ( const uint32_t iCRC ) { return iCRC == GetCRC(); } + uint32_t GetCRC(); + +protected: + uint32_t iBitOutMask; + uint32_t iPoly; + uint32_t iStateShiftReg; +}; + + +// Mathematics utilities ------------------------------------------------------- +class LlconMath +{ +public: + static int round ( double x ) + { + return (int) ( ( x - floor ( x ) ) >= 0.5 ) ? ceil(x) : floor(x); + } +}; + + +// Precise time ---------------------------------------------------------------- +// needed for ping measurement +class CPreciseTime +{ +public: +#ifdef _WIN32 + // for the Windows version we have to define a minimum timer precision + // -> set it to 1 ms + CPreciseTime() { timeBeginPeriod ( 1 ); } + virtual ~CPreciseTime() { timeEndPeriod ( 1 ); } +#endif + + // precise time (on Windows the QTime is not precise enough) + int elapsed() + { +#ifdef _WIN32 + return timeGetTime(); +#else + return QTime().elapsed(); +#endif + } +}; + + +/******************************************************************************\ +* Cycle Time Variance Measurement * +\******************************************************************************/ +// use for, e.g., measuring the variance of a timer +class CCycleTimeVariance +{ +public: + CCycleTimeVariance() : iBlockLengthAtSystemSampleRate ( 0 ), + dIntervalTime ( 0.0 ), iNewValueBoundFactor ( 0 ) {} + virtual ~CCycleTimeVariance() {} + + void Init ( const int iNewBlockLengthAtSystemSampleRate, + const int iNewSystemSampleRate, + const int iHistoryLengthTime, + const int iNewNewValueBoundFactor = 4 ) + { + // store block size and new value bound factor + iBlockLengthAtSystemSampleRate = iNewBlockLengthAtSystemSampleRate; + iNewValueBoundFactor = iNewNewValueBoundFactor; + + // calculate interval time + dIntervalTime = static_cast ( + iBlockLengthAtSystemSampleRate ) * 1000 / iNewSystemSampleRate; + + // calculate actual moving average length and initialize buffer + RespTimeMoAvBuf.Init ( iHistoryLengthTime * + iNewSystemSampleRate / iNewBlockLengthAtSystemSampleRate ); + } + + int GetBlockLength() { return iBlockLengthAtSystemSampleRate; } + + void Reset() + { + TimeLastBlock = PreciseTime.elapsed(); + RespTimeMoAvBuf.Reset(); + } + + double Update() + { + // add time difference + const int CurTime = PreciseTime.elapsed(); + + // we want to calculate the standard deviation (we assume that the mean + // is correct at the block period time) + const double dCurAddVal = + static_cast ( CurTime - TimeLastBlock ) - dIntervalTime; + + if ( iNewValueBoundFactor > 0 ) + { + // check if new value is in range + if ( fabs ( dCurAddVal ) < ( iNewValueBoundFactor * dIntervalTime ) ) + { + // add squared value + RespTimeMoAvBuf.Add ( dCurAddVal * dCurAddVal ); + } + } + else + { + // new value bound is not used, add new value (add squared value) + RespTimeMoAvBuf.Add ( dCurAddVal * dCurAddVal ); + } + + // store old time value + TimeLastBlock = CurTime; + + return dCurAddVal; + } + + // return the standard deviation, for that we need to calculate + // the sqaure root + double GetStdDev() { return sqrt ( RespTimeMoAvBuf.GetAverage() ); } + + bool IsInitialized() { return RespTimeMoAvBuf.IsInitialized(); } + +protected: + CPreciseTime PreciseTime; + CMovingAv RespTimeMoAvBuf; + int TimeLastBlock; + int iBlockLengthAtSystemSampleRate; + double dIntervalTime; + int iNewValueBoundFactor; +}; + +#endif /* !defined ( UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) */