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