From 55ee8092c8484b2f7c01ef8db8b51f94843f5afd Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 11 Nov 2024 18:17:38 +0000 Subject: [PATCH 01/18] Unchain signals via CClient for future ID mapping --- src/client.cpp | 23 ++++++++++++++++++++--- src/client.h | 2 ++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index d3d2c51c9a..2c804caa7c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -120,7 +120,6 @@ CClient::CClient ( const quint16 iPortNumber, // The first ConClientListMesReceived handler performs the necessary cleanup and has to run first: QObject::connect ( &Channel, &CChannel::ConClientListMesReceived, this, &CClient::OnConClientListMesReceived ); - QObject::connect ( &Channel, &CChannel::ConClientListMesReceived, this, &CClient::ConClientListMesReceived ); QObject::connect ( &Channel, &CChannel::Disconnected, this, &CClient::Disconnected ); @@ -130,7 +129,7 @@ CClient::CClient ( const quint16 iPortNumber, QObject::connect ( &Channel, &CChannel::ClientIDReceived, this, &CClient::OnClientIDReceived ); - QObject::connect ( &Channel, &CChannel::MuteStateHasChangedReceived, this, &CClient::MuteStateHasChangedReceived ); + QObject::connect ( &Channel, &CChannel::MuteStateHasChangedReceived, this, &CClient::OnMuteStateHasChangedReceived ); QObject::connect ( &Channel, &CChannel::LicenceRequired, this, &CClient::LicenceRequired ); @@ -154,7 +153,7 @@ CClient::CClient ( const quint16 iPortNumber, QObject::connect ( &ConnLessProtocol, &CProtocol::CLVersionAndOSReceived, this, &CClient::CLVersionAndOSReceived ); - QObject::connect ( &ConnLessProtocol, &CProtocol::CLChannelLevelListReceived, this, &CClient::CLChannelLevelListReceived ); + QObject::connect ( &ConnLessProtocol, &CProtocol::CLChannelLevelListReceived, this, &CClient::OnCLChannelLevelListReceived ); // other QObject::connect ( &Sound, &CSound::ReinitRequest, this, &CClient::OnSndCrdReinitRequest ); @@ -291,8 +290,24 @@ void CClient::OnNewConnection() //### TODO: END ###// } +void CClient::OnMuteStateHasChangedReceived ( int iChanID, bool bIsMuted ) +{ + // TODO map iChanID from server channel ID to client channel ID + + emit MuteStateHasChangedReceived ( iChanID, bIsMuted ); +} + +void CClient::OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ) +{ + // TODO reorder levels from server channel order to client channel order + + emit CLChannelLevelListReceived ( InetAddr, vecLevelList ); +} + void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) { + // TODO translate from server channel IDs to client channel IDs + // Upon receiving a new client list, we have to reset oldGain and newGain // entries for unused channels. This ensures that a disconnected channel // does not leave behind wrong cached gain values which would leak into @@ -316,6 +331,8 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) oldGain[iId] = newGain[iId] = 1; } } + + emit ConClientListMesReceived ( vecChanInfo ); } void CClient::CreateServerJitterBufferMessage() diff --git a/src/client.h b/src/client.h index 3f9e30be6d..c272965a72 100644 --- a/src/client.h +++ b/src/client.h @@ -404,6 +404,8 @@ protected slots: void OnControllerInFaderIsMute ( int iChannelIdx, bool bIsMute ); void OnControllerInMuteMyself ( bool bMute ); void OnClientIDReceived ( int iChanID ); + void OnMuteStateHasChangedReceived ( int iChanID, bool bIsMuted ); + void OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void OnConClientListMesReceived ( CVector vecChanInfo ); signals: From 16fcf1bd1a194858d04b5e1451041323a59c63bb Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 12 Nov 2024 12:27:00 +0000 Subject: [PATCH 02/18] Add some placeholder comments for changes needed --- src/client.cpp | 16 +++++++++++++--- src/client.h | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 2c804caa7c..a2af29fc1d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -307,6 +307,7 @@ void CClient::OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecChanInfo ) { // TODO translate from server channel IDs to client channel IDs + // ALSO here is where we allocate and free client channels as required // Upon receiving a new client list, we have to reset oldGain and newGain // entries for unused channels. This ensures that a disconnected channel @@ -332,6 +333,8 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) } } + // TODO vecChanInfo needs to be ordered by client channel ID instead of server channel ID + emit ConClientListMesReceived ( vecChanInfo ); } @@ -444,7 +447,7 @@ void CClient::SetRemoteChanGain ( const int iId, const float fGain, const bool b // here the timer was not active: // send the actual gain and reset the range of channel IDs to empty oldGain[iId] = newGain[iId] = fGain; - Channel.SetRemoteChanGain ( iId, fGain ); + Channel.SetRemoteChanGain ( iId, fGain ); // TODO translate client channel to server channel ID StartDelayTimer(); } @@ -460,7 +463,7 @@ void CClient::OnTimerRemoteChanGain() { // send new gain and record as old gain float fGain = oldGain[iId] = newGain[iId]; - Channel.SetRemoteChanGain ( iId, fGain ); + Channel.SetRemoteChanGain ( iId, fGain ); // TODO translate client channel to server channel ID bSent = true; } } @@ -491,6 +494,11 @@ void CClient::StartDelayTimer() } } +void CClient::SetRemoteChanPan ( const int iId, const float fPan ) +{ + Channel.SetRemoteChanPan ( iId, fPan ); // TODO translate client channel to server channel ID +} + bool CClient::SetServerAddr ( QString strNAddr ) { CHostAddress HostAddress; @@ -868,12 +876,14 @@ void CClient::OnControllerInMuteMyself ( bool bMute ) void CClient::OnClientIDReceived ( int iChanID ) { + // TODO allocate and map client-side channel 0 + // for headless mode we support to mute our own signal in the personal mix // (note that the check for headless is done in the main.cpp and must not // be checked here) if ( bMuteMeInPersonalMix ) { - SetRemoteChanGain ( iChanID, 0, false ); + SetRemoteChanGain ( iChanID, 0, false ); // TODO this will need client channel ID } emit ClientIDReceived ( iChanID ); diff --git a/src/client.h b/src/client.h index c272965a72..39a84e83aa 100644 --- a/src/client.h +++ b/src/client.h @@ -244,7 +244,7 @@ class CClient : public QObject void OnTimerRemoteChanGain(); void StartDelayTimer(); - void SetRemoteChanPan ( const int iId, const float fPan ) { Channel.SetRemoteChanPan ( iId, fPan ); } + void SetRemoteChanPan ( const int iId, const float fPan ); void SetInputBoost ( const int iNewBoost ) { iInputBoost = iNewBoost; } From 7f10dfffc181157a7f67fac69c46c17961618641 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 12 Nov 2024 17:02:51 +0000 Subject: [PATCH 03/18] Add data and methods for managing client channels --- src/client.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/client.h | 25 +++++++++++++++ 2 files changed, 104 insertions(+), 3 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index a2af29fc1d..8d9a452689 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -447,7 +447,7 @@ void CClient::SetRemoteChanGain ( const int iId, const float fGain, const bool b // here the timer was not active: // send the actual gain and reset the range of channel IDs to empty oldGain[iId] = newGain[iId] = fGain; - Channel.SetRemoteChanGain ( iId, fGain ); // TODO translate client channel to server channel ID + Channel.SetRemoteChanGain ( iId, fGain ); // TODO translate client channel to server channel ID StartDelayTimer(); } @@ -463,7 +463,7 @@ void CClient::OnTimerRemoteChanGain() { // send new gain and record as old gain float fGain = oldGain[iId] = newGain[iId]; - Channel.SetRemoteChanGain ( iId, fGain ); // TODO translate client channel to server channel ID + Channel.SetRemoteChanGain ( iId, fGain ); // TODO translate client channel to server channel ID bSent = true; } } @@ -883,7 +883,7 @@ void CClient::OnClientIDReceived ( int iChanID ) // be checked here) if ( bMuteMeInPersonalMix ) { - SetRemoteChanGain ( iChanID, 0, false ); // TODO this will need client channel ID + SetRemoteChanGain ( iChanID, 0, false ); // TODO this will need client channel ID } emit ClientIDReceived ( iChanID ); @@ -1413,3 +1413,79 @@ int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) return MathUtils::round ( fTotalBufferDelayMs + iPingTimeMs ); } + +// Management of Client Channels and mapping to/from Server Channels + +void CClient::ClearClientChannels() +{ + QMutexLocker locker ( &MutexChannels ); + + iActiveChannels = 0; + + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + { + clientChannels[i].iServerChannelID = INVALID_INDEX; + // TODO reset any other fields in CClientChannel + + clientChannelIDs[i] = INVALID_INDEX; + } +} + +void CClient::FreeClientChannel ( const int iServerChannelID ) +{ + QMutexLocker locker ( &MutexChannels ); + + if ( iServerChannelID == INVALID_INDEX || iServerChannelID >= MAX_NUM_CHANNELS ) + { + return; + } + + const int iClientChannelID = clientChannelIDs[iServerChannelID]; + + Q_ASSERT ( clientChannels[iClientChannelID].iServerChannelID == iServerChannelID ); + + clientChannelIDs[iServerChannelID] = INVALID_INDEX; + clientChannels[iClientChannelID].iServerChannelID = INVALID_INDEX; + + iActiveChannels -= 1; +} + +// find, and optionally create, a client channel for the supplied server channel ID +// returns a client channel ID or INVALID_INDEX +int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateIfNew ) +{ + QMutexLocker locker ( &MutexChannels ); + + if ( iServerChannelID == INVALID_INDEX || iServerChannelID >= MAX_NUM_CHANNELS ) + { + return INVALID_INDEX; + } + + const int iClientChannelID = clientChannelIDs[iServerChannelID]; + + if ( iClientChannelID != INVALID_INDEX ) + { + Q_ASSERT ( clientChannels[iClientChannelID].iServerChannelID == iServerChannelID ); + + return iClientChannelID; + } + + // no matching client channel - create new one if requested + if ( bCreateIfNew ) + { + // search clientChannels[] for a free client channel + for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + { + if ( clientChannels[i].iServerChannelID == INVALID_INDEX ) + { + clientChannels[i].iServerChannelID = iServerChannelID; + clientChannelIDs[iServerChannelID] = i; + + iActiveChannels += 1; + return i; // new client channel ID + } + } + } + + return INVALID_INDEX; +} diff --git a/src/client.h b/src/client.h index 39a84e83aa..99ed0de5e4 100644 --- a/src/client.h +++ b/src/client.h @@ -104,6 +104,15 @@ #define OPUS_NUM_BYTES_STEREO_HIGH_QUALITY_DBLE_FRAMESIZE 165 /* Classes ********************************************************************/ + +class CClientChannel +{ +public: + int iServerChannelID; // unused channels will contain INVALID_INDEX + + // can store here other information about an active channel +}; + class CClient : public QObject { Q_OBJECT @@ -288,10 +297,26 @@ class CClient : public QObject int EvaluatePingMessage ( const int iMs ); void CreateServerJitterBufferMessage(); + void ClearClientChannels(); + void FreeClientChannel ( const int iServerChannelID ); + int FindClientChannel ( const int iServerChannelID, const bool bCreateIfNew ); // returns a client channel ID or INVALID_INDEX + // only one channel is needed for client application CChannel Channel; CProtocol ConnLessProtocol; + // client channels, indexed by client channel ID, + // containing server channel ID (INVALID_INDEX if free) + CClientChannel clientChannels[MAX_NUM_CHANNELS]; + + // client channel IDs, indexed by server channel ID + // unused channels will contain INVALID_INDEX + int clientChannelIDs[MAX_NUM_CHANNELS]; + + // number of active channels + int iActiveChannels; + QMutex MutexChannels; + // audio encoder/decoder OpusCustomMode* Opus64Mode; OpusCustomEncoder* Opus64EncoderMono; From 144c1ebdc25279b303efaa7cee2b4bc294ac8d5b Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 16 Nov 2024 13:14:27 +0000 Subject: [PATCH 04/18] Add join sequence, init and info messages --- src/client.cpp | 17 +++++++++++++++++ src/client.h | 5 +++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 8d9a452689..56812b3c39 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -894,6 +894,9 @@ void CClient::Start() // init object Init(); + // initialise client channels + ClearClientChannels(); + // enable channel Channel.SetEnable ( true ); @@ -1421,10 +1424,12 @@ void CClient::ClearClientChannels() QMutexLocker locker ( &MutexChannels ); iActiveChannels = 0; + iJoinSequence = 0; for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { clientChannels[i].iServerChannelID = INVALID_INDEX; + clientChannels[i].iJoinSequence = 0; // TODO reset any other fields in CClientChannel clientChannelIDs[i] = INVALID_INDEX; @@ -1448,6 +1453,11 @@ void CClient::FreeClientChannel ( const int iServerChannelID ) clientChannels[iClientChannelID].iServerChannelID = INVALID_INDEX; iActiveChannels -= 1; + + qInfo() << qUtf8Printable ( QString ( "> Freed client ch %1 for server ch %2; chan count = %3" ) + .arg ( iClientChannelID ) + .arg ( iServerChannelID ) + .arg ( iActiveChannels ) ); } // find, and optionally create, a client channel for the supplied server channel ID @@ -1479,9 +1489,16 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI if ( clientChannels[i].iServerChannelID == INVALID_INDEX ) { clientChannels[i].iServerChannelID = iServerChannelID; + clientChannels[i].iJoinSequence = ++iJoinSequence; clientChannelIDs[iServerChannelID] = i; iActiveChannels += 1; + + qInfo() << qUtf8Printable ( QString ( "> Alloc client ch %1 for server ch %2; chan count = %3" ) + .arg ( iClientChannelID ) + .arg ( iServerChannelID ) + .arg ( iActiveChannels ) ); + return i; // new client channel ID } } diff --git a/src/client.h b/src/client.h index 99ed0de5e4..4dca7e00a1 100644 --- a/src/client.h +++ b/src/client.h @@ -109,6 +109,7 @@ class CClientChannel { public: int iServerChannelID; // unused channels will contain INVALID_INDEX + int iJoinSequence; // order of joining of session participants // can store here other information about an active channel }; @@ -313,8 +314,8 @@ class CClient : public QObject // unused channels will contain INVALID_INDEX int clientChannelIDs[MAX_NUM_CHANNELS]; - // number of active channels - int iActiveChannels; + int iActiveChannels; // number of active channels + int iJoinSequence; // order of joining of session participants QMutex MutexChannels; // audio encoder/decoder From 49eb6080303190b8d2c1fd73bfb38f3a085f34dc Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 16 Nov 2024 17:30:50 +0000 Subject: [PATCH 05/18] Do mapping between server and client channels --- src/client.cpp | 91 +++++++++++++++++++++++++++++++++++++------------- src/client.h | 4 +-- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 56812b3c39..0334b74242 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -290,9 +290,10 @@ void CClient::OnNewConnection() //### TODO: END ###// } -void CClient::OnMuteStateHasChangedReceived ( int iChanID, bool bIsMuted ) +void CClient::OnMuteStateHasChangedReceived ( int iServerChanID, bool bIsMuted ) { - // TODO map iChanID from server channel ID to client channel ID + // map iChanID from server channel ID to client channel ID + int iChanID = FindClientChannel ( iServerChanID, false ); emit MuteStateHasChangedReceived ( iChanID, bIsMuted ); } @@ -306,34 +307,73 @@ void CClient::OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecChanInfo ) { - // TODO translate from server channel IDs to client channel IDs + // translate from server channel IDs to client channel IDs // ALSO here is where we allocate and free client channels as required // Upon receiving a new client list, we have to reset oldGain and newGain // entries for unused channels. This ensures that a disconnected channel // does not leave behind wrong cached gain values which would leak into // any new channel which reused this channel id. - int iNumConnectedClients = vecChanInfo.Size(); - // Save what channel IDs are in use: - bool bChanIdInUse[MAX_NUM_CHANNELS] = {}; - for ( int i = 0; i < iNumConnectedClients; i++ ) + const int iNumConnectedClients = vecChanInfo.Size(); + int i, iSrvIdx; + + // this relies on the received client list being in order of server channel ID + + for ( i = 0, iSrvIdx = 0; i < iNumConnectedClients && iSrvIdx < MAX_NUM_CHANNELS; ) { - bChanIdInUse[vecChanInfo[i].iChanID] = true; + // server channel ID of this entry + const int iServerChannelID = vecChanInfo[i].iChanID; + + // find matching client channel ID, creating new if necessary + const int iClientChannelID = FindClientChannel ( iServerChannelID, true ); + + // discard any lower server channels that are no longer in our local list + while ( iSrvIdx < iServerChannelID ) + { + const int iId = FindClientChannel ( iSrvIdx, false ); + + if ( iId != INVALID_INDEX ) + { + // reset oldGain and newGain as this channel id is currently unused and will + // start with a server-side gain at 1 (100%) again. + oldGain[iId] = newGain[iId] = 1; + + // iSrvIdx contains a server channel number that has now gone + FreeClientChannel ( iSrvIdx ); + } + iSrvIdx++; + } + + // now should have matching server channel IDs + vecChanInfo[i].iChanID = iClientChannelID; // update channel number to be client-side + i++; // next list entry + iSrvIdx++; // next local server channel } - // Reset all gains for unused channel IDs: - for ( int iId = 0; iId < MAX_NUM_CHANNELS; iId++ ) + // have now run out of active channels, discard any remaining from our local list + // note that iActiveChannels will reduce as remaining channels are freed + + while ( iActiveChannels > iNumConnectedClients && iSrvIdx < MAX_NUM_CHANNELS ) { - if ( !bChanIdInUse[iId] ) + const int iId = FindClientChannel ( iSrvIdx, false ); + + if ( iId != INVALID_INDEX ) { // reset oldGain and newGain as this channel id is currently unused and will // start with a server-side gain at 1 (100%) again. oldGain[iId] = newGain[iId] = 1; + + // iSrvIdx contains a server channel number that has now gone + FreeClientChannel ( iSrvIdx ); } + iSrvIdx++; } + Q_ASSERT ( iActiveChannels == iNumConnectedClients ); + // TODO vecChanInfo needs to be ordered by client channel ID instead of server channel ID + // check if this is actually necessary, and whether it affects the level meters emit ConClientListMesReceived ( vecChanInfo ); } @@ -447,7 +487,7 @@ void CClient::SetRemoteChanGain ( const int iId, const float fGain, const bool b // here the timer was not active: // send the actual gain and reset the range of channel IDs to empty oldGain[iId] = newGain[iId] = fGain; - Channel.SetRemoteChanGain ( iId, fGain ); // TODO translate client channel to server channel ID + Channel.SetRemoteChanGain ( clientChannels[iId].iServerChannelID, fGain ); // translate client channel to server channel ID StartDelayTimer(); } @@ -463,7 +503,7 @@ void CClient::OnTimerRemoteChanGain() { // send new gain and record as old gain float fGain = oldGain[iId] = newGain[iId]; - Channel.SetRemoteChanGain ( iId, fGain ); // TODO translate client channel to server channel ID + Channel.SetRemoteChanGain ( clientChannels[iId].iServerChannelID, fGain ); // translate client channel to server channel ID bSent = true; } } @@ -496,7 +536,7 @@ void CClient::StartDelayTimer() void CClient::SetRemoteChanPan ( const int iId, const float fPan ) { - Channel.SetRemoteChanPan ( iId, fPan ); // TODO translate client channel to server channel ID + Channel.SetRemoteChanPan ( clientChannels[iId].iServerChannelID, fPan ); // translate client channel to server channel ID } bool CClient::SetServerAddr ( QString strNAddr ) @@ -874,16 +914,17 @@ void CClient::OnControllerInMuteMyself ( bool bMute ) emit ControllerInMuteMyself ( bMute ); } -void CClient::OnClientIDReceived ( int iChanID ) +void CClient::OnClientIDReceived ( int iServerChanID ) { - // TODO allocate and map client-side channel 0 + // allocate and map client-side channel 0 + int iChanID = FindClientChannel ( iServerChanID, true ); // should always return channel 0 // for headless mode we support to mute our own signal in the personal mix // (note that the check for headless is done in the main.cpp and must not // be checked here) if ( bMuteMeInPersonalMix ) { - SetRemoteChanGain ( iChanID, 0, false ); // TODO this will need client channel ID + SetRemoteChanGain ( iChanID, 0, false ); } emit ClientIDReceived ( iChanID ); @@ -1434,6 +1475,8 @@ void CClient::ClearClientChannels() clientChannelIDs[i] = INVALID_INDEX; } + + qInfo() << "> Client channel list cleared"; } void CClient::FreeClientChannel ( const int iServerChannelID ) @@ -1471,7 +1514,7 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI return INVALID_INDEX; } - const int iClientChannelID = clientChannelIDs[iServerChannelID]; + int iClientChannelID = clientChannelIDs[iServerChannelID]; if ( iClientChannelID != INVALID_INDEX ) { @@ -1484,13 +1527,13 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI if ( bCreateIfNew ) { // search clientChannels[] for a free client channel - for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) + for ( iClientChannelID = 0; iClientChannelID < MAX_NUM_CHANNELS; iClientChannelID++ ) { - if ( clientChannels[i].iServerChannelID == INVALID_INDEX ) + if ( clientChannels[iClientChannelID].iServerChannelID == INVALID_INDEX ) { - clientChannels[i].iServerChannelID = iServerChannelID; - clientChannels[i].iJoinSequence = ++iJoinSequence; - clientChannelIDs[iServerChannelID] = i; + clientChannels[iClientChannelID].iServerChannelID = iServerChannelID; + clientChannels[iClientChannelID].iJoinSequence = ++iJoinSequence; + clientChannelIDs[iServerChannelID] = iClientChannelID; iActiveChannels += 1; @@ -1499,7 +1542,7 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI .arg ( iServerChannelID ) .arg ( iActiveChannels ) ); - return i; // new client channel ID + return iClientChannelID; // new client channel ID } } } diff --git a/src/client.h b/src/client.h index 4dca7e00a1..5b5ec1d0a2 100644 --- a/src/client.h +++ b/src/client.h @@ -429,8 +429,8 @@ protected slots: void OnControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); void OnControllerInFaderIsMute ( int iChannelIdx, bool bIsMute ); void OnControllerInMuteMyself ( bool bMute ); - void OnClientIDReceived ( int iChanID ); - void OnMuteStateHasChangedReceived ( int iChanID, bool bIsMuted ); + void OnClientIDReceived ( int iServerChanID ); + void OnMuteStateHasChangedReceived ( int iServerChanID, bool bIsMuted ); void OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void OnConClientListMesReceived ( CVector vecChanInfo ); From 5d31dd12512a2567e7677e046479b5b70c5d2222 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 18 Nov 2024 12:15:57 +0000 Subject: [PATCH 06/18] Added protection against deletion of our own channel On initial connect, server sends an empty channel list (#1010) before the real one. Skip client channel management if list is empty, so that we don't lose client channel 0 for our own server channel. --- src/client.cpp | 76 ++++++++++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 0334b74242..37a8278dcf 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -318,18 +318,50 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) const int iNumConnectedClients = vecChanInfo.Size(); int i, iSrvIdx; - // this relies on the received client list being in order of server channel ID + // on a new connection, a server sends an empty channel list before sending + // the real channel list (see #1010). To avoid this discarding "our" channel + // that we have just created, we skip this processing and just pass the empty + // list to the emitted signal. - for ( i = 0, iSrvIdx = 0; i < iNumConnectedClients && iSrvIdx < MAX_NUM_CHANNELS; ) + if ( iNumConnectedClients != 0 ) { - // server channel ID of this entry - const int iServerChannelID = vecChanInfo[i].iChanID; + // this relies on the received client list being in order of server channel ID - // find matching client channel ID, creating new if necessary - const int iClientChannelID = FindClientChannel ( iServerChannelID, true ); + for ( i = 0, iSrvIdx = 0; i < iNumConnectedClients && iSrvIdx < MAX_NUM_CHANNELS; ) + { + // server channel ID of this entry + const int iServerChannelID = vecChanInfo[i].iChanID; + + // find matching client channel ID, creating new if necessary + const int iClientChannelID = FindClientChannel ( iServerChannelID, true ); + + // discard any lower server channels that are no longer in our local list + while ( iSrvIdx < iServerChannelID ) + { + const int iId = FindClientChannel ( iSrvIdx, false ); + + if ( iId != INVALID_INDEX ) + { + // reset oldGain and newGain as this channel id is currently unused and will + // start with a server-side gain at 1 (100%) again. + oldGain[iId] = newGain[iId] = 1; + + // iSrvIdx contains a server channel number that has now gone + FreeClientChannel ( iSrvIdx ); + } + iSrvIdx++; + } - // discard any lower server channels that are no longer in our local list - while ( iSrvIdx < iServerChannelID ) + // now should have matching server channel IDs + vecChanInfo[i].iChanID = iClientChannelID; // update channel number to be client-side + i++; // next list entry + iSrvIdx++; // next local server channel + } + + // have now run out of active channels, discard any remaining from our local list + // note that iActiveChannels will reduce as remaining channels are freed + + while ( iActiveChannels > iNumConnectedClients && iSrvIdx < MAX_NUM_CHANNELS ) { const int iId = FindClientChannel ( iSrvIdx, false ); @@ -345,34 +377,10 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) iSrvIdx++; } - // now should have matching server channel IDs - vecChanInfo[i].iChanID = iClientChannelID; // update channel number to be client-side - i++; // next list entry - iSrvIdx++; // next local server channel + Q_ASSERT ( iActiveChannels == iNumConnectedClients ); } - // have now run out of active channels, discard any remaining from our local list - // note that iActiveChannels will reduce as remaining channels are freed - - while ( iActiveChannels > iNumConnectedClients && iSrvIdx < MAX_NUM_CHANNELS ) - { - const int iId = FindClientChannel ( iSrvIdx, false ); - - if ( iId != INVALID_INDEX ) - { - // reset oldGain and newGain as this channel id is currently unused and will - // start with a server-side gain at 1 (100%) again. - oldGain[iId] = newGain[iId] = 1; - - // iSrvIdx contains a server channel number that has now gone - FreeClientChannel ( iSrvIdx ); - } - iSrvIdx++; - } - - Q_ASSERT ( iActiveChannels == iNumConnectedClients ); - - // TODO vecChanInfo needs to be ordered by client channel ID instead of server channel ID + // TODO Does vecChanInfo need to be ordered by client channel ID instead of server channel ID? // check if this is actually necessary, and whether it affects the level meters emit ConClientListMesReceived ( vecChanInfo ); From d26bbd8cb982e1d29cf88a10b8d8289f7c7c96b4 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 17 Nov 2024 18:01:49 +0000 Subject: [PATCH 07/18] Add entries to CClientChannel for gain and level --- src/client.cpp | 18 ++++++++++++------ src/client.h | 4 ++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 37a8278dcf..0b6521a8a7 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1478,8 +1478,7 @@ void CClient::ClearClientChannels() for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { clientChannels[i].iServerChannelID = INVALID_INDEX; - clientChannels[i].iJoinSequence = 0; - // TODO reset any other fields in CClientChannel + // all other fields will be initialised on channel allocation clientChannelIDs[i] = INVALID_INDEX; } @@ -1537,11 +1536,18 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI // search clientChannels[] for a free client channel for ( iClientChannelID = 0; iClientChannelID < MAX_NUM_CHANNELS; iClientChannelID++ ) { - if ( clientChannels[iClientChannelID].iServerChannelID == INVALID_INDEX ) + CClientChannel* clientChan = &clientChannels[iClientChannelID]; + + if ( clientChan->iServerChannelID == INVALID_INDEX ) { - clientChannels[iClientChannelID].iServerChannelID = iServerChannelID; - clientChannels[iClientChannelID].iJoinSequence = ++iJoinSequence; - clientChannelIDs[iServerChannelID] = iClientChannelID; + clientChan->iServerChannelID = iServerChannelID; + clientChan->iJoinSequence = ++iJoinSequence; + + clientChan->oldGain = clientChan->newGain = 1.0f; + + clientChan->level = 0; + + clientChannelIDs[iServerChannelID] = iClientChannelID; iActiveChannels += 1; diff --git a/src/client.h b/src/client.h index 5b5ec1d0a2..0abc646277 100644 --- a/src/client.h +++ b/src/client.h @@ -111,6 +111,10 @@ class CClientChannel int iServerChannelID; // unused channels will contain INVALID_INDEX int iJoinSequence; // order of joining of session participants + float oldGain, newGain; // for rate-limiting sending of gain messages + + uint16_t level; // last value of level meter received for channel + // can store here other information about an active channel }; From f8d1e6863777f1e371a5720d9aeb8b52b3982c01 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 17 Nov 2024 18:03:26 +0000 Subject: [PATCH 08/18] Added reordering of received level list by client channel --- src/client.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++-- src/client.h | 1 + 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 0b6521a8a7..5f28d61a06 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -300,9 +300,12 @@ void CClient::OnMuteStateHasChangedReceived ( int iServerChanID, bool bIsMuted ) void CClient::OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ) { - // TODO reorder levels from server channel order to client channel order + // reorder levels from server channel order to client channel order - emit CLChannelLevelListReceived ( InetAddr, vecLevelList ); + if ( ReorderLevelList ( vecLevelList ) ) + { + emit CLChannelLevelListReceived ( InetAddr, vecLevelList ); + } } void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) @@ -1563,3 +1566,60 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI return INVALID_INDEX; } + +bool CClient::ReorderLevelList ( CVector& vecLevelList ) +{ + QMutexLocker locker ( &MutexChannels ); + + // vecLevelList is sent from server ordered by server channel ID + // re-order it by client channel ID before passing up to the GUI + // the list is passed in by reference and modified in situ + + // check it is the right length + if ( vecLevelList.Size() != iActiveChannels ) + { + return false; // tell caller to ignore it + } + + int iClientCh; + int iServerCh = 0; + + // fetch levels by server channel ID + + for ( int i = 0; i < iActiveChannels; i++ ) + { + // find next active server channel + while ( iServerCh < MAX_NUM_CHANNELS ) + { + iClientCh = clientChannelIDs[iServerCh++]; + + if ( iClientCh != INVALID_INDEX ) + { + clientChannels[iClientCh].level = vecLevelList[i]; + break; + } + } + } + + // store levels by client channel ID + + iClientCh = 0; + + for ( int i = 0; i < iActiveChannels; i++ ) + { + while ( iClientCh < MAX_NUM_CHANNELS ) + { + uint16_t level = clientChannels[iClientCh].level; + + iServerCh = clientChannels[iClientCh++].iServerChannelID; + + if ( iServerCh != INVALID_INDEX ) + { + vecLevelList[i] = level; + break; + } + } + } + + return true; // tell caller to emit signal with new list +} diff --git a/src/client.h b/src/client.h index 0abc646277..b60ecb09f8 100644 --- a/src/client.h +++ b/src/client.h @@ -305,6 +305,7 @@ class CClient : public QObject void ClearClientChannels(); void FreeClientChannel ( const int iServerChannelID ); int FindClientChannel ( const int iServerChannelID, const bool bCreateIfNew ); // returns a client channel ID or INVALID_INDEX + bool ReorderLevelList ( CVector& vecLevelList ); // modifies vecLevelList, passed by reference // only one channel is needed for client application CChannel Channel; From 85ceaaf2353d503772a89bcc5756714c0f59738b Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 18 Nov 2024 12:39:07 +0000 Subject: [PATCH 09/18] Clear channel list if server has restarted --- src/client.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index 5f28d61a06..a2be18b684 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -927,6 +927,15 @@ void CClient::OnControllerInMuteMyself ( bool bMute ) void CClient::OnClientIDReceived ( int iServerChanID ) { + // if we have just connected to a running server, iActiveChannels will be 0 + // if iActiveChannels is not 0, the server must have been restarted on the fly + // in that case, channels might have changed, so clear our list to get it afresh. + if ( iActiveChannels != 0 ) + { + qInfo() << "> Server restarted?"; + ClearClientChannels(); + } + // allocate and map client-side channel 0 int iChanID = FindClientChannel ( iServerChanID, true ); // should always return channel 0 From b950e09a56946b5d08492e862dd2dd64faa846ff Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 18 Nov 2024 13:21:58 +0000 Subject: [PATCH 10/18] Update Sort Users by Channel menu entry --- src/clientdlg.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index cb6b134bf7..e8402f6cbc 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -333,10 +333,10 @@ CClientDlg::CClientDlg ( CClient* pNCliP, QAction* ByCityAction = pViewMenu->addAction ( tr ( "Sort Users by &City" ), this, SLOT ( OnSortChannelsByCity() ), QKeySequence ( Qt::CTRL + Qt::Key_T ) ); - QAction* ByServerChannelAction = pViewMenu->addAction ( tr ( "Sort Users by Se&rver Channel" ), + QAction* ByServerChannelAction = pViewMenu->addAction ( tr ( "Sort Users by Chann&el" ), this, SLOT ( OnSortChannelsByChannel() ), - QKeySequence ( Qt::CTRL + Qt::Key_R ) ); + QKeySequence ( Qt::CTRL + Qt::Key_E ) ); OwnFaderFirstAction->setCheckable ( true ); OwnFaderFirstAction->setChecked ( pSettings->bOwnFaderFirst ); From 42faa26c42735e4adb775805bb90ddb642b98d17 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Mon, 18 Nov 2024 18:06:13 +0000 Subject: [PATCH 11/18] Use oldGain and newGain in clientChannels[] Instead of using separate oldGain and newGain arrays. We also don't need to reset the oldGain and newGain values when freeing a channel, as they are always initialised when allocating a channel. --- src/client.cpp | 50 ++++++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index a2be18b684..06ef73e68b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -262,15 +262,6 @@ void CClient::OnJittBufSizeChanged ( int iNewJitBufSize ) void CClient::OnNewConnection() { - // The oldGain and newGain arrays are used to avoid sending duplicate gain change messages. - // As these values depend on the channel IDs of a specific server, we have - // to reset those upon connect. - // We reset to 1 because this is what the server part sets by default. - for ( int iId = 0; iId < MAX_NUM_CHANNELS; iId++ ) - { - oldGain[iId] = newGain[iId] = 1; - } - // a new connection was successfully initiated, send infos and request // connected clients list Channel.SetRemoteInfo ( ChannelInfo ); @@ -313,11 +304,6 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) // translate from server channel IDs to client channel IDs // ALSO here is where we allocate and free client channels as required - // Upon receiving a new client list, we have to reset oldGain and newGain - // entries for unused channels. This ensures that a disconnected channel - // does not leave behind wrong cached gain values which would leak into - // any new channel which reused this channel id. - const int iNumConnectedClients = vecChanInfo.Size(); int i, iSrvIdx; @@ -345,10 +331,6 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) if ( iId != INVALID_INDEX ) { - // reset oldGain and newGain as this channel id is currently unused and will - // start with a server-side gain at 1 (100%) again. - oldGain[iId] = newGain[iId] = 1; - // iSrvIdx contains a server channel number that has now gone FreeClientChannel ( iSrvIdx ); } @@ -370,10 +352,6 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) if ( iId != INVALID_INDEX ) { - // reset oldGain and newGain as this channel id is currently unused and will - // start with a server-side gain at 1 (100%) again. - oldGain[iId] = newGain[iId] = 1; - // iSrvIdx contains a server channel number that has now gone FreeClientChannel ( iSrvIdx ); } @@ -461,19 +439,21 @@ void CClient::SetDoAutoSockBufSize ( const bool bValue ) // running), it will be sent immediately, and a 300ms timer started. // // If a gain change message is requested while the timer is still running, the new gain is not sent, -// but just stored in newGain[iId], and the minGainId and maxGainId updated to note the range of -// IDs that must be checked when the time expires (this will usually be a single channel +// but just stored in clientChannels[iId].newGain, and the minGainId and maxGainId updated to note +// the range of IDs that must be checked when the time expires (this will usually be a single channel // unless channel grouping is being used). This avoids having to check all possible channels. // -// When the timer fires, the channels minGainId <= iId < maxGainId are checked by comparing -// the last sent value in oldGain[iId] with any pending value in newGain[iId], and if they differ, -// the new value is sent, updating oldGain[iId] with the sent value. If any new values are +// When the timer fires, the channels minGainId <= iId < maxGainId are checked by comparing the last sent value +// in clientChannels[iId].oldGain with any pending value in clientChannels[iId].newGain, and if they differ, +// the new value is sent, updating clientChannels[iId].oldGain with the sent value. If any new values are // sent, the timer is restarted so that further immediate updates will be pended. void CClient::SetRemoteChanGain ( const int iId, const float fGain, const bool bIsMyOwnFader ) { QMutexLocker locker ( &MutexGain ); + CClientChannel* clientChan = &clientChannels[iId]; + // if this gain is for my own channel, apply the value for the Mute Myself function if ( bIsMyOwnFader ) { @@ -483,8 +463,8 @@ void CClient::SetRemoteChanGain ( const int iId, const float fGain, const bool b if ( TimerGain.isActive() ) { // just update the new value for sending later; - // will compare with oldGain[iId] when the timer fires - newGain[iId] = fGain; + // will compare with oldGain when the timer fires + clientChan->newGain = fGain; // update range of channel IDs to check in the timer if ( iId < minGainId ) @@ -497,8 +477,8 @@ void CClient::SetRemoteChanGain ( const int iId, const float fGain, const bool b // here the timer was not active: // send the actual gain and reset the range of channel IDs to empty - oldGain[iId] = newGain[iId] = fGain; - Channel.SetRemoteChanGain ( clientChannels[iId].iServerChannelID, fGain ); // translate client channel to server channel ID + clientChan->oldGain = clientChan->newGain = fGain; + Channel.SetRemoteChanGain ( clientChan->iServerChannelID, fGain ); // translate client channel to server channel ID StartDelayTimer(); } @@ -510,11 +490,13 @@ void CClient::OnTimerRemoteChanGain() for ( int iId = minGainId; iId < maxGainId; iId++ ) { - if ( newGain[iId] != oldGain[iId] ) + CClientChannel* clientChan = &clientChannels[iId]; + + if ( clientChan->newGain != clientChan->oldGain ) { // send new gain and record as old gain - float fGain = oldGain[iId] = newGain[iId]; - Channel.SetRemoteChanGain ( clientChannels[iId].iServerChannelID, fGain ); // translate client channel to server channel ID + float fGain = clientChan->oldGain = clientChan->newGain; + Channel.SetRemoteChanGain ( clientChan->iServerChannelID, fGain ); // translate client channel to server channel ID bSent = true; } } From 75792ae932072d9a5ececd788824e83af1f69848 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 20 Nov 2024 17:43:24 +0000 Subject: [PATCH 12/18] Fix for clang-format --- src/clientdlg.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index e8402f6cbc..4ea222fb8a 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -333,10 +333,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP, QAction* ByCityAction = pViewMenu->addAction ( tr ( "Sort Users by &City" ), this, SLOT ( OnSortChannelsByCity() ), QKeySequence ( Qt::CTRL + Qt::Key_T ) ); - QAction* ByServerChannelAction = pViewMenu->addAction ( tr ( "Sort Users by Chann&el" ), - this, - SLOT ( OnSortChannelsByChannel() ), - QKeySequence ( Qt::CTRL + Qt::Key_E ) ); + QAction* ByServerChannelAction = + pViewMenu->addAction ( tr ( "Sort Users by Chann&el" ), this, SLOT ( OnSortChannelsByChannel() ), QKeySequence ( Qt::CTRL + Qt::Key_E ) ); OwnFaderFirstAction->setCheckable ( true ); OwnFaderFirstAction->setChecked ( pSettings->bOwnFaderFirst ); From 2d15a81e12efac9d3668ee42662c478266d618a0 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Wed, 20 Nov 2024 17:47:51 +0000 Subject: [PATCH 13/18] Rename action for Sort by Channel --- src/clientdlg.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 4ea222fb8a..725628d719 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -333,7 +333,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP, QAction* ByCityAction = pViewMenu->addAction ( tr ( "Sort Users by &City" ), this, SLOT ( OnSortChannelsByCity() ), QKeySequence ( Qt::CTRL + Qt::Key_T ) ); - QAction* ByServerChannelAction = + QAction* ByChannelAction = pViewMenu->addAction ( tr ( "Sort Users by Chann&el" ), this, SLOT ( OnSortChannelsByChannel() ), QKeySequence ( Qt::CTRL + Qt::Key_E ) ); OwnFaderFirstAction->setCheckable ( true ); @@ -352,8 +352,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP, SortActionGroup->addAction ( ByGroupAction ); ByCityAction->setCheckable ( true ); SortActionGroup->addAction ( ByCityAction ); - ByServerChannelAction->setCheckable ( true ); - SortActionGroup->addAction ( ByServerChannelAction ); + ByChannelAction->setCheckable ( true ); + SortActionGroup->addAction ( ByChannelAction ); // initialize sort type setting (i.e., recover stored setting) switch ( pSettings->eChannelSortType ) @@ -371,7 +371,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP, ByCityAction->setChecked ( true ); break; case ST_BY_SERVER_CHANNEL: - ByServerChannelAction->setChecked ( true ); + ByChannelAction->setChecked ( true ); break; default: // ST_NO_SORT NoSortAction->setChecked ( true ); From 1791c439a21bf6619d15ca880e2225943601cd88 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 21 Nov 2024 17:59:38 +0000 Subject: [PATCH 14/18] Add explanatory comment to CClient::ReorderLevelList() --- src/client.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index 06ef73e68b..7ec07614bb 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1558,6 +1558,24 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI return INVALID_INDEX; } +// When the client receives a channel level list from the server, the list contains one value +// for each currently-active channel, ordered by the channel ID assigned by the server. +// The values will correspond to the active channels in the last client list that was sent. +// This list is passed up to the mixer board, which will interpret the values in the order +// of channels that it knows about. +// +// Since CClient is now translating server channel IDs to local client channel IDs before +// passing the client list up to the mixer board, it is also necessary to re-order the values +// in the level list so that they are in order of mapped client channel ID. +// This function performs that re-ordering by scanning the server channels in order, once, +// for active channels, and storing the level value in the corresponding client channel. +// Then the function scans the client channels in order, fetching the level values and putting +// them back into vecLevelList in order of client channel. The mixer board will then display +// the levels against the correct channels. +// +// The list size is checked against the current number of active channels to guard against +// any unexpected temporary mismatch in size due to potential out-of-order message delivery. + bool CClient::ReorderLevelList ( CVector& vecLevelList ) { QMutexLocker locker ( &MutexChannels ); From d386e055d35869af87edaa839fbd80f25e9ac757 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 21 Nov 2024 18:26:44 +0000 Subject: [PATCH 15/18] Remove unneeded TODO comment and add clarification --- src/client.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 7ec07614bb..7e3a3c1cce 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -361,8 +361,7 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) Q_ASSERT ( iActiveChannels == iNumConnectedClients ); } - // TODO Does vecChanInfo need to be ordered by client channel ID instead of server channel ID? - // check if this is actually necessary, and whether it affects the level meters + // pass the received list onwards, now containing client channel IDs emit ConClientListMesReceived ( vecChanInfo ); } From 4b56a8cf705c4f772e65dfa282f601ffa1078194 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Thu, 21 Nov 2024 18:37:45 +0000 Subject: [PATCH 16/18] Clarify boolean result of ReorderLevelList() --- src/client.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/client.cpp b/src/client.cpp index 7e3a3c1cce..561fa5935b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1574,6 +1574,9 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI // // The list size is checked against the current number of active channels to guard against // any unexpected temporary mismatch in size due to potential out-of-order message delivery. +// +// This function returns true if the list has been processed and should be passed on, +// or false if it was the wrong size and should be discarded. bool CClient::ReorderLevelList ( CVector& vecLevelList ) { From 6a27126295f33c798d42d090606ea5bd673c3efa Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sat, 23 Nov 2024 12:09:23 +0000 Subject: [PATCH 17/18] Minor updates from review comments --- src/client.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 561fa5935b..5a386ee748 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -286,7 +286,10 @@ void CClient::OnMuteStateHasChangedReceived ( int iServerChanID, bool bIsMuted ) // map iChanID from server channel ID to client channel ID int iChanID = FindClientChannel ( iServerChanID, false ); - emit MuteStateHasChangedReceived ( iChanID, bIsMuted ); + if ( iChanID != INVALID_INDEX ) + { + emit MuteStateHasChangedReceived ( iChanID, bIsMuted ); + } } void CClient::OnCLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ) @@ -321,8 +324,9 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) // server channel ID of this entry const int iServerChannelID = vecChanInfo[i].iChanID; - // find matching client channel ID, creating new if necessary - const int iClientChannelID = FindClientChannel ( iServerChannelID, true ); + // find matching client channel ID, creating new if necessary, + // update channel number to be client-side + vecChanInfo[i].iChanID = FindClientChannel ( iServerChannelID, true ); // discard any lower server channels that are no longer in our local list while ( iSrvIdx < iServerChannelID ) @@ -337,10 +341,8 @@ void CClient::OnConClientListMesReceived ( CVector vecChanInfo ) iSrvIdx++; } - // now should have matching server channel IDs - vecChanInfo[i].iChanID = iClientChannelID; // update channel number to be client-side - i++; // next list entry - iSrvIdx++; // next local server channel + i++; // next list entry + iSrvIdx++; // next local server channel } // have now run out of active channels, discard any remaining from our local list @@ -1476,7 +1478,7 @@ void CClient::ClearClientChannels() clientChannelIDs[i] = INVALID_INDEX; } - qInfo() << "> Client channel list cleared"; + // qInfo() << "> Client channel list cleared"; } void CClient::FreeClientChannel ( const int iServerChannelID ) @@ -1497,10 +1499,12 @@ void CClient::FreeClientChannel ( const int iServerChannelID ) iActiveChannels -= 1; + /* qInfo() << qUtf8Printable ( QString ( "> Freed client ch %1 for server ch %2; chan count = %3" ) .arg ( iClientChannelID ) .arg ( iServerChannelID ) .arg ( iActiveChannels ) ); + */ } // find, and optionally create, a client channel for the supplied server channel ID @@ -1544,10 +1548,12 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI iActiveChannels += 1; + /* qInfo() << qUtf8Printable ( QString ( "> Alloc client ch %1 for server ch %2; chan count = %3" ) .arg ( iClientChannelID ) .arg ( iServerChannelID ) .arg ( iActiveChannels ) ); + */ return iClientChannelID; // new client channel ID } From 4b8b733fbf2ec093fbfab7ed52d738b1feec277a Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Sun, 24 Nov 2024 22:58:50 +0000 Subject: [PATCH 18/18] Update src/client.cpp Co-authored-by: ann0see <20726856+ann0see@users.noreply.github.com> --- src/client.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client.cpp b/src/client.cpp index 5a386ee748..fb0c4bafac 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1569,7 +1569,7 @@ int CClient::FindClientChannel ( const int iServerChannelID, const bool bCreateI // This list is passed up to the mixer board, which will interpret the values in the order // of channels that it knows about. // -// Since CClient is now translating server channel IDs to local client channel IDs before +// Since CClient is translating server channel IDs to local client channel IDs before // passing the client list up to the mixer board, it is also necessary to re-order the values // in the level list so that they are in order of mapped client channel ID. // This function performs that re-ordering by scanning the server channels in order, once,