1 /******************************************************************************\
2  * Copyright (c) 2004-2020
3  *
4  * Author(s):
5  *  Volker Fischer
6  *
7  ******************************************************************************
8  *
9  * This program is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the Free Software
11  * Foundation; either version 2 of the License, or (at your option) any later
12  * version.
13  *
14  * This program is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17  * details.
18  *
19  * You should have received a copy of the GNU General Public License along with
20  * this program; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
22  *
23 \******************************************************************************/
24 
25 #pragma once
26 
27 #include <QCoreApplication>
28 #include <QUdpSocket>
29 #include <QHostAddress>
30 #include <QHostInfo>
31 #ifndef HEADLESS
32 #    include <QMessageBox>
33 #    include <QMenu>
34 #    include <QWhatsThis>
35 #    include <QTextBrowser>
36 #    include <QLabel>
37 #    include <QCheckBox>
38 #    include <QComboBox>
39 #    include <QLineEdit>
40 #    include <QDateTime>
41 #    include <QDesktopServices>
42 #    include <QKeyEvent>
43 #    include "ui_aboutdlgbase.h"
44 #endif
45 #include <QFile>
46 #include <QDirIterator>
47 #include <QTranslator>
48 #include <QLibraryInfo>
49 #include <QUrl>
50 #include <QLocale>
51 #include <QElapsedTimer>
52 #include <vector>
53 #include <algorithm>
54 #include "global.h"
55 #ifdef _WIN32
56 #    include <winsock2.h>
57 #    include <ws2tcpip.h>
58 #    include <windows.h>
59 #    include <mmsystem.h>
60 #elif defined( __APPLE__ ) || defined( __MACOSX )
61 #    include <mach/mach.h>
62 #    include <mach/mach_error.h>
63 #    include <mach/mach_time.h>
64 #else
65 #    include <sys/time.h>
66 #endif
67 
68 class CClient; // forward declaration of CClient
69 
70 /* Definitions ****************************************************************/
71 #define METER_FLY_BACK  2
72 #define INVALID_MIDI_CH -1 // invalid MIDI channel definition
73 
74 /* Global functions ***********************************************************/
75 // converting float to short
Float2Short(const float fInput)76 inline short Float2Short ( const float fInput )
77 {
78     // lower bound
79     if ( fInput < _MINSHORT )
80     {
81         return _MINSHORT;
82     }
83 
84     // upper bound
85     if ( fInput > _MAXSHORT )
86     {
87         return _MAXSHORT;
88     }
89 
90     return static_cast<short> ( fInput );
91 }
92 
93 // calculate the bit rate in bits per second from the number of coded bytes
CalcBitRateBitsPerSecFromCodedBytes(const int iCeltNumCodedBytes,const int iFrameSize)94 inline int CalcBitRateBitsPerSecFromCodedBytes ( const int iCeltNumCodedBytes, const int iFrameSize )
95 {
96     return ( SYSTEM_SAMPLE_RATE_HZ * iCeltNumCodedBytes * 8 ) / iFrameSize;
97 }
98 
99 QString GetVersionAndNameStr ( const bool bWithHtml = true );
100 QString MakeClientNameTitle ( QString win, QString client );
101 
102 /******************************************************************************\
103 * CVector Base Class                                                           *
104 \******************************************************************************/
105 template<class TData>
106 class CVector : public std::vector<TData>
107 {
108 public:
CVector()109     CVector() {}
CVector(const int iNeSi)110     CVector ( const int iNeSi ) { Init ( iNeSi ); }
CVector(const int iNeSi,const TData tInVa)111     CVector ( const int iNeSi, const TData tInVa ) { Init ( iNeSi, tInVa ); }
112 
113     CVector ( CVector const& ) = default;
114 
115     void Init ( const int iNewSize );
116 
117     // use this init to give all elements a defined value
118     void Init ( const int iNewSize, const TData tIniVal );
119 
120     // set all values to the given reset value
Reset(const TData tResetVal)121     void Reset ( const TData tResetVal ) { std::fill ( this->begin(), this->end(), tResetVal ); }
122 
Enlarge(const int iAddedSize)123     void Enlarge ( const int iAddedSize ) { std::vector<TData>::resize ( std::vector<TData>::size() + iAddedSize ); }
124 
Add(const TData & tI)125     void Add ( const TData& tI )
126     {
127         Enlarge ( 1 );
128         std::vector<TData>::back() = tI;
129     }
130 
131     int StringFiFoWithCompare ( const QString strNewValue, const bool bDoAdding = true );
132 
133     // this function simply converts the type of size to integer
Size()134     inline int Size() const { return static_cast<int> ( std::vector<TData>::size() ); }
135 };
136 
137 /* Implementation *************************************************************/
138 template<class TData>
Init(const int iNewSize)139 void CVector<TData>::Init ( const int iNewSize )
140 {
141     // clear old buffer and reserve memory for new buffer
142     std::vector<TData>::clear();
143     std::vector<TData>::resize ( iNewSize );
144 }
145 
146 template<class TData>
Init(const int iNewSize,const TData tIniVal)147 void CVector<TData>::Init ( const int iNewSize, const TData tIniVal )
148 {
149     // call actual init routine and reset all values to the given value
150     Init ( iNewSize );
151     Reset ( tIniVal );
152 }
153 
154 // note: this is only supported for string vectors
155 template<class TData>
StringFiFoWithCompare(const QString strNewValue,const bool bDoAdding)156 int CVector<TData>::StringFiFoWithCompare ( const QString strNewValue, const bool bDoAdding )
157 {
158     const int iVectorSize = Size();
159 
160     CVector<QString> vstrTempList ( iVectorSize, "" );
161 
162     // init with illegal index per definition
163     int iOldIndex = INVALID_INDEX;
164 
165     // init temporary list count (may be overwritten later on)
166     int iTempListCnt = 0;
167 
168     if ( bDoAdding )
169     {
170         // store the new element in the current storage list at
171         // the top, make sure we do not have more than allowed stored
172         // elements
173         vstrTempList[0] = strNewValue;
174         iTempListCnt    = 1;
175     }
176 
177     for ( int iIdx = 0; iIdx < iVectorSize; iIdx++ )
178     {
179         // first check if we still have space in our data storage
180         if ( iTempListCnt < iVectorSize )
181         {
182             // only add old element if it is not the same as the
183             // selected one
184             if ( std::vector<TData>::operator[] ( iIdx ).compare ( strNewValue ) )
185             {
186                 vstrTempList[iTempListCnt] = std::vector<TData>::operator[] ( iIdx );
187 
188                 iTempListCnt++;
189             }
190             else
191             {
192                 iOldIndex = iIdx;
193             }
194         }
195     }
196 
197     // copy new generated list to data base
198     *this = vstrTempList;
199 
200     return iOldIndex;
201 }
202 
203 /******************************************************************************\
204 * CFIFO Class (First In, First Out)                                            *
205 \******************************************************************************/
206 template<class TData>
207 class CFIFO : public CVector<TData>
208 {
209 public:
CFIFO()210     CFIFO() : iCurIdx ( 0 ) {}
CFIFO(const int iNeSi)211     CFIFO ( const int iNeSi ) : CVector<TData> ( iNeSi ), iCurIdx ( 0 ) {}
CFIFO(const int iNeSi,const TData tInVa)212     CFIFO ( const int iNeSi, const TData tInVa ) : CVector<TData> ( iNeSi, tInVa ), iCurIdx ( 0 ) {}
213 
214     void         Add ( const TData tNewD );
Get()215     inline TData Get() { return CVector<TData>::operator[] ( iCurIdx ); }
216 
217     virtual void Init ( const int iNewSize );
218 
219     virtual void Init ( const int iNewSize, const TData tIniVal );
220 
221 protected:
222     int iCurIdx;
223 };
224 
225 template<class TData>
Init(const int iNewSize)226 void CFIFO<TData>::Init ( const int iNewSize )
227 {
228     iCurIdx = 0;
229     CVector<TData>::Init ( iNewSize );
230 }
231 
232 template<class TData>
Init(const int iNewSize,const TData tIniVal)233 void CFIFO<TData>::Init ( const int iNewSize, const TData tIniVal )
234 {
235     iCurIdx = 0;
236     CVector<TData>::Init ( iNewSize, tIniVal );
237 }
238 
239 template<class TData>
Add(const TData tNewD)240 void CFIFO<TData>::Add ( const TData tNewD )
241 {
242     CVector<TData>::operator[] ( iCurIdx ) = tNewD;
243 
244     // increment index and check for wrap around
245     iCurIdx++;
246 
247     if ( iCurIdx >= CVector<TData>::Size() )
248     {
249         iCurIdx = 0;
250     }
251 }
252 
253 /******************************************************************************\
254 * CMovingAv Class (Moving Average)                                             *
255 \******************************************************************************/
256 template<class TData>
257 class CMovingAv : public CVector<TData>
258 {
259 public:
CMovingAv()260     CMovingAv() : CVector<TData>(), iCurIdx ( 0 ), iNorm ( 0 ), dCurAvResult ( 0 ), dNoDataResult ( 0 ) {}
261 
262     void Add ( const TData tNewD );
263 
264     void Init ( const int iNewSize, const double dNNoDRes = 0 );
265 
266     void Reset();
267 
GetAverage()268     inline double GetAverage()
269     {
270         // make sure we do not divide by zero
271         if ( iNorm == 0 )
272         {
273             return dNoDataResult;
274         }
275         else
276         {
277             return dCurAvResult / iNorm;
278         }
279     }
280 
InitializationState()281     double InitializationState() const
282     {
283         // make sure we do not divide by zero
284         if ( CVector<TData>::Size() != 0 )
285         {
286             return static_cast<double> ( iNorm ) / CVector<TData>::Size();
287         }
288         else
289         {
290             return 0;
291         }
292     }
293 
294 protected:
295     int    iCurIdx;
296     int    iNorm;
297     double dCurAvResult;
298     double dNoDataResult;
299 };
300 
301 template<class TData>
Init(const int iNewSize,const double dNNoDRes)302 void CMovingAv<TData>::Init ( const int iNewSize, const double dNNoDRes )
303 {
304     iNorm         = 0;
305     iCurIdx       = 0;
306     dCurAvResult  = 0; // only for scalars!
307     dNoDataResult = dNNoDRes;
308     CVector<TData>::Init ( iNewSize );
309 }
310 
311 template<class TData>
Reset()312 void CMovingAv<TData>::Reset()
313 {
314     iNorm        = 0;
315     iCurIdx      = 0;
316     dCurAvResult = 0; // only for scalars!
317     CVector<TData>::Reset ( TData ( 0 ) );
318 }
319 
320 template<class TData>
Add(const TData tNewD)321 void CMovingAv<TData>::Add ( const TData tNewD )
322 {
323     /*
324         Optimized calculation of the moving average. We only add a new value and
325         subtract the old value from the result. We only need one addition and a
326         history buffer.
327     */
328 
329     // subtract oldest value
330     dCurAvResult -= CVector<TData>::operator[] ( iCurIdx );
331 
332     // add new value and write in memory
333     dCurAvResult += tNewD;
334     CVector<TData>::operator[] ( iCurIdx ) = tNewD;
335 
336     // increase position pointer and test if wrap
337     iCurIdx++;
338     if ( iCurIdx >= CVector<TData>::Size() )
339     {
340         iCurIdx = 0;
341     }
342 
343     // take care of norm
344     if ( iNorm < CVector<TData>::Size() )
345     {
346         iNorm++;
347     }
348 }
349 
350 /******************************************************************************\
351 * GUI Utilities                                                                *
352 \******************************************************************************/
353 #ifndef HEADLESS
354 // Dialog base class -----------------------------------------------------------
355 class CBaseDlg : public QDialog
356 {
357     Q_OBJECT
358 
359 public:
QDialog(parent,flags)360     CBaseDlg ( QWidget* parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags() ) : QDialog ( parent, flags ) {}
361 
362 public slots:
keyPressEvent(QKeyEvent * pEvent)363     void keyPressEvent ( QKeyEvent* pEvent )
364     {
365         // block escape key
366         if ( pEvent->key() != Qt::Key_Escape )
367         {
368 #    ifdef ANDROID
369             if ( pEvent->key() == Qt::Key_Back )
370             {
371                 close(); // otherwise, dialog does not show properly again in android (nefarius2001, #832)
372                 return;
373             }
374 #    endif
375             QDialog::keyPressEvent ( pEvent );
376         }
377     }
378 };
379 
380 // About dialog ----------------------------------------------------------------
381 class CAboutDlg : public CBaseDlg, private Ui_CAboutDlgBase
382 {
383     Q_OBJECT
384 
385 public:
386     CAboutDlg ( QWidget* parent = nullptr );
387 };
388 
389 // Licence dialog --------------------------------------------------------------
390 class CLicenceDlg : public CBaseDlg
391 {
392     Q_OBJECT
393 
394 public:
395     CLicenceDlg ( QWidget* parent = nullptr );
396 
397 protected:
398     QPushButton* butAccept;
399 
400 public slots:
OnAgreeStateChanged(int value)401     void OnAgreeStateChanged ( int value ) { butAccept->setEnabled ( value == Qt::Checked ); }
402 };
403 
404 // Help menu -------------------------------------------------------------------
405 class CHelpMenu : public QMenu
406 {
407     Q_OBJECT
408 
409 public:
410     CHelpMenu ( const bool bIsClient, QWidget* parent = nullptr );
411 
412 protected:
413     CAboutDlg AboutDlg;
414 
415 public slots:
OnHelpWhatsThis()416     void OnHelpWhatsThis() { QWhatsThis::enterWhatsThisMode(); }
OnHelpAbout()417     void OnHelpAbout() { AboutDlg.exec(); }
OnHelpAboutQt()418     void OnHelpAboutQt() { QMessageBox::aboutQt ( nullptr, QString ( tr ( "About Qt" ) ) ); }
OnHelpClientGetStarted()419     void OnHelpClientGetStarted() { QDesktopServices::openUrl ( QUrl ( CLIENT_GETTING_STARTED_URL ) ); }
OnHelpServerGetStarted()420     void OnHelpServerGetStarted() { QDesktopServices::openUrl ( QUrl ( SERVER_GETTING_STARTED_URL ) ); }
OnHelpSoftwareMan()421     void OnHelpSoftwareMan() { QDesktopServices::openUrl ( QUrl ( SOFTWARE_MANUAL_URL ) ); }
422 };
423 
424 // Language combo box ----------------------------------------------------------
425 class CLanguageComboBox : public QComboBox
426 {
427     Q_OBJECT
428 
429 public:
430     CLanguageComboBox ( QWidget* parent = nullptr );
431 
432     void Init ( QString& strSelLanguage );
433 
434 protected:
435     int iIdxSelectedLanguage;
436 
437 public slots:
438     void OnLanguageActivated ( int iLanguageIdx );
439 
440 signals:
441     void LanguageChanged ( QString strLanguage );
442 };
443 #endif
444 
445 /******************************************************************************\
446 * Other Classes/Enums                                                          *
447 \******************************************************************************/
448 // Audio channel configuration -------------------------------------------------
449 enum EAudChanConf
450 {
451     // used for settings -> enum values should be fixed
452     CC_MONO               = 0,
453     CC_MONO_IN_STEREO_OUT = 1,
454     CC_STEREO             = 2
455 };
456 
457 // Audio compression type enum -------------------------------------------------
458 enum EAudComprType
459 {
460     // used for protocol -> enum values must be fixed!
461     CT_NONE   = 0,
462     CT_CELT   = 1,
463     CT_OPUS   = 2,
464     CT_OPUS64 = 3 // using OPUS with 64 samples frame size
465 };
466 
467 // Network transport flags -----------------------------------------------------
468 enum ENetwFlags
469 {
470     // used for protocol -> enum values must be fixed!
471     NF_NONE         = 0,
472     NF_WITH_COUNTER = 1 // using a network counter to correctly order UDP packets in jitter buffer
473 };
474 
475 // Audio quality enum ----------------------------------------------------------
476 enum EAudioQuality
477 {
478     // used for settings and the comobo box index -> enum values must be fixed!
479     AQ_LOW    = 0,
480     AQ_NORMAL = 1,
481     AQ_HIGH   = 2
482 };
483 
484 // Get data status enum --------------------------------------------------------
485 enum EGetDataStat
486 {
487     GS_BUFFER_OK,
488     GS_BUFFER_UNDERRUN,
489     GS_CHAN_NOW_DISCONNECTED,
490     GS_CHAN_NOT_CONNECTED
491 };
492 
493 // GUI design enum -------------------------------------------------------------
494 enum EGUIDesign
495 {
496     // used for settings -> enum values should be fixed
497     GD_STANDARD  = 0,
498     GD_ORIGINAL  = 1,
499     GD_SLIMFADER = 2
500 };
501 
502 // Server licence type enum ----------------------------------------------------
503 enum ELicenceType
504 {
505     // used for protocol -> enum values must be fixed!
506     LT_NO_LICENCE      = 0,
507     LT_CREATIVECOMMONS = 1
508 };
509 
510 // Server jam recorder state enum ----------------------------------------------
511 enum ERecorderState
512 {
513     // used for protocol -> enum values must be fixed!
514     RS_UNDEFINED       = 0,
515     RS_NOT_INITIALISED = 1,
516     RS_NOT_ENABLED     = 2,
517     RS_RECORDING       = 3
518 };
519 
520 // Channel sort type -----------------------------------------------------------
521 enum EChSortType
522 {
523     // used for settings -> enum values should be fixed
524     ST_NO_SORT       = 0,
525     ST_BY_NAME       = 1,
526     ST_BY_INSTRUMENT = 2,
527     ST_BY_GROUPID    = 3,
528     ST_BY_CITY       = 4
529 };
530 
531 // Directory server address type -----------------------------------------------
532 enum ECSAddType
533 {
534     // used for settings -> enum values should be fixed
535     AT_DEFAULT              = 0,
536     AT_ANY_GENRE2           = 1,
537     AT_ANY_GENRE3           = 2,
538     AT_GENRE_ROCK           = 3,
539     AT_GENRE_JAZZ           = 4,
540     AT_GENRE_CLASSICAL_FOLK = 5,
541     AT_GENRE_CHORAL         = 6,
542     AT_CUSTOM               = 7 // Must be the last entry!
543 };
544 
csCentServAddrTypeToString(ECSAddType eAddrType)545 inline QString csCentServAddrTypeToString ( ECSAddType eAddrType )
546 {
547     switch ( eAddrType )
548     {
549     case AT_CUSTOM:
550         return QCoreApplication::translate ( "CClientSettingsDlg", "Custom" );
551 
552     case AT_ANY_GENRE2:
553         return QCoreApplication::translate ( "CClientSettingsDlg", "Any Genre 2" );
554 
555     case AT_ANY_GENRE3:
556         return QCoreApplication::translate ( "CClientSettingsDlg", "Any Genre 3" );
557 
558     case AT_GENRE_ROCK:
559         return QCoreApplication::translate ( "CClientSettingsDlg", "Genre Rock" );
560 
561     case AT_GENRE_JAZZ:
562         return QCoreApplication::translate ( "CClientSettingsDlg", "Genre Jazz" );
563 
564     case AT_GENRE_CLASSICAL_FOLK:
565         return QCoreApplication::translate ( "CClientSettingsDlg", "Genre Classical/Folk" );
566 
567     case AT_GENRE_CHORAL:
568         return QCoreApplication::translate ( "CClientSettingsDlg", "Genre Choral/Barbershop" );
569 
570     default: // AT_DEFAULT
571         return QCoreApplication::translate ( "CClientSettingsDlg", "Any Genre 1" );
572     }
573 }
574 
575 // Slave server registration state ---------------------------------------------
576 enum ESvrRegStatus
577 {
578     SRS_UNREGISTERED,
579     SRS_BAD_ADDRESS,
580     SRS_REQUESTED,
581     SRS_TIME_OUT,
582     SRS_UNKNOWN_RESP,
583     SRS_REGISTERED,
584     SRS_CENTRAL_SVR_FULL,
585     SRS_VERSION_TOO_OLD,
586     SRS_NOT_FULFILL_REQUIREMENTS
587 };
588 
svrRegStatusToString(ESvrRegStatus eSvrRegStatus)589 inline QString svrRegStatusToString ( ESvrRegStatus eSvrRegStatus )
590 {
591     switch ( eSvrRegStatus )
592     {
593     case SRS_UNREGISTERED:
594         return QCoreApplication::translate ( "CServerDlg", "Unregistered" );
595 
596     case SRS_BAD_ADDRESS:
597         return QCoreApplication::translate ( "CServerDlg", "Bad address" );
598 
599     case SRS_REQUESTED:
600         return QCoreApplication::translate ( "CServerDlg", "Registration requested" );
601 
602     case SRS_TIME_OUT:
603         return QCoreApplication::translate ( "CServerDlg", "Registration failed" );
604 
605     case SRS_UNKNOWN_RESP:
606         return QCoreApplication::translate ( "CServerDlg", "Check server version" );
607 
608     case SRS_REGISTERED:
609         return QCoreApplication::translate ( "CServerDlg", "Registered" );
610 
611     case SRS_CENTRAL_SVR_FULL:
612         return QCoreApplication::translate ( "CServerDlg", "Directory Server full" );
613 
614     case SRS_VERSION_TOO_OLD:
615         return QCoreApplication::translate ( "CServerDlg", "Your server version is too old" );
616 
617     case SRS_NOT_FULFILL_REQUIREMENTS:
618         return QCoreApplication::translate ( "CServerDlg", "Requirements not fulfilled" );
619     }
620 
621     return QString ( QCoreApplication::translate ( "CServerDlg", "Unknown value " ) ).append ( eSvrRegStatus );
622 }
623 
624 // Directory server registration outcome ---------------------------------------
625 enum ESvrRegResult
626 {
627     // used for protocol -> enum values must be fixed!
628     SRR_REGISTERED              = 0,
629     SRR_CENTRAL_SVR_FULL        = 1,
630     SRR_VERSION_TOO_OLD         = 2,
631     SRR_NOT_FULFILL_REQIREMENTS = 3
632 };
633 
634 // Skill level enum ------------------------------------------------------------
635 enum ESkillLevel
636 {
637     // used for protocol -> enum values must be fixed!
638     SL_NOT_SET      = 0,
639     SL_BEGINNER     = 1,
640     SL_INTERMEDIATE = 2,
641     SL_PROFESSIONAL = 3
642 };
643 
644 // define the GUI RGB colors for each skill level
645 #define RGBCOL_R_SL_NOT_SET         255
646 #define RGBCOL_G_SL_NOT_SET         255
647 #define RGBCOL_B_SL_NOT_SET         255
648 #define RGBCOL_R_SL_BEGINNER        255
649 #define RGBCOL_G_SL_BEGINNER        255
650 #define RGBCOL_B_SL_BEGINNER        200
651 #define RGBCOL_R_SL_INTERMEDIATE    225
652 #define RGBCOL_G_SL_INTERMEDIATE    255
653 #define RGBCOL_B_SL_INTERMEDIATE    225
654 #define RGBCOL_R_SL_SL_PROFESSIONAL 255
655 #define RGBCOL_G_SL_SL_PROFESSIONAL 225
656 #define RGBCOL_B_SL_SL_PROFESSIONAL 225
657 
658 // Stereo signal level meter ---------------------------------------------------
659 class CStereoSignalLevelMeter
660 {
661 public:
662     // clang-format off
663 // TODO Calculate smoothing factor from sample rate and frame size (64 or 128 samples frame size).
664 //      But tests with 128 and 64 samples frame size have shown that the meter fly back
665 //      is ok for both numbers of samples frame size with a factor of 0.99.
666     // clang-format on
667     CStereoSignalLevelMeter ( const bool bNIsStereoOut = true, const double dNSmoothingFactor = 0.99 ) :
dSmoothingFactor(dNSmoothingFactor)668         dSmoothingFactor ( dNSmoothingFactor ),
669         bIsStereoOut ( bNIsStereoOut )
670     {
671         Reset();
672     }
673 
674     void Update ( const CVector<short>& vecsAudio, const int iInSize, const bool bIsStereoIn );
675 
GetLevelForMeterdBLeftOrMono()676     double        GetLevelForMeterdBLeftOrMono() { return CalcLogResultForMeter ( dCurLevelLOrMono ); }
GetLevelForMeterdBRight()677     double        GetLevelForMeterdBRight() { return CalcLogResultForMeter ( dCurLevelR ); }
678     static double CalcLogResultForMeter ( const double& dLinearLevel );
679 
Reset()680     void Reset()
681     {
682         dCurLevelLOrMono = 0.0;
683         dCurLevelR       = 0.0;
684     }
685 
686 protected:
687     double UpdateCurLevel ( double dCurLevel, const double dMax );
688 
689     double dCurLevelLOrMono;
690     double dCurLevelR;
691     double dSmoothingFactor;
692     bool   bIsStereoOut;
693 };
694 
695 // Host address ----------------------------------------------------------------
696 class CHostAddress
697 {
698 public:
699     enum EStringMode
700     {
701         SM_IP_PORT,
702         SM_IP_NO_LAST_BYTE,
703         SM_IP_NO_LAST_BYTE_PORT
704     };
705 
CHostAddress()706     CHostAddress() : InetAddr ( static_cast<quint32> ( 0 ) ), iPort ( 0 ) {}
707 
CHostAddress(const QHostAddress NInetAddr,const quint16 iNPort)708     CHostAddress ( const QHostAddress NInetAddr, const quint16 iNPort ) : InetAddr ( NInetAddr ), iPort ( iNPort ) {}
709 
CHostAddress(const CHostAddress & NHAddr)710     CHostAddress ( const CHostAddress& NHAddr ) : InetAddr ( NHAddr.InetAddr ), iPort ( NHAddr.iPort ) {}
711 
712     // copy operator
713     CHostAddress& operator= ( const CHostAddress& NHAddr )
714     {
715         InetAddr = NHAddr.InetAddr;
716         iPort    = NHAddr.iPort;
717         return *this;
718     }
719 
720     // compare operator
721     bool operator== ( const CHostAddress& CompAddr ) const { return ( ( CompAddr.InetAddr == InetAddr ) && ( CompAddr.iPort == iPort ) ); }
722 
723     int Compare ( const CHostAddress& other ) const;
724 
725     QString toString ( const EStringMode eStringMode = SM_IP_PORT ) const
726     {
727         QString strReturn = InetAddr.toString();
728 
729         // special case: for local host address, we do not replace the last byte
730         if ( ( ( eStringMode == SM_IP_NO_LAST_BYTE ) || ( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) ) &&
731              ( InetAddr != QHostAddress ( QHostAddress::LocalHost ) ) )
732         {
733             // replace last byte by an "x"
734             strReturn = strReturn.section ( ".", 0, 2 ) + ".x";
735         }
736 
737         if ( ( eStringMode == SM_IP_PORT ) || ( eStringMode == SM_IP_NO_LAST_BYTE_PORT ) )
738         {
739             // add port number after a semicolon
740             strReturn += ":" + QString().setNum ( iPort );
741         }
742 
743         return strReturn;
744     }
745 
746     QHostAddress InetAddr;
747     quint16      iPort;
748 };
749 
750 // Instrument picture data base ------------------------------------------------
751 // this is a pure static class
752 class CInstPictures
753 {
754 public:
755     enum EInstCategory
756     {
757         IC_OTHER_INSTRUMENT,
758         IC_WIND_INSTRUMENT,
759         IC_STRING_INSTRUMENT,
760         IC_PLUCKING_INSTRUMENT,
761         IC_PERCUSSION_INSTRUMENT,
762         IC_KEYBOARD_INSTRUMENT,
763         IC_MULTIPLE_INSTRUMENT
764     };
765 
766     // per definition: the very first instrument is the "not used" instrument
GetNotUsedInstrument()767     static int  GetNotUsedInstrument() { return 0; }
IsNotUsedInstrument(const int iInstrument)768     static bool IsNotUsedInstrument ( const int iInstrument ) { return iInstrument == 0; }
769 
GetNumAvailableInst()770     static int           GetNumAvailableInst() { return GetTable().Size(); }
771     static QString       GetResourceReference ( const int iInstrument );
772     static QString       GetName ( const int iInstrument );
773     static EInstCategory GetCategory ( const int iInstrument );
UpdateTableOnLanguageChange()774     static void          UpdateTableOnLanguageChange() { GetTable ( true ); }
775 
776     // clang-format off
777 // TODO make use of instrument category (not yet implemented)
778     // clang-format on
779 
780 protected:
781     class CInstPictProps
782     {
783     public:
CInstPictProps()784         CInstPictProps() : strName ( "" ), strResourceReference ( "" ), eInstCategory ( IC_OTHER_INSTRUMENT ) {}
785 
CInstPictProps(const QString NsName,const QString NsResRef,const EInstCategory NeInstCat)786         CInstPictProps ( const QString NsName, const QString NsResRef, const EInstCategory NeInstCat ) :
787             strName ( NsName ),
788             strResourceReference ( NsResRef ),
789             eInstCategory ( NeInstCat )
790         {}
791 
792         QString       strName;
793         QString       strResourceReference;
794         EInstCategory eInstCategory;
795     };
796 
797     static bool                     IsInstIndexInRange ( const int iIdx );
798     static CVector<CInstPictProps>& GetTable ( const bool bReGenerateTable = false );
799 };
800 
801 // Locale management class -----------------------------------------------------
802 class CLocale
803 {
804 public:
805     static QString                 GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry );
806     static QMap<QString, QString>  GetAvailableTranslations();
807     static QPair<QString, QString> FindSysLangTransFileName ( const QMap<QString, QString>& TranslMap );
808     static void                    LoadTranslation ( const QString strLanguage, QCoreApplication* pApp );
809 };
810 
811 // Info of a channel -----------------------------------------------------------
812 class CChannelCoreInfo
813 {
814 public:
CChannelCoreInfo()815     CChannelCoreInfo() :
816         strName ( "" ),
817         eCountry ( QLocale::AnyCountry ),
818         strCity ( "" ),
819         iInstrument ( CInstPictures::GetNotUsedInstrument() ),
820         eSkillLevel ( SL_NOT_SET )
821     {}
822 
CChannelCoreInfo(const QString NsName,const QLocale::Country & NeCountry,const QString & NsCity,const int NiInstrument,const ESkillLevel NeSkillLevel)823     CChannelCoreInfo ( const QString           NsName,
824                        const QLocale::Country& NeCountry,
825                        const QString&          NsCity,
826                        const int               NiInstrument,
827                        const ESkillLevel       NeSkillLevel ) :
828         strName ( NsName ),
829         eCountry ( NeCountry ),
830         strCity ( NsCity ),
831         iInstrument ( NiInstrument ),
832         eSkillLevel ( NeSkillLevel )
833     {}
834 
CChannelCoreInfo(const CChannelCoreInfo & NCorInf)835     CChannelCoreInfo ( const CChannelCoreInfo& NCorInf ) :
836         strName ( NCorInf.strName ),
837         eCountry ( NCorInf.eCountry ),
838         strCity ( NCorInf.strCity ),
839         iInstrument ( NCorInf.iInstrument ),
840         eSkillLevel ( NCorInf.eSkillLevel )
841     {}
842 
843     // compare operator
844     bool operator!= ( const CChannelCoreInfo& CompChanInfo )
845     {
846         return ( ( CompChanInfo.strName != strName ) || ( CompChanInfo.eCountry != eCountry ) || ( CompChanInfo.strCity != strCity ) ||
847                  ( CompChanInfo.iInstrument != iInstrument ) || ( CompChanInfo.eSkillLevel != eSkillLevel ) );
848     }
849 
850     CChannelCoreInfo& operator= ( const CChannelCoreInfo& ) = default;
851 
852     // fader tag text (channel name)
853     QString strName;
854 
855     // country in which the client is located
856     QLocale::Country eCountry;
857 
858     // city in which the client is located
859     QString strCity;
860 
861     // instrument ID of the client (which instrument is he/she playing)
862     int iInstrument;
863 
864     // skill level of the musician
865     ESkillLevel eSkillLevel;
866 };
867 
868 class CChannelInfo : public CChannelCoreInfo
869 {
870 public:
CChannelInfo()871     CChannelInfo() : iChanID ( 0 ) {}
872 
CChannelInfo(const int NiID,const CChannelCoreInfo & NCorInf)873     CChannelInfo ( const int NiID, const CChannelCoreInfo& NCorInf ) : CChannelCoreInfo ( NCorInf ), iChanID ( NiID ) {}
874 
CChannelInfo(const int NiID,const QString NsName,const QLocale::Country & NeCountry,const QString & NsCity,const int NiInstrument,const ESkillLevel NeSkillLevel)875     CChannelInfo ( const int               NiID,
876                    const QString           NsName,
877                    const QLocale::Country& NeCountry,
878                    const QString&          NsCity,
879                    const int               NiInstrument,
880                    const ESkillLevel       NeSkillLevel ) :
881         CChannelCoreInfo ( NsName, NeCountry, NsCity, NiInstrument, NeSkillLevel ),
882         iChanID ( NiID )
883     {}
884 
885     // ID of the channel
886     int iChanID;
887 };
888 
889 // Server info -----------------------------------------------------------------
890 class CServerCoreInfo
891 {
892 public:
CServerCoreInfo()893     CServerCoreInfo() : strName ( "" ), eCountry ( QLocale::AnyCountry ), strCity ( "" ), iMaxNumClients ( 0 ), bPermanentOnline ( false ) {}
894 
CServerCoreInfo(const QString & NsName,const QLocale::Country & NeCountry,const QString & NsCity,const int NiMaxNumClients,const bool NbPermOnline)895     CServerCoreInfo ( const QString&          NsName,
896                       const QLocale::Country& NeCountry,
897                       const QString&          NsCity,
898                       const int               NiMaxNumClients,
899                       const bool              NbPermOnline ) :
900         strName ( NsName ),
901         eCountry ( NeCountry ),
902         strCity ( NsCity ),
903         iMaxNumClients ( NiMaxNumClients ),
904         bPermanentOnline ( NbPermOnline )
905     {}
906 
907     // name of the server
908     QString strName;
909 
910     // country in which the server is located
911     QLocale::Country eCountry;
912 
913     // city in which the server is located
914     QString strCity;
915 
916     // maximum number of clients which can connect to the server at the same
917     // time
918     int iMaxNumClients;
919 
920     // is the server permanently online or not (flag)
921     bool bPermanentOnline;
922 };
923 
924 class CServerInfo : public CServerCoreInfo
925 {
926 public:
CServerInfo()927     CServerInfo() : HostAddr ( CHostAddress() ), LHostAddr ( CHostAddress() ) {}
928 
CServerInfo(const CHostAddress & NHAddr,const CHostAddress & NLAddr,const QString & NsName,const QLocale::Country & NeCountry,const QString & NsCity,const int NiMaxNumClients,const bool NbPermOnline)929     CServerInfo ( const CHostAddress&     NHAddr,
930                   const CHostAddress&     NLAddr,
931                   const QString&          NsName,
932                   const QLocale::Country& NeCountry,
933                   const QString&          NsCity,
934                   const int               NiMaxNumClients,
935                   const bool              NbPermOnline ) :
936         CServerCoreInfo ( NsName, NeCountry, NsCity, NiMaxNumClients, NbPermOnline ),
937         HostAddr ( NHAddr ),
938         LHostAddr ( NLAddr )
939     {}
940 
941     // internet address of the server
942     CHostAddress HostAddr;
943 
944     // server internal address
945     CHostAddress LHostAddr;
946 };
947 
948 // Network transport properties ------------------------------------------------
949 class CNetworkTransportProps
950 {
951 public:
CNetworkTransportProps()952     CNetworkTransportProps() :
953         iBaseNetworkPacketSize ( 0 ),
954         iBlockSizeFact ( 0 ),
955         iNumAudioChannels ( 0 ),
956         iSampleRate ( 0 ),
957         eAudioCodingType ( CT_NONE ),
958         eFlags ( NF_NONE ),
959         iAudioCodingArg ( 0 )
960     {}
961 
CNetworkTransportProps(const uint32_t iNBNPS,const uint16_t iNBSF,const uint32_t iNNACH,const uint32_t iNSR,const EAudComprType eNACT,const ENetwFlags eNFlags,const int32_t iNACA)962     CNetworkTransportProps ( const uint32_t      iNBNPS,
963                              const uint16_t      iNBSF,
964                              const uint32_t      iNNACH,
965                              const uint32_t      iNSR,
966                              const EAudComprType eNACT,
967                              const ENetwFlags    eNFlags,
968                              const int32_t       iNACA ) :
969         iBaseNetworkPacketSize ( iNBNPS ),
970         iBlockSizeFact ( iNBSF ),
971         iNumAudioChannels ( iNNACH ),
972         iSampleRate ( iNSR ),
973         eAudioCodingType ( eNACT ),
974         eFlags ( eNFlags ),
975         iAudioCodingArg ( iNACA )
976     {}
977 
978     uint32_t      iBaseNetworkPacketSize;
979     uint16_t      iBlockSizeFact;
980     uint32_t      iNumAudioChannels;
981     uint32_t      iSampleRate;
982     EAudComprType eAudioCodingType;
983     ENetwFlags    eFlags;
984     int32_t       iAudioCodingArg;
985 };
986 
987 // Network utility functions ---------------------------------------------------
988 class NetworkUtil
989 {
990 public:
991     static bool ParseNetworkAddress ( QString strAddress, CHostAddress& HostAddress, bool bEnableIPv6 );
992 
993     static QString      FixAddress ( const QString& strAddress );
994     static CHostAddress GetLocalAddress();
995     static CHostAddress GetLocalAddress6();
996     static QString      GetCentralServerAddress ( const ECSAddType eCentralServerAddressType, const QString& strCentralServerAddress );
997     static bool         IsPrivateNetworkIP ( const QHostAddress& qhAddr );
998 };
999 
1000 // Operating system utility functions ------------------------------------------
1001 class COSUtil
1002 {
1003 public:
1004     enum EOpSystemType
1005     {
1006         // used for protocol -> enum values must be fixed!
1007         OT_WINDOWS = 0,
1008         OT_MAC_OS  = 1,
1009         OT_LINUX   = 2,
1010         OT_ANDROID = 3,
1011         OT_I_OS    = 4,
1012         OT_UNIX    = 5
1013     };
1014 
GetOperatingSystemString(const EOpSystemType eOSType)1015     static QString GetOperatingSystemString ( const EOpSystemType eOSType )
1016     {
1017         switch ( eOSType )
1018         {
1019         case OT_WINDOWS:
1020             return "Windows";
1021         case OT_MAC_OS:
1022             return "MacOS";
1023         case OT_LINUX:
1024             return "Linux";
1025         case OT_ANDROID:
1026             return "Android";
1027         case OT_I_OS:
1028             return "iOS";
1029         case OT_UNIX:
1030             return "Unix";
1031         default:
1032             return "Unknown";
1033         }
1034     }
1035 
GetOperatingSystem()1036     static EOpSystemType GetOperatingSystem()
1037     {
1038 #ifdef _WIN32
1039         return OT_WINDOWS;
1040 #elif defined( __APPLE__ ) || defined( __MACOSX )
1041         return OT_MAC_OS;
1042 #elif defined( ANDROID )
1043         return OT_ANDROID;
1044 #else
1045         return OT_LINUX;
1046 #endif
1047     }
1048 };
1049 
1050 // Audio reverbration ----------------------------------------------------------
1051 class CAudioReverb
1052 {
1053 public:
CAudioReverb()1054     CAudioReverb() {}
1055 
1056     void Init ( const EAudChanConf eNAudioChannelConf, const int iNStereoBlockSizeSam, const int iSampleRate, const float fT60 = 1.1f );
1057 
1058     void Clear();
1059     void Process ( CVector<int16_t>& vecsStereoInOut, const bool bReverbOnLeftChan, const float fAttenuation );
1060 
1061 protected:
1062     void setT60 ( const float fT60, const int iSampleRate );
1063     bool isPrime ( const int number );
1064 
1065     class COnePole
1066     {
1067     public:
COnePole()1068         COnePole() : fA ( 0 ), fB ( 0 ) { Reset(); }
1069         void  setPole ( const float fPole );
1070         float Calc ( const float fIn );
Reset()1071         void  Reset() { fLastSample = 0; }
1072 
1073     protected:
1074         float fA;
1075         float fB;
1076         float fLastSample;
1077     };
1078 
1079     EAudChanConf eAudioChannelConf;
1080     int          iStereoBlockSizeSam;
1081     CFIFO<float> allpassDelays[3];
1082     CFIFO<float> combDelays[4];
1083     COnePole     combFilters[4];
1084     CFIFO<float> outLeftDelay;
1085     CFIFO<float> outRightDelay;
1086     float        allpassCoefficient;
1087     float        combCoefficient[4];
1088 };
1089 
1090 // CRC -------------------------------------------------------------------------
1091 class CCRC
1092 {
1093 public:
CCRC()1094     CCRC() : iPoly ( ( 1 << 5 ) | ( 1 << 12 ) ), iBitOutMask ( 1 << 16 ) { Reset(); }
1095 
1096     void     Reset();
1097     void     AddByte ( const uint8_t byNewInput );
CheckCRC(const uint32_t iCRC)1098     bool     CheckCRC ( const uint32_t iCRC ) { return iCRC == GetCRC(); }
1099     uint32_t GetCRC();
1100 
1101 protected:
1102     uint32_t iPoly;
1103     uint32_t iBitOutMask;
1104     uint32_t iStateShiftReg;
1105 };
1106 
1107 // Mathematics utilities -------------------------------------------------------
1108 class MathUtils
1109 {
1110 public:
round(double x)1111     static int round ( double x )
1112     {
1113         return static_cast<int> ( ( x - floor ( x ) ) >= 0.5 ) ? static_cast<int> ( ceil ( x ) ) : static_cast<int> ( floor ( x ) );
1114     }
1115 
UpDownIIR1(double & dOldValue,const double & dNewValue,const double & dWeightUp,const double & dWeightDown)1116     static void UpDownIIR1 ( double& dOldValue, const double& dNewValue, const double& dWeightUp, const double& dWeightDown )
1117     {
1118         // different IIR weights for up and down direction
1119         if ( dNewValue < dOldValue )
1120         {
1121             dOldValue = dOldValue * dWeightDown + ( 1.0 - dWeightDown ) * dNewValue;
1122         }
1123         else
1124         {
1125             dOldValue = dOldValue * dWeightUp + ( 1.0 - dWeightUp ) * dNewValue;
1126         }
1127     }
1128 
DecideWithHysteresis(const double dValue,const int iOldValue,const double dHysteresis)1129     static int DecideWithHysteresis ( const double dValue, const int iOldValue, const double dHysteresis )
1130     {
1131         // apply hysteresis
1132         if ( dValue > static_cast<double> ( iOldValue ) )
1133         {
1134             return round ( dValue - dHysteresis );
1135         }
1136         else
1137         {
1138             return round ( dValue + dHysteresis );
1139         }
1140     }
1141 
1142     // calculate pan gains: in cross fade mode the pan center is attenuated
1143     // by 6 dB, otherwise the center equals full gain for both channels
GetLeftPan(const float fPan,const bool bXFade)1144     static inline float GetLeftPan ( const float fPan, const bool bXFade ) { return bXFade ? 1 - fPan : std::min ( 0.5f, 1 - fPan ) * 2; }
GetRightPan(const float fPan,const bool bXFade)1145     static inline float GetRightPan ( const float fPan, const bool bXFade ) { return bXFade ? fPan : std::min ( 0.5f, fPan ) * 2; }
1146 
1147     // calculate linear gain from fader values which are in dB
CalcFaderGain(const float fValue)1148     static float CalcFaderGain ( const float fValue )
1149     {
1150         // convert actual slider range in gain values
1151         // and normalize so that maximum gain is 1
1152         const float fInValueRange0_1 = fValue / AUD_MIX_FADER_MAX;
1153 
1154         // map range from 0..1 to range -35..0 dB and calculate linear gain
1155         if ( fValue == 0 )
1156         {
1157             return 0; // -infinity
1158         }
1159         else
1160         {
1161             return powf ( 10.0f, ( fInValueRange0_1 - 1.0f ) * AUD_MIX_FADER_RANGE_DB / 20.0f );
1162         }
1163     }
1164 };
1165 
1166 // Timing measurement ----------------------------------------------------------
1167 // intended for debugging the timing jitter of the sound card or server timer
1168 class CTimingMeas
1169 {
1170 public:
iNumMeas(iNNMeas)1171     CTimingMeas ( const int iNNMeas, const QString strNFName = "" ) : iNumMeas ( iNNMeas ), vElapsedTimes ( iNNMeas ), strFileName ( strNFName )
1172     {
1173         Reset();
1174     }
1175 
Reset()1176     void Reset() { iCnt = INVALID_INDEX; }
Measure()1177     void Measure()
1178     {
1179         // exclude the very first measurement (initialization phase)
1180         if ( iCnt == INVALID_INDEX )
1181         {
1182             iCnt = 0;
1183         }
1184         else
1185         {
1186             // store current measurement
1187             vElapsedTimes[iCnt++] = ElapsedTimer.nsecsElapsed();
1188 
1189             // reset count if number of measurements are done
1190             if ( iCnt >= iNumMeas )
1191             {
1192                 iCnt = 0;
1193 
1194                 // store results in a file if file name is given
1195                 if ( !strFileName.isEmpty() )
1196                 {
1197                     QFile File ( strFileName );
1198 
1199                     if ( File.open ( QIODevice::WriteOnly | QIODevice::Text ) )
1200                     {
1201                         QTextStream streamFile ( &File );
1202                         for ( int i = 0; i < iNumMeas; i++ )
1203                         {
1204                             // convert ns in ms and store the value
1205                             streamFile << i << " " << static_cast<double> ( vElapsedTimes[i] ) / 1000000 << endl;
1206                         }
1207                     }
1208                 }
1209             }
1210         }
1211         ElapsedTimer.start();
1212     }
1213 
1214 protected:
1215     int           iNumMeas;
1216     CVector<int>  vElapsedTimes;
1217     QString       strFileName;
1218     QElapsedTimer ElapsedTimer;
1219     int           iCnt;
1220 };
1221 
1222 /******************************************************************************\
1223 * Statistics                                                                   *
1224 \******************************************************************************/
1225 // Error rate measurement ------------------------------------------------------
1226 class CErrorRate
1227 {
1228 public:
CErrorRate()1229     CErrorRate() {}
1230 
1231     void Init ( const int iHistoryLength, const bool bNBlockOnDoubleErr = false )
1232     {
1233         // initialize buffer (use "no data result" of 1.0 which stands for the
1234         // worst error rate possible)
1235         ErrorsMovAvBuf.Init ( iHistoryLength, 1.0 );
1236 
1237         bPreviousState = true;
1238 
1239         // store setting
1240         bBlockOnDoubleErrors = bNBlockOnDoubleErr;
1241     }
1242 
Reset()1243     void Reset()
1244     {
1245         ErrorsMovAvBuf.Reset();
1246         bPreviousState = true;
1247     }
1248 
Update(const bool bState)1249     void Update ( const bool bState )
1250     {
1251         // if two states were false, do not use the new value
1252         if ( bBlockOnDoubleErrors && bPreviousState && bState )
1253         {
1254             return;
1255         }
1256 
1257         // add errors as values 0 and 1 to get correct error rate average
1258         if ( bState )
1259         {
1260             ErrorsMovAvBuf.Add ( 1 );
1261         }
1262         else
1263         {
1264             ErrorsMovAvBuf.Add ( 0 );
1265         }
1266 
1267         // store state
1268         bPreviousState = bState;
1269     }
1270 
GetAverage()1271     double GetAverage() { return ErrorsMovAvBuf.GetAverage(); }
InitializationState()1272     double InitializationState() { return ErrorsMovAvBuf.InitializationState(); }
1273 
1274 protected:
1275     CMovingAv<char> ErrorsMovAvBuf;
1276     bool            bBlockOnDoubleErrors;
1277     bool            bPreviousState;
1278 };
1279