From 46f75679abbda3b20598c22675842ae9d7620942 Mon Sep 17 00:00:00 2001 From: Tony Mountifield Date: Tue, 5 Nov 2024 18:31:20 +0000 Subject: [PATCH] Always give own channel MIDI number 0 1. Moved my channel ID from the Mixer to the Client, so it is available when headless too. 2. Client's own channel, sent by the server, is always assigned to MIDI number 0, for left-most fader. 3. Translate between server channel and MIDI number at the client level. --- src/audiomixerboard.cpp | 27 ++++++++++--------- src/audiomixerboard.h | 9 ++++--- src/client.cpp | 60 ++++++++++++++++++++++++++++++++++++++--- src/client.h | 18 ++++++++++--- src/clientdlg.cpp | 5 ++-- src/clientdlg.h | 2 -- src/sound/soundbase.h | 8 +++--- 7 files changed, 95 insertions(+), 34 deletions(-) diff --git a/src/audiomixerboard.cpp b/src/audiomixerboard.cpp index db6cab14e4..f9ad65fb01 100644 --- a/src/audiomixerboard.cpp +++ b/src/audiomixerboard.cpp @@ -260,7 +260,7 @@ void CChannelFader::SetGUIDesign ( const EGUIDesign eNewDesign ) UpdateGroupIDDependencies(); // the instrument picture might need scaling after a style change - SetChannelInfos ( cReceivedChanInfo ); + SetChannelInfos ( cReceivedChanInfo, cReceivedMIDIID ); } void CChannelFader::SetMeterStyle ( const EMeterStyle eNewMeterStyle ) @@ -436,6 +436,7 @@ void CChannelFader::Reset() plblCountryFlag->setVisible ( false ); plblCountryFlag->setToolTip ( "" ); cReceivedChanInfo = CChannelInfo(); + cReceivedMIDIID = INVALID_INDEX; SetupFaderTag ( SL_NOT_SET ); // set a defined tool tip time out @@ -666,10 +667,11 @@ void CChannelFader::UpdateSoloState ( const bool bNewOtherSoloState ) void CChannelFader::SetChannelLevel ( const uint16_t iLevel ) { plbrChannelLevel->SetValue ( iLevel ); } -void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo ) +void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo, int iMIDIID ) { // store received channel info cReceivedChanInfo = cChanInfo; + cReceivedMIDIID = iMIDIID; // init properties for the tool tip int iTTInstrument = CInstPictures::GetNotUsedInstrument(); @@ -682,7 +684,7 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo ) // show channel numbers if --ctrlmidich is used (#241, #95) if ( bMIDICtrlUsed ) { - strModText.prepend ( QString().setNum ( cChanInfo.iChanID ) + ":" ); + strModText.prepend ( QString().setNum ( iMIDIID ) + ":" ); } QTextBoundaryFinder tbfName ( QTextBoundaryFinder::Grapheme, cChanInfo.strName ); @@ -886,7 +888,6 @@ CAudioMixerBoard::CAudioMixerBoard ( QWidget* parent ) : bDisplayPans ( false ), bIsPanSupported ( false ), bNoFaderVisible ( true ), - iMyChannelID ( INVALID_INDEX ), iRunningNewClientCnt ( 0 ), iNumMixerPanelRows ( 1 ), // pSettings->iNumMixerPanelRows is not yet available strServerName ( "" ), @@ -1049,7 +1050,6 @@ void CAudioMixerBoard::HideAll() bIsPanSupported = false; bNoFaderVisible = true; eRecorderState = RS_UNDEFINED; - iMyChannelID = INVALID_INDEX; iRunningNewClientCnt = 0; // reset running counter on new server connection // use original order of channel (by server ID) @@ -1123,7 +1123,7 @@ void CAudioMixerBoard::ChangeFaderOrder ( const EChSortType eChSortType ) } break; case ST_BY_SERVER_CHANNEL: - PairList << QPair ( QString ( "%1" ).arg ( vecpChanFader[i]->GetReceivedChID(), 3, 10, QLatin1Char ( '0' ) ) + + PairList << QPair ( QString ( "%1" ).arg ( vecpChanFader[i]->GetReceivedMIDIID(), 3, 10, QLatin1Char ( '0' ) ) + vecpChanFader[i]->GetReceivedName().toLower(), i ); break; @@ -1267,7 +1267,7 @@ void CAudioMixerBoard::ApplyNewConClientList ( CVector& vecChanInf vecpChanFader[iChanID]->Reset(); vecAvgLevels[iChanID] = 0.0f; - if ( static_cast ( iChanID ) == iMyChannelID ) + if ( static_cast ( iChanID ) == pClient->GetMyChannelID() ) { // this is my own fader --> set fader property vecpChanFader[iChanID]->SetIsMyOwnFader(); @@ -1288,7 +1288,8 @@ void CAudioMixerBoard::ApplyNewConClientList ( CVector& vecChanInf // we can adjust the level even if no fader was visible. // The fader level of 100 % is the default in the // server, in that case we do not have to do anything here. - if ( ( !bNoFaderVisible || ( ( iMyChannelID != INVALID_INDEX ) && ( iMyChannelID != static_cast ( iChanID ) ) ) ) && + if ( ( !bNoFaderVisible || + ( ( pClient->GetMyChannelID() != INVALID_INDEX ) && ( pClient->GetMyChannelID() != static_cast ( iChanID ) ) ) ) && ( pSettings->iNewClientFaderLevel != 100 ) ) { // the value is in percent -> convert range @@ -1322,7 +1323,7 @@ void CAudioMixerBoard::ApplyNewConClientList ( CVector& vecChanInf } // set the channel infos - vecpChanFader[iChanID]->SetChannelInfos ( vecChanInfo[idxVecpChan] ); + vecpChanFader[iChanID]->SetChannelInfos ( vecChanInfo[idxVecpChan], pClient->ChanToMIDI ( iChanID ) ); } // update the solo states since if any channel was on solo and a new client @@ -1397,7 +1398,7 @@ void CAudioMixerBoard::SetAllFaderLevelsToNewClientLevel() for ( size_t i = 0; i < MAX_NUM_CHANNELS; i++ ) { // only apply to visible faders and not to my own channel fader - if ( vecpChanFader[i]->IsVisible() && ( static_cast ( i ) != iMyChannelID ) ) + if ( vecpChanFader[i]->IsVisible() && ( static_cast ( i ) != pClient->GetMyChannelID() ) ) { // the value is in percent -> convert range, also use the group // update flag to make sure the group values are all set to the @@ -1426,7 +1427,7 @@ void CAudioMixerBoard::AutoAdjustAllFaderLevels() for ( size_t i = 0; i < MAX_NUM_CHANNELS; ++i ) { // only apply to visible faders (and not to my own channel fader) - if ( vecpChanFader[i]->IsVisible() && ( static_cast ( i ) != iMyChannelID ) ) + if ( vecpChanFader[i]->IsVisible() && ( static_cast ( i ) != pClient->GetMyChannelID() ) ) { // map averaged meter output level to decibels // (invert CStereoSignalLevelMeter::CalcLogResultForMeter) @@ -1516,7 +1517,7 @@ void CAudioMixerBoard::AutoAdjustAllFaderLevels() for ( size_t i = 0; i < MAX_NUM_CHANNELS; ++i ) { // only apply to visible faders (and not to my own channel fader) - if ( vecpChanFader[i]->IsVisible() && ( static_cast ( i ) != iMyChannelID ) ) + if ( vecpChanFader[i]->IsVisible() && ( static_cast ( i ) != pClient->GetMyChannelID() ) ) { // map averaged meter output level to decibels // (invert CStereoSignalLevelMeter::CalcLogResultForMeter) @@ -1778,4 +1779,4 @@ void CAudioMixerBoard::SetChannelLevels ( const CVector& vecChannelLev } } -void CAudioMixerBoard::MuteMyChannel() { SetFaderIsMute ( iMyChannelID, true ); } +void CAudioMixerBoard::MuteMyChannel() { SetFaderIsMute ( pClient->GetMyChannelID(), true ); } diff --git a/src/audiomixerboard.h b/src/audiomixerboard.h index 6b42d35085..9cafe4a393 100644 --- a/src/audiomixerboard.h +++ b/src/audiomixerboard.h @@ -56,7 +56,8 @@ class CChannelFader : public QObject int GetReceivedInstrument() { return cReceivedChanInfo.iInstrument; } QString GetReceivedCity() { return cReceivedChanInfo.strCity; } int GetReceivedChID() { return cReceivedChanInfo.iChanID; } - void SetChannelInfos ( const CChannelInfo& cChanInfo ); + int GetReceivedMIDIID() { return cReceivedMIDIID; } + void SetChannelInfos ( const CChannelInfo& cChanInfo, int iMIDIID ); void Show() { pFrame->show(); } void Hide() { pFrame->hide(); } bool IsVisible() { return !pFrame->isHidden(); } @@ -120,6 +121,7 @@ class CChannelFader : public QObject QLabel* plblCountryFlag; CChannelInfo cReceivedChanInfo; + int cReceivedMIDIID; bool bOtherChannelIsSolo; bool bIsMyOwnFader; @@ -190,6 +192,7 @@ class CAudioMixerBoard : public QGroupBox, public CAudioMixerBoardSlots& vecChanInfo ); @@ -200,8 +203,6 @@ class CAudioMixerBoard : public QGroupBox, public CAudioMixerBoardSlots vecpChanFader; CMixerBoardScrollArea* pScrollArea; @@ -267,7 +269,6 @@ class CAudioMixerBoard : public QGroupBox, public CAudioMixerBoardSlots "false" */ + iMyChannelID ( INVALID_INDEX ), CurOpusEncoder ( nullptr ), CurOpusDecoder ( nullptr ), eAudioCompressionType ( CT_OPUS ), @@ -789,8 +790,44 @@ void CClient::OnHandledSignal ( int sigNum ) #endif } -void CClient::OnControllerInFaderLevel ( int iChannelIdx, int iValue ) +// Ensure user's own channel is always given MIDI offset 0, so it is the first physical fader +// All channels with a client ID less than user's own will use ID+1 as their MIDI offset + +int CClient::ChanToMIDI ( const int iChannelIdx ) +{ + if ( iMyChannelID == INVALID_INDEX ) + return iChannelIdx; + + if ( iChannelIdx == iMyChannelID ) + return 0; + + if ( iChannelIdx < iMyChannelID ) + return iChannelIdx + 1; + + return iChannelIdx; +} + +int CClient::MIDIToChan ( const int iMIDIIdx ) +{ + if ( iMyChannelID == INVALID_INDEX ) + return iMIDIIdx; + + if ( iMIDIIdx == 0 ) + return iMyChannelID; + + if ( iMIDIIdx <= iMyChannelID ) + return iMIDIIdx - 1; + + return iMIDIIdx; +} + +// Handle received MIDI controls + +void CClient::OnControllerInFaderLevel ( int iMIDIIdx, int iValue ) { + // map the MIDI index to the real channel number + const int iChannelIdx = MIDIToChan ( iMIDIIdx ); + // in case of a headless client the faders cannot be moved so we need // to send the controller information directly to the server #ifdef HEADLESS @@ -804,8 +841,11 @@ void CClient::OnControllerInFaderLevel ( int iChannelIdx, int iValue ) emit ControllerInFaderLevel ( iChannelIdx, iValue ); } -void CClient::OnControllerInPanValue ( int iChannelIdx, int iValue ) +void CClient::OnControllerInPanValue ( int iMIDIIdx, int iValue ) { + // map the MIDI index to the real channel number + const int iChannelIdx = MIDIToChan ( iMIDIIdx ); + // in case of a headless client the panners cannot be moved so we need // to send the controller information directly to the server #ifdef HEADLESS @@ -816,8 +856,11 @@ void CClient::OnControllerInPanValue ( int iChannelIdx, int iValue ) emit ControllerInPanValue ( iChannelIdx, iValue ); } -void CClient::OnControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ) +void CClient::OnControllerInFaderIsSolo ( int iMIDIIdx, bool bIsSolo ) { + // map the MIDI index to the real channel number + const int iChannelIdx = MIDIToChan ( iMIDIIdx ); + // in case of a headless client the buttons are not displayed so we need // to send the controller information directly to the server #ifdef HEADLESS @@ -827,8 +870,11 @@ void CClient::OnControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ) emit ControllerInFaderIsSolo ( iChannelIdx, bIsSolo ); } -void CClient::OnControllerInFaderIsMute ( int iChannelIdx, bool bIsMute ) +void CClient::OnControllerInFaderIsMute ( int iMIDIIdx, bool bIsMute ) { + // map the MIDI index to the real channel number + const int iChannelIdx = MIDIToChan ( iMIDIIdx ); + // in case of a headless client the buttons are not displayed so we need // to send the controller information directly to the server #ifdef HEADLESS @@ -851,6 +897,9 @@ void CClient::OnControllerInMuteMyself ( bool bMute ) void CClient::OnClientIDReceived ( int iChanID ) { + // remember our client ID received from the server + iMyChannelID = iChanID; + // 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) @@ -903,6 +952,9 @@ void CClient::Stop() // disconnects the connection anyway). ConnLessProtocol.CreateCLDisconnection ( Channel.GetAddress() ); + // forget our own channel ID + iMyChannelID = INVALID_INDEX; + // reset current signal level and LEDs bJitterBufferOK = true; SignalLevelMeter.Reset(); diff --git a/src/client.h b/src/client.h index 3f9e30be6d..24a7977f00 100644 --- a/src/client.h +++ b/src/client.h @@ -133,6 +133,8 @@ class CClient : public QObject bool IsConnected() { return Channel.IsConnected(); } + int GetMyChannelID() { return iMyChannelID; } + EGUIDesign GetGUIDesign() const { return eGUIDesign; } void SetGUIDesign ( const EGUIDesign eNGD ) { eGUIDesign = eNGD; } @@ -272,6 +274,10 @@ class CClient : public QObject Channel.GetBufErrorRates ( vecErrRates, dLimit, dMaxUpLimit ); } + // convert between MIDI index and real channel index + int ChanToMIDI ( const int iChanIdx ); + int MIDIToChan ( const int iMIDIIdx ); + // settings CChannelCoreInfo ChannelInfo; QString strClientName; @@ -292,6 +298,9 @@ class CClient : public QObject CChannel Channel; CProtocol ConnLessProtocol; + // this client's channel ID sent by the server + int iMyChannelID; // must use int (not size_t) so INVALID_INDEX can be stored + // audio encoder/decoder OpusCustomMode* Opus64Mode; OpusCustomEncoder* Opus64EncoderMono; @@ -398,10 +407,11 @@ protected slots: void OnCLPingWithNumClientsReceived ( CHostAddress InetAddr, int iMs, int iNumClients ); void OnSndCrdReinitRequest ( int iSndCrdResetType ); - void OnControllerInFaderLevel ( int iChannelIdx, int iValue ); - void OnControllerInPanValue ( int iChannelIdx, int iValue ); - void OnControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); - void OnControllerInFaderIsMute ( int iChannelIdx, bool bIsMute ); + + void OnControllerInFaderLevel ( int iMIDIIdx, int iValue ); + void OnControllerInPanValue ( int iMIDIIdx, int iValue ); + void OnControllerInFaderIsSolo ( int iMIDIIdx, bool bIsSolo ); + void OnControllerInFaderIsMute ( int iMIDIIdx, bool bIsMute ); void OnControllerInMuteMyself ( bool bMute ); void OnClientIDReceived ( int iChanID ); void OnConClientListMesReceived ( CVector vecChanInfo ); diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index cb6b134bf7..1d9b5d2cc3 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -214,7 +214,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // MeterStyle init SetMeterStyle ( pClient->GetMeterStyle() ); - // set the settings pointer to the mixer board (must be done early) + // pass the client and settings pointers to the mixer board (must be done early) + MainMixerBoard->SetClientPointer ( pClient ); MainMixerBoard->SetSettingsPointer ( pSettings ); MainMixerBoard->SetNumMixerPanelRows ( pSettings->iNumMixerPanelRows ); @@ -487,8 +488,6 @@ CClientDlg::CClientDlg ( CClient* pNCliP, QObject::connect ( pClient, &CClient::ChatTextReceived, this, &CClientDlg::OnChatTextReceived ); - QObject::connect ( pClient, &CClient::ClientIDReceived, this, &CClientDlg::OnClientIDReceived ); - QObject::connect ( pClient, &CClient::MuteStateHasChangedReceived, this, &CClientDlg::OnMuteStateHasChangedReceived ); QObject::connect ( pClient, &CClient::RecorderStateReceived, this, &CClientDlg::OnRecorderStateReceived ); diff --git a/src/clientdlg.h b/src/clientdlg.h index 2a9062a58d..3e20b3534e 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -223,8 +223,6 @@ public slots: ConnectDlg.SetConnClientsList ( InetAddr, vecChanInfo ); } - void OnClientIDReceived ( int iChanID ) { MainMixerBoard->SetMyChannelID ( iChanID ); } - void OnMuteStateHasChangedReceived ( int iChanID, bool bIsMuted ) { MainMixerBoard->SetRemoteFaderIsMute ( iChanID, bIsMuted ); } void OnCLChannelLevelListReceived ( CHostAddress /* unused */, CVector vecLevelList ) diff --git a/src/sound/soundbase.h b/src/sound/soundbase.h index 4aa6c629bc..c36205a8c9 100644 --- a/src/sound/soundbase.h +++ b/src/sound/soundbase.h @@ -168,9 +168,9 @@ class CSoundBase : public QThread signals: void ReinitRequest ( int iSndCrdResetType ); - void ControllerInFaderLevel ( int iChannelIdx, int iValue ); - void ControllerInPanValue ( int iChannelIdx, int iValue ); - void ControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); - void ControllerInFaderIsMute ( int iChannelIdx, bool bIsMute ); + void ControllerInFaderLevel ( int iMIDIIdx, int iValue ); + void ControllerInPanValue ( int iMIDIIdx, int iValue ); + void ControllerInFaderIsSolo ( int iMIDIIdx, bool bIsSolo ); + void ControllerInFaderIsMute ( int iMIDIIdx, bool bIsMute ); void ControllerInMuteMyself ( bool bMute ); };