jamulus/src/util.h

1288 lines
36 KiB
C
Raw Normal View History

2011-06-11 20:18:46 +02:00
/******************************************************************************\
2019-03-24 09:30:30 +01:00
* Copyright (c) 2004-2019
2011-06-11 20:18:46 +02:00
*
* 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 <QHostAddress>
#include <QHostInfo>
#include <QMenu>
#include <QWhatsThis>
#include <QTextBrowser>
#include <QLabel>
2015-01-17 17:53:54 +01:00
#include <QCheckBox>
#include <QComboBox>
#include <QLineEdit>
#include <QDateTime>
#include <QFile>
#include <QDesktopServices>
#include <QUrl>
#include <QLocale>
2015-03-16 17:17:31 +01:00
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
# include <QElapsedTimer>
#endif
2011-06-11 20:18:46 +02:00
#include <vector>
2013-12-16 21:36:48 +01:00
#include <algorithm>
2011-06-11 20:18:46 +02:00
#include "global.h"
using namespace std; // because of the library: "vector"
#ifdef _WIN32
# include <windows.h>
# include <mmsystem.h>
#elif defined ( __APPLE__ ) || defined ( __MACOSX )
# include <mach/mach.h>
# include <mach/mach_error.h>
# include <mach/mach_time.h>
#else
# include <sys/time.h>
2013-01-08 20:26:05 +01:00
#endif
#include "ui_aboutdlgbase.h"
2011-06-11 20:18:46 +02:00
class CClient; // forward declaration of CClient
2011-06-11 20:18:46 +02:00
/* Definitions ****************************************************************/
#define METER_FLY_BACK 2
#define INVALID_MIDI_CH -1 // invalid MIDI channel definition
2011-06-11 20:18:46 +02:00
/* 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 );
2013-02-17 11:33:01 +01:00
// calculate the bit rate in bits per second from the number of coded bytes
inline int CalcBitRateBitsPerSecFromCodedBytes ( const int iCeltNumCodedBytes )
{
return ( SYSTEM_SAMPLE_RATE_HZ * iCeltNumCodedBytes * 8 ) /
SYSTEM_FRAME_SIZE_SAMPLES;
}
2011-06-11 20:18:46 +02:00
/******************************************************************************\
* CVector Base Class *
\******************************************************************************/
template<class TData> class CVector : public std::vector<TData>
{
public:
CVector() {}
CVector ( const int iNeSi ) { Init ( iNeSi ); }
CVector ( const int iNeSi,
const TData tInVa ) { Init ( iNeSi, tInVa ); }
2011-06-11 20:18:46 +02:00
void Init ( const int iNewSize );
// use this init to give all elements a defined value
void Init ( const int iNewSize,
const TData tIniVal );
// set all values to the given reset value
void Reset ( const TData tResetVal )
{
std::fill ( this->begin(), this->end(), tResetVal );
}
void Enlarge ( const int iAddedSize )
{
std::vector<TData>::resize ( std::vector<TData>::size() + iAddedSize );
}
2011-06-11 20:18:46 +02:00
void Add ( const TData& tI )
{
Enlarge ( 1 );
std::vector<TData>::back() = tI;
}
2011-06-11 20:18:46 +02:00
int StringFiFoWithCompare ( const QString strNewValue,
const bool bDoAdding = true );
// this function simply converts the type of size to integer
2018-03-22 21:47:08 +01:00
inline int Size() const { return static_cast<int> ( std::vector<TData>::size() ); }
2011-06-11 20:18:46 +02:00
// 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 > Size() - 1 ) )
2011-06-11 20:18:46 +02:00
{
DebugError ( "Writing vector out of bounds", "Vector size",
Size(), "New parameter", iPos );
2011-06-11 20:18:46 +02:00
}
#endif
return std::vector<TData>::operator[] ( iPos );
}
2011-06-11 20:18:46 +02:00
inline TData operator[] ( const int iPos ) const
{
#ifdef _DEBUG_
if ( ( iPos < 0 ) || ( iPos > Size() - 1 ) )
2011-06-11 20:18:46 +02:00
{
DebugError ( "Reading vector out of bounds", "Vector size",
Size(), "New parameter", iPos );
2011-06-11 20:18:46 +02:00
}
#endif
return std::vector<TData>::operator[] ( iPos );
}
2011-06-11 20:18:46 +02:00
inline CVector<TData>& operator= ( const CVector<TData>& vecI )
{
#ifdef _DEBUG_
// vectors which shall be copied MUST have same size!
if ( vecI.Size() != Size() )
2011-06-11 20:18:46 +02:00
{
DebugError ( "Vector operator=() different size", "Vector size",
Size(), "New parameter", vecI.Size() );
2011-06-11 20:18:46 +02:00
}
#endif
std::vector<TData>::operator= ( vecI );
2011-06-11 20:18:46 +02:00
return *this;
}
};
/* Implementation *************************************************************/
template<class TData> void CVector<TData>::Init ( const int iNewSize )
{
// clear old buffer and reserve memory for new buffer
std::vector<TData>::clear();
std::vector<TData>::resize ( iNewSize );
2011-06-11 20:18:46 +02:00
}
template<class TData> void CVector<TData>::Init ( const int iNewSize,
2011-06-11 20:18:46 +02:00
const TData tIniVal )
{
// call actual init routine and reset all values to the given value
2011-06-11 20:18:46 +02:00
Init ( iNewSize );
Reset ( tIniVal );
}
// note: this is only supported for string vectors
template<class TData> int CVector<TData>::StringFiFoWithCompare ( const QString strNewValue,
const bool bDoAdding )
{
const int iVectorSize = Size();
CVector<QString> vstrTempList ( iVectorSize, "" );
// init with illegal index per definition
int iOldIndex = -1;
// init temporary list count (may be overwritten later on)
int iTempListCnt = 0;
if ( bDoAdding )
{
// store the new element in the current storage list at
// the top, make sure we do not have more than allowed stored
// elements
vstrTempList[0] = strNewValue;
iTempListCnt = 1;
}
for ( int iIdx = 0; iIdx < iVectorSize; iIdx++ )
{
// first check if we still have space in our data storage
if ( iTempListCnt < iVectorSize )
{
// only add old element if it is not the same as the
// selected one
if ( operator[] ( iIdx ).compare ( strNewValue ) )
{
vstrTempList[iTempListCnt] = operator[] ( iIdx );
iTempListCnt++;
}
else
{
iOldIndex = iIdx;
}
}
}
// copy new generated list to data base
*this = vstrTempList;
return iOldIndex;
}
2011-06-11 20:18:46 +02:00
/******************************************************************************\
* CFIFO Class (First In, First Out) *
\******************************************************************************/
template<class TData> class CFIFO : public CVector<TData>
{
public:
CFIFO() : iCurIdx ( 0 ) {}
CFIFO ( const int iNeSi ) : CVector<TData> ( iNeSi ), iCurIdx ( 0 ) {}
2011-06-11 20:18:46 +02:00
CFIFO ( const int iNeSi, const TData tInVa ) :
CVector<TData> ( iNeSi, tInVa ), iCurIdx ( 0 ) {}
void Add ( const TData tNewD );
inline TData Get() { return CVector<TData>::operator[] ( iCurIdx ); }
2011-06-11 20:18:46 +02:00
virtual void Init ( const int iNewSize );
virtual void Init ( const int iNewSize,
const TData tIniVal );
2011-06-11 20:18:46 +02:00
protected:
int iCurIdx;
};
template<class TData> void CFIFO<TData>::Init ( const int iNewSize )
{
iCurIdx = 0;
CVector<TData>::Init ( iNewSize );
}
template<class TData> void CFIFO<TData>::Init ( const int iNewSize,
2011-06-11 20:18:46 +02:00
const TData tIniVal )
{
iCurIdx = 0;
CVector<TData>::Init ( iNewSize, tIniVal );
}
template<class TData> void CFIFO<TData>::Add ( const TData tNewD )
{
CVector<TData>::operator[] ( iCurIdx ) = tNewD;
2011-06-11 20:18:46 +02:00
// increment index and check for wrap around
2011-06-11 20:18:46 +02:00
iCurIdx++;
if ( iCurIdx >= CVector<TData>::Size() )
2011-06-11 20:18:46 +02:00
{
iCurIdx = 0;
}
}
/******************************************************************************\
* CMovingAv Class (Moving Average) *
\******************************************************************************/
template<class TData> class CMovingAv : public CVector<TData>
{
public:
CMovingAv() :
CVector<TData>(),
iCurIdx ( 0 ),
iNorm ( 0 ),
dCurAvResult ( 0 ),
dNoDataResult ( 0 ) {}
void Add ( const TData tNewD );
void Init ( const int iNewSize,
const double dNNoDRes = 0 );
void Reset();
inline double GetAverage()
{
// make sure we do not divide by zero
if ( iNorm == 0 )
2011-06-11 20:18:46 +02:00
{
return dNoDataResult;
}
else
{
return dCurAvResult / iNorm;
2011-06-11 20:18:46 +02:00
}
}
double InitializationState() const
{
// make sure we do not divide by zero
if ( CVector<TData>::Size() != 0 )
{
return static_cast<double> ( iNorm ) / CVector<TData>::Size();
}
else
{
return 0;
}
}
2011-06-11 20:18:46 +02:00
protected:
int iCurIdx;
int iNorm;
double dCurAvResult;
double dNoDataResult;
};
template<class TData> void CMovingAv<TData>::Init ( const int iNewSize,
const double dNNoDRes )
{
iNorm = 0;
iCurIdx = 0;
dCurAvResult = 0; // only for scalars!
dNoDataResult = dNNoDRes;
CVector<TData>::Init ( iNewSize );
}
template<class TData> void CMovingAv<TData>::Reset()
{
iNorm = 0;
iCurIdx = 0;
dCurAvResult = 0; // only for scalars!
CVector<TData>::Reset ( TData ( 0 ) );
}
template<class TData> void CMovingAv<TData>::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.
*/
2011-06-11 20:18:46 +02:00
// subtract oldest value
dCurAvResult -= CVector<TData>::operator[] ( iCurIdx );
2011-06-11 20:18:46 +02:00
// add new value and write in memory
dCurAvResult += tNewD;
CVector<TData>::operator[] ( iCurIdx ) = tNewD;
2011-06-11 20:18:46 +02:00
// increase position pointer and test if wrap
iCurIdx++;
if ( iCurIdx >= CVector<TData>::Size() )
2011-06-11 20:18:46 +02:00
{
iCurIdx = 0;
}
// take care of norm
if ( iNorm < CVector<TData>::Size() )
2011-06-11 20:18:46 +02:00
{
iNorm++;
2011-06-11 20:18:46 +02:00
}
}
/******************************************************************************\
* 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 );
};
2015-01-17 17:53:54 +01:00
// Licence dialog --------------------------------------------------------------
class CLicenceDlg : public QDialog
{
Q_OBJECT
public:
CLicenceDlg ( QWidget* parent = 0 );
protected:
QPushButton* butAccept;
public slots:
void OnAgreeStateChanged ( int value ) { butAccept->setEnabled ( value == Qt::Checked ); }
};
// Musician profile dialog -----------------------------------------------------
class CMusProfDlg : public QDialog
{
Q_OBJECT
public:
CMusProfDlg ( CClient* pNCliP,
QWidget* parent = 0 );
protected:
virtual void showEvent ( QShowEvent* );
QLineEdit* pedtAlias;
QComboBox* pcbxInstrument;
QComboBox* pcbxCountry;
QLineEdit* pedtCity;
QComboBox* pcbxSkill;
CClient* pClient;
public slots:
void OnAliasTextChanged ( const QString& strNewName );
void OnInstrumentActivated ( int iCntryListItem );
void OnCountryActivated ( int iCntryListItem );
void OnCityTextChanged ( const QString& strNewName );
void OnSkillActivated ( int iCntryListItem );
};
2011-06-11 20:18:46 +02:00
// Help menu -------------------------------------------------------------------
2013-03-24 16:42:23 +01:00
class CHelpMenu : public QMenu
2011-06-11 20:18:46 +02:00
{
Q_OBJECT
public:
2013-03-24 16:42:23 +01:00
CHelpMenu ( QWidget* parent = 0 );
2011-06-11 20:18:46 +02:00
protected:
CAboutDlg AboutDlg;
public slots:
void OnHelpWhatsThis() { QWhatsThis::enterWhatsThisMode(); }
void OnHelpAbout() { AboutDlg.exec(); }
void OnHelpDownloadLink()
{ QDesktopServices::openUrl ( QUrl ( LLCON_DOWNLOAD_URL ) ); }
};
/******************************************************************************\
* Other Classes/Enums *
2011-06-11 20:18:46 +02:00
\******************************************************************************/
// Audio channel configuration -------------------------------------------------
enum EAudChanConf
{
// used for settings -> enum values must be fixed!
CC_MONO = 0,
CC_MONO_IN_STEREO_OUT = 1,
CC_STEREO = 2
};
// Audio compression type enum -------------------------------------------------
enum EAudComprType
{
// used for protocol -> enum values must be fixed!
CT_NONE = 0,
2013-02-16 19:17:12 +01:00
CT_CELT = 1,
CT_OPUS = 2
};
// Audio quality enum ----------------------------------------------------------
enum EAudioQuality
{
// used for settings and the comobo box index -> enum values must be fixed!
AQ_LOW = 0,
AQ_NORMAL = 1,
AQ_HIGH = 2
};
// Get data status enum --------------------------------------------------------
enum EGetDataStat
{
GS_BUFFER_OK,
GS_BUFFER_UNDERRUN,
GS_CHAN_NOW_DISCONNECTED,
GS_CHAN_NOT_CONNECTED
};
// GUI design enum -------------------------------------------------------------
enum EGUIDesign
{
// used for settings -> enum values must be fixed!
GD_STANDARD = 0,
GD_ORIGINAL = 1
};
2015-01-18 22:20:41 +01:00
// Server licence type enum ----------------------------------------------------
enum ELicenceType
{
// used for protocol -> enum values must be fixed!
LT_NO_LICENCE = 0,
LT_CREATIVECOMMONS = 1
2015-01-18 22:20:41 +01:00
};
// Skill level enum ------------------------------------------------------------
enum ESkillLevel
{
// used for protocol -> enum values must be fixed!
SL_NOT_SET = 0,
SL_BEGINNER = 1,
SL_INTERMEDIATE = 2,
SL_PROFESSIONAL = 3
};
// define the GUI RGB colors for each skill level
#define RGBCOL_R_SL_NOT_SET 255
#define RGBCOL_G_SL_NOT_SET 255
#define RGBCOL_B_SL_NOT_SET 255
#define RGBCOL_R_SL_BEGINNER 255
#define RGBCOL_G_SL_BEGINNER 255
#define RGBCOL_B_SL_BEGINNER 200
#define RGBCOL_R_SL_INTERMEDIATE 225
#define RGBCOL_G_SL_INTERMEDIATE 255
#define RGBCOL_B_SL_INTERMEDIATE 225
#define RGBCOL_R_SL_SL_PROFESSIONAL 255
#define RGBCOL_G_SL_SL_PROFESSIONAL 225
#define RGBCOL_B_SL_SL_PROFESSIONAL 225
2011-06-11 20:18:46 +02:00
// Stereo signal level meter ---------------------------------------------------
class CStereoSignalLevelMeter
{
public:
CStereoSignalLevelMeter() { Reset(); }
2014-01-05 21:47:35 +01:00
void Update ( const CVector<short>& vecsAudio );
2011-06-11 20:18:46 +02:00
double MicLevelLeft() { return CalcLogResult ( dCurLevelL ); }
double MicLevelRight() { return CalcLogResult ( dCurLevelR ); }
void Reset()
{
dCurLevelL = 0.0;
dCurLevelR = 0.0;
}
2011-06-11 20:18:46 +02:00
protected:
double CalcLogResult ( const double& dLinearLevel );
double UpdateCurLevel ( double dCurLevel,
const short& sMax );
2011-06-11 20:18:46 +02:00
double dCurLevelL;
double dCurLevelR;
};
// Host address ----------------------------------------------------------------
class CHostAddress
{
public:
enum EStringMode
{
SM_IP_PORT,
SM_IP_NO_LAST_BYTE,
SM_IP_NO_LAST_BYTE_PORT
};
CHostAddress() :
InetAddr ( static_cast<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 operator
CHostAddress& operator= ( const CHostAddress& NHAddr )
{
InetAddr = NHAddr.InetAddr;
iPort = NHAddr.iPort;
return *this;
}
// compare operator
2014-01-06 17:06:04 +01:00
bool operator== ( const CHostAddress& CompAddr ) const
2011-06-11 20:18:46 +02:00
{
return ( ( CompAddr.InetAddr == InetAddr ) &&
( CompAddr.iPort == iPort ) );
}
QString toString ( const EStringMode eStringMode = SM_IP_PORT ) const
{
QString strReturn = InetAddr.toString();
// special case: for local host address, we do not replace the last byte
if ( ( ( eStringMode == SM_IP_NO_LAST_BYTE ) ||
( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) ) &&
( InetAddr != QHostAddress ( QHostAddress::LocalHost ) ) )
{
// replace last byte by an "x"
strReturn = strReturn.section ( ".", 0, 2 ) + ".x";
}
if ( ( eStringMode == SM_IP_PORT ) ||
( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) )
{
// add port number after a semicolon
strReturn += ":" + QString().setNum ( iPort );
}
return strReturn;
}
QHostAddress InetAddr;
quint16 iPort;
};
// Instrument picture data base ------------------------------------------------
// this is a pure static class
class CInstPictures
2011-06-11 20:18:46 +02:00
{
public:
enum EInstCategory
{
IC_OTHER_INSTRUMENT,
IC_WIND_INSTRUMENT,
IC_STRING_INSTRUMENT,
IC_PLUCKING_INSTRUMENT,
IC_PERCUSSION_INSTRUMENT,
2015-02-04 08:02:40 +01:00
IC_KEYBOARD_INSTRUMENT,
IC_MULTIPLE_INSTRUMENT
};
2011-06-11 20:18:46 +02:00
// per definition: the very first instrument is the "not used" instrument
static int GetNotUsedInstrument() { return 0; }
static bool IsNotUsedInstrument ( const int iInstrument ) { return iInstrument == 0; }
2011-06-11 20:18:46 +02:00
static int GetNumAvailableInst() { return GetTable().Size(); }
static QString GetResourceReference ( const int iInstrument );
static QString GetName ( const int iInstrument );
// TODO make use of instrument category (not yet implemented)
protected:
class CInstPictProps
{
public:
CInstPictProps() :
strName ( "" ),
strResourceReference ( "" ),
eInstCategory ( IC_OTHER_INSTRUMENT ) {}
CInstPictProps ( const QString NsName,
const QString NsResRef,
const EInstCategory NeInstCat ) :
strName ( NsName ),
strResourceReference ( NsResRef ),
eInstCategory ( NeInstCat ) {}
QString strName;
QString strResourceReference;
EInstCategory eInstCategory;
};
static bool IsInstIndexInRange ( const int iIdx );
static CVector<CInstPictProps>& GetTable();
2011-06-11 20:18:46 +02:00
};
// Country flag icon data base -------------------------------------------------
// this is a pure static class
class CCountyFlagIcons
{
public:
static QString GetResourceReference ( const QLocale::Country eCountry );
};
// Info of a channel -----------------------------------------------------------
class CChannelCoreInfo
2013-02-10 09:52:19 +01:00
{
public:
CChannelCoreInfo() :
strName ( "" ),
eCountry ( QLocale::AnyCountry ),
strCity ( "" ),
iInstrument ( CInstPictures::GetNotUsedInstrument() ),
eSkillLevel ( SL_NOT_SET ) {}
CChannelCoreInfo ( const QString NsName,
const QLocale::Country& NeCountry,
const QString& NsCity,
const int NiInstrument,
const ESkillLevel NeSkillLevel ) :
strName ( NsName ),
2013-02-10 09:52:19 +01:00
eCountry ( NeCountry ),
strCity ( NsCity ),
iInstrument ( NiInstrument ),
eSkillLevel ( NeSkillLevel ) {}
2013-02-10 09:52:19 +01:00
CChannelCoreInfo ( const CChannelCoreInfo& NCorInf ) :
strName ( NCorInf.strName ),
eCountry ( NCorInf.eCountry ),
strCity ( NCorInf.strCity ),
iInstrument ( NCorInf.iInstrument ),
eSkillLevel ( NCorInf.eSkillLevel ) {}
// compare operator
bool operator!= ( const CChannelCoreInfo& CompChanInfo )
{
return ( ( CompChanInfo.strName != strName ) ||
( CompChanInfo.eCountry != eCountry ) ||
( CompChanInfo.strCity != strCity ) ||
( CompChanInfo.iInstrument != iInstrument ) ||
( CompChanInfo.eSkillLevel != eSkillLevel ) );
}
// fader tag text (channel name)
QString strName;
2013-02-10 09:52:19 +01:00
// country in which the client is located
QLocale::Country eCountry;
// city in which the client is located
QString strCity;
// instrument ID of the client (which instrument is he/she playing)
int iInstrument;
// skill level of the musician
ESkillLevel eSkillLevel;
};
class CChannelInfo : public CChannelCoreInfo
{
public:
CChannelInfo() :
2013-02-13 19:25:56 +01:00
bOnlyNameIsUsed ( false ),
iChanID ( 0 ),
2013-02-13 19:25:56 +01:00
iIpAddr ( 0 ) {}
CChannelInfo ( const int NiID,
const quint32 NiIP,
const CChannelCoreInfo& NCorInf ) :
CChannelCoreInfo ( NCorInf ),
2013-02-13 19:25:56 +01:00
bOnlyNameIsUsed ( false ),
iChanID ( NiID ),
2013-02-13 19:25:56 +01:00
iIpAddr ( NiIP ) {}
CChannelInfo ( const int NiID,
const quint32 NiIP,
const QString NsName,
const QLocale::Country& NeCountry,
const QString& NsCity,
const int NiInstrument,
const ESkillLevel NeSkillLevel ) :
CChannelCoreInfo ( NsName,
NeCountry,
NsCity,
NiInstrument,
NeSkillLevel ),
2013-02-13 19:25:56 +01:00
bOnlyNameIsUsed ( false ),
iChanID ( NiID ),
2013-02-13 19:25:56 +01:00
iIpAddr ( NiIP ) {}
QString GenNameForDisplay() const
{
// if text is empty, show IP address instead
if ( strName.isEmpty() )
{
// convert IP address to text and show it (use dummy port number
// since it is not used here)
const CHostAddress TempAddr =
CHostAddress ( QHostAddress ( iIpAddr ), 0 );
return TempAddr.toString ( CHostAddress::SM_IP_NO_LAST_BYTE );
}
else
{
// show name of channel
return strName;
}
}
// #### COMPATIBILITY OLD VERSION, TO BE REMOVED ####
CChannelInfo ( const int NiID,
const quint32 NiIP,
const QString NsName ) :
CChannelCoreInfo ( NsName,
QLocale::AnyCountry,
"",
CInstPictures::GetNotUsedInstrument(),
SL_NOT_SET ),
2013-02-13 19:25:56 +01:00
bOnlyNameIsUsed ( true ),
iChanID ( NiID ),
2013-02-13 19:25:56 +01:00
iIpAddr ( NiIP ) {}
// in old versions, the name was the only client info -> to be removed
// when compatiblility to old versions is removed
bool bOnlyNameIsUsed;
// ID of the channel
int iChanID;
// IP address of the channel
quint32 iIpAddr;
2013-02-10 09:52:19 +01:00
};
2011-06-11 20:18:46 +02:00
// Server info -----------------------------------------------------------------
class CServerCoreInfo
{
public:
CServerCoreInfo() :
iLocalPortNumber ( 0 ),
strName ( "" ),
strTopic ( "" ),
eCountry ( QLocale::AnyCountry ),
strCity ( "" ),
iMaxNumClients ( 0 ),
bPermanentOnline ( false ) {}
CServerCoreInfo (
const quint16 NLocPort,
const QString& NsName,
const QString& NsTopic,
const QLocale::Country& NeCountry,
const QString& NsCity,
const int NiMaxNumClients,
const bool NbPermOnline) :
iLocalPortNumber ( NLocPort ),
strName ( NsName ),
strTopic ( NsTopic ),
eCountry ( NeCountry ),
strCity ( NsCity ),
iMaxNumClients ( NiMaxNumClients ),
bPermanentOnline ( NbPermOnline ) {}
// local port number of the server
quint16 iLocalPortNumber;
// name of the server
QString strName;
// topic of the current jam session or server
QString strTopic;
// country in which the server is located
QLocale::Country eCountry;
// city in which the server is located
QString strCity;
// maximum number of clients which can connect to the server at the same
// time
int iMaxNumClients;
// is the server permanently online or not (flag)
bool bPermanentOnline;
};
class CServerInfo : public CServerCoreInfo
{
public:
CServerInfo() :
HostAddr ( CHostAddress() ) {}
2011-06-11 20:18:46 +02:00
CServerInfo (
const CHostAddress& NHAddr,
const quint16 NLocPort,
const QString& NsName,
const QString& NsTopic,
const QLocale::Country& NeCountry,
const QString& NsCity,
const int NiMaxNumClients,
const bool NbPermOnline) :
CServerCoreInfo ( NLocPort,
NsName,
NsTopic,
NeCountry,
NsCity,
NiMaxNumClients,
NbPermOnline ), HostAddr ( NHAddr ) {}
// internet address of the server
CHostAddress HostAddr;
};
// Network transport properties ------------------------------------------------
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;
};
// Network utility functions ---------------------------------------------------
2013-03-24 12:38:00 +01:00
class NetworkUtil
2011-06-11 20:18:46 +02:00
{
public:
2012-07-09 14:48:29 +02:00
static bool ParseNetworkAddress ( QString strAddress,
CHostAddress& HostAddress );
2011-06-11 20:18:46 +02:00
};
// Operating system utility functions ------------------------------------------
class COSUtil
{
public:
enum EOpSystemType
{
// used for protocol -> enum values must be fixed!
OT_WINDOWS = 0,
OT_MAC_OS = 1,
OT_LINUX = 2,
OT_ANDROID = 3,
OT_I_OS = 4,
OT_UNIX = 5
};
static QString GetOperatingSystemString ( const EOpSystemType eOSType )
{
switch ( eOSType )
{
case OT_WINDOWS: return "Windows"; break;
case OT_MAC_OS: return "MacOS"; break;
case OT_LINUX: return "Linux"; break;
case OT_ANDROID: return "Android"; break;
case OT_I_OS: return "iOS"; break;
case OT_UNIX: return "Unix"; break;
2014-02-21 22:33:25 +01:00
default: return "Unknown"; break;
}
}
static EOpSystemType GetOperatingSystem()
{
#ifdef _WIN32
return OT_WINDOWS;
#elif defined ( __APPLE__ ) || defined ( __MACOSX )
return OT_MAC_OS;
2014-02-21 22:29:33 +01:00
#elif defined ( ANDROID )
return OT_ANDROID;
#else
return OT_LINUX;
#endif
}
};
2011-06-11 20:18:46 +02:00
// Audio reverbration ----------------------------------------------------------
class CAudioReverb
{
public:
CAudioReverb() {}
void Init ( const int iSampleRate, const double rT60 = (double) 1.1 );
void Clear();
void ProcessSample ( int16_t& iInputOutputLeft,
int16_t& iInputOutputRight,
const double dAttenuation );
2011-06-11 20:18:46 +02:00
protected:
void setT60 ( const double rT60, const int iSampleRate );
bool isPrime ( const int number );
class COnePole
{
public:
COnePole() : dA ( 0 ), dB ( 0 ) { Reset(); }
void setPole ( const double dPole );
double Calc ( const double dIn );
void Reset() { dLastSample = 0; }
protected:
double dA;
double dB;
double dLastSample;
};
CFIFO<double> allpassDelays[3];
CFIFO<double> combDelays[4];
COnePole combFilters[4];
CFIFO<double> outLeftDelay;
CFIFO<double> outRightDelay;
double allpassCoefficient;
double combCoefficient[4];
2011-06-11 20:18:46 +02:00
};
// CRC -------------------------------------------------------------------------
class CCRC
{
public:
CCRC() : iPoly ( ( 1 << 5 ) | ( 1 << 12 ) ), iBitOutMask ( 1 << 16 )
{ Reset(); }
void Reset();
void AddByte ( const uint8_t byNewInput );
bool CheckCRC ( const uint32_t iCRC ) { return iCRC == GetCRC(); }
uint32_t GetCRC();
protected:
uint32_t iPoly;
uint32_t iBitOutMask;
uint32_t iStateShiftReg;
};
// Mathematics utilities -------------------------------------------------------
2013-03-24 12:38:00 +01:00
class MathUtils
2011-06-11 20:18:46 +02:00
{
public:
static int round ( double x )
{
return (int) ( ( x - floor ( x ) ) >= 0.5 ) ? ceil(x) : floor(x);
}
static void UpDownIIR1 ( double& dOldValue,
const double& dNewValue,
const double& dWeightUp,
const double& dWeightDown )
{
// different IIR weights for up and down direction
if ( dNewValue < dOldValue )
{
dOldValue =
2014-01-06 16:33:53 +01:00
dOldValue * dWeightDown + ( 1.0 - dWeightDown ) * dNewValue;
}
else
{
dOldValue =
2014-01-06 16:33:53 +01:00
dOldValue * dWeightUp + ( 1.0 - dWeightUp ) * dNewValue;
}
}
static int DecideWithHysteresis ( const double dValue,
const int iOldValue,
const double dHysteresis )
{
// apply hysteresis
if ( dValue > static_cast<double> ( iOldValue ) )
{
return round ( dValue - dHysteresis );
}
else
{
return round ( dValue + dHysteresis );
}
}
2011-06-11 20:18:46 +02:00
};
// Precise time ----------------------------------------------------------------
// required 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();
#elif defined ( __APPLE__ ) || defined ( __MACOSX )
return mach_absolute_time() / 1000000; // convert ns in ms
#else
timespec tp;
clock_gettime ( CLOCK_MONOTONIC, &tp );
return tp.tv_sec * 1000 + tp.tv_nsec / 1000000; // convert ns in ms and add the seconds part
2011-06-11 20:18:46 +02:00
#endif
}
};
2015-03-13 18:10:30 +01:00
// Timing measurement ----------------------------------------------------------
// intended for debugging the timing jitter of the sound card or server timer
2015-03-16 17:17:31 +01:00
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
2015-03-13 18:10:30 +01:00
class CTimingMeas
{
public:
CTimingMeas ( const int iNNMeas, const QString strNFName = "" ) :
iNumMeas ( iNNMeas ), vElapsedTimes ( iNNMeas ), strFileName ( strNFName ) { Reset(); }
void Reset() { iCnt = -1; }
void Measure()
{
// exclude the very first measurement (initialization phase)
if ( iCnt == -1 )
{
iCnt = 0;
}
else
{
// store current measurement
vElapsedTimes[iCnt++] = ElapsedTimer.nsecsElapsed();
// reset count if number of measurements are done
if ( iCnt >= iNumMeas )
{
iCnt = 0;
// store results in a file if file name is given
if ( !strFileName.isEmpty() )
{
QFile File ( strFileName );
if ( File.open ( QIODevice::WriteOnly | QIODevice::Text ) )
{
QTextStream streamFile ( &File );
for ( int i = 0; i < iNumMeas; i++ )
{
// convert ns in ms and store the value
2015-11-20 13:09:16 +01:00
streamFile << i << " " << static_cast<double> ( vElapsedTimes[i] ) / 1000000 << endl;
2015-03-13 18:10:30 +01:00
}
}
}
}
}
ElapsedTimer.start();
}
protected:
int iNumMeas;
CVector<int> vElapsedTimes;
QString strFileName;
QElapsedTimer ElapsedTimer;
int iCnt;
};
2015-03-16 17:17:31 +01:00
#endif
2015-03-13 18:10:30 +01:00
2011-06-11 20:18:46 +02:00
/******************************************************************************\
* Statistics *
\******************************************************************************/
// Error rate measurement ------------------------------------------------------
class CErrorRate
{
public:
CErrorRate() {}
void Init ( const int iHistoryLength,
const bool bNBlockOnDoubleErr = false )
{
// initialize buffer (use "no data result" of 1.0 which stands for the
// worst error rate possible)
ErrorsMovAvBuf.Init ( iHistoryLength, 1.0 );
bPreviousState = true;
// store setting
bBlockOnDoubleErrors = bNBlockOnDoubleErr;
}
void Reset()
{
ErrorsMovAvBuf.Reset();
bPreviousState = true;
}
void Update ( const bool bState )
{
// if two states were false, do not use the new value
if ( bBlockOnDoubleErrors && bPreviousState && bState )
{
return;
}
// add errors as values 0 and 1 to get correct error rate average
if ( bState )
{
ErrorsMovAvBuf.Add ( 1 );
}
else
{
ErrorsMovAvBuf.Add ( 0 );
}
// store state
bPreviousState = bState;
}
double GetAverage() { return ErrorsMovAvBuf.GetAverage(); }
double InitializationState() { return ErrorsMovAvBuf.InitializationState(); }
2011-06-11 20:18:46 +02:00
protected:
CMovingAv<char> ErrorsMovAvBuf;
bool bBlockOnDoubleErrors;
bool bPreviousState;
};
Add recording support with Reaper Project generation Includes the following changes * Initial .gitignore Administrative * Fix up warning message * Not all Windows file systems are case insensitive Bugfixes * (Qt5) Use QCoreApplication for headless Possible solution to get the application to run as a headless server but it loses the nice history graph, so not ideal. * Avoid ESC closing chat Because ESC shouldn't close the chat window. Or the main app window. * Add console logging support for Windows Whilst looking for the headless support, I found this idea for Windows logging. New improved version. This makes far fewer changes. ---- * Add recording support with Reaper Project generation The main feature! * New -r option to enable recording of PCM files and conversion to Reaper RPP with WAV files * New -R option to set the directory in which to create recording sessions You need to specify the -R option, there's no default... so I guess -r and -R could be combined. * New -T option to convert a session directory with PCM files into a Reaper RPP with WAV files You can use -T on "failed" sessions, if the -r option captures the PCMs but the RPP converter doesn't run for some reaon. (It was useful during development, maybe less so once things seem stable.) The recorder is implemented as a new thread with queuing from the main "real time" server thread. When a new client connects or if its audio format changes (e.g. mono to stereo), a new RIFF WAVE file is started. Each frame of decompressed audio for each client written out as LPCM to the file. When the client disconnects, the RIFF WAVE headers are updated to reflect the file length. Once all clients disconnect, the session is considered ended and a Reaper RPP file is written.
2019-04-03 19:12:45 +02:00
class ConsoleWriterFactory
{
public:
ConsoleWriterFactory() : ptsConsole ( nullptr ) { }
QTextStream* get();
private:
QTextStream* ptsConsole;
};
2011-06-11 20:18:46 +02:00
#endif /* !defined ( UTIL_HOIH934256GEKJH98_3_43445KJIUHF1912__INCLUDED_ ) */