From 3bb34f4cc30a279efb8ad187e531d86a30785ede Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Mon, 21 Mar 2022 17:52:07 +0100 Subject: [PATCH 01/25] Trying to ignore my vs files --- .gitignore | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 22de5eeaf1..b56af9cd0d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,18 +2,27 @@ Jamulus Jamulus.ini Makefile -*.pro.user* **.cppe **.he .cproject .project .settings .idea +.xcode .vscode +.vs .cache +.DS_Store *.user *.user.* +*.pro.user* *.o +*.sln +*.vcxproj +*.vcxproj.filters +*.xcodeproj +*.log +Jamulus*.ps1 moc_*.cpp ui_*.h moc_predefs.h @@ -30,20 +39,14 @@ build/ deploy/ build-gui/ build-nox/ -jamulus.sln -jamulus.vcxproj -jamulus.vcxproj.filters Jamulus.app/ -.DS_Store distributions/opus* distributions/jack2 distributions/claudio_piano.sf2 distributions/fluidsynth* distributions/jamulus.desktop distributions/jamulus-server.desktop -.xcode Debug-iphoneos/ -Jamulus.xcodeproj jamulus_plugin_import.cpp autoLatestChangelog.md debian/ From 9aeceb0e830ee8ca88e0ebba72f852b7a3540fe7 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Fri, 25 Mar 2022 22:30:21 +0100 Subject: [PATCH 02/25] Connection status issue #2519 first stage In this first stage I just renamed some functions in CClient to make the problem clear. See "---> pgScorpio:" comments in the problem area's. The next stage will actually change the code to solve this issue. --- src/client.cpp | 12 +++++++----- src/client.h | 16 ++++++++++++---- src/clientdlg.cpp | 31 ++++++++++++++++++++----------- src/clientsettingsdlg.cpp | 2 +- src/settings.cpp | 20 ++++++++++---------- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index a1b34a8eeb..0323685b22 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -186,7 +186,7 @@ CClient::CClient ( const quint16 iPortNumber, if ( !strConnOnStartupAddress.isEmpty() ) { SetServerAddr ( strConnOnStartupAddress ); - Start(); + StartConnection(); // ---> pgScorpio: Was Start() } } @@ -298,7 +298,8 @@ void CClient::CreateServerJitterBufferMessage() void CClient::OnCLPingReceived ( CHostAddress InetAddr, int iMs ) { // make sure we are running and the server address is correct - if ( IsRunning() && ( InetAddr == Channel.GetAddress() ) ) + if ( SoundIsStarted() && // ---> pgScorpio: Does NOT mean we are Connected ! + ( InetAddr == Channel.GetAddress() ) ) { // take care of wrap arounds (if wrapping, do not use result) const int iCurDiff = EvaluatePingMessage ( iMs ); @@ -819,7 +820,7 @@ void CClient::OnClientIDReceived ( int iChanID ) emit ClientIDReceived ( iChanID ); } -void CClient::Start() +void CClient::StartConnection() // ---> pgScorpio: Was Start() { // init object Init(); @@ -828,10 +829,11 @@ void CClient::Start() Channel.SetEnable ( true ); // start audio interface - Sound.Start(); + Sound.Start(); // ---> pgScorpio: There is NO Check if Sound.Start() actually is successfull !! + // ---> pgScorpio: If Sound.Start fails here GUI (clientdlg) and Channel are definitely out of sync ! } -void CClient::Stop() +void CClient::StopConnection() { // stop audio interface Sound.Stop(); diff --git a/src/client.h b/src/client.h index cdb21f6e6e..5a5a2bc788 100644 --- a/src/client.h +++ b/src/client.h @@ -119,10 +119,18 @@ class CClient : public QObject virtual ~CClient(); - void Start(); - void Stop(); - bool IsRunning() { return Sound.IsRunning(); } - bool IsCallbackEntered() const { return Sound.IsCallbackEntered(); } + void StartConnection(); // ---> pgScorpio: Was Start(), but Start what ? ( Should be Connect() ?) + void StopConnection(); // ---> pgScorpio: Was Stop(), but Stop what ? ( Should be Disconnect() ?) + bool SoundIsStarted() // ---> pgScorpio: Was IsRunning(), but what is running ?? + { + return Sound.IsRunning(); // ---> pgScorpio: Even this name is incorrect ! + // ---> pgScorpio: Sound.bRun is set when Sound is started, but this does not guarantee sound is actually running + } + + bool SoundIsRunning() const + { + return Sound.IsCallbackEntered(); + } // ---> pgScorpio: was IsCallbackEntered() but this is the actuall SoundIsRunning() ! bool SetServerAddr ( QString strNAddr ); double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); } diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index c94c03b707..e8d8163e67 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -609,9 +609,9 @@ void CClientDlg::closeEvent ( QCloseEvent* Event ) AnalyzerConsole.close(); // if connected, terminate connection - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // ---> pgScorpio: This does NOT mean the connection is started ! { - pClient->Stop(); + pClient->StopConnection(); // ---> pgScorpio: Was pClient->Stop() } // make sure all current fader settings are applied to the settings struct @@ -732,7 +732,7 @@ void CClientDlg::OnConnectDlgAccepted() // first check if we are already connected, if this is the case we have to // disconnect the old server first - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // pgScorpio: Was pClient->IsRunning() Again this is NOT connection started ! { Disconnect(); } @@ -748,7 +748,7 @@ void CClientDlg::OnConnectDlgAccepted() void CClientDlg::OnConnectDisconBut() { // the connect/disconnect button implements a toggle functionality - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // pgScorpio: Was pClient->IsRunning() Again this is NOT connection started ! { Disconnect(); SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); @@ -1141,7 +1141,7 @@ void CClientDlg::OnTimerCheckAudioDeviceOk() // timeout to check if a valid device is selected and if we do not have // fundamental settings errors (in which case the GUI would only show that // it is trying to connect the server which does not help to solve the problem (#129)) - if ( !pClient->IsCallbackEntered() ) + if ( !pClient->SoundIsRunning() ) // ---> pgScorpio Was pClient->IsCallbackEntered() { QMessageBox::warning ( this, APP_NAME, @@ -1154,10 +1154,11 @@ void CClientDlg::OnTimerDetectFeedback() { bDetectFeedback = false; } void CClientDlg::OnSoundDeviceChanged ( QString strError ) { - if ( !strError.isEmpty() ) + if ( !strError + .isEmpty() ) // ---> pgScorpio: This check should already be done in CClient ! but currently the Disconnect code is at the wrong place. { // the sound device setup has a problem, disconnect any active connection - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // ---> pgScorpio: Was pClient->IsRunning(), Again this is NOT connection started !() ) { Disconnect(); } @@ -1190,6 +1191,8 @@ void CClientDlg::OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, int void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ) { + // ---> pgScorpio: This code does not belong here but in CClient !! + // set address and check if address is valid if ( pClient->SetServerAddr ( strSelectedAddress ) ) { @@ -1197,9 +1200,9 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str // running state but show error message try { - if ( !pClient->IsRunning() ) + if ( !pClient->SoundIsStarted() ) // ---> pgScorpio: Again this is NOT connection started !() ) { - pClient->Start(); + pClient->StartConnection(); } } @@ -1210,6 +1213,8 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str return; } + // ---> pgScorpio: This code should be a OnConnecting() slot ! + // hide label connect to server lblConnectToServer->hide(); lbrInputLevelL->setEnabled ( true ); @@ -1238,15 +1243,19 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str void CClientDlg::Disconnect() { + // ---> pgScorpio: This code does not belong here but in CClient !! + // only stop client if currently running, in case we received // the stopped message, the client is already stopped but the // connect/disconnect button and other GUI controls must be // updated - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // ---> pgScorpio: Again this is NOT connection started !() ) { - pClient->Stop(); + pClient->StopConnection(); } + // ---> pgScorpio: This code should be a OnDisconncted() slot + // change connect button text to "connect" butConnect->setText ( tr ( "C&onnect" ) ); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 78f2353b50..a18663c108 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -1049,7 +1049,7 @@ void CClientSettingsDlg::UpdateDisplay() UpdateJitterBufferFrame(); UpdateSoundCardFrame(); - if ( !pClient->IsRunning() ) + if ( !pClient->SoundIsStarted() ) // pgScorpio: Was !pClient->IsRunning() Again this is NOT a connection status, Should be pClient->IsConnected() { // clear text labels with client parameters lblUpstreamValue->setText ( "---" ); diff --git a/src/settings.cpp b/src/settings.cpp index ab9b038dfd..7b1210223a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -858,16 +858,16 @@ if ( GetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", static_cas directoryType = static_cast ( iValue ); } else - // clang-format on - if ( GetNumericIniSet ( IniXMLDocument, - "server", - "directorytype", - static_cast ( AT_NONE ), - static_cast ( AT_CUSTOM ), - iValue ) ) - { - directoryType = static_cast ( iValue ); - } + // clang-format on + if ( GetNumericIniSet ( IniXMLDocument, + "server", + "directorytype", + static_cast ( AT_NONE ), + static_cast ( AT_CUSTOM ), + iValue ) ) + { + directoryType = static_cast ( iValue ); + } // clang-format off // TODO compatibility to old version < 3.9.0 From f9f5e30cdddfe52475aac716af50e3619317e99b Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Wed, 23 Mar 2022 16:14:48 +0100 Subject: [PATCH 03/25] Added CMsgBoxes and CCommanlineOptions to global.h,main.cpp --- src/global.h | 237 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/main.cpp | 221 ++++++++++++++++++++--------------------------- 2 files changed, 317 insertions(+), 141 deletions(-) diff --git a/src/global.h b/src/global.h index 22ae0c52bf..0b36e3103b 100644 --- a/src/global.h +++ b/src/global.h @@ -362,15 +362,228 @@ class CCustomEvent : public QEvent // command line parsing, TODO do not declare functions globally but in a class QString UsageArguments ( char** argv ); -bool GetFlagArgument ( char** argv, int& i, QString strShortOpt, QString strLongOpt ); - -bool GetStringArgument ( int argc, char** argv, int& i, QString strShortOpt, QString strLongOpt, QString& strArg ); - -bool GetNumericArgument ( int argc, - char** argv, - int& i, - QString strShortOpt, - QString strLongOpt, - double rRangeStart, - double rRangeStop, - double& rValue ); +//============================================================================ +// CMsgBoxes class: +// Use this static class to show basic Error, Warning and Info messageboxes +// For own created message boxes you should still use +// CMsgBoxes::MainForm() and CMsgBoxes::MainFormName() +//============================================================================ +#ifndef HEADLESS +# include +#endif + +// html text macro's (for use in gui texts) +#define htmlBold( T ) "" + T + "" +#define htmlNewLine() "
" + + +class CMsgBoxes +{ +protected: + static QDialog* pMainForm; + static QString strMainFormName; + +public: + static void init ( QDialog* theMainForm, QString theMainFormName ) + { + pMainForm = theMainForm; + strMainFormName = theMainFormName; + } + + static QDialog* MainForm() { return pMainForm; } + static const QString& MainFormName() { return strMainFormName; } + + // Message boxes: + static void ShowError ( QString strError ) + { +#ifndef HEADLESS + QMessageBox::critical ( pMainForm, strMainFormName + ": " + QObject::tr ( "Error" ), strError, QObject::tr ( "Ok" ), nullptr ); +#endif + } + + static void ShowWarning ( QString strWarning ) + { +#ifndef HEADLESS + QMessageBox::warning ( pMainForm, strMainFormName + ": " + QObject::tr ( "Warning" ), strWarning, QObject::tr ( "Ok" ), nullptr ); +#endif + } + + static void ShowInfo ( QString strInfo ) + { +#ifndef HEADLESS + QMessageBox::information ( pMainForm, strMainFormName + ": " + QObject::tr ( "Information" ), strInfo, QObject::tr ( "Ok" ), nullptr ); +#endif + } +}; + +//============================================================================ +// CCommandlineOptions class: +// Note that passing commandline arguments to classes is no longer required, +// since via this class we can get commandline options anywhere. +//============================================================================ + +class CCommandlineOptions +{ +public: + CCommandlineOptions() { reset(); } + +private: + friend int main ( int argc, char** argv ); + + // Statics assigned from main () + static int appArgc; + static char** appArgv; + +public: + static QString GetProgramPath() { return QString ( *appArgv ); } + +public: + // sequencial parse functions using the argument index: + + static bool GetFlagArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt ); + + static bool GetStringArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt, QString& strArg ); + + static bool GetNumericArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt, double rRangeStart, double rRangeStop, double& rValue ); + +public: + // find and get a specific argument: + + static bool GetFlagArgument ( const QString& strShortOpt, const QString& strLongOpt ) + { + int i = 1; + while ( i < appArgc ) + { + if ( GetFlagArgument ( i, strShortOpt, strLongOpt ) ) + { + return true; + } + + i++; + } + + return false; + } + + static bool GetStringArgument ( const QString& strShortOpt, const QString& strLongOpt, QString& strArg ) + { + int i = 1; + while ( i < appArgc ) + { + if ( GetStringArgument ( i, strShortOpt, strLongOpt, strArg ) ) + { + return true; + } + + i++; + } + + return false; + } + + static bool GetNumericArgument ( const QString& strShortOpt, const QString& strLongOpt, double rRangeStart, double rRangeStop, double& rValue ) + { + int i = 1; + while ( i < appArgc ) + { + if ( GetNumericArgument ( i, strShortOpt, strLongOpt, rRangeStart, rRangeStop, rValue ) ) + { + return true; + } + + i++; + } + + return false; + } + +//================================================= +// Non statics to parse bare arguments +// (These need an instance of CCommandlineOptions) +//================================================= + +protected: + + int currentIndex; + char** currentArgv; + + void reset() + { + currentArgv = appArgv; + currentIndex = 0; + } + +public: + + QString GetFirstArgument() + { + reset(); + // Skipping program path + return GetNextArgument(); + } + + QString GetNextArgument() + { + if ( currentIndex < appArgc ) + { + currentArgv++; + currentIndex++; + + if ( currentIndex < appArgc ) + { + return QString ( *currentArgv ); + } + } + + return QString(); + } + +}; + +// defines for commandline options in the style "shortopt", "longopt" +// Name is standard CMDLN_LONGOPTNAME +// These defines can be used for strShortOpt, strLongOpt parameters +// of the CCommandlineOptions functions. +// +// clang-format off +#define CMDLN_SERVER "-s", "--server" +#define CMDLN_INIFILE "-i", "--inifile" +#define CMDLN_NOGUI "-n", "--nogui" +#define CMDLN_PORT "-p", "--port" +#define CMDLN_QOS "-Q", "--qos" +#define CMDLN_NOTRANSLATION "-t", "--notranslation" +#define CMDLN_ENABLEIPV6 "-6", "--enableipv6" +#define CMDLN_DISCONONQUIT "-d", "--discononquit" +#define CMDLN_DIRECTORYSERVER "-e", "--directoryserver" +#define CMDLN_DIRECTORYFILE "--directoryfile", "--directoryfile" +#define CMDLN_LISTFILTER "-f", "--listfilter" +#define CMDLN_FASTUPDATE "-F", "--fastupdate" +#define CMDLN_LOG "-l", "--log" +#define CMDLN_LICENCE "-L", "--licence" +#define CMDLN_HTMLSTATUS "-m", "--htmlstatus" +#define CMDLN_SERVERINFO "-o", "--serverinfo" +#define CMDLN_SERVERPUBLICIP "--serverpublicip", "--serverpublicip" +#define CMDLN_DELAYPAN "-P", "--delaypan" +#define CMDLN_RECORDING "-R", "--recording" +#define CMDLN_NORECORD "--norecord", "--norecord" +#define CMDLN_SERVERBINDIP "--serverbindip", "--serverbindip" +#define CMDLN_MULTITHREADING "-T", "--multithreading" +#define CMDLN_NUMCHANNELS "-u", "--numchannels" +#define CMDLN_WELCOMEMESSAGE "-w", "--welcomemessage" +#define CMDLN_STARTMINIMIZED "-z", "--startminimized" +#define CMDLN_CONNECT "-c", "--connect" +#define CMDLN_NOJACKCONNECT "-j", "--nojackconnect" +#define CMDLN_MUTESTREAM "-M", "--mutestream" +#define CMDLN_MUTEMYOWN "--mutemyown", "--mutemyown" +#define CMDLN_CLIENTNAME "--clientname", "--clientname" +#define CMDLN_CTRLMIDICH "--ctrlmidich", "--ctrlmidich" +// Backwards compatibilyty: +#define CMDLN_CENTRALSERVER "--centralserver", "--centralserver" +// pgScorpio: TODO These are NOT in help !: +#define CMDLN_SHOWALLSERVERS "--showallservers", "--showallservers" +#define CMDLN_SHOWANALYZERCONSOLE "--showanalyzerconsole", "--showanalyzerconsole" +// CMDLN_SPECIAL: Mostly used for debugging, any option after --special is accepted, should NOT be in help ! +#define CMDLN_SPECIAL "--special", "--special" +// Special options for sound-redesign testing +#define CMDLN_JACKINPUTS "--jackinputs", "--jackinputs" +// clang-format on diff --git a/src/main.cpp b/src/main.cpp index 05f245afc3..5c6111417c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -55,8 +55,19 @@ extern void qt_set_sequence_auto_mnemonic ( bool bEnable ); // Implementation ************************************************************** +int CCommandlineOptions::appArgc = 0; +char** CCommandlineOptions::appArgv = NULL; + +QDialog* CMsgBoxes::pMainForm = NULL; +QString CMsgBoxes::strMainFormName = APP_NAME; + + int main ( int argc, char** argv ) { + CCommandlineOptions::appArgc = argc; + CCommandlineOptions::appArgv = argv; + + CCommandlineOptions cmdLine; // We don't really need an instance here, but using one improves the readability of the code. #if defined( Q_OS_MACX ) // Mnemonic keys are default disabled in Qt for MacOS. The following function enables them. @@ -78,6 +89,7 @@ int main ( int argc, char** argv ) #else bool bIsClient = true; #endif + bool bSpecialOptions = false; // Any options after this option will be accepted ! (mostly used for debugging purpouses) bool bUseGUI = true; bool bStartMinimized = false; bool bShowComplRegConnList = false; @@ -127,14 +139,20 @@ int main ( int argc, char** argv ) // QT docu: argv()[0] is the program name, argv()[1] is the first // argument and argv()[argc()-1] is the last argument. // Start with first argument, therefore "i = 1" + + // clang-format off +// pgScorio TODO: +// Extra Checks on parameters: +// If given, CMDLN_SERVER MUST be FIRST parameter. +// And then only check parameters valid for common, server or client ! + // clang-format on + for ( int i = 1; i < argc; i++ ) { - // Help (usage) flag --------------------------------------------------- if ( ( !strcmp ( argv[i], "--help" ) ) || ( !strcmp ( argv[i], "-h" ) ) || ( !strcmp ( argv[i], "-?" ) ) ) { - const QString strHelp = UsageArguments ( argv ); - std::cout << qUtf8Printable ( strHelp ); + std::cout << qUtf8Printable ( UsageArguments ( argv ) ); exit ( 0 ); } @@ -148,7 +166,7 @@ int main ( int argc, char** argv ) // Common options: // Initialization file ------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-i", "--inifile", strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_INIFILE, strArgument ) ) { strIniFileName = strArgument; qInfo() << qUtf8Printable ( QString ( "- initialization file name: %1" ).arg ( strIniFileName ) ); @@ -157,7 +175,7 @@ int main ( int argc, char** argv ) } // Disable GUI flag ---------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-n", "--nogui" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_NOGUI ) ) { bUseGUI = false; qInfo() << "- no GUI mode chosen"; @@ -166,7 +184,7 @@ int main ( int argc, char** argv ) } // Port number --------------------------------------------------------- - if ( GetNumericArgument ( argc, argv, i, "-p", "--port", 0, 65535, rDbleArgument ) ) + if ( cmdLine.GetNumericArgument ( i, CMDLN_PORT, 0, 65535, rDbleArgument ) ) { iPortNumber = static_cast ( rDbleArgument ); bCustomPortNumberGiven = true; @@ -175,26 +193,8 @@ int main ( int argc, char** argv ) continue; } - // JSON-RPC port number ------------------------------------------------ - if ( GetNumericArgument ( argc, argv, i, "--jsonrpcport", "--jsonrpcport", 0, 65535, rDbleArgument ) ) - { - iJsonRpcPortNumber = static_cast ( rDbleArgument ); - qInfo() << qUtf8Printable ( QString ( "- JSON-RPC port number: %1" ).arg ( iJsonRpcPortNumber ) ); - CommandLineOptions << "--jsonrpcport"; - continue; - } - - // JSON-RPC secret file name ------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "--jsonrpcsecretfile", "--jsonrpcsecretfile", strArgument ) ) - { - strJsonRpcSecretFileName = strArgument; - qInfo() << qUtf8Printable ( QString ( "- JSON-RPC secret file: %1" ).arg ( strJsonRpcSecretFileName ) ); - CommandLineOptions << "--jsonrpcsecretfile"; - continue; - } - // Quality of Service -------------------------------------------------- - if ( GetNumericArgument ( argc, argv, i, "-Q", "--qos", 0, 255, rDbleArgument ) ) + if ( cmdLine.GetNumericArgument ( i, CMDLN_QOS, 0, 255, rDbleArgument ) ) { iQosNumber = static_cast ( rDbleArgument ); qInfo() << qUtf8Printable ( QString ( "- selected QoS value: %1" ).arg ( iQosNumber ) ); @@ -203,7 +203,7 @@ int main ( int argc, char** argv ) } // Disable translations ------------------------------------------------ - if ( GetFlagArgument ( argv, i, "-t", "--notranslation" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_NOTRANSLATION ) ) { bUseTranslation = false; qInfo() << "- translations disabled"; @@ -212,7 +212,7 @@ int main ( int argc, char** argv ) } // Enable IPv6 --------------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-6", "--enableipv6" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_ENABLEIPV6 ) ) { bEnableIPv6 = true; qInfo() << "- IPv6 enabled"; @@ -223,7 +223,7 @@ int main ( int argc, char** argv ) // Server only: // Disconnect all clients on quit -------------------------------------- - if ( GetFlagArgument ( argv, i, "-d", "--discononquit" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_DISCONONQUIT ) ) { bDisconnectAllClientsOnQuit = true; qInfo() << "- disconnect all clients on quit"; @@ -233,22 +233,9 @@ int main ( int argc, char** argv ) } // Directory server ---------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-e", "--directoryserver", strArgument ) ) - { - strDirectoryServer = strArgument; - qInfo() << qUtf8Printable ( QString ( "- directory server: %1" ).arg ( strDirectoryServer ) ); - CommandLineOptions << "--directoryserver"; - ServerOnlyOptions << "--directoryserver"; - continue; - } - - // Central server ** D E P R E C A T E D ** ---------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--centralserver", // no short form - "--centralserver", - strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_DIRECTORYSERVER, strArgument ) || + cmdLine.GetStringArgument ( i, CMDLN_CENTRALSERVER, strArgument ) // ** D E P R E C A T E D ** + ) { strDirectoryServer = strArgument; qInfo() << qUtf8Printable ( QString ( "- directory server: %1" ).arg ( strDirectoryServer ) ); @@ -258,12 +245,7 @@ int main ( int argc, char** argv ) } // Directory file ------------------------------------------------------ - if ( GetStringArgument ( argc, - argv, - i, - "--directoryfile", // no short form - "--directoryfile", - strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_DIRECTORYFILE, strArgument ) ) { strServerListFileName = strArgument; qInfo() << qUtf8Printable ( QString ( "- directory server persistence file: %1" ).arg ( strServerListFileName ) ); @@ -273,7 +255,7 @@ int main ( int argc, char** argv ) } // Server list filter -------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-f", "--listfilter", strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_LISTFILTER, strArgument ) ) { strServerListFilter = strArgument; qInfo() << qUtf8Printable ( QString ( "- server list filter: %1" ).arg ( strServerListFilter ) ); @@ -283,7 +265,7 @@ int main ( int argc, char** argv ) } // Use 64 samples frame size mode -------------------------------------- - if ( GetFlagArgument ( argv, i, "-F", "--fastupdate" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_FASTUPDATE ) ) { bUseDoubleSystemFrameSize = false; // 64 samples frame size qInfo() << qUtf8Printable ( QString ( "- using %1 samples frame size mode" ).arg ( SYSTEM_FRAME_SIZE_SAMPLES ) ); @@ -293,7 +275,7 @@ int main ( int argc, char** argv ) } // Use logging --------------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-l", "--log", strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_LOG, strArgument ) ) { strLoggingFileName = strArgument; qInfo() << qUtf8Printable ( QString ( "- logging file name: %1" ).arg ( strLoggingFileName ) ); @@ -303,7 +285,7 @@ int main ( int argc, char** argv ) } // Use licence flag ---------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-L", "--licence" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_LICENCE ) ) { // LT_CREATIVECOMMONS is now used just to enable the pop up eLicenceType = LT_CREATIVECOMMONS; @@ -314,7 +296,7 @@ int main ( int argc, char** argv ) } // HTML status file ---------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-m", "--htmlstatus", strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_HTMLSTATUS, strArgument ) ) { strHTMLStatusFileName = strArgument; qInfo() << qUtf8Printable ( QString ( "- HTML status file name: %1" ).arg ( strHTMLStatusFileName ) ); @@ -324,7 +306,7 @@ int main ( int argc, char** argv ) } // Server info --------------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-o", "--serverinfo", strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_SERVERINFO, strArgument ) ) { strServerInfo = strArgument; qInfo() << qUtf8Printable ( QString ( "- server info: %1" ).arg ( strServerInfo ) ); @@ -334,12 +316,7 @@ int main ( int argc, char** argv ) } // Server Public IP ---------------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--serverpublicip", // no short form - "--serverpublicip", - strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_SERVERPUBLICIP, strArgument ) ) { strServerPublicIP = strArgument; qInfo() << qUtf8Printable ( QString ( "- server public IP: %1" ).arg ( strServerPublicIP ) ); @@ -349,7 +326,7 @@ int main ( int argc, char** argv ) } // Enable delay panning on startup ------------------------------------- - if ( GetFlagArgument ( argv, i, "-P", "--delaypan" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_DELAYPAN ) ) { bDelayPan = true; qInfo() << "- starting with delay panning"; @@ -359,7 +336,7 @@ int main ( int argc, char** argv ) } // Recording directory ------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-R", "--recording", strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_RECORDING, strArgument ) ) { strRecordingDirName = strArgument; qInfo() << qUtf8Printable ( QString ( "- recording directory name: %1" ).arg ( strRecordingDirName ) ); @@ -369,10 +346,7 @@ int main ( int argc, char** argv ) } // Disable recording on startup ---------------------------------------- - if ( GetFlagArgument ( argv, - i, - "--norecord", // no short form - "--norecord" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_NORECORD ) ) { bDisableRecording = true; qInfo() << "- recording will not take place until enabled"; @@ -382,7 +356,7 @@ int main ( int argc, char** argv ) } // Server mode flag ---------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-s", "--server" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_SERVER ) ) { bIsClient = false; qInfo() << "- server mode chosen"; @@ -392,12 +366,7 @@ int main ( int argc, char** argv ) } // Server Bind IP -------------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--serverbindip", // no short form - "--serverbindip", - strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_SERVERBINDIP, strArgument ) ) { strServerBindIP = strArgument; qInfo() << qUtf8Printable ( QString ( "- server bind IP: %1" ).arg ( strServerBindIP ) ); @@ -407,7 +376,7 @@ int main ( int argc, char** argv ) } // Use multithreading -------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-T", "--multithreading" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_MULTITHREADING ) ) { bUseMultithreading = true; qInfo() << "- using multithreading"; @@ -417,7 +386,7 @@ int main ( int argc, char** argv ) } // Maximum number of channels ------------------------------------------ - if ( GetNumericArgument ( argc, argv, i, "-u", "--numchannels", 1, MAX_NUM_CHANNELS, rDbleArgument ) ) + if ( cmdLine.GetNumericArgument ( i, CMDLN_NUMCHANNELS, 1, MAX_NUM_CHANNELS, rDbleArgument ) ) { iNumServerChannels = static_cast ( rDbleArgument ); @@ -429,7 +398,7 @@ int main ( int argc, char** argv ) } // Server welcome message ---------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-w", "--welcomemessage", strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_WELCOMEMESSAGE, strArgument ) ) { strWelcomeMessage = strArgument; qInfo() << qUtf8Printable ( QString ( "- welcome message: %1" ).arg ( strWelcomeMessage ) ); @@ -439,7 +408,7 @@ int main ( int argc, char** argv ) } // Start minimized ----------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-z", "--startminimized" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_STARTMINIMIZED ) ) { bStartMinimized = true; qInfo() << "- start minimized enabled"; @@ -451,7 +420,7 @@ int main ( int argc, char** argv ) // Client only: // Connect on startup -------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-c", "--connect", strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_CONNECT, strArgument ) ) { strConnOnStartupAddress = NetworkUtil::FixAddress ( strArgument ); qInfo() << qUtf8Printable ( QString ( "- connect on startup to address: %1" ).arg ( strConnOnStartupAddress ) ); @@ -461,7 +430,7 @@ int main ( int argc, char** argv ) } // Disabling auto Jack connections ------------------------------------- - if ( GetFlagArgument ( argv, i, "-j", "--nojackconnect" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_NOJACKCONNECT ) ) { bNoAutoJackConnect = true; qInfo() << "- disable auto Jack connections"; @@ -471,7 +440,7 @@ int main ( int argc, char** argv ) } // Mute stream on startup ---------------------------------------------- - if ( GetFlagArgument ( argv, i, "-M", "--mutestream" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_MUTESTREAM ) ) { bMuteStream = true; qInfo() << "- mute stream activated"; @@ -481,10 +450,7 @@ int main ( int argc, char** argv ) } // For headless client mute my own signal in personal mix -------------- - if ( GetFlagArgument ( argv, - i, - "--mutemyown", // no short form - "--mutemyown" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_MUTEMYOWN ) ) { bMuteMeInPersonalMix = true; qInfo() << "- mute me in my personal mix"; @@ -494,12 +460,7 @@ int main ( int argc, char** argv ) } // Client Name --------------------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--clientname", // no short form - "--clientname", - strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_CLIENTNAME, strArgument ) ) { strClientName = strArgument; qInfo() << qUtf8Printable ( QString ( "- client name: %1" ).arg ( strClientName ) ); @@ -509,12 +470,7 @@ int main ( int argc, char** argv ) } // Controller MIDI channel --------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--ctrlmidich", // no short form - "--ctrlmidich", - strArgument ) ) + if ( cmdLine.GetStringArgument ( i, CMDLN_CTRLMIDICH, strArgument ) ) { strMIDISetup = strArgument; qInfo() << qUtf8Printable ( QString ( "- MIDI controller settings: %1" ).arg ( strMIDISetup ) ); @@ -529,10 +485,7 @@ int main ( int argc, char** argv ) // Undocumented debugging command line argument: Show all registered // servers in the server list regardless if a ping to the server is // possible or not. - if ( GetFlagArgument ( argv, - i, - "--showallservers", // no short form - "--showallservers" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_SHOWALLSERVERS ) ) { bShowComplRegConnList = true; qInfo() << "- show all registered servers in server list"; @@ -544,10 +497,7 @@ int main ( int argc, char** argv ) // Show analyzer console ----------------------------------------------- // Undocumented debugging command line argument: Show the analyzer // console to debug network buffer properties. - if ( GetFlagArgument ( argv, - i, - "--showanalyzerconsole", // no short form - "--showanalyzerconsole" ) ) + if ( cmdLine.GetFlagArgument ( i, CMDLN_SHOWANALYZERCONSOLE ) ) { bShowAnalyzerConsole = true; qInfo() << "- show analyzer console"; @@ -556,14 +506,27 @@ int main ( int argc, char** argv ) continue; } + // Enable Special Options ---------------------------------------------- + if ( cmdLine.GetFlagArgument ( i, CMDLN_SPECIAL ) ) + { + bSpecialOptions = true; + qInfo() << "- Special options enabled !"; + continue; + } + // Unknown option ------------------------------------------------------ qCritical() << qUtf8Printable ( QString ( "%1: Unknown option '%2' -- use '--help' for help" ).arg ( argv[0] ).arg ( argv[i] ) ); + // pgScorpio: No exit for options after the "--special" option. + // Used for debugging and testing new options... + if ( !bSpecialOptions ) + { // clicking on the Mac application bundle, the actual application // is called with weird command line args -> do not exit on these #if !( defined( Q_OS_MACX ) ) - exit ( 1 ); + exit ( 1 ); #endif + } } // Dependencies ------------------------------------------------------------ @@ -595,8 +558,7 @@ int main ( int argc, char** argv ) if ( ServerOnlyOptions.size() != 0 ) { qCritical() << qUtf8Printable ( QString ( "%1: Server only option(s) '%2' used. Did you omit '--server'?" ) - .arg ( argv[0] ) - .arg ( ServerOnlyOptions.join ( ", " ) ) ); + .arg ( CCommandlineOptions::GetProgramPath(), ServerOnlyOptions.join ( ", " ) ) ); exit ( 1 ); } @@ -900,6 +862,9 @@ int main ( int argc, char** argv ) bEnableIPv6, nullptr ); + // initialise message boxes + CMsgBoxes::init ( &ClientDlg, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); + // show dialog ClientDlg.show(); pApp->exec(); @@ -1082,9 +1047,9 @@ QString UsageArguments ( char** argv ) // clang-format on } -bool GetFlagArgument ( char** argv, int& i, QString strShortOpt, QString strLongOpt ) +bool CCommandlineOptions::GetFlagArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt ) { - if ( ( !strShortOpt.compare ( argv[i] ) ) || ( !strLongOpt.compare ( argv[i] ) ) ) + if ( ( !strShortOpt.compare ( appArgv[i] ) ) || ( !strLongOpt.compare ( appArgv[i] ) ) ) { return true; } @@ -1094,17 +1059,17 @@ bool GetFlagArgument ( char** argv, int& i, QString strShortOpt, QString strLong } } -bool GetStringArgument ( int argc, char** argv, int& i, QString strShortOpt, QString strLongOpt, QString& strArg ) +bool CCommandlineOptions::GetStringArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt, QString& strArg ) { - if ( ( !strShortOpt.compare ( argv[i] ) ) || ( !strLongOpt.compare ( argv[i] ) ) ) + if ( ( !strShortOpt.compare ( appArgv[i] ) ) || ( !strLongOpt.compare ( appArgv[i] ) ) ) { - if ( ++i >= argc ) + if ( ++i >= appArgc ) { - qCritical() << qUtf8Printable ( QString ( "%1: '%2' needs a string argument." ).arg ( argv[0] ).arg ( argv[i - 1] ) ); + qCritical() << qUtf8Printable ( QString ( "%1: '%2' needs a string argument." ).arg ( appArgv[0] ).arg ( appArgv[i - 1] ) ); exit ( 1 ); } - strArg = argv[i]; + strArg = appArgv[i]; return true; } @@ -1114,29 +1079,27 @@ bool GetStringArgument ( int argc, char** argv, int& i, QString strShortOpt, QSt } } -bool GetNumericArgument ( int argc, - char** argv, - int& i, - QString strShortOpt, - QString strLongOpt, - double rRangeStart, - double rRangeStop, - double& rValue ) +bool CCommandlineOptions::GetNumericArgument ( int& i, + const QString& strShortOpt, + const QString& strLongOpt, + double rRangeStart, + double rRangeStop, + double& rValue ) { - if ( ( !strShortOpt.compare ( argv[i] ) ) || ( !strLongOpt.compare ( argv[i] ) ) ) + if ( ( !strShortOpt.compare ( appArgv[i] ) ) || ( !strLongOpt.compare ( appArgv[i] ) ) ) { QString errmsg = "%1: '%2' needs a numeric argument from '%3' to '%4'."; - if ( ++i >= argc ) + if ( ++i >= appArgc ) { - qCritical() << qUtf8Printable ( errmsg.arg ( argv[0] ).arg ( argv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); + qCritical() << qUtf8Printable ( errmsg.arg ( appArgv[0] ).arg ( appArgv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); exit ( 1 ); } char* p; - rValue = strtod ( argv[i], &p ); + rValue = strtod ( appArgv[i], &p ); if ( *p || ( rValue < rRangeStart ) || ( rValue > rRangeStop ) ) { - qCritical() << qUtf8Printable ( errmsg.arg ( argv[0] ).arg ( argv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); + qCritical() << qUtf8Printable ( errmsg.arg ( appArgv[0] ).arg ( appArgv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); exit ( 1 ); } From 24f59e04f9c9e8ca0fa1ae16b238d6f1c382c2cd Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Tue, 5 Apr 2022 19:54:40 +0200 Subject: [PATCH 04/25] Modified messageboxes to use the new CMsgBoxes class --- src/chatdlg.cpp | 4 ++-- src/clientdlg.cpp | 13 ++++++------- src/main.cpp | 2 +- src/serverdlg.cpp | 4 +--- src/settings.cpp | 4 +--- src/soundbase.cpp | 2 +- src/util.cpp | 2 +- src/util.h | 2 +- windows/sound.cpp | 9 ++++----- 9 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/chatdlg.cpp b/src/chatdlg.cpp index 9e275633ea..8d8c7c3efa 100644 --- a/src/chatdlg.cpp +++ b/src/chatdlg.cpp @@ -140,8 +140,8 @@ void CChatDlg::OnAnchorClicked ( const QUrl& Url ) // only allow http(s) URLs to be opened in an external browser if ( Url.scheme() == QLatin1String ( "https" ) || Url.scheme() == QLatin1String ( "http" ) ) { - if ( QMessageBox::question ( this, - APP_NAME, + if ( QMessageBox::question ( CMsgBoxes::MainForm(), + CMsgBoxes::MainFormName(), tr ( "Do you want to open the link '%1' in your browser?" ).arg ( "" + Url.toString() + "" ), QMessageBox::Yes | QMessageBox::No ) == QMessageBox::Yes ) { diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index e8d8163e67..b2433340b7 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -1059,7 +1059,8 @@ void CClientDlg::OnTimerSigMet() // show message box about feedback issue QCheckBox* chb = new QCheckBox ( tr ( "Enable feedback detection" ) ); chb->setCheckState ( pSettings->bEnableFeedbackDetection ? Qt::Checked : Qt::Unchecked ); - QMessageBox msgbox; + QMessageBox msgbox ( CMsgBoxes::MainForm()); + msgbox.setWindowTitle(CMsgBoxes::MainFormName() + ": " + tr( "Warning" )); msgbox.setText ( tr ( "Audio feedback or loud signal detected.\n\n" "We muted your channel and activated 'Mute Myself'. Please solve " "the feedback issue first and unmute yourself afterwards." ) ); @@ -1143,10 +1144,8 @@ void CClientDlg::OnTimerCheckAudioDeviceOk() // it is trying to connect the server which does not help to solve the problem (#129)) if ( !pClient->SoundIsRunning() ) // ---> pgScorpio Was pClient->IsCallbackEntered() { - QMessageBox::warning ( this, - APP_NAME, - tr ( "Your sound card is not working correctly. " - "Please open the settings dialog and check the device selection and the driver settings." ) ); + CMsgBoxes::ShowWarning( tr ( "Your sound card is not working correctly. " + "Please open the settings dialog and check the device selection and the driver settings." ) ); } } @@ -1164,7 +1163,7 @@ void CClientDlg::OnSoundDeviceChanged ( QString strError ) } // show the error message of the device setup - QMessageBox::critical ( this, APP_NAME, strError, tr ( "Ok" ), nullptr ); + CMsgBoxes::ShowError ( strError ); } // if the check audio device timer is running, it must be restarted on a device change @@ -1209,7 +1208,7 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str catch ( const CGenErr& generr ) { // show error message and return the function - QMessageBox::critical ( this, APP_NAME, generr.GetErrorText(), "Close", nullptr ); + CMsgBoxes::ShowError( generr.GetErrorText() ); return; } diff --git a/src/main.cpp b/src/main.cpp index 5c6111417c..7b52f3ece8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -957,7 +957,7 @@ int main ( int argc, char** argv ) #ifndef HEADLESS if ( bUseGUI ) { - QMessageBox::critical ( nullptr, APP_NAME, generr.GetErrorText(), "Quit", nullptr ); + CMsgBoxes::ShowError( generr.GetErrorText() ); } else #endif diff --git a/src/serverdlg.cpp b/src/serverdlg.cpp index f467a2f22b..6c370569ab 100644 --- a/src/serverdlg.cpp +++ b/src/serverdlg.cpp @@ -571,9 +571,7 @@ void CServerDlg::OnStopRecorder() UpdateRecorderStatus ( QString() ); if ( pServer->GetRecorderErrMsg() != QString() ) { - QMessageBox::warning ( this, - APP_NAME, - tr ( "Recorder failed to start. " + CMsgBoxes::ShowWarning( tr ( "Recorder failed to start. " "Please check available disk space and permissions and try again. " "Error: " ) + pServer->GetRecorderErrMsg() ); diff --git a/src/settings.cpp b/src/settings.cpp index 7b1210223a..886f0b5052 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -336,11 +336,9 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, if ( !strError.isEmpty() ) { -# ifndef HEADLESS // special case: when settings are loaded no GUI is yet created, therefore // we have to create a warning message box here directly - QMessageBox::warning ( nullptr, APP_NAME, strError ); -# endif + CMsgBoxes::ShowWarning( strError ); } // sound card channel mapping settings: make sure these settings are diff --git a/src/soundbase.cpp b/src/soundbase.cpp index 3e8f2c2b21..af1f3d7091 100644 --- a/src/soundbase.cpp +++ b/src/soundbase.cpp @@ -182,7 +182,7 @@ QString CSoundBase::SetDev ( const QString strDevName ) // ASIO drivers sErrorMessage += "
" + tr ( "Do you want to open the ASIO driver setup to try changing your configuration to a working state?" ); - if ( QMessageBox::Yes == QMessageBox::information ( nullptr, APP_NAME, sErrorMessage, QMessageBox::Yes | QMessageBox::No ) ) + if ( QMessageBox::Yes == QMessageBox::information ( CMsgBoxes::MainForm(), CMsgBoxes::MainFormName(), sErrorMessage, QMessageBox::Yes | QMessageBox::No ) ) { LoadAndInitializeFirstValidDriver ( true ); } diff --git a/src/util.cpp b/src/util.cpp index d2f7d6f78b..bfd5034042 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -666,7 +666,7 @@ void CLanguageComboBox::OnLanguageActivated ( int iLanguageIdx ) // only update if the language selection is different from the current selected language if ( iIdxSelectedLanguage != iLanguageIdx ) { - QMessageBox::information ( this, tr ( "Restart Required" ), tr ( "Please restart the application for the language change to take effect." ) ); + QMessageBox::information ( CMsgBoxes::MainForm(), CMsgBoxes::MainFormName() + ": " + tr ( "Restart Required" ), tr ( "Please restart the application for the language change to take effect." ) ); emit LanguageChanged ( itemData ( iLanguageIdx ).toString() ); } diff --git a/src/util.h b/src/util.h index 4a546781c9..72b32ff828 100644 --- a/src/util.h +++ b/src/util.h @@ -421,7 +421,7 @@ class CHelpMenu : public QMenu public slots: void OnHelpWhatsThis() { QWhatsThis::enterWhatsThisMode(); } void OnHelpAbout() { AboutDlg.exec(); } - void OnHelpAboutQt() { QMessageBox::aboutQt ( nullptr, QString ( tr ( "About Qt" ) ) ); } + void OnHelpAboutQt() { QMessageBox::aboutQt ( CMsgBoxes::MainForm(), QString ( tr ( "About Qt" ) ) ); } void OnHelpClientGetStarted() { QDesktopServices::openUrl ( QUrl ( CLIENT_GETTING_STARTED_URL ) ); } void OnHelpServerGetStarted() { QDesktopServices::openUrl ( QUrl ( SERVER_GETTING_STARTED_URL ) ); } void OnHelpSoftwareMan() { QDesktopServices::openUrl ( QUrl ( SOFTWARE_MANUAL_URL ) ); } diff --git a/windows/sound.cpp b/windows/sound.cpp index 58ad034e8a..72f9fcd139 100644 --- a/windows/sound.cpp +++ b/windows/sound.cpp @@ -101,8 +101,8 @@ QString CSound::LoadAndInitializeDriver ( QString strDriverName, bool bOpenDrive if ( bOpenDriverSetup ) { OpenDriverSetup(); - QMessageBox::question ( nullptr, - APP_NAME, + QMessageBox::question ( CMsgBoxes::MainForm(), + CMsgBoxes::MainFormName(), "Are you done with your ASIO driver settings of " + GetDeviceName ( iDriverIdx ) + "?", QMessageBox::Yes ); } @@ -314,9 +314,8 @@ int CSound::GetActualBufferSize ( const int iDesiredBufferSizeMono ) // clang-format off /* // TEST -#include -QMessageBox::information ( 0, "APP_NAME", QString("lMinSize: %1, lMaxSize: %2, lPreferredSize: %3, lGranularity: %4"). - arg(HWBufferInfo.lMinSize).arg(HWBufferInfo.lMaxSize).arg(HWBufferInfo.lPreferredSize).arg(HWBufferInfo.lGranularity) ); +CMsgBoxes::ShowInfo( QString("lMinSize: %1, lMaxSize: %2, lPreferredSize: %3, lGranularity: %4"). + arg(HWBufferInfo.lMinSize).arg(HWBufferInfo.lMaxSize).arg(HWBufferInfo.lPreferredSize).arg(HWBufferInfo.lGranularity) ); _exit(1); */ // clang-format on From 0923cffd6d5e48834a5c20b47741e3fd3ffd7f30 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Thu, 24 Mar 2022 00:09:54 +0100 Subject: [PATCH 05/25] clang-format update (hopefully ok now) --- src/clientdlg.cpp | 10 +++++----- src/global.h | 19 ++++++++++--------- src/main.cpp | 3 +-- src/settings.cpp | 2 +- src/soundbase.cpp | 3 ++- src/util.cpp | 4 +++- 6 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index b2433340b7..da6ba92ef8 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -1059,8 +1059,8 @@ void CClientDlg::OnTimerSigMet() // show message box about feedback issue QCheckBox* chb = new QCheckBox ( tr ( "Enable feedback detection" ) ); chb->setCheckState ( pSettings->bEnableFeedbackDetection ? Qt::Checked : Qt::Unchecked ); - QMessageBox msgbox ( CMsgBoxes::MainForm()); - msgbox.setWindowTitle(CMsgBoxes::MainFormName() + ": " + tr( "Warning" )); + QMessageBox msgbox ( CMsgBoxes::MainForm() ); + msgbox.setWindowTitle ( CMsgBoxes::MainFormName() + ": " + tr ( "Warning" ) ); msgbox.setText ( tr ( "Audio feedback or loud signal detected.\n\n" "We muted your channel and activated 'Mute Myself'. Please solve " "the feedback issue first and unmute yourself afterwards." ) ); @@ -1144,8 +1144,8 @@ void CClientDlg::OnTimerCheckAudioDeviceOk() // it is trying to connect the server which does not help to solve the problem (#129)) if ( !pClient->SoundIsRunning() ) // ---> pgScorpio Was pClient->IsCallbackEntered() { - CMsgBoxes::ShowWarning( tr ( "Your sound card is not working correctly. " - "Please open the settings dialog and check the device selection and the driver settings." ) ); + CMsgBoxes::ShowWarning ( tr ( "Your sound card is not working correctly. " + "Please open the settings dialog and check the device selection and the driver settings." ) ); } } @@ -1208,7 +1208,7 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str catch ( const CGenErr& generr ) { // show error message and return the function - CMsgBoxes::ShowError( generr.GetErrorText() ); + CMsgBoxes::ShowError ( generr.GetErrorText() ); return; } diff --git a/src/global.h b/src/global.h index 0b36e3103b..4a9020cd4d 100644 --- a/src/global.h +++ b/src/global.h @@ -376,7 +376,6 @@ QString UsageArguments ( char** argv ); #define htmlBold( T ) "" + T + "" #define htmlNewLine() "
" - class CMsgBoxes { protected: @@ -444,7 +443,12 @@ class CCommandlineOptions static bool GetStringArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt, QString& strArg ); - static bool GetNumericArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt, double rRangeStart, double rRangeStop, double& rValue ); + static bool GetNumericArgument ( int& i, + const QString& strShortOpt, + const QString& strLongOpt, + double rRangeStart, + double rRangeStop, + double& rValue ); public: // find and get a specific argument: @@ -497,13 +501,12 @@ class CCommandlineOptions return false; } -//================================================= -// Non statics to parse bare arguments -// (These need an instance of CCommandlineOptions) -//================================================= + //================================================= + // Non statics to parse bare arguments + // (These need an instance of CCommandlineOptions) + //================================================= protected: - int currentIndex; char** currentArgv; @@ -514,7 +517,6 @@ class CCommandlineOptions } public: - QString GetFirstArgument() { reset(); @@ -537,7 +539,6 @@ class CCommandlineOptions return QString(); } - }; // defines for commandline options in the style "shortopt", "longopt" diff --git a/src/main.cpp b/src/main.cpp index 7b52f3ece8..719dc0bb14 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,7 +61,6 @@ char** CCommandlineOptions::appArgv = NULL; QDialog* CMsgBoxes::pMainForm = NULL; QString CMsgBoxes::strMainFormName = APP_NAME; - int main ( int argc, char** argv ) { CCommandlineOptions::appArgc = argc; @@ -957,7 +956,7 @@ int main ( int argc, char** argv ) #ifndef HEADLESS if ( bUseGUI ) { - CMsgBoxes::ShowError( generr.GetErrorText() ); + CMsgBoxes::ShowError ( generr.GetErrorText() ); } else #endif diff --git a/src/settings.cpp b/src/settings.cpp index 886f0b5052..2adeb05db3 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -338,7 +338,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, { // special case: when settings are loaded no GUI is yet created, therefore // we have to create a warning message box here directly - CMsgBoxes::ShowWarning( strError ); + CMsgBoxes::ShowWarning ( strError ); } // sound card channel mapping settings: make sure these settings are diff --git a/src/soundbase.cpp b/src/soundbase.cpp index af1f3d7091..c62895b5cf 100644 --- a/src/soundbase.cpp +++ b/src/soundbase.cpp @@ -182,7 +182,8 @@ QString CSoundBase::SetDev ( const QString strDevName ) // ASIO drivers sErrorMessage += "
" + tr ( "Do you want to open the ASIO driver setup to try changing your configuration to a working state?" ); - if ( QMessageBox::Yes == QMessageBox::information ( CMsgBoxes::MainForm(), CMsgBoxes::MainFormName(), sErrorMessage, QMessageBox::Yes | QMessageBox::No ) ) + if ( QMessageBox::Yes == + QMessageBox::information ( CMsgBoxes::MainForm(), CMsgBoxes::MainFormName(), sErrorMessage, QMessageBox::Yes | QMessageBox::No ) ) { LoadAndInitializeFirstValidDriver ( true ); } diff --git a/src/util.cpp b/src/util.cpp index bfd5034042..0aba0d8fc8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -666,7 +666,9 @@ void CLanguageComboBox::OnLanguageActivated ( int iLanguageIdx ) // only update if the language selection is different from the current selected language if ( iIdxSelectedLanguage != iLanguageIdx ) { - QMessageBox::information ( CMsgBoxes::MainForm(), CMsgBoxes::MainFormName() + ": " + tr ( "Restart Required" ), tr ( "Please restart the application for the language change to take effect." ) ); + QMessageBox::information ( CMsgBoxes::MainForm(), + CMsgBoxes::MainFormName() + ": " + tr ( "Restart Required" ), + tr ( "Please restart the application for the language change to take effect." ) ); emit LanguageChanged ( itemData ( iLanguageIdx ).toString() ); } From 03a878247da939f7848a2839012c519008f5dfb5 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Thu, 24 Mar 2022 16:50:49 +0100 Subject: [PATCH 06/25] Solution for headless build? define the in headless non existing QDialog as void and send messages to the appropriate output stream --- src/global.h | 25 +++++-------------------- src/main.cpp | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/global.h b/src/global.h index 4a9020cd4d..0a31b856f7 100644 --- a/src/global.h +++ b/src/global.h @@ -370,6 +370,8 @@ QString UsageArguments ( char** argv ); //============================================================================ #ifndef HEADLESS # include +#else +# define QDialog void #endif // html text macro's (for use in gui texts) @@ -393,26 +395,9 @@ class CMsgBoxes static const QString& MainFormName() { return strMainFormName; } // Message boxes: - static void ShowError ( QString strError ) - { -#ifndef HEADLESS - QMessageBox::critical ( pMainForm, strMainFormName + ": " + QObject::tr ( "Error" ), strError, QObject::tr ( "Ok" ), nullptr ); -#endif - } - - static void ShowWarning ( QString strWarning ) - { -#ifndef HEADLESS - QMessageBox::warning ( pMainForm, strMainFormName + ": " + QObject::tr ( "Warning" ), strWarning, QObject::tr ( "Ok" ), nullptr ); -#endif - } - - static void ShowInfo ( QString strInfo ) - { -#ifndef HEADLESS - QMessageBox::information ( pMainForm, strMainFormName + ": " + QObject::tr ( "Information" ), strInfo, QObject::tr ( "Ok" ), nullptr ); -#endif - } + static void ShowError ( QString strError ); + static void ShowWarning ( QString strWarning ); + static void ShowInfo ( QString strInfo ); }; //============================================================================ diff --git a/src/main.cpp b/src/main.cpp index 719dc0bb14..88b850f055 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -973,6 +973,36 @@ int main ( int argc, char** argv ) return 0; } +/******************************************************************************\ +* Message Boxes * +\******************************************************************************/ +void CMsgBoxes::ShowError ( QString strError ) +{ +#ifndef HEADLESS + QMessageBox::critical ( pMainForm, strMainFormName + ": " + QObject::tr ( "Error" ), strError, QObject::tr ( "Ok" ), nullptr ); +#else + qCritical() << "Error: " << strError.toLocal8Bit().data(); +#endif +} + +void CMsgBoxes::ShowWarning ( QString strWarning ) +{ +#ifndef HEADLESS + QMessageBox::warning ( pMainForm, strMainFormName + ": " + QObject::tr ( "Warning" ), strWarning, QObject::tr ( "Ok" ), nullptr ); +#else + qWarning() << "Warning: " << strWarning.toLocal8Bit().data(); +#endif +} + +void CMsgBoxes::ShowInfo ( QString strInfo ) +{ +#ifndef HEADLESS + QMessageBox::information ( pMainForm, strMainFormName + ": " + QObject::tr ( "Information" ), strInfo, QObject::tr ( "Ok" ), nullptr ); +#else + qInfo() << "Info: " << strInfo.toLocal8Bit().data(); +#endif +} + /******************************************************************************\ * Command Line Argument Parsing * \******************************************************************************/ From b152344febeb1aa0134cb7386cba06829e1244e3 Mon Sep 17 00:00:00 2001 From: ann0see <20726856+ann0see@users.noreply.github.com> Date: Thu, 24 Mar 2022 13:32:28 +0100 Subject: [PATCH 07/25] clang-format update (hopefully ok now) Co-Authored-By: pgScorpio --- src/serverdlg.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/serverdlg.cpp b/src/serverdlg.cpp index 6c370569ab..8b827a8681 100644 --- a/src/serverdlg.cpp +++ b/src/serverdlg.cpp @@ -571,10 +571,10 @@ void CServerDlg::OnStopRecorder() UpdateRecorderStatus ( QString() ); if ( pServer->GetRecorderErrMsg() != QString() ) { - CMsgBoxes::ShowWarning( tr ( "Recorder failed to start. " - "Please check available disk space and permissions and try again. " - "Error: " ) + - pServer->GetRecorderErrMsg() ); + CMsgBoxes::ShowWarning ( tr ( "Recorder failed to start. " + "Please check available disk space and permissions and try again. " + "Error: " ) + + pServer->GetRecorderErrMsg() ); } } From 1299e73105d214f679d6c0b0d5a9a819287ef69d Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Sat, 26 Mar 2022 14:54:59 +0100 Subject: [PATCH 08/25] Bugfix non Windows exit --- src/client.cpp | 62 +++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 0323685b22..957f14d456 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -732,10 +732,7 @@ void CClient::OnHandledSignal ( int sigNum ) case SIGINT: case SIGTERM: // if connected, terminate connection (needed for headless mode) - if ( IsRunning() ) - { - Stop(); - } + StopConnection(); // this should trigger OnAboutToQuit QCoreApplication::instance()->exit(); @@ -835,36 +832,39 @@ void CClient::StartConnection() // ---> pgScorpio: Was Start() void CClient::StopConnection() { - // stop audio interface - Sound.Stop(); - - // disable channel - Channel.SetEnable ( false ); - - // wait for approx. 100 ms to make sure no audio packet is still in the - // network queue causing the channel to be reconnected right after having - // received the disconnect message (seems not to gain much, disconnect is - // still not working reliably) - QTime DieTime = QTime::currentTime().addMSecs ( 100 ); - while ( QTime::currentTime() < DieTime ) + if ( SoundIsRunning() || Channel.IsEnabled() ) { - // exclude user input events because if we use AllEvents, it happens - // that if the user initiates a connection and disconnection quickly - // (e.g. quickly pressing enter five times), the software can get into - // an unknown state - QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); - } + // stop audio interface + Sound.Stop(); + + // disable channel + Channel.SetEnable ( false ); - // Send disconnect message to server (Since we disable our protocol - // receive mechanism with the next command, we do not evaluate any - // respond from the server, therefore we just hope that the message - // gets its way to the server, if not, the old behaviour time-out - // disconnects the connection anyway). - ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); + // wait for approx. 100 ms to make sure no audio packet is still in the + // network queue causing the channel to be reconnected right after having + // received the disconnect message (seems not to gain much, disconnect is + // still not working reliably) + QTime DieTime = QTime::currentTime().addMSecs ( 100 ); + while ( QTime::currentTime() < DieTime ) + { + // exclude user input events because if we use AllEvents, it happens + // that if the user initiates a connection and disconnection quickly + // (e.g. quickly pressing enter five times), the software can get into + // an unknown state + QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); + } + + // Send disconnect message to server (Since we disable our protocol + // receive mechanism with the next command, we do not evaluate any + // respond from the server, therefore we just hope that the message + // gets its way to the server, if not, the old behaviour time-out + // disconnects the connection anyway). + ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); - // reset current signal level and LEDs - bJitterBufferOK = true; - SignalLevelMeter.Reset(); + // reset current signal level and LEDs + bJitterBufferOK = true; + SignalLevelMeter.Reset(); + } } void CClient::Init() From 553d98b8b6949e852601395017964d4a0c6bfdbc Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Sat, 26 Mar 2022 20:27:58 +0100 Subject: [PATCH 09/25] Connection status issue jamulussoftware#2519 second stage Moved Connect/Disconnect code from CClientdlg to CClient. Now using the proper connected checks in several places. Added bDisconnectAndDisable to CChannel. (For a Client now Channel.Disconnect() will block audio data and auto disable the channel on disconnected) --- src/channel.cpp | 29 +++++++- src/channel.h | 3 +- src/client.cpp | 126 +++++++++++++++++++++++----------- src/client.h | 22 ++---- src/clientdlg.cpp | 141 ++++++++++---------------------------- src/clientdlg.h | 13 ++-- src/clientsettingsdlg.cpp | 21 +++--- src/main.cpp | 36 +++++----- src/util.cpp | 1 + 9 files changed, 196 insertions(+), 196 deletions(-) diff --git a/src/channel.cpp b/src/channel.cpp index a35576a34d..9cd4f57a1e 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -37,6 +37,7 @@ CChannel::CChannel ( const bool bNIsServer ) : bIsEnabled ( false ), bIsServer ( bNIsServer ), bIsIdentified ( false ), + bDisconnectAndDisable ( false ), iAudioFrameSizeSamples ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ), SignalLevelMeter ( false, 0.5 ) // server mode with mono out and faster smoothing { @@ -125,7 +126,8 @@ void CChannel::SetEnable ( const bool bNEnStat ) QMutexLocker locker ( &Mutex ); // set internal parameter - bIsEnabled = bNEnStat; + bIsEnabled = bNEnStat; + bDisconnectAndDisable = false; // The support for the packet sequence number must be reset if the client // disconnects from a server since we do not yet know if the next server we @@ -506,11 +508,20 @@ void CChannel::Disconnect() // we only have to disconnect the channel if it is actually connected if ( IsConnected() ) { + // for a Client we will block further audio data and disable the channel as soon as disconnected; + bDisconnectAndDisable = !bIsServer; + // set time out counter to a small value > 0 so that the next time a // received audio block is queried, the disconnection is performed // (assuming that no audio packet is received in the meantime) iConTimeOut = 1; // a small number > 0 } + else if ( !bIsServer ) + { + bDisconnectAndDisable = false; + bIsEnabled = false; + iConTimeOut = 0; + } } void CChannel::PutProtocolData ( const int iRecCounter, const int iRecID, const CVector& vecbyMesBodyData, const CHostAddress& RecHostAddr ) @@ -534,7 +545,7 @@ EPutDataStat CChannel::PutAudioData ( const CVector& vecbyData, const i // Only process audio data if: // - for client only: the packet comes from the server we want to talk to // - the channel is enabled - if ( ( bIsServer || ( GetAddress() == RecHostAddr ) ) && IsEnabled() ) + if ( ( bIsServer || ( GetAddress() == RecHostAddr ) ) && IsEnabled() && !bDisconnectAndDisable ) { MutexSocketBuf.lock(); { @@ -622,6 +633,12 @@ EGetDataStat CChannel::GetData ( CVector& vecbyData, const int iNumByte eGetStatus = GS_CHAN_NOW_DISCONNECTED; iConTimeOut = 0; // make sure we do not have negative values + if ( bDisconnectAndDisable ) + { + bDisconnectAndDisable = false; + bIsEnabled = false; + } + // reset network transport properties ResetNetworkTransportProperties(); } @@ -643,6 +660,13 @@ EGetDataStat CChannel::GetData ( CVector& vecbyData, const int iNumByte { // channel is disconnected eGetStatus = GS_CHAN_NOT_CONNECTED; + + if ( bDisconnectAndDisable ) + { + bDisconnectAndDisable = false; + bIsEnabled = false; + iConTimeOut = 0; + } } } MutexSocketBuf.unlock(); @@ -652,7 +676,6 @@ EGetDataStat CChannel::GetData ( CVector& vecbyData, const int iNumByte { // reset the protocol Protocol.Reset(); - // emit message emit Disconnected(); } diff --git a/src/channel.h b/src/channel.h index 86791494b3..32463bfe1d 100644 --- a/src/channel.h +++ b/src/channel.h @@ -76,7 +76,7 @@ class CChannel : public QObject void PrepAndSendPacket ( CHighPrioSocket* pSocket, const CVector& vecbyNPacket, const int iNPacketLen ); - void ResetTimeOutCounter() { iConTimeOut = iConTimeOutStartVal; } + void ResetTimeOutCounter() { iConTimeOut = bDisconnectAndDisable ? 1 : iConTimeOutStartVal; } bool IsConnected() const { return iConTimeOut > 0; } void Disconnect(); @@ -216,6 +216,7 @@ void CreateReqChannelLevelListMes() { Protocol.CreateReqChannelLevelListMes(); } bool bIsEnabled; bool bIsServer; bool bIsIdentified; + bool bDisconnectAndDisable; int iNetwFrameSizeFact; int iNetwFrameSize; diff --git a/src/client.cpp b/src/client.cpp index 957f14d456..4eb43ee74e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -27,7 +27,6 @@ /* Implementation *************************************************************/ CClient::CClient ( const quint16 iPortNumber, const quint16 iQosNumber, - const QString& strConnOnStartupAddress, const QString& strMIDISetup, const bool bNoAutoJackConnect, const QString& strNClientName, @@ -181,13 +180,6 @@ CClient::CClient ( const quint16 iPortNumber, // start the socket (it is important to start the socket after all // initializations and connections) Socket.Start(); - - // do an immediate start if a server address is given - if ( !strConnOnStartupAddress.isEmpty() ) - { - SetServerAddr ( strConnOnStartupAddress ); - StartConnection(); // ---> pgScorpio: Was Start() - } } CClient::~CClient() @@ -298,8 +290,7 @@ void CClient::CreateServerJitterBufferMessage() void CClient::OnCLPingReceived ( CHostAddress InetAddr, int iMs ) { // make sure we are running and the server address is correct - if ( SoundIsStarted() && // ---> pgScorpio: Does NOT mean we are Connected ! - ( InetAddr == Channel.GetAddress() ) ) + if ( Channel.IsEnabled() && ( InetAddr == Channel.GetAddress() ) ) { // take care of wrap arounds (if wrapping, do not use result) const int iCurDiff = EvaluatePingMessage ( iMs ); @@ -436,22 +427,6 @@ void CClient::StartDelayTimer() } } -bool CClient::SetServerAddr ( QString strNAddr ) -{ - CHostAddress HostAddress; - if ( NetworkUtil().ParseNetworkAddress ( strNAddr, HostAddress, bEnableIPv6 ) ) - { - // apply address to the channel - Channel.SetAddress ( HostAddress ); - - return true; - } - else - { - return false; // invalid address - } -} - bool CClient::GetAndResetbJitterBufferOKFlag() { // get the socket buffer put status flag and reset it @@ -587,9 +562,12 @@ QString CClient::SetSndCrdDev ( const QString strNewDev ) // in case of an error inform the GUI about it if ( !strError.isEmpty() ) { - emit SoundDeviceChanged ( strError ); + CMsgBoxes::ShowError ( strError ); + Disconnect(); } + emit SoundDeviceChanged(); + return strError; } @@ -716,8 +694,13 @@ void CClient::OnSndCrdReinitRequest ( int iSndCrdResetType ) } MutexDriverReinit.unlock(); + if ( !strError.isEmpty() ) + { + CMsgBoxes::ShowError ( strError ); + } + // inform GUI about the sound card device change - emit SoundDeviceChanged ( strError ); + emit SoundDeviceChanged(); } void CClient::OnHandledSignal ( int sigNum ) @@ -732,7 +715,7 @@ void CClient::OnHandledSignal ( int sigNum ) case SIGINT: case SIGTERM: // if connected, terminate connection (needed for headless mode) - StopConnection(); + Disconnect(); // this should trigger OnAboutToQuit QCoreApplication::instance()->exit(); @@ -817,23 +800,41 @@ void CClient::OnClientIDReceived ( int iChanID ) emit ClientIDReceived ( iChanID ); } -void CClient::StartConnection() // ---> pgScorpio: Was Start() +bool CClient::Connect ( QString strServerAddress, QString strServerName ) { - // init object - Init(); + if ( !Channel.IsEnabled() ) + { + CHostAddress HostAddress; + + if ( NetworkUtil().ParseNetworkAddress ( strServerAddress, HostAddress, bEnableIPv6 ) ) + { + // init object + Init(); + + // apply address to the channel + Channel.SetAddress ( HostAddress ); - // enable channel - Channel.SetEnable ( true ); + // enable channel + Channel.SetEnable ( true ); + + // start audio interface + Sound.Start(); + + // Notify ClientDlg + emit Connecting ( strServerName ); + + return true; + } + } - // start audio interface - Sound.Start(); // ---> pgScorpio: There is NO Check if Sound.Start() actually is successfull !! - // ---> pgScorpio: If Sound.Start fails here GUI (clientdlg) and Channel are definitely out of sync ! + return false; } -void CClient::StopConnection() +bool CClient::Disconnect() { - if ( SoundIsRunning() || Channel.IsEnabled() ) + if ( Channel.IsEnabled() ) { + /* // stop audio interface Sound.Stop(); @@ -844,6 +845,10 @@ void CClient::StopConnection() // network queue causing the channel to be reconnected right after having // received the disconnect message (seems not to gain much, disconnect is // still not working reliably) + // pgScorpio: I think the disconnect problem should be solved in CChannel. + // CChannel should ignore all audio data while disconnecting + // and disable itself as soon as disconnected. + QTime DieTime = QTime::currentTime().addMSecs ( 100 ); while ( QTime::currentTime() < DieTime ) { @@ -854,6 +859,36 @@ void CClient::StopConnection() QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); } + // disable channel + Channel.SetEnable(false); + */ + + // start disconnection + Channel.Disconnect(); + + // Channel.Disconnect() should now automatically disable Channel as soon as disconnected. + // Note that this only works if Sound is Active ! + + QTime DieTime = QTime::currentTime().addMSecs ( 500 ); + while ( ( QTime::currentTime() < DieTime ) && Channel.IsEnabled() ) + { + // exclude user input events because if we use AllEvents, it happens + // that if the user initiates a connection and disconnection quickly + // (e.g. quickly pressing enter five times), the software can get into + // an unknown state + QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); + } + + // Now stop the audio interface + Sound.Stop(); + + // in case we timed out, log warning and make sure Channel is disabled + if ( Channel.IsEnabled() ) + { + Channel.SetEnable ( false ); + qWarning() << CMsgBoxes::MainFormName() + " Warning: Disconnect failed."; + } + // Send disconnect message to server (Since we disable our protocol // receive mechanism with the next command, we do not evaluate any // respond from the server, therefore we just hope that the message @@ -864,6 +899,19 @@ void CClient::StopConnection() // reset current signal level and LEDs bJitterBufferOK = true; SignalLevelMeter.Reset(); + + emit Disconnected(); + + return true; + } + else + { + // make sure sound is stopped too + Sound.Stop(); + + emit Disconnected(); + + return false; } } diff --git a/src/client.h b/src/client.h index 5a5a2bc788..d2b750c0e6 100644 --- a/src/client.h +++ b/src/client.h @@ -110,7 +110,6 @@ class CClient : public QObject public: CClient ( const quint16 iPortNumber, const quint16 iQosNumber, - const QString& strConnOnStartupAddress, const QString& strMIDISetup, const bool bNoAutoJackConnect, const QString& strNClientName, @@ -119,27 +118,17 @@ class CClient : public QObject virtual ~CClient(); - void StartConnection(); // ---> pgScorpio: Was Start(), but Start what ? ( Should be Connect() ?) - void StopConnection(); // ---> pgScorpio: Was Stop(), but Stop what ? ( Should be Disconnect() ?) - bool SoundIsStarted() // ---> pgScorpio: Was IsRunning(), but what is running ?? - { - return Sound.IsRunning(); // ---> pgScorpio: Even this name is incorrect ! - // ---> pgScorpio: Sound.bRun is set when Sound is started, but this does not guarantee sound is actually running - } + bool Connect ( QString strServerAddress, QString strServerName ); + bool Disconnect(); - bool SoundIsRunning() const - { - return Sound.IsCallbackEntered(); - } // ---> pgScorpio: was IsCallbackEntered() but this is the actuall SoundIsRunning() ! - bool SetServerAddr ( QString strNAddr ); + bool SoundIsRunning() const { return Sound.IsCallbackEntered(); } // For OnTimerCheckAudioDeviceOk only ! + bool IsConnected() { return Channel.IsConnected(); } double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); } double GetLevelForMeterdBRight() { return SignalLevelMeter.GetLevelForMeterdBRight(); } bool GetAndResetbJitterBufferOKFlag(); - bool IsConnected() { return Channel.IsConnected(); } - EGUIDesign GetGUIDesign() const { return eGUIDesign; } void SetGUIDesign ( const EGUIDesign eNGD ) { eGUIDesign = eNGD; } @@ -433,8 +422,9 @@ protected slots: void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); + void Connecting ( QString strServerName ); void Disconnected(); - void SoundDeviceChanged ( QString strError ); + void SoundDeviceChanged(); void ControllerInFaderLevel ( int iChannelIdx, int iValue ); void ControllerInPanValue ( int iChannelIdx, int iValue ); void ControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index da6ba92ef8..fee662654c 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -27,7 +27,6 @@ /* Implementation *************************************************************/ CClientDlg::CClientDlg ( CClient* pNCliP, CClientSettings* pNSetP, - const QString& strConnOnStartupAddress, const QString& strMIDISetup, const bool bNewShowComplRegConnList, const bool bShowAnalyzerConsole, @@ -272,14 +271,6 @@ CClientDlg::CClientDlg ( CClient* pNCliP, TimerCheckAudioDeviceOk.setSingleShot ( true ); // only check once after connection TimerDetectFeedback.setSingleShot ( true ); - // Connect on startup ------------------------------------------------------ - if ( !strConnOnStartupAddress.isEmpty() ) - { - // initiate connection (always show the address in the mixer board - // (no alias)) - Connect ( strConnOnStartupAddress, strConnOnStartupAddress ); - } - // File menu -------------------------------------------------------------- QMenu* pFileMenu = new QMenu ( tr ( "&File" ), this ); @@ -473,7 +464,9 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // other QObject::connect ( pClient, &CClient::ConClientListMesReceived, this, &CClientDlg::OnConClientListMesReceived ); - QObject::connect ( pClient, &CClient::Disconnected, this, &CClientDlg::OnDisconnected ); + QObject::connect ( pClient, &CClient::Connecting, this, &CClientDlg::OnConnect ); + + QObject::connect ( pClient, &CClient::Disconnected, this, &CClientDlg::OnDisconnect ); QObject::connect ( pClient, &CClient::ChatTextReceived, this, &CClientDlg::OnChatTextReceived ); @@ -609,10 +602,7 @@ void CClientDlg::closeEvent ( QCloseEvent* Event ) AnalyzerConsole.close(); // if connected, terminate connection - if ( pClient->SoundIsStarted() ) // ---> pgScorpio: This does NOT mean the connection is started ! - { - pClient->StopConnection(); // ---> pgScorpio: Was pClient->Stop() - } + pClient->Disconnect(); // make sure all current fader settings are applied to the settings struct MainMixerBoard->StoreAllFaderSettings(); @@ -730,16 +720,12 @@ void CClientDlg::OnConnectDlgAccepted() } } - // first check if we are already connected, if this is the case we have to - // disconnect the old server first - if ( pClient->SoundIsStarted() ) // pgScorpio: Was pClient->IsRunning() Again this is NOT connection started ! + // initiate connection + if ( pClient->Connect ( strSelectedAddress, strMixerBoardLabel ) ) { - Disconnect(); + OnConnect ( strMixerBoardLabel ); } - // initiate connection - Connect ( strSelectedAddress, strMixerBoardLabel ); - // reset flag bConnectDlgWasShown = false; } @@ -748,10 +734,9 @@ void CClientDlg::OnConnectDlgAccepted() void CClientDlg::OnConnectDisconBut() { // the connect/disconnect button implements a toggle functionality - if ( pClient->SoundIsStarted() ) // pgScorpio: Was pClient->IsRunning() Again this is NOT connection started ! + if ( pClient->Disconnect() ) { - Disconnect(); - SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); + // Client was Connected, now disconnected } else { @@ -835,7 +820,7 @@ void CClientDlg::OnChatTextReceived ( QString strChatText ) // always when a new message arrives since this is annoying. ShowChatWindow ( ( strChatText.indexOf ( WELCOME_MESSAGE_PREFIX ) == 0 ) ); - UpdateDisplay(); + UpdateSettingsAndChatButtons(); } void CClientDlg::OnLicenceRequired ( ELicenceType eLicenceType ) @@ -853,7 +838,7 @@ void CClientDlg::OnLicenceRequired ( ELicenceType eLicenceType ) // disconnect from that server. if ( !LicenceDlg.exec() ) { - Disconnect(); + pClient->Disconnect(); } // unmute the client output stream if local mute button is not pressed @@ -991,7 +976,7 @@ void CClientDlg::ShowChatWindow ( const bool bForceRaise ) ChatDlg.activateWindow(); } - UpdateDisplay(); + UpdateSettingsAndChatButtons(); } void CClientDlg::ShowAnalyzerConsole() @@ -1151,21 +1136,8 @@ void CClientDlg::OnTimerCheckAudioDeviceOk() void CClientDlg::OnTimerDetectFeedback() { bDetectFeedback = false; } -void CClientDlg::OnSoundDeviceChanged ( QString strError ) +void CClientDlg::OnSoundDeviceChanged() { - if ( !strError - .isEmpty() ) // ---> pgScorpio: This check should already be done in CClient ! but currently the Disconnect code is at the wrong place. - { - // the sound device setup has a problem, disconnect any active connection - if ( pClient->SoundIsStarted() ) // ---> pgScorpio: Was pClient->IsRunning(), Again this is NOT connection started !() ) - { - Disconnect(); - } - - // show the error message of the device setup - CMsgBoxes::ShowError ( strError ); - } - // if the check audio device timer is running, it must be restarted on a device change if ( TimerCheckAudioDeviceOk.isActive() ) { @@ -1188,73 +1160,36 @@ void CClientDlg::OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, int ConnectDlg.SetPingTimeAndNumClientsResult ( InetAddr, iPingTime, iNumClients ); } -void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ) +void CClientDlg::OnConnect ( QString strServerName ) { - // ---> pgScorpio: This code does not belong here but in CClient !! - // set address and check if address is valid - if ( pClient->SetServerAddr ( strSelectedAddress ) ) - { - // try to start client, if error occurred, do not go in - // running state but show error message - try - { - if ( !pClient->SoundIsStarted() ) // ---> pgScorpio: Again this is NOT connection started !() ) - { - pClient->StartConnection(); - } - } - - catch ( const CGenErr& generr ) - { - // show error message and return the function - CMsgBoxes::ShowError ( generr.GetErrorText() ); - return; - } + // hide label connect to server + lblConnectToServer->hide(); + lbrInputLevelL->setEnabled ( true ); + lbrInputLevelR->setEnabled ( true ); - // ---> pgScorpio: This code should be a OnConnecting() slot ! + // change connect button text to "disconnect" + butConnect->setText ( tr ( "&Disconnect" ) ); - // hide label connect to server - lblConnectToServer->hide(); - lbrInputLevelL->setEnabled ( true ); - lbrInputLevelR->setEnabled ( true ); + // set server name in audio mixer group box title + MainMixerBoard->SetServerName ( strServerName ); - // change connect button text to "disconnect" - butConnect->setText ( tr ( "&Disconnect" ) ); + // start timer for level meter bar and ping time measurement + TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); + TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); + TimerPing.start ( PING_UPDATE_TIME_MS ); + TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer - // set server name in audio mixer group box title - MainMixerBoard->SetServerName ( strMixerBoardLabel ); - - // start timer for level meter bar and ping time measurement - TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); - TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); - TimerPing.start ( PING_UPDATE_TIME_MS ); - TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer - - // audio feedback detection - if ( pSettings->bEnableFeedbackDetection ) - { - TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer - bDetectFeedback = true; - } + // audio feedback detection + if ( pSettings->bEnableFeedbackDetection ) + { + TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer + bDetectFeedback = true; } } -void CClientDlg::Disconnect() +void CClientDlg::OnDisconnect() { - // ---> pgScorpio: This code does not belong here but in CClient !! - - // only stop client if currently running, in case we received - // the stopped message, the client is already stopped but the - // connect/disconnect button and other GUI controls must be - // updated - if ( pClient->SoundIsStarted() ) // ---> pgScorpio: Again this is NOT connection started !() ) - { - pClient->StopConnection(); - } - - // ---> pgScorpio: This code should be a OnDisconncted() slot - // change connect button text to "connect" butConnect->setText ( tr ( "C&onnect" ) ); @@ -1278,11 +1213,7 @@ void CClientDlg::Disconnect() TimerDetectFeedback.stop(); bDetectFeedback = false; - // clang-format off -// TODO is this still required??? -// immediately update status bar -OnTimerStatus(); - // clang-format on + UpdateSettingsAndChatButtons(); // reset LEDs ledBuffers->Reset(); @@ -1296,9 +1227,11 @@ OnTimerStatus(); // clear mixer board (remove all faders) MainMixerBoard->HideAll(); + + SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); } -void CClientDlg::UpdateDisplay() +void CClientDlg::UpdateSettingsAndChatButtons() { // update settings/chat buttons (do not fire signals since it is an update) if ( chbSettings->isChecked() && !ClientSettingsDlg.isVisible() ) diff --git a/src/clientdlg.h b/src/clientdlg.h index bbcc604766..79d8190dea 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -76,7 +76,6 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase public: CClientDlg ( CClient* pNCliP, CClientSettings* pNSetP, - const QString& strConnOnStartupAddress, const QString& strMIDISetup, const bool bNewShowComplRegConnList, const bool bShowAnalyzerConsole, @@ -94,8 +93,6 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase void ShowAnalyzerConsole(); void UpdateAudioFaderSlider(); void UpdateRevSelection(); - void Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ); - void Disconnect(); void ManageDragNDrop ( QDropEvent* Event, const bool bCheckAccept ); void SetPingTime ( const int iPingTime, const int iOverallDelayMs, const CMultiColorLED::ELightColor eOverallDelayLEDColor ); @@ -119,7 +116,7 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase virtual void closeEvent ( QCloseEvent* Event ); virtual void dragEnterEvent ( QDragEnterEvent* Event ) { ManageDragNDrop ( Event, true ); } virtual void dropEvent ( QDropEvent* Event ) { ManageDragNDrop ( Event, false ); } - void UpdateDisplay(); + void UpdateSettingsAndChatButtons(); CClientSettingsDlg ClientSettingsDlg; CChatDlg ChatDlg; @@ -127,13 +124,16 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase CAnalyzerConsole AnalyzerConsole; public slots: + void OnConnect ( QString strServerName ); + void OnDisconnect(); + void OnConnectDisconBut(); void OnTimerSigMet(); void OnTimerBuffersLED(); void OnTimerCheckAudioDeviceOk(); void OnTimerDetectFeedback(); - void OnTimerStatus() { UpdateDisplay(); } + void OnTimerStatus() { UpdateSettingsAndChatButtons(); } void OnTimerPing(); void OnPingTimeResult ( int iPingTime ); @@ -191,7 +191,7 @@ public slots: void OnConClientListMesReceived ( CVector vecChanInfo ); void OnChatTextReceived ( QString strChatText ); void OnLicenceRequired ( ELicenceType eLicenceType ); - void OnSoundDeviceChanged ( QString strError ); + void OnSoundDeviceChanged(); void OnChangeChanGain ( int iId, float fGain, bool bIsMyOwnFader ) { pClient->SetRemoteChanGain ( iId, fGain, bIsMyOwnFader ); } @@ -232,7 +232,6 @@ public slots: } void OnConnectDlgAccepted(); - void OnDisconnected() { Disconnect(); } void OnGUIDesignChanged(); void OnMeterStyleChanged(); void OnRecorderStateReceived ( ERecorderState eRecorderState ); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index a18663c108..77e19ab82b 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -1039,8 +1039,16 @@ void CClientSettingsDlg::OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton void CClientSettingsDlg::UpdateUploadRate() { // update upstream rate information label - lblUpstreamValue->setText ( QString().setNum ( pClient->GetUploadRateKbps() ) ); - lblUpstreamUnit->setText ( "kbps" ); + if ( pClient->IsConnected() ) + { + lblUpstreamValue->setText ( QString().setNum ( pClient->GetUploadRateKbps() ) ); + lblUpstreamUnit->setText ( "kbps" ); + } + else + { + lblUpstreamValue->setText ( "---" ); + lblUpstreamUnit->setText ( "" ); + } } void CClientSettingsDlg::UpdateDisplay() @@ -1048,13 +1056,8 @@ void CClientSettingsDlg::UpdateDisplay() // update slider controls (settings might have been changed) UpdateJitterBufferFrame(); UpdateSoundCardFrame(); - - if ( !pClient->SoundIsStarted() ) // pgScorpio: Was !pClient->IsRunning() Again this is NOT a connection status, Should be pClient->IsConnected() - { - // clear text labels with client parameters - lblUpstreamValue->setText ( "---" ); - lblUpstreamUnit->setText ( "" ); - } + // update upstream rate information label + UpdateUploadRate(); } void CClientSettingsDlg::UpdateDirectoryServerComboBox() diff --git a/src/main.cpp b/src/main.cpp index 88b850f055..7ebddd4d2c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -822,14 +822,7 @@ int main ( int argc, char** argv ) { // Client: // actual client object - CClient Client ( iPortNumber, - iQosNumber, - strConnOnStartupAddress, - strMIDISetup, - bNoAutoJackConnect, - strClientName, - bEnableIPv6, - bMuteMeInPersonalMix ); + CClient Client ( iPortNumber, iQosNumber, strMIDISetup, bNoAutoJackConnect, strClientName, bEnableIPv6, bMuteMeInPersonalMix ); // load settings from init-file (command line options override) CClientSettings Settings ( &Client, strIniFileName ); @@ -851,21 +844,21 @@ int main ( int argc, char** argv ) if ( bUseGUI ) { // GUI object - CClientDlg ClientDlg ( &Client, - &Settings, - strConnOnStartupAddress, - strMIDISetup, - bShowComplRegConnList, - bShowAnalyzerConsole, - bMuteStream, - bEnableIPv6, - nullptr ); + CClientDlg + ClientDlg ( &Client, &Settings, strMIDISetup, bShowComplRegConnList, bShowAnalyzerConsole, bMuteStream, bEnableIPv6, nullptr ); // initialise message boxes CMsgBoxes::init ( &ClientDlg, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); // show dialog ClientDlg.show(); + + // Connect on startup ------------------------------------------------------ + if ( !strConnOnStartupAddress.isEmpty() ) + { + Client.Connect ( strConnOnStartupAddress, strConnOnStartupAddress ); + } + pApp->exec(); } else @@ -874,6 +867,15 @@ int main ( int argc, char** argv ) // only start application without using the GUI qInfo() << qUtf8Printable ( GetVersionAndNameStr ( false ) ); + // initialise message boxes + CMsgBoxes::init ( NULL, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); + + // Connect on startup ------------------------------------------------------ + if ( !strConnOnStartupAddress.isEmpty() ) + { + Client.Connect ( strConnOnStartupAddress, strConnOnStartupAddress ); + } + pApp->exec(); } } diff --git a/src/util.cpp b/src/util.cpp index 0aba0d8fc8..37faa9c537 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -495,6 +495,7 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : CBaseDlg ( parent ) "

RobyDati (RobyDati)

" "

Rob-NY (Rob-NY)

" "

Thai Pangsakulyanont (dtinth)

" + "

Peter Goderie (pgScorpio)

" "
" + tr ( "For details on the contributions check out the %1" ) .arg ( "" + tr ( "Github Contributors list" ) + "." ) ); From a22559610fd027024cceba606bf50cf81c4e728d Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Wed, 23 Mar 2022 16:14:48 +0100 Subject: [PATCH 10/25] Added CMsgBoxes and CCommanlineOptions to global.h,main.cpp --- src/main.cpp | 141 ++++++++++++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 70 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7ebddd4d2c..41b15e33c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,6 +61,77 @@ char** CCommandlineOptions::appArgv = NULL; QDialog* CMsgBoxes::pMainForm = NULL; QString CMsgBoxes::strMainFormName = APP_NAME; + +QString UsageArguments ( char** argv ) +{ + // clang-format off + return QString ( + "\n" + "Usage: %1 [option] [option argument] ...\n" + "\n" + " -h, -?, --help display this help text and exit\n" + " -v, --version display version information and exit\n" + "\n" + "Common options:\n" + " -i, --inifile initialization file name\n" + " (not supported for headless Server mode)\n" + " -n, --nogui disable GUI (\"headless\")\n" + " -p, --port set the local port number\n" + " --jsonrpcport enable JSON-RPC server, set TCP port number\n" + " (EXPERIMENTAL, APIs might still change;\n" + " only accessible from localhost)\n" + " --jsonrpcsecretfile\n" + " path to a single-line file which contains a freely\n" + " chosen secret to authenticate JSON-RPC users.\n" + " -Q, --qos set the QoS value. Default is 128. Disable with 0\n" + " (see the Jamulus website to enable QoS on Windows)\n" + " -t, --notranslation disable translation (use English language)\n" + " -6, --enableipv6 enable IPv6 addressing (IPv4 is always enabled)\n" + "\n" + "Server only:\n" + " -d, --discononquit disconnect all Clients on quit\n" + " -e, --directoryserver address of the directory Server with which to register\n" + " (or 'localhost' to host a server list on this Server)\n" + " --directoryfile Remember registered Servers even if the Directory is restarted. Directory Servers only.\n" + " -f, --listfilter Server list whitelist filter. Format:\n" + " [IP address 1];[IP address 2];[IP address 3]; ...\n" + " -F, --fastupdate use 64 samples frame size mode\n" + " -l, --log enable logging, set file name\n" + " -L, --licence show an agreement window before users can connect\n" + " -m, --htmlstatus enable HTML status file, set file name\n" + " -o, --serverinfo registration info for this Server. Format:\n" + " [name];[city];[country as QLocale ID]\n" + " --serverpublicip public IP address for this Server. Needed when\n" + " registering with a server list hosted\n" + " behind the same NAT\n" + " -P, --delaypan start with delay panning enabled\n" + " -R, --recording sets directory to contain recorded jams\n" + " --norecord disables recording (when enabled by default by -R)\n" + " -s, --server start Server\n" + " --serverbindip IP address the Server will bind to (rather than all)\n" + " -T, --multithreading use multithreading to make better use of\n" + " multi-core CPUs and support more Clients\n" + " -u, --numchannels maximum number of channels\n" + " -w, --welcomemessage welcome message to display on connect\n" + " (string or filename, HTML supported)\n" + " -z, --startminimized start minimizied\n" + "\n" + "Client only:\n" + " -c, --connect connect to given Server address on startup\n" + " -j, --nojackconnect disable auto JACK connections\n" + " -M, --mutestream starts the application in muted state\n" + " --mutemyown mute me in my personal mix (headless only)\n" + " --clientname Client name (window title and JACK client name)\n" + " --ctrlmidich MIDI controller channel to listen\n" + "\n" + "Example: %1 -s --inifile myinifile.ini\n" + "\n" + "For more information and localized help see:\n" + "https://jamulus.io/wiki/Command-Line-Options\n" + ).arg( argv[0] ); + // clang-format on +} + int main ( int argc, char** argv ) { CCommandlineOptions::appArgc = argc; @@ -1008,76 +1079,6 @@ void CMsgBoxes::ShowInfo ( QString strInfo ) /******************************************************************************\ * Command Line Argument Parsing * \******************************************************************************/ -QString UsageArguments ( char** argv ) -{ - // clang-format off - return QString ( - "\n" - "Usage: %1 [option] [option argument] ...\n" - "\n" - " -h, -?, --help display this help text and exit\n" - " -v, --version display version information and exit\n" - "\n" - "Common options:\n" - " -i, --inifile initialization file name\n" - " (not supported for headless Server mode)\n" - " -n, --nogui disable GUI (\"headless\")\n" - " -p, --port set the local port number\n" - " --jsonrpcport enable JSON-RPC server, set TCP port number\n" - " (EXPERIMENTAL, APIs might still change;\n" - " only accessible from localhost)\n" - " --jsonrpcsecretfile\n" - " path to a single-line file which contains a freely\n" - " chosen secret to authenticate JSON-RPC users.\n" - " -Q, --qos set the QoS value. Default is 128. Disable with 0\n" - " (see the Jamulus website to enable QoS on Windows)\n" - " -t, --notranslation disable translation (use English language)\n" - " -6, --enableipv6 enable IPv6 addressing (IPv4 is always enabled)\n" - "\n" - "Server only:\n" - " -d, --discononquit disconnect all Clients on quit\n" - " -e, --directoryserver address of the directory Server with which to register\n" - " (or 'localhost' to host a server list on this Server)\n" - " --directoryfile Remember registered Servers even if the Directory is restarted. Directory Servers only.\n" - " -f, --listfilter Server list whitelist filter. Format:\n" - " [IP address 1];[IP address 2];[IP address 3]; ...\n" - " -F, --fastupdate use 64 samples frame size mode\n" - " -l, --log enable logging, set file name\n" - " -L, --licence show an agreement window before users can connect\n" - " -m, --htmlstatus enable HTML status file, set file name\n" - " -o, --serverinfo registration info for this Server. Format:\n" - " [name];[city];[country as QLocale ID]\n" - " --serverpublicip public IP address for this Server. Needed when\n" - " registering with a server list hosted\n" - " behind the same NAT\n" - " -P, --delaypan start with delay panning enabled\n" - " -R, --recording sets directory to contain recorded jams\n" - " --norecord disables recording (when enabled by default by -R)\n" - " -s, --server start Server\n" - " --serverbindip IP address the Server will bind to (rather than all)\n" - " -T, --multithreading use multithreading to make better use of\n" - " multi-core CPUs and support more Clients\n" - " -u, --numchannels maximum number of channels\n" - " -w, --welcomemessage welcome message to display on connect\n" - " (string or filename, HTML supported)\n" - " -z, --startminimized start minimizied\n" - "\n" - "Client only:\n" - " -c, --connect connect to given Server address on startup\n" - " -j, --nojackconnect disable auto JACK connections\n" - " -M, --mutestream starts the application in muted state\n" - " --mutemyown mute me in my personal mix (headless only)\n" - " --clientname Client name (window title and JACK client name)\n" - " --ctrlmidich MIDI controller channel to listen\n" - "\n" - "Example: %1 -s --inifile myinifile.ini\n" - "\n" - "For more information and localized help see:\n" - "https://jamulus.io/wiki/Command-Line-Options\n" - ).arg( argv[0] ); - // clang-format on -} - bool CCommandlineOptions::GetFlagArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt ) { if ( ( !strShortOpt.compare ( appArgv[i] ) ) || ( !strLongOpt.compare ( appArgv[i] ) ) ) From a96660a9b7d9d6ec7b85fe9544037c150f5c5946 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Wed, 23 Mar 2022 17:02:23 +0100 Subject: [PATCH 11/25] Modified messageboxes to use the new CMsgBoxes class --- src/clientdlg.cpp | 42 ++++++++++++++++++++++++++++++++++++++++-- src/main.cpp | 1 - 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index fee662654c..82dbb1888a 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -1183,8 +1183,46 @@ void CClientDlg::OnConnect ( QString strServerName ) // audio feedback detection if ( pSettings->bEnableFeedbackDetection ) { - TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer - bDetectFeedback = true; + // try to start client, if error occurred, do not go in + // running state but show error message + try + { + if ( !pClient->IsRunning() ) + { + pClient->Start(); + } + } + + catch ( const CGenErr& generr ) + { + // show error message and return the function + CMsgBoxes::ShowError ( generr.GetErrorText() ); + return; + } + + // hide label connect to server + lblConnectToServer->hide(); + lbrInputLevelL->setEnabled ( true ); + lbrInputLevelR->setEnabled ( true ); + + // change connect button text to "disconnect" + butConnect->setText ( tr ( "&Disconnect" ) ); + + // set server name in audio mixer group box title + MainMixerBoard->SetServerName ( strMixerBoardLabel ); + + // start timer for level meter bar and ping time measurement + TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); + TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); + TimerPing.start ( PING_UPDATE_TIME_MS ); + TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer + + // audio feedback detection + if ( pSettings->bEnableFeedbackDetection ) + { + TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer + bDetectFeedback = true; + } } } diff --git a/src/main.cpp b/src/main.cpp index 41b15e33c1..e4f821ec93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -61,7 +61,6 @@ char** CCommandlineOptions::appArgv = NULL; QDialog* CMsgBoxes::pMainForm = NULL; QString CMsgBoxes::strMainFormName = APP_NAME; - QString UsageArguments ( char** argv ) { // clang-format off From 6af7e96c4c527932a17b127cd524e02061a9582e Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Thu, 24 Mar 2022 16:50:49 +0100 Subject: [PATCH 12/25] Solution for headless build? define the in headless non existing QDialog as void and send messages to the appropriate output stream --- src/main.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index e4f821ec93..3b0d944add 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1075,6 +1075,33 @@ void CMsgBoxes::ShowInfo ( QString strInfo ) #endif } +void CMsgBoxes::ShowError ( QString strError ) +{ +#ifndef HEADLESS + QMessageBox::critical ( pMainForm, strMainFormName + ": " + QObject::tr ( "Error" ), strError, QObject::tr ( "Ok" ), nullptr ); +#else + qCritical() << "Error: " << strError.toLocal8Bit().data(); +#endif +} + +void CMsgBoxes::ShowWarning ( QString strWarning ) +{ +#ifndef HEADLESS + QMessageBox::warning ( pMainForm, strMainFormName + ": " + QObject::tr ( "Warning" ), strWarning, QObject::tr ( "Ok" ), nullptr ); +#else + qWarning() << "Warning: " << strWarning.toLocal8Bit().data(); +#endif +} + +void CMsgBoxes::ShowInfo ( QString strInfo ) +{ +#ifndef HEADLESS + QMessageBox::information ( pMainForm, strMainFormName + ": " + QObject::tr ( "Information" ), strInfo, QObject::tr ( "Ok" ), nullptr ); +#else + qInfo() << "Info: " << strInfo.toLocal8Bit().data(); +#endif +} + /******************************************************************************\ * Command Line Argument Parsing * \******************************************************************************/ From 4570ec4f1a99471f2834f3657c76e0e8004f4932 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Sat, 26 Mar 2022 20:27:58 +0100 Subject: [PATCH 13/25] Connection status issue jamulussoftware#2519 second stage Moved Connect/Disconnect code from CClientdlg to CClient. Now using the proper connected checks in several places. Added bDisconnectAndDisable to CChannel. (For a Client now Channel.Disconnect() will block audio data and auto disable the channel on disconnected) --- src/clientdlg.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 82dbb1888a..a3c9814a01 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -1183,6 +1183,7 @@ void CClientDlg::OnConnect ( QString strServerName ) // audio feedback detection if ( pSettings->bEnableFeedbackDetection ) { +<<<<<<< HEAD // try to start client, if error occurred, do not go in // running state but show error message try @@ -1223,6 +1224,10 @@ void CClientDlg::OnConnect ( QString strServerName ) TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer bDetectFeedback = true; } +======= + TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer + bDetectFeedback = true; +>>>>>>> f50b4535 (Connection status issue jamulussoftware#2519 second stage) } } From 7125707dc9bdd83b5a59ee1d498820bc76c1a1f2 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Mon, 28 Mar 2022 21:54:25 +0200 Subject: [PATCH 14/25] Fixing merge errors --- src/client.cpp | 19 +++++++++++-------- src/main.cpp | 27 --------------------------- 2 files changed, 11 insertions(+), 35 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 4eb43ee74e..4a237b0e10 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -716,6 +716,9 @@ void CClient::OnHandledSignal ( int sigNum ) case SIGTERM: // if connected, terminate connection (needed for headless mode) Disconnect(); + { + Stop(); + } // this should trigger OnAboutToQuit QCoreApplication::instance()->exit(); @@ -827,9 +830,6 @@ bool CClient::Connect ( QString strServerAddress, QString strServerName ) } } - return false; -} - bool CClient::Disconnect() { if ( Channel.IsEnabled() ) @@ -837,9 +837,9 @@ bool CClient::Disconnect() /* // stop audio interface Sound.Stop(); - - // disable channel - Channel.SetEnable ( false ); +{ + // stop audio interface + Sound.Stop(); // wait for approx. 100 ms to make sure no audio packet is still in the // network queue causing the channel to be reconnected right after having @@ -878,7 +878,7 @@ bool CClient::Disconnect() // an unknown state QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); } - + // an unknown state // Now stop the audio interface Sound.Stop(); @@ -895,7 +895,7 @@ bool CClient::Disconnect() // gets its way to the server, if not, the old behaviour time-out // disconnects the connection anyway). ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); - + // gets its way to the server, if not, the old behaviour time-out // reset current signal level and LEDs bJitterBufferOK = true; SignalLevelMeter.Reset(); @@ -914,6 +914,9 @@ bool CClient::Disconnect() return false; } } + bJitterBufferOK = true; + SignalLevelMeter.Reset(); +} void CClient::Init() { diff --git a/src/main.cpp b/src/main.cpp index 3b0d944add..e4f821ec93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1075,33 +1075,6 @@ void CMsgBoxes::ShowInfo ( QString strInfo ) #endif } -void CMsgBoxes::ShowError ( QString strError ) -{ -#ifndef HEADLESS - QMessageBox::critical ( pMainForm, strMainFormName + ": " + QObject::tr ( "Error" ), strError, QObject::tr ( "Ok" ), nullptr ); -#else - qCritical() << "Error: " << strError.toLocal8Bit().data(); -#endif -} - -void CMsgBoxes::ShowWarning ( QString strWarning ) -{ -#ifndef HEADLESS - QMessageBox::warning ( pMainForm, strMainFormName + ": " + QObject::tr ( "Warning" ), strWarning, QObject::tr ( "Ok" ), nullptr ); -#else - qWarning() << "Warning: " << strWarning.toLocal8Bit().data(); -#endif -} - -void CMsgBoxes::ShowInfo ( QString strInfo ) -{ -#ifndef HEADLESS - QMessageBox::information ( pMainForm, strMainFormName + ": " + QObject::tr ( "Information" ), strInfo, QObject::tr ( "Ok" ), nullptr ); -#else - qInfo() << "Info: " << strInfo.toLocal8Bit().data(); -#endif -} - /******************************************************************************\ * Command Line Argument Parsing * \******************************************************************************/ From 1b17d48b818cf51fa18b77b9d616b45df2bd2ce0 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Wed, 23 Mar 2022 16:14:48 +0100 Subject: [PATCH 15/25] Added CMsgBoxes and CCommanlineOptions to global.h,main.cpp --- src/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index e4f821ec93..5057c2a067 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -920,6 +920,9 @@ int main ( int argc, char** argv ) // initialise message boxes CMsgBoxes::init ( &ClientDlg, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); + // initialise message boxes + CMsgBoxes::init ( &ClientDlg, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); + // show dialog ClientDlg.show(); From cd7b785e2991d1ba656c105efcd44f0fb959af69 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Wed, 23 Mar 2022 17:02:23 +0100 Subject: [PATCH 16/25] Modified messageboxes to use the new CMsgBoxes class --- src/clientdlg.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index a3c9814a01..82dbb1888a 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -1183,7 +1183,6 @@ void CClientDlg::OnConnect ( QString strServerName ) // audio feedback detection if ( pSettings->bEnableFeedbackDetection ) { -<<<<<<< HEAD // try to start client, if error occurred, do not go in // running state but show error message try @@ -1224,10 +1223,6 @@ void CClientDlg::OnConnect ( QString strServerName ) TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer bDetectFeedback = true; } -======= - TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer - bDetectFeedback = true; ->>>>>>> f50b4535 (Connection status issue jamulussoftware#2519 second stage) } } From a6758333237a804d9f7704b3289dd94052a2dddf Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Sat, 26 Mar 2022 14:54:59 +0100 Subject: [PATCH 17/25] Bugfix non Windows exit --- src/client.cpp | 889 ++++++++++++++++++++++++------------------------- 1 file changed, 428 insertions(+), 461 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 4a237b0e10..774df12b13 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -716,9 +716,7 @@ void CClient::OnHandledSignal ( int sigNum ) case SIGTERM: // if connected, terminate connection (needed for headless mode) Disconnect(); - { - Stop(); - } + Stop(); // this should trigger OnAboutToQuit QCoreApplication::instance()->exit(); @@ -830,581 +828,550 @@ bool CClient::Connect ( QString strServerAddress, QString strServerName ) } } -bool CClient::Disconnect() -{ - if ( Channel.IsEnabled() ) + bool CClient::Disconnect() { - /* - // stop audio interface - Sound.Stop(); -{ - // stop audio interface - Sound.Stop(); - - // wait for approx. 100 ms to make sure no audio packet is still in the - // network queue causing the channel to be reconnected right after having - // received the disconnect message (seems not to gain much, disconnect is - // still not working reliably) - // pgScorpio: I think the disconnect problem should be solved in CChannel. - // CChannel should ignore all audio data while disconnecting - // and disable itself as soon as disconnected. - - QTime DieTime = QTime::currentTime().addMSecs ( 100 ); - while ( QTime::currentTime() < DieTime ) + if ( Channel.IsEnabled() ) { - // exclude user input events because if we use AllEvents, it happens - // that if the user initiates a connection and disconnection quickly - // (e.g. quickly pressing enter five times), the software can get into - // an unknown state - QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); - } - - // disable channel - Channel.SetEnable(false); - */ - - // start disconnection - Channel.Disconnect(); + // start disconnection + Channel.Disconnect(); - // Channel.Disconnect() should now automatically disable Channel as soon as disconnected. - // Note that this only works if Sound is Active ! + // Channel.Disconnect() should now automatically disable Channel as soon as disconnected. + // Note that this only works if Sound is Active ! - QTime DieTime = QTime::currentTime().addMSecs ( 500 ); - while ( ( QTime::currentTime() < DieTime ) && Channel.IsEnabled() ) - { - // exclude user input events because if we use AllEvents, it happens - // that if the user initiates a connection and disconnection quickly - // (e.g. quickly pressing enter five times), the software can get into + QTime DieTime = QTime::currentTime().addMSecs ( 500 ); + while ( ( QTime::currentTime() < DieTime ) && Channel.IsEnabled() ) + { + // exclude user input events because if we use AllEvents, it happens + // that if the user initiates a connection and disconnection quickly + // (e.g. quickly pressing enter five times), the software can get into + // an unknown state + QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); + } // an unknown state - QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); - } - // an unknown state - // Now stop the audio interface - Sound.Stop(); + // Now stop the audio interface + Sound.Stop(); - // in case we timed out, log warning and make sure Channel is disabled - if ( Channel.IsEnabled() ) - { - Channel.SetEnable ( false ); - qWarning() << CMsgBoxes::MainFormName() + " Warning: Disconnect failed."; - } + // in case we timed out, log warning and make sure Channel is disabled + if ( Channel.IsEnabled() ) + { + Channel.SetEnable ( false ); + qWarning() << CMsgBoxes::MainFormName() + " Warning: Disconnect failed."; + } - // Send disconnect message to server (Since we disable our protocol - // receive mechanism with the next command, we do not evaluate any - // respond from the server, therefore we just hope that the message - // gets its way to the server, if not, the old behaviour time-out - // disconnects the connection anyway). - ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); - // gets its way to the server, if not, the old behaviour time-out - // reset current signal level and LEDs - bJitterBufferOK = true; - SignalLevelMeter.Reset(); + // Send disconnect message to server (Since we disable our protocol + // receive mechanism with the next command, we do not evaluate any + // respond from the server, therefore we just hope that the message + // gets its way to the server, if not, the old behaviour time-out + // disconnects the connection anyway). + ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); - emit Disconnected(); + // gets its way to the server, if not, the old behaviour time-out + // reset current signal level and LEDs + bJitterBufferOK = true; + SignalLevelMeter.Reset(); - return true; - } - else - { - // make sure sound is stopped too - Sound.Stop(); + emit Disconnected(); - emit Disconnected(); + return true; + } + else + { + // make sure sound is stopped too + Sound.Stop(); - return false; + emit Disconnected(); + + return false; + } } -} - bJitterBufferOK = true; - SignalLevelMeter.Reset(); -} -void CClient::Init() -{ - // check if possible frame size factors are supported - const int iFraSizePreffered = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED; - const int iFraSizeDefault = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT; - const int iFraSizeSafe = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE; + void CClient::Init() + { + // check if possible frame size factors are supported + const int iFraSizePreffered = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED; + const int iFraSizeDefault = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT; + const int iFraSizeSafe = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE; #if defined( Q_OS_IOS ) - bFraSiFactPrefSupported = true; // to reduce sound init time, because we know it's supported in iOS - bFraSiFactDefSupported = true; - bFraSiFactSafeSupported = true; + bFraSiFactPrefSupported = true; // to reduce sound init time, because we know it's supported in iOS + bFraSiFactDefSupported = true; + bFraSiFactSafeSupported = true; #else - bFraSiFactPrefSupported = ( Sound.Init ( iFraSizePreffered ) == iFraSizePreffered ); - bFraSiFactDefSupported = ( Sound.Init ( iFraSizeDefault ) == iFraSizeDefault ); - bFraSiFactSafeSupported = ( Sound.Init ( iFraSizeSafe ) == iFraSizeSafe ); + bFraSiFactPrefSupported = ( Sound.Init ( iFraSizePreffered ) == iFraSizePreffered ); + bFraSiFactDefSupported = ( Sound.Init ( iFraSizeDefault ) == iFraSizeDefault ); + bFraSiFactSafeSupported = ( Sound.Init ( iFraSizeSafe ) == iFraSizeSafe ); #endif - // translate block size index in actual block size - const int iPrefMonoFrameSize = iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; - - // get actual sound card buffer size using preferred size - // TODO - iOS needs 1 init only, now: 9 inits at launch <- slow - // Initially, I tried to fix this as follows (inside #ifdef ios tag): - // if ( Sound.isInitialized ) - // iMonoBlockSizeSam = iPrefMonoFrameSize; - // else - // iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); - // Problem is legitimate setting changes (buffer size for example). - // so the condition should be something like "if ( Sound.isInitialized and APP_IS_INIALIZING)" - iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); - - // Calculate the current sound card frame size factor. In case - // the current mono block size is not a multiple of the system - // frame size, we have to use a sound card conversion buffer. - if ( ( ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) && bEnableOPUS64 ) || - ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) || - ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) - { - // regular case: one of our predefined buffer sizes is available - iSndCrdFrameSizeFactor = iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES; - - // no sound card conversion buffer required - bSndCrdConversionBufferRequired = false; - } - else - { - // An unsupported sound card buffer size is currently used -> we have - // to use a conversion buffer. Per definition we use the smallest buffer - // size as the current frame size. - - // store actual sound card buffer size (stereo) - bSndCrdConversionBufferRequired = true; - iSndCardMonoBlockSizeSamConvBuff = iMonoBlockSizeSam; - - // overwrite block size factor by using one frame - iSndCrdFrameSizeFactor = 1; - } - - // select the OPUS frame size mode depending on current mono block size samples - if ( bSndCrdConversionBufferRequired ) - { - if ( ( iSndCardMonoBlockSizeSamConvBuff < DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) && bEnableOPUS64 ) + // translate block size index in actual block size + const int iPrefMonoFrameSize = iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; + + // get actual sound card buffer size using preferred size + // TODO - iOS needs 1 init only, now: 9 inits at launch <- slow + // Initially, I tried to fix this as follows (inside #ifdef ios tag): + // if ( Sound.isInitialized ) + // iMonoBlockSizeSam = iPrefMonoFrameSize; + // else + // iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); + // Problem is legitimate setting changes (buffer size for example). + // so the condition should be something like "if ( Sound.isInitialized and APP_IS_INIALIZING)" + iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); + + // Calculate the current sound card frame size factor. In case + // the current mono block size is not a multiple of the system + // frame size, we have to use a sound card conversion buffer. + if ( ( ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) && bEnableOPUS64 ) || + ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) || + ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) { - iMonoBlockSizeSam = SYSTEM_FRAME_SIZE_SAMPLES; - eAudioCompressionType = CT_OPUS64; - } - else - { - iMonoBlockSizeSam = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; - eAudioCompressionType = CT_OPUS; - } - } - else - { - if ( iMonoBlockSizeSam < DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) - { - eAudioCompressionType = CT_OPUS64; + // regular case: one of our predefined buffer sizes is available + iSndCrdFrameSizeFactor = iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES; + + // no sound card conversion buffer required + bSndCrdConversionBufferRequired = false; } else { - // since we use double size frame size for OPUS, we have to adjust the frame size factor - iSndCrdFrameSizeFactor /= 2; - eAudioCompressionType = CT_OPUS; - } - } + // An unsupported sound card buffer size is currently used -> we have + // to use a conversion buffer. Per definition we use the smallest buffer + // size as the current frame size. - // inits for audio coding - if ( eAudioCompressionType == CT_OPUS ) - { - iOPUSFrameSizeSamples = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; + // store actual sound card buffer size (stereo) + bSndCrdConversionBufferRequired = true; + iSndCardMonoBlockSizeSamConvBuff = iMonoBlockSizeSam; - if ( eAudioChannelConf == CC_MONO ) - { - CurOpusEncoder = OpusEncoderMono; - CurOpusDecoder = OpusDecoderMono; - iNumAudioChannels = 1; + // overwrite block size factor by using one frame + iSndCrdFrameSizeFactor = 1; + } - switch ( eAudioQuality ) + // select the OPUS frame size mode depending on current mono block size samples + if ( bSndCrdConversionBufferRequired ) + { + if ( ( iSndCardMonoBlockSizeSamConvBuff < DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) && bEnableOPUS64 ) { - case AQ_LOW: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_LOW_QUALITY_DBLE_FRAMESIZE; - break; - case AQ_NORMAL: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_NORMAL_QUALITY_DBLE_FRAMESIZE; - break; - case AQ_HIGH: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY_DBLE_FRAMESIZE; - break; + iMonoBlockSizeSam = SYSTEM_FRAME_SIZE_SAMPLES; + eAudioCompressionType = CT_OPUS64; + } + else + { + iMonoBlockSizeSam = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; + eAudioCompressionType = CT_OPUS; } } else { - CurOpusEncoder = OpusEncoderStereo; - CurOpusDecoder = OpusDecoderStereo; - iNumAudioChannels = 2; - - switch ( eAudioQuality ) + if ( iMonoBlockSizeSam < DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) + { + eAudioCompressionType = CT_OPUS64; + } + else { - case AQ_LOW: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_LOW_QUALITY_DBLE_FRAMESIZE; - break; - case AQ_NORMAL: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY_DBLE_FRAMESIZE; - break; - case AQ_HIGH: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY_DBLE_FRAMESIZE; - break; + // since we use double size frame size for OPUS, we have to adjust the frame size factor + iSndCrdFrameSizeFactor /= 2; + eAudioCompressionType = CT_OPUS; } } - } - else /* CT_OPUS64 */ - { - iOPUSFrameSizeSamples = SYSTEM_FRAME_SIZE_SAMPLES; - if ( eAudioChannelConf == CC_MONO ) + // inits for audio coding + if ( eAudioCompressionType == CT_OPUS ) { - CurOpusEncoder = Opus64EncoderMono; - CurOpusDecoder = Opus64DecoderMono; - iNumAudioChannels = 1; + iOPUSFrameSizeSamples = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; - switch ( eAudioQuality ) + if ( eAudioChannelConf == CC_MONO ) + { + CurOpusEncoder = OpusEncoderMono; + CurOpusDecoder = OpusDecoderMono; + iNumAudioChannels = 1; + + switch ( eAudioQuality ) + { + case AQ_LOW: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_LOW_QUALITY_DBLE_FRAMESIZE; + break; + case AQ_NORMAL: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_NORMAL_QUALITY_DBLE_FRAMESIZE; + break; + case AQ_HIGH: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY_DBLE_FRAMESIZE; + break; + } + } + else { - case AQ_LOW: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_LOW_QUALITY; - break; - case AQ_NORMAL: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_NORMAL_QUALITY; - break; - case AQ_HIGH: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY; - break; + CurOpusEncoder = OpusEncoderStereo; + CurOpusDecoder = OpusDecoderStereo; + iNumAudioChannels = 2; + + switch ( eAudioQuality ) + { + case AQ_LOW: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_LOW_QUALITY_DBLE_FRAMESIZE; + break; + case AQ_NORMAL: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY_DBLE_FRAMESIZE; + break; + case AQ_HIGH: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY_DBLE_FRAMESIZE; + break; + } } } - else + else /* CT_OPUS64 */ { - CurOpusEncoder = Opus64EncoderStereo; - CurOpusDecoder = Opus64DecoderStereo; - iNumAudioChannels = 2; + iOPUSFrameSizeSamples = SYSTEM_FRAME_SIZE_SAMPLES; - switch ( eAudioQuality ) + if ( eAudioChannelConf == CC_MONO ) + { + CurOpusEncoder = Opus64EncoderMono; + CurOpusDecoder = Opus64DecoderMono; + iNumAudioChannels = 1; + + switch ( eAudioQuality ) + { + case AQ_LOW: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_LOW_QUALITY; + break; + case AQ_NORMAL: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_NORMAL_QUALITY; + break; + case AQ_HIGH: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY; + break; + } + } + else { - case AQ_LOW: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_LOW_QUALITY; - break; - case AQ_NORMAL: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY; - break; - case AQ_HIGH: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY; - break; + CurOpusEncoder = Opus64EncoderStereo; + CurOpusDecoder = Opus64DecoderStereo; + iNumAudioChannels = 2; + + switch ( eAudioQuality ) + { + case AQ_LOW: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_LOW_QUALITY; + break; + case AQ_NORMAL: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY; + break; + case AQ_HIGH: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY; + break; + } } } - } - // calculate stereo (two channels) buffer size - iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; + // calculate stereo (two channels) buffer size + iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; - vecCeltData.Init ( iCeltNumCodedBytes ); - vecZeros.Init ( iStereoBlockSizeSam, 0 ); - vecsStereoSndCrdMuteStream.Init ( iStereoBlockSizeSam ); + vecCeltData.Init ( iCeltNumCodedBytes ); + vecZeros.Init ( iStereoBlockSizeSam, 0 ); + vecsStereoSndCrdMuteStream.Init ( iStereoBlockSizeSam ); - opus_custom_encoder_ctl ( CurOpusEncoder, - OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iOPUSFrameSizeSamples ) ) ); + opus_custom_encoder_ctl ( CurOpusEncoder, + OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iOPUSFrameSizeSamples ) ) ); - // inits for network and channel - vecbyNetwData.Init ( iCeltNumCodedBytes ); + // inits for network and channel + vecbyNetwData.Init ( iCeltNumCodedBytes ); - // set the channel network properties - Channel.SetAudioStreamProperties ( eAudioCompressionType, iCeltNumCodedBytes, iSndCrdFrameSizeFactor, iNumAudioChannels ); + // set the channel network properties + Channel.SetAudioStreamProperties ( eAudioCompressionType, iCeltNumCodedBytes, iSndCrdFrameSizeFactor, iNumAudioChannels ); - // init reverberation - AudioReverb.Init ( eAudioChannelConf, iStereoBlockSizeSam, SYSTEM_SAMPLE_RATE_HZ ); + // init reverberation + AudioReverb.Init ( eAudioChannelConf, iStereoBlockSizeSam, SYSTEM_SAMPLE_RATE_HZ ); - // init the sound card conversion buffers - if ( bSndCrdConversionBufferRequired ) - { - // inits for conversion buffer (the size of the conversion buffer must - // be the sum of input/output sizes which is the worst case fill level) - const int iSndCardStereoBlockSizeSamConvBuff = 2 * iSndCardMonoBlockSizeSamConvBuff; - const int iConBufSize = iStereoBlockSizeSam + iSndCardStereoBlockSizeSamConvBuff; - - SndCrdConversionBufferIn.Init ( iConBufSize ); - SndCrdConversionBufferOut.Init ( iConBufSize ); - vecDataConvBuf.Init ( iStereoBlockSizeSam ); - - // the output conversion buffer must be filled with the inner - // block size for initialization (this is the latency which is - // introduced by the conversion buffer) to avoid buffer underruns - SndCrdConversionBufferOut.Put ( vecZeros, iStereoBlockSizeSam ); - } + // init the sound card conversion buffers + if ( bSndCrdConversionBufferRequired ) + { + // inits for conversion buffer (the size of the conversion buffer must + // be the sum of input/output sizes which is the worst case fill level) + const int iSndCardStereoBlockSizeSamConvBuff = 2 * iSndCardMonoBlockSizeSamConvBuff; + const int iConBufSize = iStereoBlockSizeSam + iSndCardStereoBlockSizeSamConvBuff; + + SndCrdConversionBufferIn.Init ( iConBufSize ); + SndCrdConversionBufferOut.Init ( iConBufSize ); + vecDataConvBuf.Init ( iStereoBlockSizeSam ); + + // the output conversion buffer must be filled with the inner + // block size for initialization (this is the latency which is + // introduced by the conversion buffer) to avoid buffer underruns + SndCrdConversionBufferOut.Put ( vecZeros, iStereoBlockSizeSam ); + } - // reset initialization phase flag and mute flag - bIsInitializationPhase = true; -} + // reset initialization phase flag and mute flag + bIsInitializationPhase = true; + } -void CClient::AudioCallback ( CVector& psData, void* arg ) -{ - // get the pointer to the object - CClient* pMyClientObj = static_cast ( arg ); + void CClient::AudioCallback ( CVector & psData, void* arg ) + { + // get the pointer to the object + CClient* pMyClientObj = static_cast ( arg ); - // process audio data - pMyClientObj->ProcessSndCrdAudioData ( psData ); + // process audio data + pMyClientObj->ProcessSndCrdAudioData ( psData ); - // clang-format off + // clang-format off /* // TEST do a soundcard jitter measurement static CTimingMeas JitterMeas ( 1000, "test2.dat" ); JitterMeas.Measure(); */ - // clang-format on -} + // clang-format on + } -void CClient::ProcessSndCrdAudioData ( CVector& vecsStereoSndCrd ) -{ - // check if a conversion buffer is required or not - if ( bSndCrdConversionBufferRequired ) + void CClient::ProcessSndCrdAudioData ( CVector & vecsStereoSndCrd ) { - // add new sound card block in conversion buffer - SndCrdConversionBufferIn.Put ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); - - // process all available blocks of data - while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam ) + // check if a conversion buffer is required or not + if ( bSndCrdConversionBufferRequired ) { - // get one block of data for processing - SndCrdConversionBufferIn.Get ( vecDataConvBuf, iStereoBlockSizeSam ); - - // process audio data - ProcessAudioDataIntern ( vecDataConvBuf ); - - SndCrdConversionBufferOut.Put ( vecDataConvBuf, iStereoBlockSizeSam ); - } + // add new sound card block in conversion buffer + SndCrdConversionBufferIn.Put ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); - // get processed sound card block out of the conversion buffer - SndCrdConversionBufferOut.Get ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); - } - else - { - // regular case: no conversion buffer required - // process audio data - ProcessAudioDataIntern ( vecsStereoSndCrd ); - } -} + // process all available blocks of data + while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam ) + { + // get one block of data for processing + SndCrdConversionBufferIn.Get ( vecDataConvBuf, iStereoBlockSizeSam ); -void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) -{ - int i, j, iUnused; - unsigned char* pCurCodedData; + // process audio data + ProcessAudioDataIntern ( vecDataConvBuf ); - // Transmit signal --------------------------------------------------------- + SndCrdConversionBufferOut.Put ( vecDataConvBuf, iStereoBlockSizeSam ); + } - if ( iInputBoost != 1 ) - { - // apply a general gain boost to all audio input: - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + // get processed sound card block out of the conversion buffer + SndCrdConversionBufferOut.Get ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); + } + else { - vecsStereoSndCrd[j + 1] = static_cast ( iInputBoost * vecsStereoSndCrd[j + 1] ); - vecsStereoSndCrd[j] = static_cast ( iInputBoost * vecsStereoSndCrd[j] ); + // regular case: no conversion buffer required + // process audio data + ProcessAudioDataIntern ( vecsStereoSndCrd ); } } - // update stereo signal level meter (not needed in headless mode) -#ifndef HEADLESS - SignalLevelMeter.Update ( vecsStereoSndCrd, iMonoBlockSizeSam, true ); -#endif - - // add reverberation effect if activated - if ( iReverbLevel != 0 ) + void CClient::ProcessAudioDataIntern ( CVector & vecsStereoSndCrd ) { - AudioReverb.Process ( vecsStereoSndCrd, bReverbOnLeftChan, static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 4 ); - } + int i, j, iUnused; + unsigned char* pCurCodedData; - // apply pan (audio fader) and mix mono signals - if ( !( ( iAudioInFader == AUD_FADER_IN_MIDDLE ) && ( eAudioChannelConf == CC_STEREO ) ) ) - { - // calculate pan gain in the range 0 to 1, where 0.5 is the middle position - const float fPan = static_cast ( iAudioInFader ) / AUD_FADER_IN_MAX; + // Transmit signal --------------------------------------------------------- - if ( eAudioChannelConf == CC_STEREO ) + if ( iInputBoost != 1 ) { - // for stereo only apply pan attenuation on one channel (same as pan in the server) - const float fGainL = MathUtils::GetLeftPan ( fPan, false ); - const float fGainR = MathUtils::GetRightPan ( fPan, false ); - + // apply a general gain boost to all audio input: for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) { - // note that the gain is always <= 1, therefore a simple cast is - // ok since we never can get an overload - vecsStereoSndCrd[j + 1] = static_cast ( fGainR * vecsStereoSndCrd[j + 1] ); - vecsStereoSndCrd[j] = static_cast ( fGainL * vecsStereoSndCrd[j] ); + vecsStereoSndCrd[j + 1] = static_cast ( iInputBoost * vecsStereoSndCrd[j + 1] ); + vecsStereoSndCrd[j] = static_cast ( iInputBoost * vecsStereoSndCrd[j] ); } } - else - { - // for mono implement a cross-fade between channels and mix them, for - // mono-in/stereo-out use no attenuation in pan center - const float fGainL = MathUtils::GetLeftPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); - const float fGainR = MathUtils::GetRightPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // note that we need the Float2Short for stereo pan mode - vecsStereoSndCrd[i] = Float2Short ( fGainL * vecsStereoSndCrd[j] + fGainR * vecsStereoSndCrd[j + 1] ); - } - } - } + // update stereo signal level meter (not needed in headless mode) +#ifndef HEADLESS + SignalLevelMeter.Update ( vecsStereoSndCrd, iMonoBlockSizeSam, true ); +#endif - // Support for mono-in/stereo-out mode: Per definition this mode works in - // full stereo mode at the transmission level. The only thing which is done - // is to mix both sound card inputs together and then put this signal on - // both stereo channels to be transmitted to the server. - if ( eAudioChannelConf == CC_MONO_IN_STEREO_OUT ) - { - // copy mono data in stereo sound card buffer (note that since the input - // and output is the same buffer, we have to start from the end not to - // overwrite input values) - for ( i = iMonoBlockSizeSam - 1, j = iStereoBlockSizeSam - 2; i >= 0; i--, j -= 2 ) + // add reverberation effect if activated + if ( iReverbLevel != 0 ) { - vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = vecsStereoSndCrd[i]; + AudioReverb.Process ( vecsStereoSndCrd, bReverbOnLeftChan, static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 4 ); } - } - for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) - { - // OPUS encoding - if ( CurOpusEncoder != nullptr ) + // apply pan (audio fader) and mix mono signals + if ( !( ( iAudioInFader == AUD_FADER_IN_MIDDLE ) && ( eAudioChannelConf == CC_STEREO ) ) ) { - if ( bMuteOutStream ) + // calculate pan gain in the range 0 to 1, where 0.5 is the middle position + const float fPan = static_cast ( iAudioInFader ) / AUD_FADER_IN_MAX; + + if ( eAudioChannelConf == CC_STEREO ) { - iUnused = opus_custom_encode ( CurOpusEncoder, - &vecZeros[i * iNumAudioChannels * iOPUSFrameSizeSamples], - iOPUSFrameSizeSamples, - &vecCeltData[0], - iCeltNumCodedBytes ); + // for stereo only apply pan attenuation on one channel (same as pan in the server) + const float fGainL = MathUtils::GetLeftPan ( fPan, false ); + const float fGainR = MathUtils::GetRightPan ( fPan, false ); + + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // note that the gain is always <= 1, therefore a simple cast is + // ok since we never can get an overload + vecsStereoSndCrd[j + 1] = static_cast ( fGainR * vecsStereoSndCrd[j + 1] ); + vecsStereoSndCrd[j] = static_cast ( fGainL * vecsStereoSndCrd[j] ); + } } else { - iUnused = opus_custom_encode ( CurOpusEncoder, - &vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples], - iOPUSFrameSizeSamples, - &vecCeltData[0], - iCeltNumCodedBytes ); + // for mono implement a cross-fade between channels and mix them, for + // mono-in/stereo-out use no attenuation in pan center + const float fGainL = MathUtils::GetLeftPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); + const float fGainR = MathUtils::GetRightPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); + + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // note that we need the Float2Short for stereo pan mode + vecsStereoSndCrd[i] = Float2Short ( fGainL * vecsStereoSndCrd[j] + fGainR * vecsStereoSndCrd[j + 1] ); + } } } - // send coded audio through the network - Channel.PrepAndSendPacket ( &Socket, vecCeltData, iCeltNumCodedBytes ); - } - - // Receive signal ---------------------------------------------------------- - // in case of mute stream, store local data - if ( bMuteOutStream ) - { - vecsStereoSndCrdMuteStream = vecsStereoSndCrd; - } - - for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) - { - // receive a new block - const bool bReceiveDataOk = ( Channel.GetData ( vecbyNetwData, iCeltNumCodedBytes ) == GS_BUFFER_OK ); + // Support for mono-in/stereo-out mode: Per definition this mode works in + // full stereo mode at the transmission level. The only thing which is done + // is to mix both sound card inputs together and then put this signal on + // both stereo channels to be transmitted to the server. + if ( eAudioChannelConf == CC_MONO_IN_STEREO_OUT ) + { + // copy mono data in stereo sound card buffer (note that since the input + // and output is the same buffer, we have to start from the end not to + // overwrite input values) + for ( i = iMonoBlockSizeSam - 1, j = iStereoBlockSizeSam - 2; i >= 0; i--, j -= 2 ) + { + vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = vecsStereoSndCrd[i]; + } + } - // get pointer to coded data and manage the flags - if ( bReceiveDataOk ) + for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) { - pCurCodedData = &vecbyNetwData[0]; + // OPUS encoding + if ( CurOpusEncoder != nullptr ) + { + if ( bMuteOutStream ) + { + iUnused = opus_custom_encode ( CurOpusEncoder, + &vecZeros[i * iNumAudioChannels * iOPUSFrameSizeSamples], + iOPUSFrameSizeSamples, + &vecCeltData[0], + iCeltNumCodedBytes ); + } + else + { + iUnused = opus_custom_encode ( CurOpusEncoder, + &vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples], + iOPUSFrameSizeSamples, + &vecCeltData[0], + iCeltNumCodedBytes ); + } + } - // on any valid received packet, we clear the initialization phase flag - bIsInitializationPhase = false; + // send coded audio through the network + Channel.PrepAndSendPacket ( &Socket, vecCeltData, iCeltNumCodedBytes ); } - else - { - // for lost packets use null pointer as coded input data - pCurCodedData = nullptr; - // invalidate the buffer OK status flag - bJitterBufferOK = false; + // Receive signal ---------------------------------------------------------- + // in case of mute stream, store local data + if ( bMuteOutStream ) + { + vecsStereoSndCrdMuteStream = vecsStereoSndCrd; } - // OPUS decoding - if ( CurOpusDecoder != nullptr ) + for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) { - iUnused = opus_custom_decode ( CurOpusDecoder, - pCurCodedData, - iCeltNumCodedBytes, - &vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples], - iOPUSFrameSizeSamples ); + // receive a new block + const bool bReceiveDataOk = ( Channel.GetData ( vecbyNetwData, iCeltNumCodedBytes ) == GS_BUFFER_OK ); + + // get pointer to coded data and manage the flags + if ( bReceiveDataOk ) + { + pCurCodedData = &vecbyNetwData[0]; + + // on any valid received packet, we clear the initialization phase flag + bIsInitializationPhase = false; + } + else + { + // for lost packets use null pointer as coded input data + pCurCodedData = nullptr; + + // invalidate the buffer OK status flag + bJitterBufferOK = false; + } + + // OPUS decoding + if ( CurOpusDecoder != nullptr ) + { + iUnused = opus_custom_decode ( CurOpusDecoder, + pCurCodedData, + iCeltNumCodedBytes, + &vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples], + iOPUSFrameSizeSamples ); + } } - } - // for muted stream we have to add our local data here - if ( bMuteOutStream ) - { - for ( i = 0; i < iStereoBlockSizeSam; i++ ) + // for muted stream we have to add our local data here + if ( bMuteOutStream ) { - vecsStereoSndCrd[i] = Float2Short ( vecsStereoSndCrd[i] + vecsStereoSndCrdMuteStream[i] * fMuteOutStreamGain ); + for ( i = 0; i < iStereoBlockSizeSam; i++ ) + { + vecsStereoSndCrd[i] = Float2Short ( vecsStereoSndCrd[i] + vecsStereoSndCrdMuteStream[i] * fMuteOutStreamGain ); + } } - } - // check if channel is connected and if we do not have the initialization phase - if ( Channel.IsConnected() && ( !bIsInitializationPhase ) ) - { - if ( eAudioChannelConf == CC_MONO ) + // check if channel is connected and if we do not have the initialization phase + if ( Channel.IsConnected() && ( !bIsInitializationPhase ) ) { - // copy mono data in stereo sound card buffer (note that since the input - // and output is the same buffer, we have to start from the end not to - // overwrite input values) - for ( i = iMonoBlockSizeSam - 1, j = iStereoBlockSizeSam - 2; i >= 0; i--, j -= 2 ) + if ( eAudioChannelConf == CC_MONO ) { - vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = vecsStereoSndCrd[i]; + // copy mono data in stereo sound card buffer (note that since the input + // and output is the same buffer, we have to start from the end not to + // overwrite input values) + for ( i = iMonoBlockSizeSam - 1, j = iStereoBlockSizeSam - 2; i >= 0; i--, j -= 2 ) + { + vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = vecsStereoSndCrd[i]; + } } } - } - else - { - // if not connected, clear data - vecsStereoSndCrd.Reset ( 0 ); - } + else + { + // if not connected, clear data + vecsStereoSndCrd.Reset ( 0 ); + } - // update socket buffer size - Channel.UpdateSocketBufferSize(); + // update socket buffer size + Channel.UpdateSocketBufferSize(); - Q_UNUSED ( iUnused ) -} + Q_UNUSED ( iUnused ) + } -int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) -{ - const float fSystemBlockDurationMs = static_cast ( iOPUSFrameSizeSamples ) / SYSTEM_SAMPLE_RATE_HZ * 1000; + int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) + { + const float fSystemBlockDurationMs = static_cast ( iOPUSFrameSizeSamples ) / SYSTEM_SAMPLE_RATE_HZ * 1000; - // If the jitter buffers are set effectively, i.e. they are exactly the - // size of the network jitter, then the delay of the buffer is the buffer - // length. Since that is usually not the case but the buffers are usually - // a bit larger than necessary, we introduce some factor for compensation. - // Consider the jitter buffer on the client and on the server side, too. - const float fTotalJitterBufferDelayMs = fSystemBlockDurationMs * ( GetSockBufNumFrames() + GetServerSockBufNumFrames() ) * 0.7f; + // If the jitter buffers are set effectively, i.e. they are exactly the + // size of the network jitter, then the delay of the buffer is the buffer + // length. Since that is usually not the case but the buffers are usually + // a bit larger than necessary, we introduce some factor for compensation. + // Consider the jitter buffer on the client and on the server side, too. + const float fTotalJitterBufferDelayMs = fSystemBlockDurationMs * ( GetSockBufNumFrames() + GetServerSockBufNumFrames() ) * 0.7f; - // consider delay introduced by the sound card conversion buffer by using - // "GetSndCrdConvBufAdditionalDelayMonoBlSize()" - float fTotalSoundCardDelayMs = GetSndCrdConvBufAdditionalDelayMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; + // consider delay introduced by the sound card conversion buffer by using + // "GetSndCrdConvBufAdditionalDelayMonoBlSize()" + float fTotalSoundCardDelayMs = GetSndCrdConvBufAdditionalDelayMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; - // try to get the actual input/output sound card delay from the audio - // interface, per definition it is not available if a 0 is returned - const float fSoundCardInputOutputLatencyMs = Sound.GetInOutLatencyMs(); + // try to get the actual input/output sound card delay from the audio + // interface, per definition it is not available if a 0 is returned + const float fSoundCardInputOutputLatencyMs = Sound.GetInOutLatencyMs(); - if ( fSoundCardInputOutputLatencyMs == 0.0f ) - { - // use an alternative approach for estimating the sound card delay: - // - // we assume that we have two period sizes for the input and one for the - // output, therefore we have "3 *" instead of "2 *" (for input and output) - // the actual sound card buffer size - // "GetSndCrdConvBufAdditionalDelayMonoBlSize" - fTotalSoundCardDelayMs += ( 3 * GetSndCrdActualMonoBlSize() ) * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; - } - else - { - // add the actual sound card latency in ms - fTotalSoundCardDelayMs += fSoundCardInputOutputLatencyMs; - } + if ( fSoundCardInputOutputLatencyMs == 0.0f ) + { + // use an alternative approach for estimating the sound card delay: + // + // we assume that we have two period sizes for the input and one for the + // output, therefore we have "3 *" instead of "2 *" (for input and output) + // the actual sound card buffer size + // "GetSndCrdConvBufAdditionalDelayMonoBlSize" + fTotalSoundCardDelayMs += ( 3 * GetSndCrdActualMonoBlSize() ) * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; + } + else + { + // add the actual sound card latency in ms + fTotalSoundCardDelayMs += fSoundCardInputOutputLatencyMs; + } - // network packets are of the same size as the audio packets per definition - // if no sound card conversion buffer is used - const float fDelayToFillNetworkPacketsMs = GetSystemMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; + // network packets are of the same size as the audio packets per definition + // if no sound card conversion buffer is used + const float fDelayToFillNetworkPacketsMs = GetSystemMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; - // OPUS additional delay at small frame sizes is half a frame size - const float fAdditionalAudioCodecDelayMs = fSystemBlockDurationMs / 2; + // OPUS additional delay at small frame sizes is half a frame size + const float fAdditionalAudioCodecDelayMs = fSystemBlockDurationMs / 2; - const float fTotalBufferDelayMs = - fDelayToFillNetworkPacketsMs + fTotalJitterBufferDelayMs + fTotalSoundCardDelayMs + fAdditionalAudioCodecDelayMs; + const float fTotalBufferDelayMs = + fDelayToFillNetworkPacketsMs + fTotalJitterBufferDelayMs + fTotalSoundCardDelayMs + fAdditionalAudioCodecDelayMs; - return MathUtils::round ( fTotalBufferDelayMs + iPingTimeMs ); -} + return MathUtils::round ( fTotalBufferDelayMs + iPingTimeMs ); + } From 95a65096c468193bcc8a731fd5f8fdcd0523fd47 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Sat, 26 Mar 2022 20:27:58 +0100 Subject: [PATCH 18/25] Connection status issue jamulussoftware#2519 second stage Moved Connect/Disconnect code from CClientdlg to CClient. Now using the proper connected checks in several places. Added bDisconnectAndDisable to CChannel. (For a Client now Channel.Disconnect() will block audio data and auto disable the channel on disconnected) --- src/main.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 5057c2a067..e4f821ec93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -920,9 +920,6 @@ int main ( int argc, char** argv ) // initialise message boxes CMsgBoxes::init ( &ClientDlg, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); - // initialise message boxes - CMsgBoxes::init ( &ClientDlg, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); - // show dialog ClientDlg.show(); From 8df0e2950d129145ae5fd8b53dbf98e930f82057 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Tue, 5 Apr 2022 20:49:34 +0200 Subject: [PATCH 19/25] Fixing rebase issues again... --- src/client.cpp | 857 +++++++++++++++++++++++----------------------- src/clientdlg.cpp | 42 +-- 2 files changed, 432 insertions(+), 467 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 774df12b13..8947504033 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -828,550 +828,553 @@ bool CClient::Connect ( QString strServerAddress, QString strServerName ) } } - bool CClient::Disconnect() + return false; +} + +bool CClient::Disconnect() +{ + if ( Channel.IsEnabled() ) { - if ( Channel.IsEnabled() ) - { - // start disconnection - Channel.Disconnect(); + // start disconnection + Channel.Disconnect(); - // Channel.Disconnect() should now automatically disable Channel as soon as disconnected. - // Note that this only works if Sound is Active ! + // Channel.Disconnect() should now automatically disable Channel as soon as disconnected. + // Note that this only works if Sound is Active ! - QTime DieTime = QTime::currentTime().addMSecs ( 500 ); - while ( ( QTime::currentTime() < DieTime ) && Channel.IsEnabled() ) - { - // exclude user input events because if we use AllEvents, it happens - // that if the user initiates a connection and disconnection quickly - // (e.g. quickly pressing enter five times), the software can get into - // an unknown state - QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); - } + QTime DieTime = QTime::currentTime().addMSecs ( 500 ); + while ( ( QTime::currentTime() < DieTime ) && Channel.IsEnabled() ) + { + // exclude user input events because if we use AllEvents, it happens + // that if the user initiates a connection and disconnection quickly + // (e.g. quickly pressing enter five times), the software can get into // an unknown state - // Now stop the audio interface - Sound.Stop(); + QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); + } + // an unknown state + // Now stop the audio interface + Sound.Stop(); - // in case we timed out, log warning and make sure Channel is disabled - if ( Channel.IsEnabled() ) - { - Channel.SetEnable ( false ); - qWarning() << CMsgBoxes::MainFormName() + " Warning: Disconnect failed."; - } + // in case we timed out, log warning and make sure Channel is disabled + if ( Channel.IsEnabled() ) + { + Channel.SetEnable ( false ); + qWarning() << CMsgBoxes::MainFormName() + " Warning: Disconnect failed."; + } - // Send disconnect message to server (Since we disable our protocol - // receive mechanism with the next command, we do not evaluate any - // respond from the server, therefore we just hope that the message - // gets its way to the server, if not, the old behaviour time-out - // disconnects the connection anyway). - ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); + // Send disconnect message to server (Since we disable our protocol + // receive mechanism with the next command, we do not evaluate any + // respond from the server, therefore we just hope that the message + // gets its way to the server, if not, the old behaviour time-out + // disconnects the connection anyway). + ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); - // gets its way to the server, if not, the old behaviour time-out - // reset current signal level and LEDs - bJitterBufferOK = true; - SignalLevelMeter.Reset(); + // gets its way to the server, if not, the old behaviour time-out + // reset current signal level and LEDs + bJitterBufferOK = true; + SignalLevelMeter.Reset(); - emit Disconnected(); + emit Disconnected(); - return true; - } - else - { - // make sure sound is stopped too - Sound.Stop(); + return true; + } + else + { + // make sure sound is stopped too + Sound.Stop(); - emit Disconnected(); + emit Disconnected(); - return false; - } + return false; } +} - void CClient::Init() - { - // check if possible frame size factors are supported - const int iFraSizePreffered = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED; - const int iFraSizeDefault = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT; - const int iFraSizeSafe = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE; +void CClient::Init() +{ + // check if possible frame size factors are supported + const int iFraSizePreffered = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED; + const int iFraSizeDefault = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT; + const int iFraSizeSafe = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE; #if defined( Q_OS_IOS ) - bFraSiFactPrefSupported = true; // to reduce sound init time, because we know it's supported in iOS - bFraSiFactDefSupported = true; - bFraSiFactSafeSupported = true; + bFraSiFactPrefSupported = true; // to reduce sound init time, because we know it's supported in iOS + bFraSiFactDefSupported = true; + bFraSiFactSafeSupported = true; #else - bFraSiFactPrefSupported = ( Sound.Init ( iFraSizePreffered ) == iFraSizePreffered ); - bFraSiFactDefSupported = ( Sound.Init ( iFraSizeDefault ) == iFraSizeDefault ); - bFraSiFactSafeSupported = ( Sound.Init ( iFraSizeSafe ) == iFraSizeSafe ); + bFraSiFactPrefSupported = ( Sound.Init ( iFraSizePreffered ) == iFraSizePreffered ); + bFraSiFactDefSupported = ( Sound.Init ( iFraSizeDefault ) == iFraSizeDefault ); + bFraSiFactSafeSupported = ( Sound.Init ( iFraSizeSafe ) == iFraSizeSafe ); #endif - // translate block size index in actual block size - const int iPrefMonoFrameSize = iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; - - // get actual sound card buffer size using preferred size - // TODO - iOS needs 1 init only, now: 9 inits at launch <- slow - // Initially, I tried to fix this as follows (inside #ifdef ios tag): - // if ( Sound.isInitialized ) - // iMonoBlockSizeSam = iPrefMonoFrameSize; - // else - // iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); - // Problem is legitimate setting changes (buffer size for example). - // so the condition should be something like "if ( Sound.isInitialized and APP_IS_INIALIZING)" - iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); - - // Calculate the current sound card frame size factor. In case - // the current mono block size is not a multiple of the system - // frame size, we have to use a sound card conversion buffer. - if ( ( ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) && bEnableOPUS64 ) || - ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) || - ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) - { - // regular case: one of our predefined buffer sizes is available - iSndCrdFrameSizeFactor = iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES; + // translate block size index in actual block size + const int iPrefMonoFrameSize = iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; + + // get actual sound card buffer size using preferred size + // TODO - iOS needs 1 init only, now: 9 inits at launch <- slow + // Initially, I tried to fix this as follows (inside #ifdef ios tag): + // if ( Sound.isInitialized ) + // iMonoBlockSizeSam = iPrefMonoFrameSize; + // else + // iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); + // Problem is legitimate setting changes (buffer size for example). + // so the condition should be something like "if ( Sound.isInitialized and APP_IS_INIALIZING)" + iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); + + // Calculate the current sound card frame size factor. In case + // the current mono block size is not a multiple of the system + // frame size, we have to use a sound card conversion buffer. + if ( ( ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_PREFERRED ) ) && bEnableOPUS64 ) || + ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT ) ) || + ( iMonoBlockSizeSam == ( SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE ) ) ) + { + // regular case: one of our predefined buffer sizes is available + iSndCrdFrameSizeFactor = iMonoBlockSizeSam / SYSTEM_FRAME_SIZE_SAMPLES; + + // no sound card conversion buffer required + bSndCrdConversionBufferRequired = false; + } + else + { + // An unsupported sound card buffer size is currently used -> we have + // to use a conversion buffer. Per definition we use the smallest buffer + // size as the current frame size. + + // store actual sound card buffer size (stereo) + bSndCrdConversionBufferRequired = true; + iSndCardMonoBlockSizeSamConvBuff = iMonoBlockSizeSam; + + // overwrite block size factor by using one frame + iSndCrdFrameSizeFactor = 1; + } - // no sound card conversion buffer required - bSndCrdConversionBufferRequired = false; + // select the OPUS frame size mode depending on current mono block size samples + if ( bSndCrdConversionBufferRequired ) + { + if ( ( iSndCardMonoBlockSizeSamConvBuff < DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) && bEnableOPUS64 ) + { + iMonoBlockSizeSam = SYSTEM_FRAME_SIZE_SAMPLES; + eAudioCompressionType = CT_OPUS64; } else { - // An unsupported sound card buffer size is currently used -> we have - // to use a conversion buffer. Per definition we use the smallest buffer - // size as the current frame size. - - // store actual sound card buffer size (stereo) - bSndCrdConversionBufferRequired = true; - iSndCardMonoBlockSizeSamConvBuff = iMonoBlockSizeSam; - - // overwrite block size factor by using one frame - iSndCrdFrameSizeFactor = 1; + iMonoBlockSizeSam = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; + eAudioCompressionType = CT_OPUS; + } + } + else + { + if ( iMonoBlockSizeSam < DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) + { + eAudioCompressionType = CT_OPUS64; + } + else + { + // since we use double size frame size for OPUS, we have to adjust the frame size factor + iSndCrdFrameSizeFactor /= 2; + eAudioCompressionType = CT_OPUS; } + } + + // inits for audio coding + if ( eAudioCompressionType == CT_OPUS ) + { + iOPUSFrameSizeSamples = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; - // select the OPUS frame size mode depending on current mono block size samples - if ( bSndCrdConversionBufferRequired ) + if ( eAudioChannelConf == CC_MONO ) { - if ( ( iSndCardMonoBlockSizeSamConvBuff < DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) && bEnableOPUS64 ) - { - iMonoBlockSizeSam = SYSTEM_FRAME_SIZE_SAMPLES; - eAudioCompressionType = CT_OPUS64; - } - else + CurOpusEncoder = OpusEncoderMono; + CurOpusDecoder = OpusDecoderMono; + iNumAudioChannels = 1; + + switch ( eAudioQuality ) { - iMonoBlockSizeSam = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; - eAudioCompressionType = CT_OPUS; + case AQ_LOW: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_LOW_QUALITY_DBLE_FRAMESIZE; + break; + case AQ_NORMAL: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_NORMAL_QUALITY_DBLE_FRAMESIZE; + break; + case AQ_HIGH: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY_DBLE_FRAMESIZE; + break; } } else { - if ( iMonoBlockSizeSam < DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) - { - eAudioCompressionType = CT_OPUS64; - } - else + CurOpusEncoder = OpusEncoderStereo; + CurOpusDecoder = OpusDecoderStereo; + iNumAudioChannels = 2; + + switch ( eAudioQuality ) { - // since we use double size frame size for OPUS, we have to adjust the frame size factor - iSndCrdFrameSizeFactor /= 2; - eAudioCompressionType = CT_OPUS; + case AQ_LOW: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_LOW_QUALITY_DBLE_FRAMESIZE; + break; + case AQ_NORMAL: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY_DBLE_FRAMESIZE; + break; + case AQ_HIGH: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY_DBLE_FRAMESIZE; + break; } } + } + else /* CT_OPUS64 */ + { + iOPUSFrameSizeSamples = SYSTEM_FRAME_SIZE_SAMPLES; - // inits for audio coding - if ( eAudioCompressionType == CT_OPUS ) + if ( eAudioChannelConf == CC_MONO ) { - iOPUSFrameSizeSamples = DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES; + CurOpusEncoder = Opus64EncoderMono; + CurOpusDecoder = Opus64DecoderMono; + iNumAudioChannels = 1; - if ( eAudioChannelConf == CC_MONO ) - { - CurOpusEncoder = OpusEncoderMono; - CurOpusDecoder = OpusDecoderMono; - iNumAudioChannels = 1; - - switch ( eAudioQuality ) - { - case AQ_LOW: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_LOW_QUALITY_DBLE_FRAMESIZE; - break; - case AQ_NORMAL: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_NORMAL_QUALITY_DBLE_FRAMESIZE; - break; - case AQ_HIGH: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY_DBLE_FRAMESIZE; - break; - } - } - else + switch ( eAudioQuality ) { - CurOpusEncoder = OpusEncoderStereo; - CurOpusDecoder = OpusDecoderStereo; - iNumAudioChannels = 2; - - switch ( eAudioQuality ) - { - case AQ_LOW: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_LOW_QUALITY_DBLE_FRAMESIZE; - break; - case AQ_NORMAL: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY_DBLE_FRAMESIZE; - break; - case AQ_HIGH: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY_DBLE_FRAMESIZE; - break; - } + case AQ_LOW: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_LOW_QUALITY; + break; + case AQ_NORMAL: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_NORMAL_QUALITY; + break; + case AQ_HIGH: + iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY; + break; } } - else /* CT_OPUS64 */ + else { - iOPUSFrameSizeSamples = SYSTEM_FRAME_SIZE_SAMPLES; + CurOpusEncoder = Opus64EncoderStereo; + CurOpusDecoder = Opus64DecoderStereo; + iNumAudioChannels = 2; - if ( eAudioChannelConf == CC_MONO ) - { - CurOpusEncoder = Opus64EncoderMono; - CurOpusDecoder = Opus64DecoderMono; - iNumAudioChannels = 1; - - switch ( eAudioQuality ) - { - case AQ_LOW: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_LOW_QUALITY; - break; - case AQ_NORMAL: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_NORMAL_QUALITY; - break; - case AQ_HIGH: - iCeltNumCodedBytes = OPUS_NUM_BYTES_MONO_HIGH_QUALITY; - break; - } - } - else + switch ( eAudioQuality ) { - CurOpusEncoder = Opus64EncoderStereo; - CurOpusDecoder = Opus64DecoderStereo; - iNumAudioChannels = 2; - - switch ( eAudioQuality ) - { - case AQ_LOW: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_LOW_QUALITY; - break; - case AQ_NORMAL: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY; - break; - case AQ_HIGH: - iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY; - break; - } + case AQ_LOW: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_LOW_QUALITY; + break; + case AQ_NORMAL: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_NORMAL_QUALITY; + break; + case AQ_HIGH: + iCeltNumCodedBytes = OPUS_NUM_BYTES_STEREO_HIGH_QUALITY; + break; } } + } - // calculate stereo (two channels) buffer size - iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; - - vecCeltData.Init ( iCeltNumCodedBytes ); - vecZeros.Init ( iStereoBlockSizeSam, 0 ); - vecsStereoSndCrdMuteStream.Init ( iStereoBlockSizeSam ); + // calculate stereo (two channels) buffer size + iStereoBlockSizeSam = 2 * iMonoBlockSizeSam; - opus_custom_encoder_ctl ( CurOpusEncoder, - OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iOPUSFrameSizeSamples ) ) ); + vecCeltData.Init ( iCeltNumCodedBytes ); + vecZeros.Init ( iStereoBlockSizeSam, 0 ); + vecsStereoSndCrdMuteStream.Init ( iStereoBlockSizeSam ); - // inits for network and channel - vecbyNetwData.Init ( iCeltNumCodedBytes ); + opus_custom_encoder_ctl ( CurOpusEncoder, + OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iOPUSFrameSizeSamples ) ) ); - // set the channel network properties - Channel.SetAudioStreamProperties ( eAudioCompressionType, iCeltNumCodedBytes, iSndCrdFrameSizeFactor, iNumAudioChannels ); + // inits for network and channel + vecbyNetwData.Init ( iCeltNumCodedBytes ); - // init reverberation - AudioReverb.Init ( eAudioChannelConf, iStereoBlockSizeSam, SYSTEM_SAMPLE_RATE_HZ ); + // set the channel network properties + Channel.SetAudioStreamProperties ( eAudioCompressionType, iCeltNumCodedBytes, iSndCrdFrameSizeFactor, iNumAudioChannels ); - // init the sound card conversion buffers - if ( bSndCrdConversionBufferRequired ) - { - // inits for conversion buffer (the size of the conversion buffer must - // be the sum of input/output sizes which is the worst case fill level) - const int iSndCardStereoBlockSizeSamConvBuff = 2 * iSndCardMonoBlockSizeSamConvBuff; - const int iConBufSize = iStereoBlockSizeSam + iSndCardStereoBlockSizeSamConvBuff; - - SndCrdConversionBufferIn.Init ( iConBufSize ); - SndCrdConversionBufferOut.Init ( iConBufSize ); - vecDataConvBuf.Init ( iStereoBlockSizeSam ); - - // the output conversion buffer must be filled with the inner - // block size for initialization (this is the latency which is - // introduced by the conversion buffer) to avoid buffer underruns - SndCrdConversionBufferOut.Put ( vecZeros, iStereoBlockSizeSam ); - } + // init reverberation + AudioReverb.Init ( eAudioChannelConf, iStereoBlockSizeSam, SYSTEM_SAMPLE_RATE_HZ ); - // reset initialization phase flag and mute flag - bIsInitializationPhase = true; + // init the sound card conversion buffers + if ( bSndCrdConversionBufferRequired ) + { + // inits for conversion buffer (the size of the conversion buffer must + // be the sum of input/output sizes which is the worst case fill level) + const int iSndCardStereoBlockSizeSamConvBuff = 2 * iSndCardMonoBlockSizeSamConvBuff; + const int iConBufSize = iStereoBlockSizeSam + iSndCardStereoBlockSizeSamConvBuff; + + SndCrdConversionBufferIn.Init ( iConBufSize ); + SndCrdConversionBufferOut.Init ( iConBufSize ); + vecDataConvBuf.Init ( iStereoBlockSizeSam ); + + // the output conversion buffer must be filled with the inner + // block size for initialization (this is the latency which is + // introduced by the conversion buffer) to avoid buffer underruns + SndCrdConversionBufferOut.Put ( vecZeros, iStereoBlockSizeSam ); } - void CClient::AudioCallback ( CVector & psData, void* arg ) - { - // get the pointer to the object - CClient* pMyClientObj = static_cast ( arg ); + // reset initialization phase flag and mute flag + bIsInitializationPhase = true; +} - // process audio data - pMyClientObj->ProcessSndCrdAudioData ( psData ); +void CClient::AudioCallback ( CVector& psData, void* arg ) +{ + // get the pointer to the object + CClient* pMyClientObj = static_cast ( arg ); - // clang-format off + // process audio data + pMyClientObj->ProcessSndCrdAudioData ( psData ); + + // clang-format off /* // TEST do a soundcard jitter measurement static CTimingMeas JitterMeas ( 1000, "test2.dat" ); JitterMeas.Measure(); */ - // clang-format on - } + // clang-format on +} - void CClient::ProcessSndCrdAudioData ( CVector & vecsStereoSndCrd ) +void CClient::ProcessSndCrdAudioData ( CVector& vecsStereoSndCrd ) +{ + // check if a conversion buffer is required or not + if ( bSndCrdConversionBufferRequired ) { - // check if a conversion buffer is required or not - if ( bSndCrdConversionBufferRequired ) + // add new sound card block in conversion buffer + SndCrdConversionBufferIn.Put ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); + + // process all available blocks of data + while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam ) { - // add new sound card block in conversion buffer - SndCrdConversionBufferIn.Put ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); + // get one block of data for processing + SndCrdConversionBufferIn.Get ( vecDataConvBuf, iStereoBlockSizeSam ); - // process all available blocks of data - while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam ) - { - // get one block of data for processing - SndCrdConversionBufferIn.Get ( vecDataConvBuf, iStereoBlockSizeSam ); + // process audio data + ProcessAudioDataIntern ( vecDataConvBuf ); - // process audio data - ProcessAudioDataIntern ( vecDataConvBuf ); + SndCrdConversionBufferOut.Put ( vecDataConvBuf, iStereoBlockSizeSam ); + } - SndCrdConversionBufferOut.Put ( vecDataConvBuf, iStereoBlockSizeSam ); - } + // get processed sound card block out of the conversion buffer + SndCrdConversionBufferOut.Get ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); + } + else + { + // regular case: no conversion buffer required + // process audio data + ProcessAudioDataIntern ( vecsStereoSndCrd ); + } +} - // get processed sound card block out of the conversion buffer - SndCrdConversionBufferOut.Get ( vecsStereoSndCrd, vecsStereoSndCrd.Size() ); - } - else +void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) +{ + int i, j, iUnused; + unsigned char* pCurCodedData; + + // Transmit signal --------------------------------------------------------- + + if ( iInputBoost != 1 ) + { + // apply a general gain boost to all audio input: + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) { - // regular case: no conversion buffer required - // process audio data - ProcessAudioDataIntern ( vecsStereoSndCrd ); + vecsStereoSndCrd[j + 1] = static_cast ( iInputBoost * vecsStereoSndCrd[j + 1] ); + vecsStereoSndCrd[j] = static_cast ( iInputBoost * vecsStereoSndCrd[j] ); } } - void CClient::ProcessAudioDataIntern ( CVector & vecsStereoSndCrd ) + // update stereo signal level meter (not needed in headless mode) +#ifndef HEADLESS + SignalLevelMeter.Update ( vecsStereoSndCrd, iMonoBlockSizeSam, true ); +#endif + + // add reverberation effect if activated + if ( iReverbLevel != 0 ) { - int i, j, iUnused; - unsigned char* pCurCodedData; + AudioReverb.Process ( vecsStereoSndCrd, bReverbOnLeftChan, static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 4 ); + } - // Transmit signal --------------------------------------------------------- + // apply pan (audio fader) and mix mono signals + if ( !( ( iAudioInFader == AUD_FADER_IN_MIDDLE ) && ( eAudioChannelConf == CC_STEREO ) ) ) + { + // calculate pan gain in the range 0 to 1, where 0.5 is the middle position + const float fPan = static_cast ( iAudioInFader ) / AUD_FADER_IN_MAX; - if ( iInputBoost != 1 ) + if ( eAudioChannelConf == CC_STEREO ) { - // apply a general gain boost to all audio input: + // for stereo only apply pan attenuation on one channel (same as pan in the server) + const float fGainL = MathUtils::GetLeftPan ( fPan, false ); + const float fGainR = MathUtils::GetRightPan ( fPan, false ); + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) { - vecsStereoSndCrd[j + 1] = static_cast ( iInputBoost * vecsStereoSndCrd[j + 1] ); - vecsStereoSndCrd[j] = static_cast ( iInputBoost * vecsStereoSndCrd[j] ); + // note that the gain is always <= 1, therefore a simple cast is + // ok since we never can get an overload + vecsStereoSndCrd[j + 1] = static_cast ( fGainR * vecsStereoSndCrd[j + 1] ); + vecsStereoSndCrd[j] = static_cast ( fGainL * vecsStereoSndCrd[j] ); } } + else + { + // for mono implement a cross-fade between channels and mix them, for + // mono-in/stereo-out use no attenuation in pan center + const float fGainL = MathUtils::GetLeftPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); + const float fGainR = MathUtils::GetRightPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); - // update stereo signal level meter (not needed in headless mode) -#ifndef HEADLESS - SignalLevelMeter.Update ( vecsStereoSndCrd, iMonoBlockSizeSam, true ); -#endif + for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) + { + // note that we need the Float2Short for stereo pan mode + vecsStereoSndCrd[i] = Float2Short ( fGainL * vecsStereoSndCrd[j] + fGainR * vecsStereoSndCrd[j + 1] ); + } + } + } - // add reverberation effect if activated - if ( iReverbLevel != 0 ) + // Support for mono-in/stereo-out mode: Per definition this mode works in + // full stereo mode at the transmission level. The only thing which is done + // is to mix both sound card inputs together and then put this signal on + // both stereo channels to be transmitted to the server. + if ( eAudioChannelConf == CC_MONO_IN_STEREO_OUT ) + { + // copy mono data in stereo sound card buffer (note that since the input + // and output is the same buffer, we have to start from the end not to + // overwrite input values) + for ( i = iMonoBlockSizeSam - 1, j = iStereoBlockSizeSam - 2; i >= 0; i--, j -= 2 ) { - AudioReverb.Process ( vecsStereoSndCrd, bReverbOnLeftChan, static_cast ( iReverbLevel ) / AUD_REVERB_MAX / 4 ); + vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = vecsStereoSndCrd[i]; } + } - // apply pan (audio fader) and mix mono signals - if ( !( ( iAudioInFader == AUD_FADER_IN_MIDDLE ) && ( eAudioChannelConf == CC_STEREO ) ) ) + for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) + { + // OPUS encoding + if ( CurOpusEncoder != nullptr ) { - // calculate pan gain in the range 0 to 1, where 0.5 is the middle position - const float fPan = static_cast ( iAudioInFader ) / AUD_FADER_IN_MAX; - - if ( eAudioChannelConf == CC_STEREO ) + if ( bMuteOutStream ) { - // for stereo only apply pan attenuation on one channel (same as pan in the server) - const float fGainL = MathUtils::GetLeftPan ( fPan, false ); - const float fGainR = MathUtils::GetRightPan ( fPan, false ); - - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // note that the gain is always <= 1, therefore a simple cast is - // ok since we never can get an overload - vecsStereoSndCrd[j + 1] = static_cast ( fGainR * vecsStereoSndCrd[j + 1] ); - vecsStereoSndCrd[j] = static_cast ( fGainL * vecsStereoSndCrd[j] ); - } + iUnused = opus_custom_encode ( CurOpusEncoder, + &vecZeros[i * iNumAudioChannels * iOPUSFrameSizeSamples], + iOPUSFrameSizeSamples, + &vecCeltData[0], + iCeltNumCodedBytes ); } else { - // for mono implement a cross-fade between channels and mix them, for - // mono-in/stereo-out use no attenuation in pan center - const float fGainL = MathUtils::GetLeftPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); - const float fGainR = MathUtils::GetRightPan ( fPan, eAudioChannelConf != CC_MONO_IN_STEREO_OUT ); - - for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) - { - // note that we need the Float2Short for stereo pan mode - vecsStereoSndCrd[i] = Float2Short ( fGainL * vecsStereoSndCrd[j] + fGainR * vecsStereoSndCrd[j + 1] ); - } + iUnused = opus_custom_encode ( CurOpusEncoder, + &vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples], + iOPUSFrameSizeSamples, + &vecCeltData[0], + iCeltNumCodedBytes ); } } - // Support for mono-in/stereo-out mode: Per definition this mode works in - // full stereo mode at the transmission level. The only thing which is done - // is to mix both sound card inputs together and then put this signal on - // both stereo channels to be transmitted to the server. - if ( eAudioChannelConf == CC_MONO_IN_STEREO_OUT ) - { - // copy mono data in stereo sound card buffer (note that since the input - // and output is the same buffer, we have to start from the end not to - // overwrite input values) - for ( i = iMonoBlockSizeSam - 1, j = iStereoBlockSizeSam - 2; i >= 0; i--, j -= 2 ) - { - vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = vecsStereoSndCrd[i]; - } - } + // send coded audio through the network + Channel.PrepAndSendPacket ( &Socket, vecCeltData, iCeltNumCodedBytes ); + } - for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) - { - // OPUS encoding - if ( CurOpusEncoder != nullptr ) - { - if ( bMuteOutStream ) - { - iUnused = opus_custom_encode ( CurOpusEncoder, - &vecZeros[i * iNumAudioChannels * iOPUSFrameSizeSamples], - iOPUSFrameSizeSamples, - &vecCeltData[0], - iCeltNumCodedBytes ); - } - else - { - iUnused = opus_custom_encode ( CurOpusEncoder, - &vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples], - iOPUSFrameSizeSamples, - &vecCeltData[0], - iCeltNumCodedBytes ); - } - } + // Receive signal ---------------------------------------------------------- + // in case of mute stream, store local data + if ( bMuteOutStream ) + { + vecsStereoSndCrdMuteStream = vecsStereoSndCrd; + } - // send coded audio through the network - Channel.PrepAndSendPacket ( &Socket, vecCeltData, iCeltNumCodedBytes ); - } + for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) + { + // receive a new block + const bool bReceiveDataOk = ( Channel.GetData ( vecbyNetwData, iCeltNumCodedBytes ) == GS_BUFFER_OK ); - // Receive signal ---------------------------------------------------------- - // in case of mute stream, store local data - if ( bMuteOutStream ) + // get pointer to coded data and manage the flags + if ( bReceiveDataOk ) { - vecsStereoSndCrdMuteStream = vecsStereoSndCrd; - } + pCurCodedData = &vecbyNetwData[0]; - for ( i = 0; i < iSndCrdFrameSizeFactor; i++ ) + // on any valid received packet, we clear the initialization phase flag + bIsInitializationPhase = false; + } + else { - // receive a new block - const bool bReceiveDataOk = ( Channel.GetData ( vecbyNetwData, iCeltNumCodedBytes ) == GS_BUFFER_OK ); - - // get pointer to coded data and manage the flags - if ( bReceiveDataOk ) - { - pCurCodedData = &vecbyNetwData[0]; - - // on any valid received packet, we clear the initialization phase flag - bIsInitializationPhase = false; - } - else - { - // for lost packets use null pointer as coded input data - pCurCodedData = nullptr; + // for lost packets use null pointer as coded input data + pCurCodedData = nullptr; - // invalidate the buffer OK status flag - bJitterBufferOK = false; - } + // invalidate the buffer OK status flag + bJitterBufferOK = false; + } - // OPUS decoding - if ( CurOpusDecoder != nullptr ) - { - iUnused = opus_custom_decode ( CurOpusDecoder, - pCurCodedData, - iCeltNumCodedBytes, - &vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples], - iOPUSFrameSizeSamples ); - } + // OPUS decoding + if ( CurOpusDecoder != nullptr ) + { + iUnused = opus_custom_decode ( CurOpusDecoder, + pCurCodedData, + iCeltNumCodedBytes, + &vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples], + iOPUSFrameSizeSamples ); } + } - // for muted stream we have to add our local data here - if ( bMuteOutStream ) + // for muted stream we have to add our local data here + if ( bMuteOutStream ) + { + for ( i = 0; i < iStereoBlockSizeSam; i++ ) { - for ( i = 0; i < iStereoBlockSizeSam; i++ ) - { - vecsStereoSndCrd[i] = Float2Short ( vecsStereoSndCrd[i] + vecsStereoSndCrdMuteStream[i] * fMuteOutStreamGain ); - } + vecsStereoSndCrd[i] = Float2Short ( vecsStereoSndCrd[i] + vecsStereoSndCrdMuteStream[i] * fMuteOutStreamGain ); } + } - // check if channel is connected and if we do not have the initialization phase - if ( Channel.IsConnected() && ( !bIsInitializationPhase ) ) + // check if channel is connected and if we do not have the initialization phase + if ( Channel.IsConnected() && ( !bIsInitializationPhase ) ) + { + if ( eAudioChannelConf == CC_MONO ) { - if ( eAudioChannelConf == CC_MONO ) + // copy mono data in stereo sound card buffer (note that since the input + // and output is the same buffer, we have to start from the end not to + // overwrite input values) + for ( i = iMonoBlockSizeSam - 1, j = iStereoBlockSizeSam - 2; i >= 0; i--, j -= 2 ) { - // copy mono data in stereo sound card buffer (note that since the input - // and output is the same buffer, we have to start from the end not to - // overwrite input values) - for ( i = iMonoBlockSizeSam - 1, j = iStereoBlockSizeSam - 2; i >= 0; i--, j -= 2 ) - { - vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = vecsStereoSndCrd[i]; - } + vecsStereoSndCrd[j] = vecsStereoSndCrd[j + 1] = vecsStereoSndCrd[i]; } } - else - { - // if not connected, clear data - vecsStereoSndCrd.Reset ( 0 ); - } + } + else + { + // if not connected, clear data + vecsStereoSndCrd.Reset ( 0 ); + } - // update socket buffer size - Channel.UpdateSocketBufferSize(); + // update socket buffer size + Channel.UpdateSocketBufferSize(); - Q_UNUSED ( iUnused ) - } + Q_UNUSED ( iUnused ) +} - int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) - { - const float fSystemBlockDurationMs = static_cast ( iOPUSFrameSizeSamples ) / SYSTEM_SAMPLE_RATE_HZ * 1000; +int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) +{ + const float fSystemBlockDurationMs = static_cast ( iOPUSFrameSizeSamples ) / SYSTEM_SAMPLE_RATE_HZ * 1000; - // If the jitter buffers are set effectively, i.e. they are exactly the - // size of the network jitter, then the delay of the buffer is the buffer - // length. Since that is usually not the case but the buffers are usually - // a bit larger than necessary, we introduce some factor for compensation. - // Consider the jitter buffer on the client and on the server side, too. - const float fTotalJitterBufferDelayMs = fSystemBlockDurationMs * ( GetSockBufNumFrames() + GetServerSockBufNumFrames() ) * 0.7f; + // If the jitter buffers are set effectively, i.e. they are exactly the + // size of the network jitter, then the delay of the buffer is the buffer + // length. Since that is usually not the case but the buffers are usually + // a bit larger than necessary, we introduce some factor for compensation. + // Consider the jitter buffer on the client and on the server side, too. + const float fTotalJitterBufferDelayMs = fSystemBlockDurationMs * ( GetSockBufNumFrames() + GetServerSockBufNumFrames() ) * 0.7f; - // consider delay introduced by the sound card conversion buffer by using - // "GetSndCrdConvBufAdditionalDelayMonoBlSize()" - float fTotalSoundCardDelayMs = GetSndCrdConvBufAdditionalDelayMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; + // consider delay introduced by the sound card conversion buffer by using + // "GetSndCrdConvBufAdditionalDelayMonoBlSize()" + float fTotalSoundCardDelayMs = GetSndCrdConvBufAdditionalDelayMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; - // try to get the actual input/output sound card delay from the audio - // interface, per definition it is not available if a 0 is returned - const float fSoundCardInputOutputLatencyMs = Sound.GetInOutLatencyMs(); + // try to get the actual input/output sound card delay from the audio + // interface, per definition it is not available if a 0 is returned + const float fSoundCardInputOutputLatencyMs = Sound.GetInOutLatencyMs(); - if ( fSoundCardInputOutputLatencyMs == 0.0f ) - { - // use an alternative approach for estimating the sound card delay: - // - // we assume that we have two period sizes for the input and one for the - // output, therefore we have "3 *" instead of "2 *" (for input and output) - // the actual sound card buffer size - // "GetSndCrdConvBufAdditionalDelayMonoBlSize" - fTotalSoundCardDelayMs += ( 3 * GetSndCrdActualMonoBlSize() ) * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; - } - else - { - // add the actual sound card latency in ms - fTotalSoundCardDelayMs += fSoundCardInputOutputLatencyMs; - } + if ( fSoundCardInputOutputLatencyMs == 0.0f ) + { + // use an alternative approach for estimating the sound card delay: + // + // we assume that we have two period sizes for the input and one for the + // output, therefore we have "3 *" instead of "2 *" (for input and output) + // the actual sound card buffer size + // "GetSndCrdConvBufAdditionalDelayMonoBlSize" + fTotalSoundCardDelayMs += ( 3 * GetSndCrdActualMonoBlSize() ) * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; + } + else + { + // add the actual sound card latency in ms + fTotalSoundCardDelayMs += fSoundCardInputOutputLatencyMs; + } - // network packets are of the same size as the audio packets per definition - // if no sound card conversion buffer is used - const float fDelayToFillNetworkPacketsMs = GetSystemMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; + // network packets are of the same size as the audio packets per definition + // if no sound card conversion buffer is used + const float fDelayToFillNetworkPacketsMs = GetSystemMonoBlSize() * 1000.0f / SYSTEM_SAMPLE_RATE_HZ; - // OPUS additional delay at small frame sizes is half a frame size - const float fAdditionalAudioCodecDelayMs = fSystemBlockDurationMs / 2; + // OPUS additional delay at small frame sizes is half a frame size + const float fAdditionalAudioCodecDelayMs = fSystemBlockDurationMs / 2; - const float fTotalBufferDelayMs = - fDelayToFillNetworkPacketsMs + fTotalJitterBufferDelayMs + fTotalSoundCardDelayMs + fAdditionalAudioCodecDelayMs; + const float fTotalBufferDelayMs = + fDelayToFillNetworkPacketsMs + fTotalJitterBufferDelayMs + fTotalSoundCardDelayMs + fAdditionalAudioCodecDelayMs; - return MathUtils::round ( fTotalBufferDelayMs + iPingTimeMs ); - } + return MathUtils::round ( fTotalBufferDelayMs + iPingTimeMs ); +} diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 82dbb1888a..fee662654c 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -1183,46 +1183,8 @@ void CClientDlg::OnConnect ( QString strServerName ) // audio feedback detection if ( pSettings->bEnableFeedbackDetection ) { - // try to start client, if error occurred, do not go in - // running state but show error message - try - { - if ( !pClient->IsRunning() ) - { - pClient->Start(); - } - } - - catch ( const CGenErr& generr ) - { - // show error message and return the function - CMsgBoxes::ShowError ( generr.GetErrorText() ); - return; - } - - // hide label connect to server - lblConnectToServer->hide(); - lbrInputLevelL->setEnabled ( true ); - lbrInputLevelR->setEnabled ( true ); - - // change connect button text to "disconnect" - butConnect->setText ( tr ( "&Disconnect" ) ); - - // set server name in audio mixer group box title - MainMixerBoard->SetServerName ( strMixerBoardLabel ); - - // start timer for level meter bar and ping time measurement - TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); - TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); - TimerPing.start ( PING_UPDATE_TIME_MS ); - TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer - - // audio feedback detection - if ( pSettings->bEnableFeedbackDetection ) - { - TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer - bDetectFeedback = true; - } + TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer + bDetectFeedback = true; } } From beed54e3a6a28a099b9b80c81ccd43715422a333 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Tue, 5 Apr 2022 23:32:36 +0200 Subject: [PATCH 20/25] Fix comments on PR --- src/global.h | 21 ++++++++++++--------- src/main.cpp | 52 ++++++++++++++++++++++++++++++++-------------------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/src/global.h b/src/global.h index 0a31b856f7..cda4bf107e 100644 --- a/src/global.h +++ b/src/global.h @@ -359,8 +359,7 @@ class CCustomEvent : public QEvent }; /* Prototypes for global functions ********************************************/ -// command line parsing, TODO do not declare functions globally but in a class -QString UsageArguments ( char** argv ); +extern QString UsageArguments ( char** argv ); //============================================================================ // CMsgBoxes class: @@ -370,8 +369,9 @@ QString UsageArguments ( char** argv ); //============================================================================ #ifndef HEADLESS # include +# define tMainform QDialog #else -# define QDialog void +# define tMainform void #endif // html text macro's (for use in gui texts) @@ -381,17 +381,17 @@ QString UsageArguments ( char** argv ); class CMsgBoxes { protected: - static QDialog* pMainForm; - static QString strMainFormName; + static tMainform* pMainForm; + static QString strMainFormName; public: - static void init ( QDialog* theMainForm, QString theMainFormName ) + static void init ( tMainform* theMainForm, QString theMainFormName ) { pMainForm = theMainForm; strMainFormName = theMainFormName; } - static QDialog* MainForm() { return pMainForm; } + static tMainform* MainForm() { return pMainForm; } static const QString& MainFormName() { return strMainFormName; } // Message boxes: @@ -536,6 +536,8 @@ class CCommandlineOptions #define CMDLN_INIFILE "-i", "--inifile" #define CMDLN_NOGUI "-n", "--nogui" #define CMDLN_PORT "-p", "--port" +#define CMDLN_JSONRPCPORT "--jsonrpcport", "--jsonrpcport" +#define CMDLN_JSONRPCSECRETFILE "--jsonrpcsecretfile", "--jsonrpcsecretfile" #define CMDLN_QOS "-Q", "--qos" #define CMDLN_NOTRANSLATION "-t", "--notranslation" #define CMDLN_ENABLEIPV6 "-6", "--enableipv6" @@ -565,10 +567,11 @@ class CCommandlineOptions #define CMDLN_CTRLMIDICH "--ctrlmidich", "--ctrlmidich" // Backwards compatibilyty: #define CMDLN_CENTRALSERVER "--centralserver", "--centralserver" -// pgScorpio: TODO These are NOT in help !: +// Debug options: (not in help) #define CMDLN_SHOWALLSERVERS "--showallservers", "--showallservers" #define CMDLN_SHOWANALYZERCONSOLE "--showanalyzerconsole", "--showanalyzerconsole" -// CMDLN_SPECIAL: Mostly used for debugging, any option after --special is accepted, should NOT be in help ! +// CMDLN_SPECIAL: Used for debugging, should NOT be in help, nor documented elsewhere! +// any option after --special is accepted #define CMDLN_SPECIAL "--special", "--special" // Special options for sound-redesign testing #define CMDLN_JACKINPUTS "--jackinputs", "--jackinputs" diff --git a/src/main.cpp b/src/main.cpp index e4f821ec93..012bdd0cfa 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,8 +58,8 @@ extern void qt_set_sequence_auto_mnemonic ( bool bEnable ); int CCommandlineOptions::appArgc = 0; char** CCommandlineOptions::appArgv = NULL; -QDialog* CMsgBoxes::pMainForm = NULL; -QString CMsgBoxes::strMainFormName = APP_NAME; +tMainform* CMsgBoxes::pMainForm = NULL; +QString CMsgBoxes::strMainFormName = APP_NAME; QString UsageArguments ( char** argv ) { @@ -209,13 +209,6 @@ int main ( int argc, char** argv ) // argument and argv()[argc()-1] is the last argument. // Start with first argument, therefore "i = 1" - // clang-format off -// pgScorio TODO: -// Extra Checks on parameters: -// If given, CMDLN_SERVER MUST be FIRST parameter. -// And then only check parameters valid for common, server or client ! - // clang-format on - for ( int i = 1; i < argc; i++ ) { // Help (usage) flag --------------------------------------------------- @@ -262,6 +255,24 @@ int main ( int argc, char** argv ) continue; } + // JSON-RPC port number ------------------------------------------------ + if ( cmdLine.GetNumericArgument ( i, CMDLN_JSONRPCPORT, 0, 65535, rDbleArgument ) ) + { + iJsonRpcPortNumber = static_cast ( rDbleArgument ); + qInfo() << qUtf8Printable ( QString ( "- JSON-RPC port number: %1" ).arg ( iJsonRpcPortNumber ) ); + CommandLineOptions << "--jsonrpcport"; + continue; + } + + // JSON-RPC secret file name ------------------------------------------- + if ( cmdLine.GetStringArgument ( i, CMDLN_JSONRPCSECRETFILE, strArgument ) ) + { + strJsonRpcSecretFileName = strArgument; + qInfo() << qUtf8Printable ( QString ( "- JSON-RPC secret file: %1" ).arg ( strJsonRpcSecretFileName ) ); + CommandLineOptions << "--jsonrpcsecretfile"; + continue; + } + // Quality of Service -------------------------------------------------- if ( cmdLine.GetNumericArgument ( i, CMDLN_QOS, 0, 255, rDbleArgument ) ) { @@ -586,16 +597,17 @@ int main ( int argc, char** argv ) // Unknown option ------------------------------------------------------ qCritical() << qUtf8Printable ( QString ( "%1: Unknown option '%2' -- use '--help' for help" ).arg ( argv[0] ).arg ( argv[i] ) ); +#if !( defined( Q_OS_MACX ) ) + // clicking on the Mac application bundle, the actual application + // is called with weird command line args -> do not exit on these + // pgScorpio: No exit for options after the "--special" option. // Used for debugging and testing new options... if ( !bSpecialOptions ) { -// clicking on the Mac application bundle, the actual application -// is called with weird command line args -> do not exit on these -#if !( defined( Q_OS_MACX ) ) exit ( 1 ); -#endif } +#endif } // Dependencies ------------------------------------------------------------ @@ -842,7 +854,7 @@ int main ( int argc, char** argv ) Q_INIT_RESOURCE ( resources ); #ifndef SERVER_ONLY - // clang-format off +// clang-format off // TEST -> activate the following line to activate the test bench, //CTestbench Testbench ( "127.0.0.1", DEFAULT_PORT_NUMBER ); // clang-format on @@ -923,12 +935,6 @@ int main ( int argc, char** argv ) // show dialog ClientDlg.show(); - // Connect on startup ------------------------------------------------------ - if ( !strConnOnStartupAddress.isEmpty() ) - { - Client.Connect ( strConnOnStartupAddress, strConnOnStartupAddress ); - } - pApp->exec(); } else @@ -996,6 +1002,9 @@ int main ( int argc, char** argv ) // GUI object for the server CServerDlg ServerDlg ( &Server, &Settings, bStartMinimized, nullptr ); + // initialise message boxes + CMsgBoxes::init ( &ServerDlg, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); + // show dialog (if not the minimized flag is set) if ( !bStartMinimized ) { @@ -1017,6 +1026,9 @@ int main ( int argc, char** argv ) Server.SetDirectoryType ( AT_CUSTOM ); } + // initialise message boxes + CMsgBoxes::init ( NULL, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); + pApp->exec(); } } From 648880cbfec48ab10b3eaac06c846c9d6f365c20 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Fri, 25 Mar 2022 22:30:21 +0100 Subject: [PATCH 21/25] Connection status issue #2519 first stage In this first stage I just renamed some functions in CClient to make the problem clear. See "---> pgScorpio:" comments in the problem area's. The next stage will actually change the code to solve this issue. --- src/client.cpp | 12 +++++++----- src/client.h | 16 ++++++++++++---- src/clientdlg.cpp | 31 ++++++++++++++++++++----------- src/clientsettingsdlg.cpp | 2 +- src/settings.cpp | 20 ++++++++++---------- 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index a1b34a8eeb..0323685b22 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -186,7 +186,7 @@ CClient::CClient ( const quint16 iPortNumber, if ( !strConnOnStartupAddress.isEmpty() ) { SetServerAddr ( strConnOnStartupAddress ); - Start(); + StartConnection(); // ---> pgScorpio: Was Start() } } @@ -298,7 +298,8 @@ void CClient::CreateServerJitterBufferMessage() void CClient::OnCLPingReceived ( CHostAddress InetAddr, int iMs ) { // make sure we are running and the server address is correct - if ( IsRunning() && ( InetAddr == Channel.GetAddress() ) ) + if ( SoundIsStarted() && // ---> pgScorpio: Does NOT mean we are Connected ! + ( InetAddr == Channel.GetAddress() ) ) { // take care of wrap arounds (if wrapping, do not use result) const int iCurDiff = EvaluatePingMessage ( iMs ); @@ -819,7 +820,7 @@ void CClient::OnClientIDReceived ( int iChanID ) emit ClientIDReceived ( iChanID ); } -void CClient::Start() +void CClient::StartConnection() // ---> pgScorpio: Was Start() { // init object Init(); @@ -828,10 +829,11 @@ void CClient::Start() Channel.SetEnable ( true ); // start audio interface - Sound.Start(); + Sound.Start(); // ---> pgScorpio: There is NO Check if Sound.Start() actually is successfull !! + // ---> pgScorpio: If Sound.Start fails here GUI (clientdlg) and Channel are definitely out of sync ! } -void CClient::Stop() +void CClient::StopConnection() { // stop audio interface Sound.Stop(); diff --git a/src/client.h b/src/client.h index cdb21f6e6e..5a5a2bc788 100644 --- a/src/client.h +++ b/src/client.h @@ -119,10 +119,18 @@ class CClient : public QObject virtual ~CClient(); - void Start(); - void Stop(); - bool IsRunning() { return Sound.IsRunning(); } - bool IsCallbackEntered() const { return Sound.IsCallbackEntered(); } + void StartConnection(); // ---> pgScorpio: Was Start(), but Start what ? ( Should be Connect() ?) + void StopConnection(); // ---> pgScorpio: Was Stop(), but Stop what ? ( Should be Disconnect() ?) + bool SoundIsStarted() // ---> pgScorpio: Was IsRunning(), but what is running ?? + { + return Sound.IsRunning(); // ---> pgScorpio: Even this name is incorrect ! + // ---> pgScorpio: Sound.bRun is set when Sound is started, but this does not guarantee sound is actually running + } + + bool SoundIsRunning() const + { + return Sound.IsCallbackEntered(); + } // ---> pgScorpio: was IsCallbackEntered() but this is the actuall SoundIsRunning() ! bool SetServerAddr ( QString strNAddr ); double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); } diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index c94c03b707..e8d8163e67 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -609,9 +609,9 @@ void CClientDlg::closeEvent ( QCloseEvent* Event ) AnalyzerConsole.close(); // if connected, terminate connection - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // ---> pgScorpio: This does NOT mean the connection is started ! { - pClient->Stop(); + pClient->StopConnection(); // ---> pgScorpio: Was pClient->Stop() } // make sure all current fader settings are applied to the settings struct @@ -732,7 +732,7 @@ void CClientDlg::OnConnectDlgAccepted() // first check if we are already connected, if this is the case we have to // disconnect the old server first - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // pgScorpio: Was pClient->IsRunning() Again this is NOT connection started ! { Disconnect(); } @@ -748,7 +748,7 @@ void CClientDlg::OnConnectDlgAccepted() void CClientDlg::OnConnectDisconBut() { // the connect/disconnect button implements a toggle functionality - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // pgScorpio: Was pClient->IsRunning() Again this is NOT connection started ! { Disconnect(); SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); @@ -1141,7 +1141,7 @@ void CClientDlg::OnTimerCheckAudioDeviceOk() // timeout to check if a valid device is selected and if we do not have // fundamental settings errors (in which case the GUI would only show that // it is trying to connect the server which does not help to solve the problem (#129)) - if ( !pClient->IsCallbackEntered() ) + if ( !pClient->SoundIsRunning() ) // ---> pgScorpio Was pClient->IsCallbackEntered() { QMessageBox::warning ( this, APP_NAME, @@ -1154,10 +1154,11 @@ void CClientDlg::OnTimerDetectFeedback() { bDetectFeedback = false; } void CClientDlg::OnSoundDeviceChanged ( QString strError ) { - if ( !strError.isEmpty() ) + if ( !strError + .isEmpty() ) // ---> pgScorpio: This check should already be done in CClient ! but currently the Disconnect code is at the wrong place. { // the sound device setup has a problem, disconnect any active connection - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // ---> pgScorpio: Was pClient->IsRunning(), Again this is NOT connection started !() ) { Disconnect(); } @@ -1190,6 +1191,8 @@ void CClientDlg::OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, int void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ) { + // ---> pgScorpio: This code does not belong here but in CClient !! + // set address and check if address is valid if ( pClient->SetServerAddr ( strSelectedAddress ) ) { @@ -1197,9 +1200,9 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str // running state but show error message try { - if ( !pClient->IsRunning() ) + if ( !pClient->SoundIsStarted() ) // ---> pgScorpio: Again this is NOT connection started !() ) { - pClient->Start(); + pClient->StartConnection(); } } @@ -1210,6 +1213,8 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str return; } + // ---> pgScorpio: This code should be a OnConnecting() slot ! + // hide label connect to server lblConnectToServer->hide(); lbrInputLevelL->setEnabled ( true ); @@ -1238,15 +1243,19 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str void CClientDlg::Disconnect() { + // ---> pgScorpio: This code does not belong here but in CClient !! + // only stop client if currently running, in case we received // the stopped message, the client is already stopped but the // connect/disconnect button and other GUI controls must be // updated - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) // ---> pgScorpio: Again this is NOT connection started !() ) { - pClient->Stop(); + pClient->StopConnection(); } + // ---> pgScorpio: This code should be a OnDisconncted() slot + // change connect button text to "connect" butConnect->setText ( tr ( "C&onnect" ) ); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 78f2353b50..a18663c108 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -1049,7 +1049,7 @@ void CClientSettingsDlg::UpdateDisplay() UpdateJitterBufferFrame(); UpdateSoundCardFrame(); - if ( !pClient->IsRunning() ) + if ( !pClient->SoundIsStarted() ) // pgScorpio: Was !pClient->IsRunning() Again this is NOT a connection status, Should be pClient->IsConnected() { // clear text labels with client parameters lblUpstreamValue->setText ( "---" ); diff --git a/src/settings.cpp b/src/settings.cpp index ab9b038dfd..7b1210223a 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -858,16 +858,16 @@ if ( GetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", static_cas directoryType = static_cast ( iValue ); } else - // clang-format on - if ( GetNumericIniSet ( IniXMLDocument, - "server", - "directorytype", - static_cast ( AT_NONE ), - static_cast ( AT_CUSTOM ), - iValue ) ) - { - directoryType = static_cast ( iValue ); - } + // clang-format on + if ( GetNumericIniSet ( IniXMLDocument, + "server", + "directorytype", + static_cast ( AT_NONE ), + static_cast ( AT_CUSTOM ), + iValue ) ) + { + directoryType = static_cast ( iValue ); + } // clang-format off // TODO compatibility to old version < 3.9.0 From ae07615901028cac011fb6c69fbfa439f62b58c0 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Wed, 6 Apr 2022 17:30:33 +0200 Subject: [PATCH 22/25] rebase Moved Connect/Disconnect code from CClientdlg to CClient. Now using the proper connected checks in several places. Added bDisconnectAndDisable to CChannel. (For a Client now Channel.Disconnect() will block audio data and auto disable the channel on disconnected) --- src/channel.cpp | 29 +++++++- src/channel.h | 3 +- src/client.cpp | 146 +++++++++++++++++++++++--------------- src/client.h | 22 ++---- src/clientdlg.cpp | 141 ++++++++++-------------------------- src/clientdlg.h | 13 ++-- src/clientsettingsdlg.cpp | 21 +++--- src/main.cpp | 33 +++++---- src/settings.cpp | 82 +++++++++++---------- src/util.cpp | 1 + 10 files changed, 238 insertions(+), 253 deletions(-) diff --git a/src/channel.cpp b/src/channel.cpp index a35576a34d..9cd4f57a1e 100644 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -37,6 +37,7 @@ CChannel::CChannel ( const bool bNIsServer ) : bIsEnabled ( false ), bIsServer ( bNIsServer ), bIsIdentified ( false ), + bDisconnectAndDisable ( false ), iAudioFrameSizeSamples ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ), SignalLevelMeter ( false, 0.5 ) // server mode with mono out and faster smoothing { @@ -125,7 +126,8 @@ void CChannel::SetEnable ( const bool bNEnStat ) QMutexLocker locker ( &Mutex ); // set internal parameter - bIsEnabled = bNEnStat; + bIsEnabled = bNEnStat; + bDisconnectAndDisable = false; // The support for the packet sequence number must be reset if the client // disconnects from a server since we do not yet know if the next server we @@ -506,11 +508,20 @@ void CChannel::Disconnect() // we only have to disconnect the channel if it is actually connected if ( IsConnected() ) { + // for a Client we will block further audio data and disable the channel as soon as disconnected; + bDisconnectAndDisable = !bIsServer; + // set time out counter to a small value > 0 so that the next time a // received audio block is queried, the disconnection is performed // (assuming that no audio packet is received in the meantime) iConTimeOut = 1; // a small number > 0 } + else if ( !bIsServer ) + { + bDisconnectAndDisable = false; + bIsEnabled = false; + iConTimeOut = 0; + } } void CChannel::PutProtocolData ( const int iRecCounter, const int iRecID, const CVector& vecbyMesBodyData, const CHostAddress& RecHostAddr ) @@ -534,7 +545,7 @@ EPutDataStat CChannel::PutAudioData ( const CVector& vecbyData, const i // Only process audio data if: // - for client only: the packet comes from the server we want to talk to // - the channel is enabled - if ( ( bIsServer || ( GetAddress() == RecHostAddr ) ) && IsEnabled() ) + if ( ( bIsServer || ( GetAddress() == RecHostAddr ) ) && IsEnabled() && !bDisconnectAndDisable ) { MutexSocketBuf.lock(); { @@ -622,6 +633,12 @@ EGetDataStat CChannel::GetData ( CVector& vecbyData, const int iNumByte eGetStatus = GS_CHAN_NOW_DISCONNECTED; iConTimeOut = 0; // make sure we do not have negative values + if ( bDisconnectAndDisable ) + { + bDisconnectAndDisable = false; + bIsEnabled = false; + } + // reset network transport properties ResetNetworkTransportProperties(); } @@ -643,6 +660,13 @@ EGetDataStat CChannel::GetData ( CVector& vecbyData, const int iNumByte { // channel is disconnected eGetStatus = GS_CHAN_NOT_CONNECTED; + + if ( bDisconnectAndDisable ) + { + bDisconnectAndDisable = false; + bIsEnabled = false; + iConTimeOut = 0; + } } } MutexSocketBuf.unlock(); @@ -652,7 +676,6 @@ EGetDataStat CChannel::GetData ( CVector& vecbyData, const int iNumByte { // reset the protocol Protocol.Reset(); - // emit message emit Disconnected(); } diff --git a/src/channel.h b/src/channel.h index 86791494b3..32463bfe1d 100644 --- a/src/channel.h +++ b/src/channel.h @@ -76,7 +76,7 @@ class CChannel : public QObject void PrepAndSendPacket ( CHighPrioSocket* pSocket, const CVector& vecbyNPacket, const int iNPacketLen ); - void ResetTimeOutCounter() { iConTimeOut = iConTimeOutStartVal; } + void ResetTimeOutCounter() { iConTimeOut = bDisconnectAndDisable ? 1 : iConTimeOutStartVal; } bool IsConnected() const { return iConTimeOut > 0; } void Disconnect(); @@ -216,6 +216,7 @@ void CreateReqChannelLevelListMes() { Protocol.CreateReqChannelLevelListMes(); } bool bIsEnabled; bool bIsServer; bool bIsIdentified; + bool bDisconnectAndDisable; int iNetwFrameSizeFact; int iNetwFrameSize; diff --git a/src/client.cpp b/src/client.cpp index 0323685b22..fc66292586 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -23,11 +23,11 @@ \******************************************************************************/ #include "client.h" +#include /* Implementation *************************************************************/ CClient::CClient ( const quint16 iPortNumber, const quint16 iQosNumber, - const QString& strConnOnStartupAddress, const QString& strMIDISetup, const bool bNoAutoJackConnect, const QString& strNClientName, @@ -181,13 +181,6 @@ CClient::CClient ( const quint16 iPortNumber, // start the socket (it is important to start the socket after all // initializations and connections) Socket.Start(); - - // do an immediate start if a server address is given - if ( !strConnOnStartupAddress.isEmpty() ) - { - SetServerAddr ( strConnOnStartupAddress ); - StartConnection(); // ---> pgScorpio: Was Start() - } } CClient::~CClient() @@ -298,8 +291,7 @@ void CClient::CreateServerJitterBufferMessage() void CClient::OnCLPingReceived ( CHostAddress InetAddr, int iMs ) { // make sure we are running and the server address is correct - if ( SoundIsStarted() && // ---> pgScorpio: Does NOT mean we are Connected ! - ( InetAddr == Channel.GetAddress() ) ) + if ( Channel.IsEnabled() && ( InetAddr == Channel.GetAddress() ) ) { // take care of wrap arounds (if wrapping, do not use result) const int iCurDiff = EvaluatePingMessage ( iMs ); @@ -436,22 +428,6 @@ void CClient::StartDelayTimer() } } -bool CClient::SetServerAddr ( QString strNAddr ) -{ - CHostAddress HostAddress; - if ( NetworkUtil().ParseNetworkAddress ( strNAddr, HostAddress, bEnableIPv6 ) ) - { - // apply address to the channel - Channel.SetAddress ( HostAddress ); - - return true; - } - else - { - return false; // invalid address - } -} - bool CClient::GetAndResetbJitterBufferOKFlag() { // get the socket buffer put status flag and reset it @@ -587,9 +563,12 @@ QString CClient::SetSndCrdDev ( const QString strNewDev ) // in case of an error inform the GUI about it if ( !strError.isEmpty() ) { - emit SoundDeviceChanged ( strError ); + QMessageBox::critical ( 0, APP_NAME, strError ); + Disconnect(); } + emit SoundDeviceChanged(); + return strError; } @@ -716,8 +695,13 @@ void CClient::OnSndCrdReinitRequest ( int iSndCrdResetType ) } MutexDriverReinit.unlock(); + if ( !strError.isEmpty() ) + { + QMessageBox::critical ( 0, APP_NAME, strError ); + } + // inform GUI about the sound card device change - emit SoundDeviceChanged ( strError ); + emit SoundDeviceChanged(); } void CClient::OnHandledSignal ( int sigNum ) @@ -732,10 +716,7 @@ void CClient::OnHandledSignal ( int sigNum ) case SIGINT: case SIGTERM: // if connected, terminate connection (needed for headless mode) - if ( IsRunning() ) - { - Stop(); - } + Disconnect(); // this should trigger OnAboutToQuit QCoreApplication::instance()->exit(); @@ -820,39 +801,88 @@ void CClient::OnClientIDReceived ( int iChanID ) emit ClientIDReceived ( iChanID ); } -void CClient::StartConnection() // ---> pgScorpio: Was Start() +bool CClient::Connect ( QString strServerAddress, QString strServerName ) { - // init object - Init(); + if ( !Channel.IsEnabled() ) + { + CHostAddress HostAddress; + + if ( NetworkUtil().ParseNetworkAddress ( strServerAddress, HostAddress, bEnableIPv6 ) ) + { + // init object + Init(); + + // apply address to the channel + Channel.SetAddress ( HostAddress ); + + // enable channel + Channel.SetEnable ( true ); - // enable channel - Channel.SetEnable ( true ); + // start audio interface + Sound.Start(); + + // Notify ClientDlg + emit Connecting ( strServerName ); - // start audio interface - Sound.Start(); // ---> pgScorpio: There is NO Check if Sound.Start() actually is successfull !! - // ---> pgScorpio: If Sound.Start fails here GUI (clientdlg) and Channel are definitely out of sync ! + return true; + } + } + + return false; } -void CClient::StopConnection() +bool CClient::Disconnect() { - // stop audio interface - Sound.Stop(); - - // disable channel - Channel.SetEnable ( false ); - - // wait for approx. 100 ms to make sure no audio packet is still in the - // network queue causing the channel to be reconnected right after having - // received the disconnect message (seems not to gain much, disconnect is - // still not working reliably) - QTime DieTime = QTime::currentTime().addMSecs ( 100 ); - while ( QTime::currentTime() < DieTime ) + if ( Channel.IsEnabled() ) { - // exclude user input events because if we use AllEvents, it happens - // that if the user initiates a connection and disconnection quickly - // (e.g. quickly pressing enter five times), the software can get into - // an unknown state - QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); + // start disconnection + Channel.Disconnect(); + + // Channel.Disconnect() should now automatically disable Channel as soon as disconnected. + // Note that this only works if Sound is Active ! + + QTime DieTime = QTime::currentTime().addMSecs ( 500 ); + while ( ( QTime::currentTime() < DieTime ) && Channel.IsEnabled() ) + { + // exclude user input events because if we use AllEvents, it happens + // that if the user initiates a connection and disconnection quickly + // (e.g. quickly pressing enter five times), the software can get into + // an unknown state + QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 ); + } + + // Now stop the audio interface + Sound.Stop(); + + // in case we timed out, log warning and make sure Channel is disabled + if ( Channel.IsEnabled() ) + { + Channel.SetEnable ( false ); + } + + // Send disconnect message to server (Since we disable our protocol + // receive mechanism with the next command, we do not evaluate any + // respond from the server, therefore we just hope that the message + // gets its way to the server, if not, the old behaviour time-out + // disconnects the connection anyway). + ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); + + // reset current signal level and LEDs + bJitterBufferOK = true; + SignalLevelMeter.Reset(); + + emit Disconnected(); + + return true; + } + else + { + // make sure sound is stopped too + Sound.Stop(); + + emit Disconnected(); + + return false; } // Send disconnect message to server (Since we disable our protocol diff --git a/src/client.h b/src/client.h index 5a5a2bc788..d2b750c0e6 100644 --- a/src/client.h +++ b/src/client.h @@ -110,7 +110,6 @@ class CClient : public QObject public: CClient ( const quint16 iPortNumber, const quint16 iQosNumber, - const QString& strConnOnStartupAddress, const QString& strMIDISetup, const bool bNoAutoJackConnect, const QString& strNClientName, @@ -119,27 +118,17 @@ class CClient : public QObject virtual ~CClient(); - void StartConnection(); // ---> pgScorpio: Was Start(), but Start what ? ( Should be Connect() ?) - void StopConnection(); // ---> pgScorpio: Was Stop(), but Stop what ? ( Should be Disconnect() ?) - bool SoundIsStarted() // ---> pgScorpio: Was IsRunning(), but what is running ?? - { - return Sound.IsRunning(); // ---> pgScorpio: Even this name is incorrect ! - // ---> pgScorpio: Sound.bRun is set when Sound is started, but this does not guarantee sound is actually running - } + bool Connect ( QString strServerAddress, QString strServerName ); + bool Disconnect(); - bool SoundIsRunning() const - { - return Sound.IsCallbackEntered(); - } // ---> pgScorpio: was IsCallbackEntered() but this is the actuall SoundIsRunning() ! - bool SetServerAddr ( QString strNAddr ); + bool SoundIsRunning() const { return Sound.IsCallbackEntered(); } // For OnTimerCheckAudioDeviceOk only ! + bool IsConnected() { return Channel.IsConnected(); } double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); } double GetLevelForMeterdBRight() { return SignalLevelMeter.GetLevelForMeterdBRight(); } bool GetAndResetbJitterBufferOKFlag(); - bool IsConnected() { return Channel.IsConnected(); } - EGUIDesign GetGUIDesign() const { return eGUIDesign; } void SetGUIDesign ( const EGUIDesign eNGD ) { eGUIDesign = eNGD; } @@ -433,8 +422,9 @@ protected slots: void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); + void Connecting ( QString strServerName ); void Disconnected(); - void SoundDeviceChanged ( QString strError ); + void SoundDeviceChanged(); void ControllerInFaderLevel ( int iChannelIdx, int iValue ); void ControllerInPanValue ( int iChannelIdx, int iValue ); void ControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index e8d8163e67..ee88fa9d72 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -27,7 +27,6 @@ /* Implementation *************************************************************/ CClientDlg::CClientDlg ( CClient* pNCliP, CClientSettings* pNSetP, - const QString& strConnOnStartupAddress, const QString& strMIDISetup, const bool bNewShowComplRegConnList, const bool bShowAnalyzerConsole, @@ -272,14 +271,6 @@ CClientDlg::CClientDlg ( CClient* pNCliP, TimerCheckAudioDeviceOk.setSingleShot ( true ); // only check once after connection TimerDetectFeedback.setSingleShot ( true ); - // Connect on startup ------------------------------------------------------ - if ( !strConnOnStartupAddress.isEmpty() ) - { - // initiate connection (always show the address in the mixer board - // (no alias)) - Connect ( strConnOnStartupAddress, strConnOnStartupAddress ); - } - // File menu -------------------------------------------------------------- QMenu* pFileMenu = new QMenu ( tr ( "&File" ), this ); @@ -473,7 +464,9 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // other QObject::connect ( pClient, &CClient::ConClientListMesReceived, this, &CClientDlg::OnConClientListMesReceived ); - QObject::connect ( pClient, &CClient::Disconnected, this, &CClientDlg::OnDisconnected ); + QObject::connect ( pClient, &CClient::Connecting, this, &CClientDlg::OnConnect ); + + QObject::connect ( pClient, &CClient::Disconnected, this, &CClientDlg::OnDisconnect ); QObject::connect ( pClient, &CClient::ChatTextReceived, this, &CClientDlg::OnChatTextReceived ); @@ -609,10 +602,7 @@ void CClientDlg::closeEvent ( QCloseEvent* Event ) AnalyzerConsole.close(); // if connected, terminate connection - if ( pClient->SoundIsStarted() ) // ---> pgScorpio: This does NOT mean the connection is started ! - { - pClient->StopConnection(); // ---> pgScorpio: Was pClient->Stop() - } + pClient->Disconnect(); // make sure all current fader settings are applied to the settings struct MainMixerBoard->StoreAllFaderSettings(); @@ -730,16 +720,12 @@ void CClientDlg::OnConnectDlgAccepted() } } - // first check if we are already connected, if this is the case we have to - // disconnect the old server first - if ( pClient->SoundIsStarted() ) // pgScorpio: Was pClient->IsRunning() Again this is NOT connection started ! + // initiate connection + if ( pClient->Connect ( strSelectedAddress, strMixerBoardLabel ) ) { - Disconnect(); + OnConnect ( strMixerBoardLabel ); } - // initiate connection - Connect ( strSelectedAddress, strMixerBoardLabel ); - // reset flag bConnectDlgWasShown = false; } @@ -748,10 +734,9 @@ void CClientDlg::OnConnectDlgAccepted() void CClientDlg::OnConnectDisconBut() { // the connect/disconnect button implements a toggle functionality - if ( pClient->SoundIsStarted() ) // pgScorpio: Was pClient->IsRunning() Again this is NOT connection started ! + if ( pClient->Disconnect() ) { - Disconnect(); - SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); + // Client was Connected, now disconnected } else { @@ -835,7 +820,7 @@ void CClientDlg::OnChatTextReceived ( QString strChatText ) // always when a new message arrives since this is annoying. ShowChatWindow ( ( strChatText.indexOf ( WELCOME_MESSAGE_PREFIX ) == 0 ) ); - UpdateDisplay(); + UpdateSettingsAndChatButtons(); } void CClientDlg::OnLicenceRequired ( ELicenceType eLicenceType ) @@ -853,7 +838,7 @@ void CClientDlg::OnLicenceRequired ( ELicenceType eLicenceType ) // disconnect from that server. if ( !LicenceDlg.exec() ) { - Disconnect(); + pClient->Disconnect(); } // unmute the client output stream if local mute button is not pressed @@ -991,7 +976,7 @@ void CClientDlg::ShowChatWindow ( const bool bForceRaise ) ChatDlg.activateWindow(); } - UpdateDisplay(); + UpdateSettingsAndChatButtons(); } void CClientDlg::ShowAnalyzerConsole() @@ -1152,21 +1137,8 @@ void CClientDlg::OnTimerCheckAudioDeviceOk() void CClientDlg::OnTimerDetectFeedback() { bDetectFeedback = false; } -void CClientDlg::OnSoundDeviceChanged ( QString strError ) +void CClientDlg::OnSoundDeviceChanged() { - if ( !strError - .isEmpty() ) // ---> pgScorpio: This check should already be done in CClient ! but currently the Disconnect code is at the wrong place. - { - // the sound device setup has a problem, disconnect any active connection - if ( pClient->SoundIsStarted() ) // ---> pgScorpio: Was pClient->IsRunning(), Again this is NOT connection started !() ) - { - Disconnect(); - } - - // show the error message of the device setup - QMessageBox::critical ( this, APP_NAME, strError, tr ( "Ok" ), nullptr ); - } - // if the check audio device timer is running, it must be restarted on a device change if ( TimerCheckAudioDeviceOk.isActive() ) { @@ -1189,73 +1161,36 @@ void CClientDlg::OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, int ConnectDlg.SetPingTimeAndNumClientsResult ( InetAddr, iPingTime, iNumClients ); } -void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ) +void CClientDlg::OnConnect ( QString strServerName ) { - // ---> pgScorpio: This code does not belong here but in CClient !! - // set address and check if address is valid - if ( pClient->SetServerAddr ( strSelectedAddress ) ) - { - // try to start client, if error occurred, do not go in - // running state but show error message - try - { - if ( !pClient->SoundIsStarted() ) // ---> pgScorpio: Again this is NOT connection started !() ) - { - pClient->StartConnection(); - } - } - - catch ( const CGenErr& generr ) - { - // show error message and return the function - QMessageBox::critical ( this, APP_NAME, generr.GetErrorText(), "Close", nullptr ); - return; - } + // hide label connect to server + lblConnectToServer->hide(); + lbrInputLevelL->setEnabled ( true ); + lbrInputLevelR->setEnabled ( true ); - // ---> pgScorpio: This code should be a OnConnecting() slot ! + // change connect button text to "disconnect" + butConnect->setText ( tr ( "&Disconnect" ) ); - // hide label connect to server - lblConnectToServer->hide(); - lbrInputLevelL->setEnabled ( true ); - lbrInputLevelR->setEnabled ( true ); + // set server name in audio mixer group box title + MainMixerBoard->SetServerName ( strServerName ); - // change connect button text to "disconnect" - butConnect->setText ( tr ( "&Disconnect" ) ); + // start timer for level meter bar and ping time measurement + TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); + TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); + TimerPing.start ( PING_UPDATE_TIME_MS ); + TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer - // set server name in audio mixer group box title - MainMixerBoard->SetServerName ( strMixerBoardLabel ); - - // start timer for level meter bar and ping time measurement - TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); - TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); - TimerPing.start ( PING_UPDATE_TIME_MS ); - TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer - - // audio feedback detection - if ( pSettings->bEnableFeedbackDetection ) - { - TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer - bDetectFeedback = true; - } + // audio feedback detection + if ( pSettings->bEnableFeedbackDetection ) + { + TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer + bDetectFeedback = true; } } -void CClientDlg::Disconnect() +void CClientDlg::OnDisconnect() { - // ---> pgScorpio: This code does not belong here but in CClient !! - - // only stop client if currently running, in case we received - // the stopped message, the client is already stopped but the - // connect/disconnect button and other GUI controls must be - // updated - if ( pClient->SoundIsStarted() ) // ---> pgScorpio: Again this is NOT connection started !() ) - { - pClient->StopConnection(); - } - - // ---> pgScorpio: This code should be a OnDisconncted() slot - // change connect button text to "connect" butConnect->setText ( tr ( "C&onnect" ) ); @@ -1279,11 +1214,7 @@ void CClientDlg::Disconnect() TimerDetectFeedback.stop(); bDetectFeedback = false; - // clang-format off -// TODO is this still required??? -// immediately update status bar -OnTimerStatus(); - // clang-format on + UpdateSettingsAndChatButtons(); // reset LEDs ledBuffers->Reset(); @@ -1297,9 +1228,11 @@ OnTimerStatus(); // clear mixer board (remove all faders) MainMixerBoard->HideAll(); + + SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); } -void CClientDlg::UpdateDisplay() +void CClientDlg::UpdateSettingsAndChatButtons() { // update settings/chat buttons (do not fire signals since it is an update) if ( chbSettings->isChecked() && !ClientSettingsDlg.isVisible() ) diff --git a/src/clientdlg.h b/src/clientdlg.h index bbcc604766..79d8190dea 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -76,7 +76,6 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase public: CClientDlg ( CClient* pNCliP, CClientSettings* pNSetP, - const QString& strConnOnStartupAddress, const QString& strMIDISetup, const bool bNewShowComplRegConnList, const bool bShowAnalyzerConsole, @@ -94,8 +93,6 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase void ShowAnalyzerConsole(); void UpdateAudioFaderSlider(); void UpdateRevSelection(); - void Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ); - void Disconnect(); void ManageDragNDrop ( QDropEvent* Event, const bool bCheckAccept ); void SetPingTime ( const int iPingTime, const int iOverallDelayMs, const CMultiColorLED::ELightColor eOverallDelayLEDColor ); @@ -119,7 +116,7 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase virtual void closeEvent ( QCloseEvent* Event ); virtual void dragEnterEvent ( QDragEnterEvent* Event ) { ManageDragNDrop ( Event, true ); } virtual void dropEvent ( QDropEvent* Event ) { ManageDragNDrop ( Event, false ); } - void UpdateDisplay(); + void UpdateSettingsAndChatButtons(); CClientSettingsDlg ClientSettingsDlg; CChatDlg ChatDlg; @@ -127,13 +124,16 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase CAnalyzerConsole AnalyzerConsole; public slots: + void OnConnect ( QString strServerName ); + void OnDisconnect(); + void OnConnectDisconBut(); void OnTimerSigMet(); void OnTimerBuffersLED(); void OnTimerCheckAudioDeviceOk(); void OnTimerDetectFeedback(); - void OnTimerStatus() { UpdateDisplay(); } + void OnTimerStatus() { UpdateSettingsAndChatButtons(); } void OnTimerPing(); void OnPingTimeResult ( int iPingTime ); @@ -191,7 +191,7 @@ public slots: void OnConClientListMesReceived ( CVector vecChanInfo ); void OnChatTextReceived ( QString strChatText ); void OnLicenceRequired ( ELicenceType eLicenceType ); - void OnSoundDeviceChanged ( QString strError ); + void OnSoundDeviceChanged(); void OnChangeChanGain ( int iId, float fGain, bool bIsMyOwnFader ) { pClient->SetRemoteChanGain ( iId, fGain, bIsMyOwnFader ); } @@ -232,7 +232,6 @@ public slots: } void OnConnectDlgAccepted(); - void OnDisconnected() { Disconnect(); } void OnGUIDesignChanged(); void OnMeterStyleChanged(); void OnRecorderStateReceived ( ERecorderState eRecorderState ); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index a18663c108..77e19ab82b 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -1039,8 +1039,16 @@ void CClientSettingsDlg::OnSndCrdBufferDelayButtonGroupClicked ( QAbstractButton void CClientSettingsDlg::UpdateUploadRate() { // update upstream rate information label - lblUpstreamValue->setText ( QString().setNum ( pClient->GetUploadRateKbps() ) ); - lblUpstreamUnit->setText ( "kbps" ); + if ( pClient->IsConnected() ) + { + lblUpstreamValue->setText ( QString().setNum ( pClient->GetUploadRateKbps() ) ); + lblUpstreamUnit->setText ( "kbps" ); + } + else + { + lblUpstreamValue->setText ( "---" ); + lblUpstreamUnit->setText ( "" ); + } } void CClientSettingsDlg::UpdateDisplay() @@ -1048,13 +1056,8 @@ void CClientSettingsDlg::UpdateDisplay() // update slider controls (settings might have been changed) UpdateJitterBufferFrame(); UpdateSoundCardFrame(); - - if ( !pClient->SoundIsStarted() ) // pgScorpio: Was !pClient->IsRunning() Again this is NOT a connection status, Should be pClient->IsConnected() - { - // clear text labels with client parameters - lblUpstreamValue->setText ( "---" ); - lblUpstreamUnit->setText ( "" ); - } + // update upstream rate information label + UpdateUploadRate(); } void CClientSettingsDlg::UpdateDirectoryServerComboBox() diff --git a/src/main.cpp b/src/main.cpp index 0bc792376b..e453624579 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -862,14 +862,7 @@ int main ( int argc, char** argv ) { // Client: // actual client object - CClient Client ( iPortNumber, - iQosNumber, - strConnOnStartupAddress, - strMIDISetup, - bNoAutoJackConnect, - strClientName, - bEnableIPv6, - bMuteMeInPersonalMix ); + CClient Client ( iPortNumber, iQosNumber, strMIDISetup, bNoAutoJackConnect, strClientName, bEnableIPv6, bMuteMeInPersonalMix ); // load settings from init-file (command line options override) CClientSettings Settings ( &Client, strIniFileName ); @@ -891,18 +884,18 @@ int main ( int argc, char** argv ) if ( bUseGUI ) { // GUI object - CClientDlg ClientDlg ( &Client, - &Settings, - strConnOnStartupAddress, - strMIDISetup, - bShowComplRegConnList, - bShowAnalyzerConsole, - bMuteStream, - bEnableIPv6, - nullptr ); + CClientDlg + ClientDlg ( &Client, &Settings, strMIDISetup, bShowComplRegConnList, bShowAnalyzerConsole, bMuteStream, bEnableIPv6, nullptr ); // show dialog ClientDlg.show(); + + // Connect on startup ------------------------------------------------------ + if ( !strConnOnStartupAddress.isEmpty() ) + { + Client.Connect ( strConnOnStartupAddress, strConnOnStartupAddress ); + } + pApp->exec(); } else @@ -911,6 +904,12 @@ int main ( int argc, char** argv ) // only start application without using the GUI qInfo() << qUtf8Printable ( GetVersionAndNameStr ( false ) ); + // Connect on startup ------------------------------------------------------ + if ( !strConnOnStartupAddress.isEmpty() ) + { + Client.Connect ( strConnOnStartupAddress, strConnOnStartupAddress ); + } + pApp->exec(); } } diff --git a/src/settings.cpp b/src/settings.cpp index 7b1210223a..50290fab41 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -458,14 +458,14 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // custom directories // clang-format off // TODO compatibility to old version (< 3.6.1) -QString strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", "centralservaddr", "" ); // clang-format on + QString strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", "centralservaddr", "" ); for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) { // clang-format off // TODO compatibility to old version (< 3.8.2) -strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", QString ( "centralservaddr%1" ).arg ( iIdx ), strDirectoryAddress ); // clang-format on + strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", QString ( "centralservaddr%1" ).arg ( iIdx ), strDirectoryAddress ); vstrDirectoryAddress[iIdx] = GetIniSetting ( IniXMLDocument, "client", QString ( "directoryaddress%1" ).arg ( iIdx ), strDirectoryAddress ); strDirectoryAddress = ""; } @@ -473,17 +473,19 @@ strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", QString ( "centr // directory type // clang-format off // TODO compatibility to old version (<3.4.7) -// only the case that "centralservaddr" was set in old ini must be considered -if ( !vstrDirectoryAddress[0].isEmpty() && GetFlagIniSet ( IniXMLDocument, "client", "defcentservaddr", bValue ) && !bValue ) -{ - eDirectoryType = AT_CUSTOM; -} + // clang-format on + // only the case that "centralservaddr" was set in old ini must be considered + if ( !vstrDirectoryAddress[0].isEmpty() && GetFlagIniSet ( IniXMLDocument, "client", "defcentservaddr", bValue ) && !bValue ) + { + eDirectoryType = AT_CUSTOM; + } + // clang-format off // TODO compatibility to old version (< 3.8.2) -else if ( GetNumericIniSet ( IniXMLDocument, "client", "centservaddrtype", 0, static_cast ( AT_CUSTOM ), iValue ) ) -{ - eDirectoryType = static_cast ( iValue ); -} // clang-format on + else if ( GetNumericIniSet ( IniXMLDocument, "client", "centservaddrtype", 0, static_cast ( AT_CUSTOM ), iValue ) ) + { + eDirectoryType = static_cast ( iValue ); + } else if ( GetNumericIniSet ( IniXMLDocument, "client", "directorytype", 0, static_cast ( AT_CUSTOM ), iValue ) ) { eDirectoryType = static_cast ( iValue ); @@ -822,7 +824,7 @@ void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, QString directoryAddress = ""; // clang-format off // TODO compatibility to old version < 3.8.2 -directoryAddress = GetIniSetting ( IniXMLDocument, "server", "centralservaddr", directoryAddress ); +// directoryAddress = GetIniSetting ( IniXMLDocument, "server", "centralservaddr", directoryAddress ); // clang-format on directoryAddress = GetIniSetting ( IniXMLDocument, "server", "directoryaddress", directoryAddress ); @@ -843,40 +845,44 @@ directoryAddress = GetIniSetting ( IniXMLDocument, "server", "centralservaddr", { // clang-format off // TODO compatibility to old version < 3.4.7 -if ( GetFlagIniSet ( IniXMLDocument, "server", "defcentservaddr", bValue ) ) -{ - directoryType = bValue ? AT_DEFAULT : AT_CUSTOM; -} -else - // clang-format on + // clang-format on + if ( GetFlagIniSet ( IniXMLDocument, "server", "defcentservaddr", bValue ) ) + { + directoryType = bValue ? AT_DEFAULT : AT_CUSTOM; + } + else // if "directorytype" itself is set, use it (note "AT_NONE", "AT_DEFAULT" and "AT_CUSTOM" are min/max directory type here) // clang-format off // TODO compatibility to old version < 3.8.2 -if ( GetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", static_cast ( AT_DEFAULT ), static_cast ( AT_CUSTOM ), iValue ) ) -{ - directoryType = static_cast ( iValue ); -} -else - // clang-format on - if ( GetNumericIniSet ( IniXMLDocument, - "server", - "directorytype", - static_cast ( AT_NONE ), - static_cast ( AT_CUSTOM ), - iValue ) ) - { - directoryType = static_cast ( iValue ); - } + // clang-format on + if ( GetNumericIniSet ( IniXMLDocument, + "server", + "centservaddrtype", + static_cast ( AT_DEFAULT ), + static_cast ( AT_CUSTOM ), + iValue ) ) + { + directoryType = static_cast ( iValue ); + } + else if ( GetNumericIniSet ( IniXMLDocument, + "server", + "directorytype", + static_cast ( AT_NONE ), + static_cast ( AT_CUSTOM ), + iValue ) ) + { + directoryType = static_cast ( iValue ); + } // clang-format off // TODO compatibility to old version < 3.9.0 -// override type to AT_NONE if servlistenabled exists and is false -if ( GetFlagIniSet ( IniXMLDocument, "server", "servlistenabled", bValue ) && !bValue ) -{ - directoryType = AT_NONE; -} // clang-format on + // override type to AT_NONE if servlistenabled exists and is false + if ( GetFlagIniSet ( IniXMLDocument, "server", "servlistenabled", bValue ) && !bValue ) + { + directoryType = AT_NONE; + } } pServer->SetDirectoryType ( directoryType ); diff --git a/src/util.cpp b/src/util.cpp index d2f7d6f78b..820c758de8 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -495,6 +495,7 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : CBaseDlg ( parent ) "

RobyDati (RobyDati)

" "

Rob-NY (Rob-NY)

" "

Thai Pangsakulyanont (dtinth)

" + "

Peter Goderie (pgScorpio)

" "
" + tr ( "For details on the contributions check out the %1" ) .arg ( "" + tr ( "Github Contributors list" ) + "." ) ); From 2b6641401fbc194eddac6035ed7e52ef2c6f77b8 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Sat, 26 Mar 2022 20:27:58 +0100 Subject: [PATCH 23/25] rebase --- src/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main.cpp b/src/main.cpp index e453624579..ce1085a692 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -904,6 +904,9 @@ int main ( int argc, char** argv ) // only start application without using the GUI qInfo() << qUtf8Printable ( GetVersionAndNameStr ( false ) ); + // initialise message boxes + CMsgBoxes::init ( NULL, strClientName.isEmpty() ? QString ( APP_NAME ) : QString ( APP_NAME ) + " " + strClientName ); + // Connect on startup ------------------------------------------------------ if ( !strConnOnStartupAddress.isEmpty() ) { From 6ea6c19afbfb5304421b96321341c973658a634c Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Wed, 6 Apr 2022 17:47:17 +0200 Subject: [PATCH 24/25] fix rebase error --- src/client.cpp | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 32f1fc4b1f..13dfa6f2cc 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -884,32 +884,6 @@ bool CClient::Disconnect() return false; } - - // Send disconnect message to server (Since we disable our protocol - // receive mechanism with the next command, we do not evaluate any - // respond from the server, therefore we just hope that the message - // gets its way to the server, if not, the old behaviour time-out - // disconnects the connection anyway). - ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); - - // gets its way to the server, if not, the old behaviour time-out - // reset current signal level and LEDs - bJitterBufferOK = true; - SignalLevelMeter.Reset(); - - emit Disconnected(); - - return true; - } - else - { - // make sure sound is stopped too - Sound.Stop(); - - emit Disconnected(); - - return false; - } } void CClient::Init() From 20fe0b0e97538ab0766678842cfb85a625e952d0 Mon Sep 17 00:00:00 2001 From: pgScorpio Date: Fri, 8 Apr 2022 09:42:07 +0200 Subject: [PATCH 25/25] Fixing autobuild issues clang-format settings.cpp invalid include client.cpp --- src/client.cpp | 1 - src/settings.cpp | 44 +++++++++++++++++++++----------------------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 13dfa6f2cc..37a6000d73 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -23,7 +23,6 @@ \******************************************************************************/ #include "client.h" -#include /* Implementation *************************************************************/ CClient::CClient ( const quint16 iPortNumber, diff --git a/src/settings.cpp b/src/settings.cpp index 688a2416c8..7bb2282809 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -848,30 +848,28 @@ void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, { directoryType = bValue ? AT_DEFAULT : AT_CUSTOM; } - else - - // if "directorytype" itself is set, use it (note "AT_NONE", "AT_DEFAULT" and "AT_CUSTOM" are min/max directory type here) - // clang-format off + // if "directorytype" itself is set, use it (note "AT_NONE", "AT_DEFAULT" and "AT_CUSTOM" are min/max directory type here) + // clang-format off // TODO compatibility to old version < 3.8.2 - // clang-format on - if ( GetNumericIniSet ( IniXMLDocument, - "server", - "centservaddrtype", - static_cast ( AT_DEFAULT ), - static_cast ( AT_CUSTOM ), - iValue ) ) - { - directoryType = static_cast ( iValue ); - } - else if ( GetNumericIniSet ( IniXMLDocument, - "server", - "directorytype", - static_cast ( AT_NONE ), - static_cast ( AT_CUSTOM ), - iValue ) ) - { - directoryType = static_cast ( iValue ); - } + // clang-format on + else if ( GetNumericIniSet ( IniXMLDocument, + "server", + "centservaddrtype", + static_cast ( AT_DEFAULT ), + static_cast ( AT_CUSTOM ), + iValue ) ) + { + directoryType = static_cast ( iValue ); + } + else if ( GetNumericIniSet ( IniXMLDocument, + "server", + "directorytype", + static_cast ( AT_NONE ), + static_cast ( AT_CUSTOM ), + iValue ) ) + { + directoryType = static_cast ( iValue ); + } // clang-format off // TODO compatibility to old version < 3.9.0