diff --git a/src/channel.cpp b/src/channel.cpp index 53f91766a8..04febb9e49 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 @@ -511,6 +513,24 @@ void CChannel::Disconnect() // (assuming that no audio packet is received in the meantime) iConTimeOut = 1; // a small number > 0 } + + if ( !bIsServer ) + { + if ( IsConnected() ) + { + // for a Client, block further audio data and disable the channel as soon as Disconnect() is called + // TODO: Add reasoning from #2550 + bDisconnectAndDisable = true; + } + else + { + // For disconnected clients set defaults + + bDisconnectAndDisable = false; + bIsEnabled = false; + iConTimeOut = 0; + } + } } void CChannel::PutProtocolData ( const int iRecCounter, const int iRecID, const CVector& vecbyMesBodyData, const CHostAddress& RecHostAddr ) @@ -532,9 +552,9 @@ EPutDataStat CChannel::PutAudioData ( const CVector& vecbyData, const i EPutDataStat eRet = PS_GEN_ERROR; // Only process audio data if: - // - for client only: the packet comes from the server we want to talk to + // - for client only: the packet comes from the server we want to talk to and we aren't disconnecting // - the channel is enabled - if ( ( bIsServer || ( GetAddress() == RecHostAddr ) ) && IsEnabled() ) + if ( ( bIsServer || ( !bIsServer && GetAddress() == RecHostAddr && !bDisconnectAndDisable ) ) && IsEnabled() ) { MutexSocketBuf.lock(); { @@ -622,6 +642,11 @@ 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 && !bIsServer ) + { + bDisconnectAndDisable = false; + bIsEnabled = false; + } // reset network transport properties ResetNetworkTransportProperties(); } @@ -643,6 +668,13 @@ EGetDataStat CChannel::GetData ( CVector& vecbyData, const int iNumByte { // channel is disconnected eGetStatus = GS_CHAN_NOT_CONNECTED; + + if ( bDisconnectAndDisable && !bIsServer ) + { + bDisconnectAndDisable = false; + bIsEnabled = false; + iConTimeOut = 0; + } } } MutexSocketBuf.unlock(); diff --git a/src/channel.h b/src/channel.h index 9f70bfee6f..90bd95f244 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 && !bIsServer ) ? 1 : iConTimeOutStartVal; } bool IsConnected() const { return iConTimeOut > 0; } void Disconnect(); @@ -216,6 +216,7 @@ class CChannel : public QObject bool bIsEnabled; bool bIsServer; bool bIsIdentified; + bool bDisconnectAndDisable; int iNetwFrameSizeFact; int iNetwFrameSize; diff --git a/src/client.cpp b/src/client.cpp index d3d2c51c9a..6addfe5cbd 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, @@ -184,13 +183,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 ); - Start(); - } } CClient::~CClient() @@ -337,7 +329,7 @@ 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 ( Channel.IsEnabled() && ( InetAddr == Channel.GetAddress() ) ) { // take care of wrap arounds (if wrapping, do not use result) const int iCurDiff = EvaluatePingMessage ( iMs ); @@ -773,11 +765,8 @@ void CClient::OnHandledSignal ( int sigNum ) { case SIGINT: case SIGTERM: - // if connected, terminate connection (needed for headless mode) - if ( IsRunning() ) - { - Stop(); - } + // if connected, terminate connection + Disconnect(); // this should trigger OnAboutToQuit QCoreApplication::instance()->exit(); @@ -876,18 +865,13 @@ void CClient::Start() void CClient::Stop() { - // stop audio interface - Sound.Stop(); - - // disable channel - Channel.SetEnable ( false ); + // start disconnection + // Channel.Disconnect() should automatically disable Channel as soon as disconnected. + // Note that this only works if sound is active! + Channel.Disconnect(); - // 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 ) + 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 @@ -896,6 +880,19 @@ void CClient::Stop() 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() ) + { + //### TODO: BEGIN ###// + // Add error logging + //### TODO: END ###// + + 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 @@ -908,6 +905,46 @@ void CClient::Stop() SignalLevelMeter.Reset(); } +bool CClient::Connect ( QString strServerAddress, QString strServerName ) +{ + if ( !Channel.IsEnabled() ) + { + // Set server address and connect if valid address was supplied + if ( SetServerAddr ( strServerAddress ) ) + { + + Start(); + + emit Connecting ( strServerName ); + + return true; + } + } + + return false; +} + +bool CClient::Disconnect() +{ + if ( Channel.IsEnabled() ) + { + Stop(); + + emit Disconnected(); + + return true; + } + else + { + // make sure sound is stopped too + Sound.Stop(); + + emit Disconnected(); + + return false; + } +} + void CClient::Init() { // check if possible frame size factors are supported diff --git a/src/client.h b/src/client.h index 1fc33d8430..38626cf2e7 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, @@ -121,8 +120,11 @@ class CClient : public QObject void Start(); void Stop(); + bool Connect ( QString strServerAddress, QString strServerName ); + bool Disconnect(); + bool IsRunning() { return Sound.IsRunning(); } - bool IsCallbackEntered() const { return Sound.IsCallbackEntered(); } + bool IsCallbackEntered() const { return Sound.IsCallbackEntered(); } // For OnTimerCheckAudioDeviceOk only bool SetServerAddr ( QString strNAddr ); double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); } @@ -427,6 +429,7 @@ protected slots: void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); + void Connecting ( QString strServerName ); void Disconnected(); void SoundDeviceChanged ( QString strError ); void ControllerInFaderLevel ( int iChannelIdx, int iValue ); diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 99b9fa6433..d09984e478 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 ); @@ -608,11 +601,7 @@ void CClientDlg::closeEvent ( QCloseEvent* Event ) ConnectDlg.close(); AnalyzerConsole.close(); - // if connected, terminate connection - if ( pClient->IsRunning() ) - { - pClient->Stop(); - } + pClient->Disconnect(); // make sure all current fader settings are applied to the settings struct MainMixerBoard->StoreAllFaderSettings(); @@ -730,15 +719,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->IsRunning() ) + // initiate connection + if ( pClient->Connect ( strSelectedAddress, strMixerBoardLabel ) ) { - Disconnect(); + OnConnect ( strMixerBoardLabel ); } - - // initiate connection - Connect ( strSelectedAddress, strMixerBoardLabel ); + // TODO: investigate and add error handling on failed Connect() call. // reset flag bConnectDlgWasShown = false; @@ -748,10 +734,10 @@ void CClientDlg::OnConnectDlgAccepted() void CClientDlg::OnConnectDisconBut() { // the connect/disconnect button implements a toggle functionality - if ( pClient->IsRunning() ) + if ( pClient->Disconnect() ) { - Disconnect(); - SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); + // Client was Connected, now disconnected + // TODO: Refactor this with !pClient->Disconnect() } else { @@ -859,7 +845,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 @@ -1164,7 +1150,7 @@ void CClientDlg::OnSoundDeviceChanged ( QString strError ) // the sound device setup has a problem, disconnect any active connection if ( pClient->IsRunning() ) { - Disconnect(); + pClient->Disconnect(); } // show the error message of the device setup @@ -1193,65 +1179,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 strMixerBoardLabel ) { - // 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->IsRunning() ) - { - pClient->Start(); - } - } - - 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 ); + // hide label connect to server + lblConnectToServer->hide(); + lbrInputLevelL->setEnabled ( true ); + lbrInputLevelR->setEnabled ( true ); - // change connect button text to "disconnect" - butConnect->setText ( tr ( "&Disconnect" ) ); + // change connect button text to "disconnect" + butConnect->setText ( tr ( "&Disconnect" ) ); - // set server name in audio mixer group box title - MainMixerBoard->SetServerName ( strMixerBoardLabel ); + // 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 + // 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() { - // 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() ) - { - pClient->Stop(); - } - // change connect button text to "connect" butConnect->setText ( tr ( "C&onnect" ) ); @@ -1275,11 +1232,7 @@ void CClientDlg::Disconnect() TimerDetectFeedback.stop(); bDetectFeedback = false; - //### TODO: BEGIN ###// - // is this still required??? - // immediately update status bar - OnTimerStatus(); - //### TODO: END ###// + UpdateDisplay(); // reset LEDs ledBuffers->Reset(); @@ -1293,6 +1246,8 @@ void CClientDlg::Disconnect() // clear mixer board (remove all faders) MainMixerBoard->HideAll(); + + SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); } void CClientDlg::UpdateDisplay() diff --git a/src/clientdlg.h b/src/clientdlg.h index 9737bcd2ca..593038eeb3 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 ); @@ -127,6 +124,8 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase CAnalyzerConsole AnalyzerConsole; public slots: + void OnConnect ( QString strServerName ); + void OnDisconnect(); void OnConnectDisconBut(); void OnTimerSigMet(); void OnTimerBuffersLED(); @@ -232,7 +231,6 @@ public slots: } void OnConnectDlgAccepted(); - void OnDisconnected() { Disconnect(); } void OnGUIDesignChanged(); void OnMeterStyleChanged(); void OnRecorderStateReceived ( ERecorderState eRecorderState ); diff --git a/src/connectdlg.cpp b/src/connectdlg.cpp old mode 100755 new mode 100644 diff --git a/src/main.cpp b/src/main.cpp index 42d17a8dea..d8386e6b55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -921,7 +921,6 @@ int main ( int argc, char** argv ) // actual client object CClient Client ( iPortNumber, iQosNumber, - strConnOnStartupAddress, strMIDISetup, bNoAutoJackConnect, strClientName, @@ -952,7 +951,6 @@ int main ( int argc, char** argv ) // GUI object CClientDlg ClientDlg ( &Client, &Settings, - strConnOnStartupAddress, strMIDISetup, bShowComplRegConnList, bShowAnalyzerConsole, @@ -963,6 +961,12 @@ int main ( int argc, char** argv ) // show dialog ClientDlg.show(); + // Connect on startup + if ( !strConnOnStartupAddress.isEmpty() ) + { + Client.Connect ( strConnOnStartupAddress, strConnOnStartupAddress ); + } + pApp->exec(); } else @@ -971,6 +975,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(); } }