From 6f049a5d06c74ed92b2590a644ec7d72d2a8a2ca Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Thu, 22 May 2025 12:41:28 +0200 Subject: [PATCH 01/24] Add MIDI GUI tab and learn function --- src/client.cpp | 12 + src/client.h | 6 + src/clientdlg.cpp | 13 +- src/clientdlg.h | 2 + src/clientsettingsdlg.cpp | 259 +++++++++++++- src/clientsettingsdlg.h | 14 + src/clientsettingsdlgbase.ui | 654 ++++++++++++++++++++++++++++++++++- src/global.h | 1 + src/main.cpp | 94 ++++- src/settings.cpp | 310 ++++++++++------- src/settings.h | 12 + src/sound/soundbase.cpp | 11 + src/sound/soundbase.h | 2 + 13 files changed, 1250 insertions(+), 140 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 0aee733ca4..96613e9428 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -172,6 +172,8 @@ CClient::CClient ( const quint16 iPortNumber, QObject::connect ( pSignalHandler, &CSignalHandler::HandledSignal, this, &CClient::OnHandledSignal ); + QObject::connect ( &Sound, &CSoundBase::MidiCCReceived, this, &CClient::OnMidiCCReceived ); + // start timer so that elapsed time works PreciseTime.start(); @@ -1550,6 +1552,16 @@ void CClient::FreeClientChannel ( const int iServerChannelID ) */ } +void CClient::ApplyMIDIMapping ( const QString& midiMap ) +{ + Sound.SetMIDIMapping ( midiMap ); +} + +void CClient::OnMidiCCReceived ( int ccNumber ) +{ + emit MidiCCReceived ( ccNumber ); +} + // 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 ) diff --git a/src/client.h b/src/client.h index 344db5163d..4d54be1833 100644 --- a/src/client.h +++ b/src/client.h @@ -295,6 +295,8 @@ class CClient : public QObject CChannelCoreInfo ChannelInfo; QString strClientName; + void ApplyMIDIMapping(const QString& midiMap); + protected: // callback function must be static, otherwise it does not work static void AudioCallback ( CVector& psData, void* arg ); @@ -471,4 +473,8 @@ protected slots: void ControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); void ControllerInFaderIsMute ( int iChannelIdx, bool bIsMute ); void ControllerInMuteMyself ( bool bMute ); + void MidiCCReceived ( int ccNumber ); + +private slots: + void OnMidiCCReceived ( int ccNumber ); }; diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index b7048155b1..66e0c8c493 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -400,6 +400,9 @@ CClientDlg::CClientDlg ( CClient* pNCliP, pSettingsMenu->addAction ( tr ( "A&dvanced Settings..." ), this, SLOT ( OnOpenAdvancedSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_D ) ); + pSettingsMenu->addAction ( tr ( "&MIDI Settings..." ), this, SLOT ( OnOpenMidiSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_M ) ); + + // Main menu bar ----------------------------------------------------------- QMenuBar* pMenu = new QMenuBar ( this ); @@ -985,11 +988,10 @@ void CClientDlg::ShowConnectionSetupDialog() void CClientDlg::ShowGeneralSettings ( int iTab ) { // open general settings dialog - emit SendTabChange ( iTab ); + emit SendTabChange ( iTab ); ClientSettingsDlg.show(); - ClientSettingsDlg.setWindowTitle ( MakeClientNameTitle ( tr ( "Settings" ), pClient->strClientName ) ); - // make sure dialog is upfront and has focus + ClientSettingsDlg.setWindowTitle ( MakeClientNameTitle ( tr ( "Settings" ), pClient->strClientName ) ); ClientSettingsDlg.raise(); ClientSettingsDlg.activateWindow(); } @@ -1516,3 +1518,8 @@ void CClientDlg::SetPingTime ( const int iPingTime, const int iOverallDelayMs, c // set current LED status ledDelay->SetLight ( eOverallDelayLEDColor ); } + +void CClientDlg::OnOpenMidiSettings() +{ + ShowGeneralSettings ( SETTING_TAB_MIDI ); +} diff --git a/src/clientdlg.h b/src/clientdlg.h index ba01ea648e..a3910a18f0 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -243,6 +243,8 @@ public slots: void accept() { close(); } // introduced by pljones + void OnOpenMidiSettings(); + signals: void SendTabChange ( int iTabIdx ); }; diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 07b76a64c2..9a2d96e49d 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -397,6 +397,39 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet "A second sound device may be required to hear the alerts." ) ); chbAudioAlerts->setAccessibleName ( tr ( "Audio Alerts check box" ) ); + // MIDI settings + QString strMidiSettings = "" + tr ( "MIDI controller settings" ) + ": " + + tr ( "There is one global MIDI channel parameter (1-16) and two parameters you can set " + "for each item controlled: offset and consecutive CC numbers (count). First set the " + "channel you want Jamulus to listen on (0 for all channels). Then, for each item " + "you want to control (volume fader, pan, solo, mute), set the offset (CC number " + "to start from) and number of consecutive CC numbers (count). There is one " + "exception that does not require establishing consecutive CC numbers which is " + "the “Mute Myself” parameter - it only requires a single CC number as it is only " + "applied to one’s own audio stream." ) + + "
" + + tr ( "You can either type in the MIDI CC values or use the \"Learn\" button: click on " + "\"Learn\", move the fader/knob on your MIDI controller, and the MIDI CC number " + "will be saved." ); + lblChannel->setWhatsThis ( strMidiSettings ); + lblMuteMyself->setWhatsThis ( strMidiSettings ); + faderGroup->setWhatsThis ( strMidiSettings ); + panGroup->setWhatsThis ( strMidiSettings ); + soloGroup->setWhatsThis ( strMidiSettings ); + muteGroup->setWhatsThis ( strMidiSettings ); + + spnChannel->setAccessibleName ( tr ( "MIDI channel spin box" ) ); + spnMuteMyself->setAccessibleName ( tr ( "Mute Myself MIDI CC number spin box" ) ); + spnFaderOffset->setAccessibleName ( tr ( "Fader offset spin box" ) ); + spnPanOffset->setAccessibleName ( tr ( "Pan offset spin box" ) ); + spnSoloOffset->setAccessibleName ( tr ( "Solo offset spin box" ) ); + spnMuteOffset->setAccessibleName ( tr ( "Mute offset spin box" ) ); + butLearnMuteMyself->setAccessibleName ( tr ( "Mute Myself MIDI learn button" ) ); + butLearnFaderOffset->setAccessibleName ( tr ( "Fader offset MIDI learn button" ) ); + butLearnPanOffset->setAccessibleName ( tr ( "Pan offset MIDI learn button" ) ); + butLearnSoloOffset->setAccessibleName ( tr ( "Solo offset MIDI learn button" ) ); + butLearnMuteOffset->setAccessibleName ( tr ( "Mute offset MIDI learn button" ) ); + // init driver button #if defined( _WIN32 ) && !defined( WITH_JACK ) butDriverSetup->setText ( tr ( "ASIO Device Settings" ) ); @@ -750,12 +783,72 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet tabSettings->setCurrentIndex ( pSettings->iSettingsTab ); + // MIDI tab + QObject::connect ( spnChannel, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiChannel = v; ApplyMIDIMappingFromSettings(); } ); + + QObject::connect ( spnMuteMyself, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiMuteMyself = v; ApplyMIDIMappingFromSettings(); } ); + + QObject::connect ( spnFaderOffset, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiFaderOffset = v; ApplyMIDIMappingFromSettings(); } ); + + QObject::connect ( spnFaderCount, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiFaderCount = v; ApplyMIDIMappingFromSettings(); } ); + + QObject::connect ( spnPanOffset, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiPanOffset = v; ApplyMIDIMappingFromSettings(); } ); + + QObject::connect ( spnPanCount, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiPanCount = v; ApplyMIDIMappingFromSettings(); } ); + + QObject::connect ( spnSoloOffset, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiSoloOffset = v; ApplyMIDIMappingFromSettings(); } ); + + QObject::connect ( spnSoloCount, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiSoloCount = v; ApplyMIDIMappingFromSettings(); } ); + + QObject::connect ( spnMuteOffset, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiMuteOffset = v; ApplyMIDIMappingFromSettings(); } ); + + QObject::connect ( spnMuteCount, + static_cast ( &QSpinBox::valueChanged ), + this, + [this] ( int v ) { pSettings->midiMuteCount = v; ApplyMIDIMappingFromSettings(); } ); + + // MIDI Learn buttons + QObject::connect ( butLearnMuteMyself, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnMuteMyself ); + QObject::connect ( butLearnFaderOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnFaderOffset ); + QObject::connect ( butLearnPanOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnPanOffset ); + QObject::connect ( butLearnSoloOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnSoloOffset ); + QObject::connect ( butLearnMuteOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnMuteOffset ); + // Connect MIDI CC signal from sound engine + QObject::connect ( pClient, &CClient::MidiCCReceived, this, &CClientSettingsDlg::OnMidiCCReceived ); + // Timers ------------------------------------------------------------------ // start timer for status bar TimerStatus.start ( DISPLAY_UPDATE_TIME ); } -void CClientSettingsDlg::showEvent ( QShowEvent* ) +void CClientSettingsDlg::showEvent ( QShowEvent* event ) { UpdateDisplay(); UpdateDirectoryComboBox(); @@ -774,6 +867,20 @@ void CClientSettingsDlg::showEvent ( QShowEvent* ) // select the skill level pcbxSkill->setCurrentIndex ( pcbxSkill->findData ( static_cast ( pClient->ChannelInfo.eSkillLevel ) ) ); + + // MIDI tab: set widgets from settings + spnChannel->setValue ( pSettings->midiChannel ); + spnMuteMyself->setValue ( pSettings->midiMuteMyself ); + spnFaderOffset->setValue ( pSettings->midiFaderOffset ); + spnFaderCount->setValue ( pSettings->midiFaderCount ); + spnPanOffset->setValue ( pSettings->midiPanOffset ); + spnPanCount->setValue ( pSettings->midiPanCount ); + spnSoloOffset->setValue ( pSettings->midiSoloOffset ); + spnSoloCount->setValue ( pSettings->midiSoloCount ); + spnMuteOffset->setValue ( pSettings->midiMuteOffset ); + spnMuteCount->setValue ( pSettings->midiMuteCount ); + + QDialog::showEvent ( event ) ; } void CClientSettingsDlg::UpdateJitterBufferFrame() @@ -1216,3 +1323,153 @@ void CClientSettingsDlg::OnAudioPanValueChanged ( int value ) pClient->SetAudioInFader ( value ); UpdateAudioFaderSlider(); } + +void CClientSettingsDlg::ApplyMIDIMappingFromSettings() +{ + QString midiMap = QString ( "%1;f%2*%3;p%4*%5;s%6*%7;m%8*%9;o%10" ) + .arg ( pSettings->midiChannel ) + .arg ( pSettings->midiFaderOffset ) + .arg ( pSettings->midiFaderCount ) + .arg ( pSettings->midiPanOffset ) + .arg ( pSettings->midiPanCount ) + .arg ( pSettings->midiSoloOffset ) + .arg ( pSettings->midiSoloCount ) + .arg ( pSettings->midiMuteOffset ) + .arg ( pSettings->midiMuteCount ) + .arg ( pSettings->midiMuteMyself ); + + pClient->ApplyMIDIMapping ( midiMap ); +} + +void CClientSettingsDlg::ResetMidiLearn() +{ + midiLearnTarget = None; + butLearnMuteMyself->setText ( tr ( "Learn" ) ); + butLearnMuteMyself->setEnabled ( true ); + butLearnFaderOffset->setText ( tr ( "Learn" ) ); + butLearnFaderOffset->setEnabled ( true ); + butLearnPanOffset->setText ( tr ( "Learn" ) ); + butLearnPanOffset->setEnabled(true); + butLearnSoloOffset->setText ( tr ( "Learn" ) ); + butLearnSoloOffset->setEnabled ( true ); + butLearnMuteOffset->setText ( tr ( "Learn" ) ); + butLearnMuteOffset->setEnabled ( true ); +} + +void CClientSettingsDlg::OnLearnMuteMyself() +{ + if ( midiLearnTarget == MuteMyself ) + { + ResetMidiLearn(); + return; + } + + ResetMidiLearn(); + midiLearnTarget = MuteMyself; + butLearnMuteMyself->setText ( tr ( "Listening..." ) ); + butLearnMuteMyself->setEnabled ( true ); + butLearnFaderOffset->setEnabled ( false ); + butLearnPanOffset->setEnabled ( false ); + butLearnSoloOffset->setEnabled ( false ); + butLearnMuteOffset->setEnabled ( false ); +} + +void CClientSettingsDlg::OnLearnFaderOffset() +{ + if ( midiLearnTarget == Fader ) + { + ResetMidiLearn(); + return; + } + + ResetMidiLearn(); + midiLearnTarget = Fader; + butLearnFaderOffset->setText ( tr ( "Listening..." ) ); + butLearnFaderOffset->setEnabled ( true ); + butLearnMuteMyself->setEnabled ( false ); + butLearnPanOffset->setEnabled ( false ); + butLearnSoloOffset->setEnabled ( false ); + butLearnMuteOffset->setEnabled ( false ); +} + +void CClientSettingsDlg::OnLearnPanOffset() +{ + if ( midiLearnTarget == Pan ) + { + ResetMidiLearn(); + return; + } + + ResetMidiLearn(); + midiLearnTarget = Pan; + butLearnPanOffset->setText (tr ( "Listening..." ) ); + butLearnPanOffset->setEnabled ( true ); + butLearnMuteMyself->setEnabled ( false ); + butLearnFaderOffset->setEnabled ( false ); + butLearnSoloOffset->setEnabled ( false ); + butLearnMuteOffset->setEnabled ( false ); +} + +void CClientSettingsDlg::OnLearnSoloOffset() +{ + if (midiLearnTarget == Solo) + { + ResetMidiLearn(); + return; + } + + ResetMidiLearn(); + midiLearnTarget = Solo; + butLearnSoloOffset->setText ( tr ( "Listening..." ) ); + butLearnSoloOffset->setEnabled ( true ); + butLearnMuteMyself->setEnabled ( false ); + butLearnFaderOffset->setEnabled ( false ); + butLearnPanOffset->setEnabled ( false ); + butLearnMuteOffset->setEnabled ( false ); +} + +void CClientSettingsDlg::OnLearnMuteOffset() +{ + if (midiLearnTarget == Mute) + { + ResetMidiLearn(); + return; + } + + ResetMidiLearn(); + midiLearnTarget = Mute; + butLearnMuteOffset->setText ( tr ( "Listening..." ) ); + butLearnMuteOffset->setEnabled ( true ); + butLearnMuteMyself->setEnabled ( false ); + butLearnFaderOffset->setEnabled ( false ); + butLearnPanOffset->setEnabled ( false ); + butLearnSoloOffset->setEnabled ( false ); +} + +void CClientSettingsDlg::OnMidiCCReceived ( int ccNumber ) +{ + if ( midiLearnTarget == None ) + return; + + switch ( midiLearnTarget ) + { + case Fader: + spnFaderOffset->setValue ( ccNumber ); + break; + case Pan: + spnPanOffset->setValue ( ccNumber ); + break; + case Solo: + spnSoloOffset->setValue ( ccNumber ); + break; + case Mute: + spnMuteOffset->setValue ( ccNumber ); + break; + case MuteMyself: + spnMuteMyself->setValue ( ccNumber ); + break; + default: + break; + } + ResetMidiLearn(); +} diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 6a6f1cc7d3..1e478cc783 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -67,6 +67,7 @@ class CClientSettingsDlg : public CBaseDlg, private Ui_CClientSettingsDlgBase void UpdateSoundCardFrame(); void UpdateDirectoryComboBox(); void UpdateAudioFaderSlider(); + void ApplyMIDIMappingFromSettings(); QString GenSndCrdBufferDelayString ( const int iFrameSize, const QString strAddText = "" ); virtual void showEvent ( QShowEvent* ); @@ -119,4 +120,17 @@ public slots: void AudioChannelsChanged(); void CustomDirectoriesChanged(); void NumMixerPanelRowsChanged ( int value ); + +private: + enum MidiLearnTarget { None, MuteMyself, Fader, Pan, Solo, Mute }; + MidiLearnTarget midiLearnTarget = None; + void ResetMidiLearn(); + +private slots: + void OnLearnMuteMyself(); + void OnLearnFaderOffset(); + void OnLearnPanOffset(); + void OnLearnSoloOffset(); + void OnLearnMuteOffset(); + void OnMidiCCReceived ( int ccNumber ); }; diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index 7ee311701a..bfdc2575ba 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -6,8 +6,8 @@ 0 0 - 436 - 524 + 622 + 647 @@ -30,7 +30,7 @@ - 1 + 3 true @@ -1336,6 +1336,639 @@ + + + MIDI + + + + + + + + + 75 + true + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Channel + + + + + + + + 50 + false + + + + 16 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Mute Myself + + + + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 50 + false + + + + Fader + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Offset + + + + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Count + + + + + + + + 50 + false + + + + 1 + + + 127 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 50 + false + + + + Pan + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Offset + + + + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Count + + + + + + + + 50 + false + + + + 1 + + + 127 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 50 + false + + + + Solo + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Offset + + + + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Count + + + + + + + + 50 + false + + + + 1 + + + 127 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 50 + false + + + + Mute + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Offset + + + + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Count + + + + + + + + 50 + false + + + + 1 + + + 127 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + @@ -1379,6 +2012,21 @@ cbxInputBoost chbDetectFeedback sldAudioPan + spnChannel + spnMuteMyself + butLearnMuteMyself + spnFaderOffset + butLearnFaderOffset + spnFaderCount + spnPanOffset + butLearnPanOffset + spnPanCount + spnSoloOffset + butLearnSoloOffset + spnSoloCount + spnMuteOffset + butLearnMuteOffset + spnMuteCount diff --git a/src/global.h b/src/global.h index 55d2b57683..256b1d3f40 100644 --- a/src/global.h +++ b/src/global.h @@ -266,6 +266,7 @@ LED bar: lbr #define SETTING_TAB_USER 0 #define SETTING_TAB_AUDIONET 1 #define SETTING_TAB_ADVANCED 2 +#define SETTING_TAB_MIDI 3 // common tool tip bottom line text #define TOOLTIP_COM_END_TEXT \ diff --git a/src/main.cpp b/src/main.cpp index cb81b387ca..9a9dee4359 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -920,21 +920,93 @@ int main ( int argc, char** argv ) #ifndef SERVER_ONLY if ( bIsClient ) { - // Client: - // actual client object - CClient Client ( iPortNumber, - iQosNumber, - strConnOnStartupAddress, - strMIDISetup, - bNoAutoJackConnect, - strClientName, - bEnableIPv6, - bMuteMeInPersonalMix ); + if ( strMIDISetup.isEmpty() ) + { + CClientSettings tmpSettings ( nullptr, strIniFileName ); + tmpSettings.Load ( CommandLineOptions ); + + strMIDISetup = QString ( "%1;f%2*%3;p%4*%5;s%6*%7;m%8*%9;o%10" ) + .arg ( tmpSettings.midiChannel ) + .arg ( tmpSettings.midiFaderOffset ) + .arg ( tmpSettings.midiFaderCount ) + .arg ( tmpSettings.midiPanOffset ) + .arg ( tmpSettings.midiPanCount ) + .arg ( tmpSettings.midiSoloOffset ) + .arg ( tmpSettings.midiSoloCount ) + .arg ( tmpSettings.midiMuteOffset ) + .arg ( tmpSettings.midiMuteCount ) + .arg ( tmpSettings.midiMuteMyself ); + } - // load settings from init-file (command line options override) + CClient Client( + iPortNumber, + iQosNumber, + strConnOnStartupAddress, + strMIDISetup, + bNoAutoJackConnect, + strClientName, + bEnableIPv6, + bMuteMeInPersonalMix + ); + + // Create Settings with the client pointer CClientSettings Settings ( &Client, strIniFileName ); Settings.Load ( CommandLineOptions ); + if ( !strMIDISetup.isEmpty() ) + { + QStringList slMIDIParams = strMIDISetup.split ( ";" ); + if ( slMIDIParams.count() >= 1 ) + { + Settings.midiChannel = slMIDIParams[0].toInt(); + for ( int i = 1; i < slMIDIParams.count(); ++i ) + { + QString sParm = slMIDIParams[i].trimmed(); + if ( sParm.startsWith ( "f" ) ) + { + QStringList slP = sParm.mid ( 1 ).split ( '*' ); + Settings.midiFaderOffset = slP[0].toInt(); + if ( slP.size() > 1 ) + { + Settings.midiFaderCount = slP[1].toInt(); + } + } + else if ( sParm.startsWith ( "p" ) ) + { + QStringList slP = sParm.mid ( 1 ).split ( '*' ); + Settings.midiPanOffset = slP[0].toInt(); + if ( slP.size() > 1 ) + { + Settings.midiPanCount = slP[1].toInt(); + } + } + else if ( sParm.startsWith ( "s" ) ) + { + QStringList slP = sParm.mid ( 1 ).split ( '*' ); + Settings.midiSoloOffset = slP[0].toInt(); + if ( slP.size() > 1 ) + { + Settings.midiSoloCount = slP[1].toInt(); + } + } + else if ( sParm.startsWith ( "m" ) ) + { + QStringList slP = sParm.mid ( 1 ).split ( '*' ); + Settings.midiMuteOffset = slP[0].toInt(); + if ( slP.size() > 1 ) + { + Settings.midiMuteCount = slP[1].toInt(); + } + } + else if ( sParm.startsWith ( "o" ) ) + { + QStringList slP = sParm.mid ( 1 ).split ( '*' ); + Settings.midiMuteMyself = slP[0].toInt(); + } + } + } + } + # ifndef NO_JSON_RPC if ( pRpcServer ) { diff --git a/src/settings.cpp b/src/settings.cpp index e9ccf90d23..c46f05e6f5 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -289,176 +289,230 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, bEnableAudioAlerts = bValue; } - // name - pClient->ChannelInfo.strName = FromBase64ToString ( - GetIniSetting ( IniXMLDocument, "client", "name_base64", ToBase64 ( QCoreApplication::translate ( "CMusProfDlg", "No Name" ) ) ) ); - - // instrument - if ( GetNumericIniSet ( IniXMLDocument, "client", "instrument", 0, CInstPictures::GetNumAvailableInst() - 1, iValue ) ) + // --- MIDI settings (do NOT require pClient) --- + if ( GetNumericIniSet ( IniXMLDocument, "client", "midichannel", 0, 15, iValue ) ) { - pClient->ChannelInfo.iInstrument = iValue; + midiChannel = iValue; } - // country - if ( GetNumericIniSet ( IniXMLDocument, "client", "country", 0, static_cast ( QLocale::LastCountry ), iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midifaderoffset", 0, 127, iValue ) ) { - pClient->ChannelInfo.eCountry = CLocale::WireFormatCountryCodeToQtCountry ( iValue ); - } - else - { - // if no country is given, use the one from the operating system - pClient->ChannelInfo.eCountry = QLocale::system().country(); + midiFaderOffset = iValue; } - // city - pClient->ChannelInfo.strCity = FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "city_base64" ) ); - - // skill level - if ( GetNumericIniSet ( IniXMLDocument, "client", "skill", 0, 3 /* SL_PROFESSIONAL */, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midifadercount", 0, 127, iValue ) ) { - pClient->ChannelInfo.eSkillLevel = static_cast ( iValue ); + midiFaderCount = iValue; } - // audio fader - if ( GetNumericIniSet ( IniXMLDocument, "client", "audfad", AUD_FADER_IN_MIN, AUD_FADER_IN_MAX, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midipanoffset", 0, 127, iValue ) ) { - pClient->SetAudioInFader ( iValue ); + midiPanOffset = iValue; } - // reverberation level - if ( GetNumericIniSet ( IniXMLDocument, "client", "revlev", 0, AUD_REVERB_MAX, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midipancount", 0, 127, iValue ) ) { - pClient->SetReverbLevel ( iValue ); + midiPanCount = iValue; } - - // reverberation channel assignment - if ( GetFlagIniSet ( IniXMLDocument, "client", "reverblchan", bValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midisolooffset", 0, 127, iValue ) ) { - pClient->SetReverbOnLeftChan ( bValue ); + midiSoloOffset = iValue; } - // sound card selection - const QString strError = pClient->SetSndCrdDev ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "auddev_base64", "" ) ) ); - - if ( !strError.isEmpty() ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midisolocount", 0, 127, iValue ) ) { -# 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 + midiSoloCount = iValue; } - // sound card channel mapping settings: make sure these settings are - // set AFTER the sound card device is set, otherwise the settings are - // overwritten by the defaults - // - // sound card left input channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midimuteoffset", 0, 127, iValue ) ) { - pClient->SetSndCrdLeftInputChannel ( iValue ); + midiMuteOffset = iValue; } - // sound card right input channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midimutecount", 0, 127, iValue ) ) { - pClient->SetSndCrdRightInputChannel ( iValue ); + midiMuteCount = iValue; } - // sound card left output channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midimutemyself", 0, 127, iValue ) ) { - pClient->SetSndCrdLeftOutputChannel ( iValue ); + midiMuteMyself = iValue; } - // sound card right output channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + // All code that uses pClient must be guarded + if ( pClient ) { - pClient->SetSndCrdRightOutputChannel ( iValue ); - } + // name + pClient->ChannelInfo.strName = FromBase64ToString ( + GetIniSetting ( IniXMLDocument, "client", "name_base64", ToBase64 ( QCoreApplication::translate ( "CMusProfDlg", "No Name" ) ) ) ); - // sound card preferred buffer size index - if ( GetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", FRAME_SIZE_FACTOR_PREFERRED, FRAME_SIZE_FACTOR_SAFE, iValue ) ) - { - // additional check required since only a subset of factors are - // defined - if ( ( iValue == FRAME_SIZE_FACTOR_PREFERRED ) || ( iValue == FRAME_SIZE_FACTOR_DEFAULT ) || ( iValue == FRAME_SIZE_FACTOR_SAFE ) ) + // instrument + if ( GetNumericIniSet ( IniXMLDocument, "client", "instrument", 0, CInstPictures::GetNumAvailableInst() - 1, iValue ) ) { - pClient->SetSndCrdPrefFrameSizeFactor ( iValue ); + pClient->ChannelInfo.iInstrument = iValue; } - } - // automatic network jitter buffer size setting - if ( GetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", bValue ) ) - { - pClient->SetDoAutoSockBufSize ( bValue ); - } + // country + if ( GetNumericIniSet ( IniXMLDocument, "client", "country", 0, static_cast ( QLocale::LastCountry ), iValue ) ) + { + pClient->ChannelInfo.eCountry = CLocale::WireFormatCountryCodeToQtCountry ( iValue ); + } + else + { + // if no country is given, use the one from the operating system + pClient->ChannelInfo.eCountry = QLocale::system().country(); + } - // network jitter buffer size - if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbuf", MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) - { - pClient->SetSockBufNumFrames ( iValue ); - } + // city + pClient->ChannelInfo.strCity = FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "city_base64" ) ); - // network jitter buffer size for server - if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbufserver", MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) - { - pClient->SetServerSockBufNumFrames ( iValue ); - } + // skill level + if ( GetNumericIniSet ( IniXMLDocument, "client", "skill", 0, 3 /* SL_PROFESSIONAL */, iValue ) ) + { + pClient->ChannelInfo.eSkillLevel = static_cast ( iValue ); + } - // enable OPUS64 setting - if ( GetFlagIniSet ( IniXMLDocument, "client", "enableopussmall", bValue ) ) - { - pClient->SetEnableOPUS64 ( bValue ); - } + // audio fader + if ( GetNumericIniSet ( IniXMLDocument, "client", "audfad", AUD_FADER_IN_MIN, AUD_FADER_IN_MAX, iValue ) ) + { + pClient->SetAudioInFader ( iValue ); + } - // GUI design - if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", 0, 2 /* GD_SLIMFADER */, iValue ) ) - { - pClient->SetGUIDesign ( static_cast ( iValue ) ); - } + // reverberation level + if ( GetNumericIniSet ( IniXMLDocument, "client", "revlev", 0, AUD_REVERB_MAX, iValue ) ) + { + pClient->SetReverbLevel ( iValue ); + } - // MeterStyle - if ( GetNumericIniSet ( IniXMLDocument, "client", "meterstyle", 0, 4 /* MT_LED_ROUND_BIG */, iValue ) ) - { - pClient->SetMeterStyle ( static_cast ( iValue ) ); - } - else - { - // if MeterStyle is not found in the ini, set it based on the GUI design + // reverberation channel assignment + if ( GetFlagIniSet ( IniXMLDocument, "client", "reverblchan", bValue ) ) + { + pClient->SetReverbOnLeftChan ( bValue ); + } + + // sound card selection + const QString strError = pClient->SetSndCrdDev ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "auddev_base64", "" ) ) ); + + 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 + } + + // sound card channel mapping settings: make sure these settings are + // set AFTER the sound card device is set, otherwise the settings are + // overwritten by the defaults + // + // sound card left input channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + { + pClient->SetSndCrdLeftInputChannel ( iValue ); + } + + // sound card right input channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + { + pClient->SetSndCrdRightInputChannel ( iValue ); + } + + // sound card left output channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + { + pClient->SetSndCrdLeftOutputChannel ( iValue ); + } + + // sound card right output channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + { + pClient->SetSndCrdRightOutputChannel ( iValue ); + } + + // sound card preferred buffer size index + if ( GetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", FRAME_SIZE_FACTOR_PREFERRED, FRAME_SIZE_FACTOR_SAFE, iValue ) ) + { + // additional check required since only a subset of factors are + // defined + if ( ( iValue == FRAME_SIZE_FACTOR_PREFERRED ) || ( iValue == FRAME_SIZE_FACTOR_DEFAULT ) || ( iValue == FRAME_SIZE_FACTOR_SAFE ) ) + { + pClient->SetSndCrdPrefFrameSizeFactor ( iValue ); + } + } + + // automatic network jitter buffer size setting + if ( GetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", bValue ) ) + { + pClient->SetDoAutoSockBufSize ( bValue ); + } + + // network jitter buffer size + if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbuf", MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) + { + pClient->SetSockBufNumFrames ( iValue ); + } + + // network jitter buffer size for server + if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbufserver", MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) + { + pClient->SetServerSockBufNumFrames ( iValue ); + } + + // enable OPUS64 setting + if ( GetFlagIniSet ( IniXMLDocument, "client", "enableopussmall", bValue ) ) + { + pClient->SetEnableOPUS64 ( bValue ); + } + + // GUI design if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", 0, 2 /* GD_SLIMFADER */, iValue ) ) { - switch ( iValue ) + pClient->SetGUIDesign ( static_cast ( iValue ) ); + } + + // MeterStyle + if ( GetNumericIniSet ( IniXMLDocument, "client", "meterstyle", 0, 4 /* MT_LED_ROUND_BIG */, iValue ) ) + { + pClient->SetMeterStyle ( static_cast ( iValue ) ); + } + else + { + // if MeterStyle is not found in the ini, set it based on the GUI design + if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", 0, 2 /* GD_SLIMFADER */, iValue ) ) { - case GD_STANDARD: - pClient->SetMeterStyle ( MT_BAR_WIDE ); - break; + switch ( iValue ) + { + case GD_STANDARD: + pClient->SetMeterStyle ( MT_BAR_WIDE ); + break; - case GD_ORIGINAL: - pClient->SetMeterStyle ( MT_LED_STRIPE ); - break; + case GD_ORIGINAL: + pClient->SetMeterStyle ( MT_LED_STRIPE ); + break; - case GD_SLIMFADER: - pClient->SetMeterStyle ( MT_BAR_NARROW ); - break; + case GD_SLIMFADER: + pClient->SetMeterStyle ( MT_BAR_NARROW ); + break; - default: - pClient->SetMeterStyle ( MT_LED_STRIPE ); - break; + default: + pClient->SetMeterStyle ( MT_LED_STRIPE ); + break; + } } } - } - // audio channels - if ( GetNumericIniSet ( IniXMLDocument, "client", "audiochannels", 0, 2 /* CC_STEREO */, iValue ) ) - { - pClient->SetAudioChannels ( static_cast ( iValue ) ); - } + // audio channels + if ( GetNumericIniSet ( IniXMLDocument, "client", "audiochannels", 0, 2 /* CC_STEREO */, iValue ) ) + { + pClient->SetAudioChannels ( static_cast ( iValue ) ); + } - // audio quality - if ( GetNumericIniSet ( IniXMLDocument, "client", "audioquality", 0, 2 /* AQ_HIGH */, iValue ) ) - { - pClient->SetAudioQuality ( static_cast ( iValue ) ); + // audio quality + if ( GetNumericIniSet ( IniXMLDocument, "client", "audioquality", 0, 2 /* AQ_HIGH */, iValue ) ) + { + pClient->SetAudioQuality ( static_cast ( iValue ) ); + } } // custom directories @@ -548,7 +602,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, } // selected Settings Tab - if ( GetNumericIniSet ( IniXMLDocument, "client", "settingstab", 0, 2, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "settingstab", 0, 3, iValue ) ) { iSettingsTab = iValue; } @@ -754,6 +808,18 @@ void CClientSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument, bool is // Settings Tab SetNumericIniSet ( IniXMLDocument, "client", "settingstab", iSettingsTab ); + // MIDI settings + SetNumericIniSet ( IniXMLDocument, "client", "midichannel", midiChannel ); + SetNumericIniSet ( IniXMLDocument, "client", "midifaderoffset", midiFaderOffset ); + SetNumericIniSet ( IniXMLDocument, "client", "midifadercount", midiFaderCount ); + SetNumericIniSet ( IniXMLDocument, "client", "midipanoffset", midiPanOffset ); + SetNumericIniSet ( IniXMLDocument, "client", "midipancount", midiPanCount ); + SetNumericIniSet ( IniXMLDocument, "client", "midisolooffset", midiSoloOffset ); + SetNumericIniSet ( IniXMLDocument, "client", "midisolocount", midiSoloCount ); + SetNumericIniSet ( IniXMLDocument, "client", "midimuteoffset", midiMuteOffset ); + SetNumericIniSet ( IniXMLDocument, "client", "midimutecount", midiMuteCount ); + SetNumericIniSet ( IniXMLDocument, "client", "midimutemyself", midiMuteMyself ); + // fader settings WriteFaderSettingsToXML ( IniXMLDocument ); } diff --git a/src/settings.h b/src/settings.h index 7e3a882cc1..d19a9cd3d8 100644 --- a/src/settings.h +++ b/src/settings.h @@ -201,6 +201,18 @@ class CClientSettings : public CSettings bool bWindowWasShownConnect; bool bOwnFaderFirst; + // MIDI settings + int midiChannel = 0; // Default MIDI channel 0 + int midiMuteMyself = 0; + int midiFaderOffset = 70; + int midiFaderCount = 0; + int midiPanOffset = 0; + int midiPanCount = 0; + int midiSoloOffset = 0; + int midiSoloCount = 0; + int midiMuteOffset = 0; + int midiMuteCount = 0; + protected: virtual void WriteSettingsToXML ( QDomDocument& IniXMLDocument, bool isAboutToQuit ) override; virtual void ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, const QList& ) override; diff --git a/src/sound/soundbase.cpp b/src/sound/soundbase.cpp index a5fbf188f8..35a2ad3759 100644 --- a/src/sound/soundbase.cpp +++ b/src/sound/soundbase.cpp @@ -237,6 +237,10 @@ QVector CSoundBase::LoadAndInitializeFirstValidDriver ( const bool bOpe \******************************************************************************/ void CSoundBase::ParseCommandLineArgument ( const QString& strMIDISetup ) { + // Clear all previous MIDI mappings + for ( int i = 0; i < aMidiCtls.size(); ++i ) + aMidiCtls[i] = { None, 0 }; + int iMIDIOffsetFader = 70; // Behringer X-TOUCH: offset of 0x46 // parse the server info string according to definition: there is @@ -367,6 +371,7 @@ void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) { const CMidiCtlEntry& cCtrl = aMidiCtls[vMIDIPaketBytes[1]]; const int iValue = vMIDIPaketBytes[2]; + emit MidiCCReceived ( vMIDIPaketBytes[1] ); ; switch ( cCtrl.eType ) { @@ -416,3 +421,9 @@ void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) } } } + + +void CSoundBase::SetMIDIMapping ( const QString& strMIDISetup ) +{ + ParseCommandLineArgument ( strMIDISetup ); +} diff --git a/src/sound/soundbase.h b/src/sound/soundbase.h index 13041143d8..26188161d7 100644 --- a/src/sound/soundbase.h +++ b/src/sound/soundbase.h @@ -118,6 +118,7 @@ class CSoundBase : public QThread // this needs to be public so that it can be called from CMidi void ParseMIDIMessage ( const CVector& vMIDIPaketBytes ); + void SetMIDIMapping ( const QString& strMIDISetup ); protected: virtual QString LoadAndInitializeDriver ( QString, bool ) { return ""; } @@ -179,4 +180,5 @@ class CSoundBase : public QThread void ControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); void ControllerInFaderIsMute ( int iChannelIdx, bool bIsMute ); void ControllerInMuteMyself ( bool bMute ); + void MidiCCReceived ( int ccNumber ); }; From 21d39a27dddcf93b78b84eb0d85de891658eee09 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Thu, 22 May 2025 13:32:41 +0200 Subject: [PATCH 02/24] Fix clang-format issues --- src/client.cpp | 10 +-- src/client.h | 2 +- src/clientdlg.cpp | 7 +- src/clientsettingsdlg.cpp | 176 +++++++++++++++++++------------------- src/clientsettingsdlg.h | 12 ++- src/main.cpp | 48 +++++------ src/settings.h | 18 ++-- src/sound/soundbase.cpp | 8 +- 8 files changed, 137 insertions(+), 144 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index 96613e9428..d2509b3dec 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1552,15 +1552,9 @@ void CClient::FreeClientChannel ( const int iServerChannelID ) */ } -void CClient::ApplyMIDIMapping ( const QString& midiMap ) -{ - Sound.SetMIDIMapping ( midiMap ); -} +void CClient::ApplyMIDIMapping ( const QString& midiMap ) { Sound.SetMIDIMapping ( midiMap ); } -void CClient::OnMidiCCReceived ( int ccNumber ) -{ - emit MidiCCReceived ( ccNumber ); -} +void CClient::OnMidiCCReceived ( int ccNumber ) { emit MidiCCReceived ( ccNumber ); } // find, and optionally create, a client channel for the supplied server channel ID // returns a client channel ID or INVALID_INDEX diff --git a/src/client.h b/src/client.h index 4d54be1833..7412582618 100644 --- a/src/client.h +++ b/src/client.h @@ -295,7 +295,7 @@ class CClient : public QObject CChannelCoreInfo ChannelInfo; QString strClientName; - void ApplyMIDIMapping(const QString& midiMap); + void ApplyMIDIMapping ( const QString& midiMap ); protected: // callback function must be static, otherwise it does not work diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 66e0c8c493..baac7920ff 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -988,7 +988,7 @@ void CClientDlg::ShowConnectionSetupDialog() void CClientDlg::ShowGeneralSettings ( int iTab ) { // open general settings dialog - emit SendTabChange ( iTab ); + emit SendTabChange ( iTab ); ClientSettingsDlg.show(); // make sure dialog is upfront and has focus ClientSettingsDlg.setWindowTitle ( MakeClientNameTitle ( tr ( "Settings" ), pClient->strClientName ) ); @@ -1519,7 +1519,4 @@ void CClientDlg::SetPingTime ( const int iPingTime, const int iOverallDelayMs, c ledDelay->SetLight ( eOverallDelayLEDColor ); } -void CClientDlg::OnOpenMidiSettings() -{ - ShowGeneralSettings ( SETTING_TAB_MIDI ); -} +void CClientDlg::OnOpenMidiSettings() { ShowGeneralSettings ( SETTING_TAB_MIDI ); } diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 9a2d96e49d..db46a1ac79 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -399,18 +399,18 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet // MIDI settings QString strMidiSettings = "" + tr ( "MIDI controller settings" ) + ": " + - tr ( "There is one global MIDI channel parameter (1-16) and two parameters you can set " - "for each item controlled: offset and consecutive CC numbers (count). First set the " - "channel you want Jamulus to listen on (0 for all channels). Then, for each item " - "you want to control (volume fader, pan, solo, mute), set the offset (CC number " - "to start from) and number of consecutive CC numbers (count). There is one " - "exception that does not require establishing consecutive CC numbers which is " - "the “Mute Myself” parameter - it only requires a single CC number as it is only " - "applied to one’s own audio stream." ) + - "
" + - tr ( "You can either type in the MIDI CC values or use the \"Learn\" button: click on " - "\"Learn\", move the fader/knob on your MIDI controller, and the MIDI CC number " - "will be saved." ); + tr ( "There is one global MIDI channel parameter (1-16) and two parameters you can set " + "for each item controlled: offset and consecutive CC numbers (count). First set the " + "channel you want Jamulus to listen on (0 for all channels). Then, for each item " + "you want to control (volume fader, pan, solo, mute), set the offset (CC number " + "to start from) and number of consecutive CC numbers (count). There is one " + "exception that does not require establishing consecutive CC numbers which is " + "the “Mute Myself” parameter - it only requires a single CC number as it is only " + "applied to one’s own audio stream." ) + + "
" + + tr ( "You can either type in the MIDI CC values or use the \"Learn\" button: click on " + "\"Learn\", move the fader/knob on your MIDI controller, and the MIDI CC number " + "will be saved." ); lblChannel->setWhatsThis ( strMidiSettings ); lblMuteMyself->setWhatsThis ( strMidiSettings ); faderGroup->setWhatsThis ( strMidiSettings ); @@ -784,62 +784,62 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet tabSettings->setCurrentIndex ( pSettings->iSettingsTab ); // MIDI tab - QObject::connect ( spnChannel, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiChannel = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnChannel, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiChannel = v; + ApplyMIDIMappingFromSettings(); + } ); - QObject::connect ( spnMuteMyself, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiMuteMyself = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnMuteMyself, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiMuteMyself = v; + ApplyMIDIMappingFromSettings(); + } ); - QObject::connect ( spnFaderOffset, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiFaderOffset = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnFaderOffset, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiFaderOffset = v; + ApplyMIDIMappingFromSettings(); + } ); - QObject::connect ( spnFaderCount, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiFaderCount = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnFaderCount, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiFaderCount = v; + ApplyMIDIMappingFromSettings(); + } ); - QObject::connect ( spnPanOffset, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiPanOffset = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnPanOffset, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiPanOffset = v; + ApplyMIDIMappingFromSettings(); + } ); - QObject::connect ( spnPanCount, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiPanCount = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnPanCount, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiPanCount = v; + ApplyMIDIMappingFromSettings(); + } ); - QObject::connect ( spnSoloOffset, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiSoloOffset = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnSoloOffset, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiSoloOffset = v; + ApplyMIDIMappingFromSettings(); + } ); - QObject::connect ( spnSoloCount, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiSoloCount = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnSoloCount, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiSoloCount = v; + ApplyMIDIMappingFromSettings(); + } ); - QObject::connect ( spnMuteOffset, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiMuteOffset = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnMuteOffset, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiMuteOffset = v; + ApplyMIDIMappingFromSettings(); + } ); - QObject::connect ( spnMuteCount, - static_cast ( &QSpinBox::valueChanged ), - this, - [this] ( int v ) { pSettings->midiMuteCount = v; ApplyMIDIMappingFromSettings(); } ); + QObject::connect ( spnMuteCount, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { + pSettings->midiMuteCount = v; + ApplyMIDIMappingFromSettings(); + } ); // MIDI Learn buttons - QObject::connect ( butLearnMuteMyself, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnMuteMyself ); + QObject::connect ( butLearnMuteMyself, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnMuteMyself ); QObject::connect ( butLearnFaderOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnFaderOffset ); - QObject::connect ( butLearnPanOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnPanOffset ); - QObject::connect ( butLearnSoloOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnSoloOffset ); - QObject::connect ( butLearnMuteOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnMuteOffset ); + QObject::connect ( butLearnPanOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnPanOffset ); + QObject::connect ( butLearnSoloOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnSoloOffset ); + QObject::connect ( butLearnMuteOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnMuteOffset ); // Connect MIDI CC signal from sound engine QObject::connect ( pClient, &CClient::MidiCCReceived, this, &CClientSettingsDlg::OnMidiCCReceived ); @@ -880,7 +880,7 @@ void CClientSettingsDlg::showEvent ( QShowEvent* event ) spnMuteOffset->setValue ( pSettings->midiMuteOffset ); spnMuteCount->setValue ( pSettings->midiMuteCount ); - QDialog::showEvent ( event ) ; + QDialog::showEvent ( event ); } void CClientSettingsDlg::UpdateJitterBufferFrame() @@ -1327,16 +1327,16 @@ void CClientSettingsDlg::OnAudioPanValueChanged ( int value ) void CClientSettingsDlg::ApplyMIDIMappingFromSettings() { QString midiMap = QString ( "%1;f%2*%3;p%4*%5;s%6*%7;m%8*%9;o%10" ) - .arg ( pSettings->midiChannel ) - .arg ( pSettings->midiFaderOffset ) - .arg ( pSettings->midiFaderCount ) - .arg ( pSettings->midiPanOffset ) - .arg ( pSettings->midiPanCount ) - .arg ( pSettings->midiSoloOffset ) - .arg ( pSettings->midiSoloCount ) - .arg ( pSettings->midiMuteOffset ) - .arg ( pSettings->midiMuteCount ) - .arg ( pSettings->midiMuteMyself ); + .arg ( pSettings->midiChannel ) + .arg ( pSettings->midiFaderOffset ) + .arg ( pSettings->midiFaderCount ) + .arg ( pSettings->midiPanOffset ) + .arg ( pSettings->midiPanCount ) + .arg ( pSettings->midiSoloOffset ) + .arg ( pSettings->midiSoloCount ) + .arg ( pSettings->midiMuteOffset ) + .arg ( pSettings->midiMuteCount ) + .arg ( pSettings->midiMuteMyself ); pClient->ApplyMIDIMapping ( midiMap ); } @@ -1349,7 +1349,7 @@ void CClientSettingsDlg::ResetMidiLearn() butLearnFaderOffset->setText ( tr ( "Learn" ) ); butLearnFaderOffset->setEnabled ( true ); butLearnPanOffset->setText ( tr ( "Learn" ) ); - butLearnPanOffset->setEnabled(true); + butLearnPanOffset->setEnabled ( true ); butLearnSoloOffset->setText ( tr ( "Learn" ) ); butLearnSoloOffset->setEnabled ( true ); butLearnMuteOffset->setText ( tr ( "Learn" ) ); @@ -1402,7 +1402,7 @@ void CClientSettingsDlg::OnLearnPanOffset() ResetMidiLearn(); midiLearnTarget = Pan; - butLearnPanOffset->setText (tr ( "Listening..." ) ); + butLearnPanOffset->setText ( tr ( "Listening..." ) ); butLearnPanOffset->setEnabled ( true ); butLearnMuteMyself->setEnabled ( false ); butLearnFaderOffset->setEnabled ( false ); @@ -1412,7 +1412,7 @@ void CClientSettingsDlg::OnLearnPanOffset() void CClientSettingsDlg::OnLearnSoloOffset() { - if (midiLearnTarget == Solo) + if ( midiLearnTarget == Solo ) { ResetMidiLearn(); return; @@ -1430,7 +1430,7 @@ void CClientSettingsDlg::OnLearnSoloOffset() void CClientSettingsDlg::OnLearnMuteOffset() { - if (midiLearnTarget == Mute) + if ( midiLearnTarget == Mute ) { ResetMidiLearn(); return; @@ -1453,23 +1453,23 @@ void CClientSettingsDlg::OnMidiCCReceived ( int ccNumber ) switch ( midiLearnTarget ) { - case Fader: - spnFaderOffset->setValue ( ccNumber ); - break; - case Pan: - spnPanOffset->setValue ( ccNumber ); - break; - case Solo: - spnSoloOffset->setValue ( ccNumber ); - break; - case Mute: - spnMuteOffset->setValue ( ccNumber ); - break; - case MuteMyself: - spnMuteMyself->setValue ( ccNumber ); - break; - default: - break; + case Fader: + spnFaderOffset->setValue ( ccNumber ); + break; + case Pan: + spnPanOffset->setValue ( ccNumber ); + break; + case Solo: + spnSoloOffset->setValue ( ccNumber ); + break; + case Mute: + spnMuteOffset->setValue ( ccNumber ); + break; + case MuteMyself: + spnMuteMyself->setValue ( ccNumber ); + break; + default: + break; } ResetMidiLearn(); } diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 1e478cc783..7aa76d91f8 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -122,9 +122,17 @@ public slots: void NumMixerPanelRowsChanged ( int value ); private: - enum MidiLearnTarget { None, MuteMyself, Fader, Pan, Solo, Mute }; + enum MidiLearnTarget + { + None, + MuteMyself, + Fader, + Pan, + Solo, + Mute + }; MidiLearnTarget midiLearnTarget = None; - void ResetMidiLearn(); + void ResetMidiLearn(); private slots: void OnLearnMuteMyself(); diff --git a/src/main.cpp b/src/main.cpp index 9a9dee4359..7b3a45f5dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -926,28 +926,26 @@ int main ( int argc, char** argv ) tmpSettings.Load ( CommandLineOptions ); strMIDISetup = QString ( "%1;f%2*%3;p%4*%5;s%6*%7;m%8*%9;o%10" ) - .arg ( tmpSettings.midiChannel ) - .arg ( tmpSettings.midiFaderOffset ) - .arg ( tmpSettings.midiFaderCount ) - .arg ( tmpSettings.midiPanOffset ) - .arg ( tmpSettings.midiPanCount ) - .arg ( tmpSettings.midiSoloOffset ) - .arg ( tmpSettings.midiSoloCount ) - .arg ( tmpSettings.midiMuteOffset ) - .arg ( tmpSettings.midiMuteCount ) - .arg ( tmpSettings.midiMuteMyself ); + .arg ( tmpSettings.midiChannel ) + .arg ( tmpSettings.midiFaderOffset ) + .arg ( tmpSettings.midiFaderCount ) + .arg ( tmpSettings.midiPanOffset ) + .arg ( tmpSettings.midiPanCount ) + .arg ( tmpSettings.midiSoloOffset ) + .arg ( tmpSettings.midiSoloCount ) + .arg ( tmpSettings.midiMuteOffset ) + .arg ( tmpSettings.midiMuteCount ) + .arg ( tmpSettings.midiMuteMyself ); } - CClient Client( - iPortNumber, - iQosNumber, - strConnOnStartupAddress, - strMIDISetup, - bNoAutoJackConnect, - strClientName, - bEnableIPv6, - bMuteMeInPersonalMix - ); + CClient Client ( iPortNumber, + iQosNumber, + strConnOnStartupAddress, + strMIDISetup, + bNoAutoJackConnect, + strClientName, + bEnableIPv6, + bMuteMeInPersonalMix ); // Create Settings with the client pointer CClientSettings Settings ( &Client, strIniFileName ); @@ -964,7 +962,7 @@ int main ( int argc, char** argv ) QString sParm = slMIDIParams[i].trimmed(); if ( sParm.startsWith ( "f" ) ) { - QStringList slP = sParm.mid ( 1 ).split ( '*' ); + QStringList slP = sParm.mid ( 1 ).split ( '*' ); Settings.midiFaderOffset = slP[0].toInt(); if ( slP.size() > 1 ) { @@ -973,7 +971,7 @@ int main ( int argc, char** argv ) } else if ( sParm.startsWith ( "p" ) ) { - QStringList slP = sParm.mid ( 1 ).split ( '*' ); + QStringList slP = sParm.mid ( 1 ).split ( '*' ); Settings.midiPanOffset = slP[0].toInt(); if ( slP.size() > 1 ) { @@ -982,7 +980,7 @@ int main ( int argc, char** argv ) } else if ( sParm.startsWith ( "s" ) ) { - QStringList slP = sParm.mid ( 1 ).split ( '*' ); + QStringList slP = sParm.mid ( 1 ).split ( '*' ); Settings.midiSoloOffset = slP[0].toInt(); if ( slP.size() > 1 ) { @@ -991,7 +989,7 @@ int main ( int argc, char** argv ) } else if ( sParm.startsWith ( "m" ) ) { - QStringList slP = sParm.mid ( 1 ).split ( '*' ); + QStringList slP = sParm.mid ( 1 ).split ( '*' ); Settings.midiMuteOffset = slP[0].toInt(); if ( slP.size() > 1 ) { @@ -1000,7 +998,7 @@ int main ( int argc, char** argv ) } else if ( sParm.startsWith ( "o" ) ) { - QStringList slP = sParm.mid ( 1 ).split ( '*' ); + QStringList slP = sParm.mid ( 1 ).split ( '*' ); Settings.midiMuteMyself = slP[0].toInt(); } } diff --git a/src/settings.h b/src/settings.h index d19a9cd3d8..8cde0f15df 100644 --- a/src/settings.h +++ b/src/settings.h @@ -202,16 +202,16 @@ class CClientSettings : public CSettings bool bOwnFaderFirst; // MIDI settings - int midiChannel = 0; // Default MIDI channel 0 - int midiMuteMyself = 0; + int midiChannel = 0; // Default MIDI channel 0 + int midiMuteMyself = 0; int midiFaderOffset = 70; - int midiFaderCount = 0; - int midiPanOffset = 0; - int midiPanCount = 0; - int midiSoloOffset = 0; - int midiSoloCount = 0; - int midiMuteOffset = 0; - int midiMuteCount = 0; + int midiFaderCount = 0; + int midiPanOffset = 0; + int midiPanCount = 0; + int midiSoloOffset = 0; + int midiSoloCount = 0; + int midiMuteOffset = 0; + int midiMuteCount = 0; protected: virtual void WriteSettingsToXML ( QDomDocument& IniXMLDocument, bool isAboutToQuit ) override; diff --git a/src/sound/soundbase.cpp b/src/sound/soundbase.cpp index 35a2ad3759..d517219bc6 100644 --- a/src/sound/soundbase.cpp +++ b/src/sound/soundbase.cpp @@ -371,7 +371,7 @@ void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) { const CMidiCtlEntry& cCtrl = aMidiCtls[vMIDIPaketBytes[1]]; const int iValue = vMIDIPaketBytes[2]; - emit MidiCCReceived ( vMIDIPaketBytes[1] ); + emit MidiCCReceived ( vMIDIPaketBytes[1] ); ; switch ( cCtrl.eType ) { @@ -422,8 +422,4 @@ void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) } } - -void CSoundBase::SetMIDIMapping ( const QString& strMIDISetup ) -{ - ParseCommandLineArgument ( strMIDISetup ); -} +void CSoundBase::SetMIDIMapping ( const QString& strMIDISetup ) { ParseCommandLineArgument ( strMIDISetup ); } From 084fe9a5be0439d4c72300cddc73ade0a4b69f40 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Thu, 22 May 2025 13:36:49 +0200 Subject: [PATCH 03/24] Delete extra line --- src/clientdlg.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index baac7920ff..7ae940a0bf 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -402,7 +402,6 @@ CClientDlg::CClientDlg ( CClient* pNCliP, pSettingsMenu->addAction ( tr ( "&MIDI Settings..." ), this, SLOT ( OnOpenMidiSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_M ) ); - // Main menu bar ----------------------------------------------------------- QMenuBar* pMenu = new QMenuBar ( this ); From 6df6339dc80bda87915420632849f5cc951bc2a8 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Fri, 23 May 2025 17:42:00 +0200 Subject: [PATCH 04/24] Fix GetNumericIniSet function range --- src/settings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings.cpp b/src/settings.cpp index c46f05e6f5..100a7a67b0 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -290,7 +290,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, } // --- MIDI settings (do NOT require pClient) --- - if ( GetNumericIniSet ( IniXMLDocument, "client", "midichannel", 0, 15, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "midichannel", 0, 16, iValue ) ) { midiChannel = iValue; } From 9a2257fbad35795a8e8a038cea2afc4672bd3386 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Wed, 28 May 2025 09:23:39 +0200 Subject: [PATCH 05/24] Avoid repetitions in learn button handling --- src/clientsettingsdlg.cpp | 125 +++++++++++--------------------------- src/clientsettingsdlg.h | 11 ++-- 2 files changed, 42 insertions(+), 94 deletions(-) diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index db46a1ac79..1d46f65d4b 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -835,11 +835,17 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet } ); // MIDI Learn buttons - QObject::connect ( butLearnMuteMyself, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnMuteMyself ); - QObject::connect ( butLearnFaderOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnFaderOffset ); - QObject::connect ( butLearnPanOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnPanOffset ); - QObject::connect ( butLearnSoloOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnSoloOffset ); - QObject::connect ( butLearnMuteOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnMuteOffset ); + midiLearnButtons[0] = butLearnMuteMyself; + midiLearnButtons[1] = butLearnFaderOffset; + midiLearnButtons[2] = butLearnPanOffset; + midiLearnButtons[3] = butLearnSoloOffset; + midiLearnButtons[4] = butLearnMuteOffset; + + QObject::connect ( butLearnMuteMyself, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); + QObject::connect ( butLearnFaderOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); + QObject::connect ( butLearnPanOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); + QObject::connect ( butLearnSoloOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); + QObject::connect ( butLearnMuteOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); // Connect MIDI CC signal from sound engine QObject::connect ( pClient, &CClient::MidiCCReceived, this, &CClientSettingsDlg::OnMidiCCReceived ); @@ -1344,106 +1350,49 @@ void CClientSettingsDlg::ApplyMIDIMappingFromSettings() void CClientSettingsDlg::ResetMidiLearn() { midiLearnTarget = None; - butLearnMuteMyself->setText ( tr ( "Learn" ) ); - butLearnMuteMyself->setEnabled ( true ); - butLearnFaderOffset->setText ( tr ( "Learn" ) ); - butLearnFaderOffset->setEnabled ( true ); - butLearnPanOffset->setText ( tr ( "Learn" ) ); - butLearnPanOffset->setEnabled ( true ); - butLearnSoloOffset->setText ( tr ( "Learn" ) ); - butLearnSoloOffset->setEnabled ( true ); - butLearnMuteOffset->setText ( tr ( "Learn" ) ); - butLearnMuteOffset->setEnabled ( true ); -} - -void CClientSettingsDlg::OnLearnMuteMyself() -{ - if ( midiLearnTarget == MuteMyself ) - { - ResetMidiLearn(); - return; - } - - ResetMidiLearn(); - midiLearnTarget = MuteMyself; - butLearnMuteMyself->setText ( tr ( "Listening..." ) ); - butLearnMuteMyself->setEnabled ( true ); - butLearnFaderOffset->setEnabled ( false ); - butLearnPanOffset->setEnabled ( false ); - butLearnSoloOffset->setEnabled ( false ); - butLearnMuteOffset->setEnabled ( false ); -} - -void CClientSettingsDlg::OnLearnFaderOffset() -{ - if ( midiLearnTarget == Fader ) + for ( QPushButton* button : midiLearnButtons ) { - ResetMidiLearn(); - return; + button->setText ( tr ( "Learn" ) ); + button->setEnabled ( true ); } - - ResetMidiLearn(); - midiLearnTarget = Fader; - butLearnFaderOffset->setText ( tr ( "Listening..." ) ); - butLearnFaderOffset->setEnabled ( true ); - butLearnMuteMyself->setEnabled ( false ); - butLearnPanOffset->setEnabled ( false ); - butLearnSoloOffset->setEnabled ( false ); - butLearnMuteOffset->setEnabled ( false ); } -void CClientSettingsDlg::OnLearnPanOffset() +void CClientSettingsDlg::SetMidiLearnTarget ( MidiLearnTarget target, QPushButton* activeButton ) { - if ( midiLearnTarget == Pan ) + if ( midiLearnTarget == target ) { ResetMidiLearn(); return; } ResetMidiLearn(); - midiLearnTarget = Pan; - butLearnPanOffset->setText ( tr ( "Listening..." ) ); - butLearnPanOffset->setEnabled ( true ); - butLearnMuteMyself->setEnabled ( false ); - butLearnFaderOffset->setEnabled ( false ); - butLearnSoloOffset->setEnabled ( false ); - butLearnMuteOffset->setEnabled ( false ); -} + midiLearnTarget = target; + activeButton->setText ( tr ( "Listening..." ) ); -void CClientSettingsDlg::OnLearnSoloOffset() -{ - if ( midiLearnTarget == Solo ) + // Disable all buttons except the active one + for ( QPushButton* button : midiLearnButtons ) { - ResetMidiLearn(); - return; + button->setEnabled ( button == activeButton ); } - - ResetMidiLearn(); - midiLearnTarget = Solo; - butLearnSoloOffset->setText ( tr ( "Listening..." ) ); - butLearnSoloOffset->setEnabled ( true ); - butLearnMuteMyself->setEnabled ( false ); - butLearnFaderOffset->setEnabled ( false ); - butLearnPanOffset->setEnabled ( false ); - butLearnMuteOffset->setEnabled ( false ); } -void CClientSettingsDlg::OnLearnMuteOffset() +void CClientSettingsDlg::OnLearnButtonClicked() { - if ( midiLearnTarget == Mute ) - { - ResetMidiLearn(); - return; - } - - ResetMidiLearn(); - midiLearnTarget = Mute; - butLearnMuteOffset->setText ( tr ( "Listening..." ) ); - butLearnMuteOffset->setEnabled ( true ); - butLearnMuteMyself->setEnabled ( false ); - butLearnFaderOffset->setEnabled ( false ); - butLearnPanOffset->setEnabled ( false ); - butLearnSoloOffset->setEnabled ( false ); + QPushButton* sender = qobject_cast ( QObject::sender() ); + + MidiLearnTarget target = None; + if ( sender == butLearnMuteMyself ) + target = MuteMyself; + else if ( sender == butLearnFaderOffset ) + target = Fader; + else if ( sender == butLearnPanOffset ) + target = Pan; + else if ( sender == butLearnSoloOffset ) + target = Solo; + else if ( sender == butLearnMuteOffset ) + target = Mute; + + SetMidiLearnTarget ( target, sender ); } void CClientSettingsDlg::OnMidiCCReceived ( int ccNumber ) diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 7aa76d91f8..d9cc5c31d2 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -132,13 +132,12 @@ public slots: Mute }; MidiLearnTarget midiLearnTarget = None; - void ResetMidiLearn(); + + QPushButton* midiLearnButtons[5]; + void SetMidiLearnTarget ( MidiLearnTarget target, QPushButton* activeButton ); + void ResetMidiLearn(); private slots: - void OnLearnMuteMyself(); - void OnLearnFaderOffset(); - void OnLearnPanOffset(); - void OnLearnSoloOffset(); - void OnLearnMuteOffset(); + void OnLearnButtonClicked(); void OnMidiCCReceived ( int ccNumber ); }; From aedd89459079366ca151934c55b124695f52c0b3 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Wed, 28 May 2025 11:48:25 +0200 Subject: [PATCH 06/24] Remove duplicate code for MIDI strings --- src/clientsettingsdlg.cpp | 17 +---------- src/clientsettingsdlg.h | 2 +- src/main.cpp | 31 ++++++-------------- src/settings.cpp | 61 ++++++++++++++++++++++++--------------- src/settings.h | 28 ++++++++---------- 5 files changed, 61 insertions(+), 78 deletions(-) diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 1d46f65d4b..4ad0d33604 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -1330,22 +1330,7 @@ void CClientSettingsDlg::OnAudioPanValueChanged ( int value ) UpdateAudioFaderSlider(); } -void CClientSettingsDlg::ApplyMIDIMappingFromSettings() -{ - QString midiMap = QString ( "%1;f%2*%3;p%4*%5;s%6*%7;m%8*%9;o%10" ) - .arg ( pSettings->midiChannel ) - .arg ( pSettings->midiFaderOffset ) - .arg ( pSettings->midiFaderCount ) - .arg ( pSettings->midiPanOffset ) - .arg ( pSettings->midiPanCount ) - .arg ( pSettings->midiSoloOffset ) - .arg ( pSettings->midiSoloCount ) - .arg ( pSettings->midiMuteOffset ) - .arg ( pSettings->midiMuteCount ) - .arg ( pSettings->midiMuteMyself ); - - pClient->ApplyMIDIMapping ( midiMap ); -} +void CClientSettingsDlg::ApplyMIDIMappingFromSettings() { pClient->ApplyMIDIMapping ( pSettings->GetMIDIMapString() ); } void CClientSettingsDlg::ResetMidiLearn() { diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index d9cc5c31d2..6c90b9d8e2 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -60,7 +60,7 @@ class CClientSettingsDlg : public CBaseDlg, private Ui_CClientSettingsDlgBase void UpdateDisplay(); void UpdateSoundDeviceChannelSelectionFrame(); - void SetEnableFeedbackDetection ( bool enable ); + void SetEnableFeedbackDetection ( bool enable ); protected: void UpdateJitterBufferFrame(); diff --git a/src/main.cpp b/src/main.cpp index 7b3a45f5dd..7bc81a4893 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -662,14 +662,12 @@ int main ( int argc, char** argv ) if ( !strServerListFileName.isEmpty() ) { - qInfo() << "Note:" - << "Server list persistence file will only take effect when running as a directory."; + qInfo() << "Note:" << "Server list persistence file will only take effect when running as a directory."; } if ( !strServerListFilter.isEmpty() ) { - qInfo() << "Note:" - << "Server list filter will only take effect when running as a directory."; + qInfo() << "Note:" << "Server list filter will only take effect when running as a directory."; } } else @@ -821,7 +819,7 @@ int main ( int argc, char** argv ) bIsClient = true; // Client only - TODO: maybe a switch in interface to change to server? // bUseMultithreading = true; - QApplication* pApp = new QApplication ( argc, argv ); + QApplication* pApp = new QApplication ( argc, argv ); # else QCoreApplication* pApp = bUseGUI ? new QApplication ( argc, argv ) : new QCoreApplication ( argc, argv ); # endif @@ -865,10 +863,10 @@ int main ( int argc, char** argv ) Q_INIT_RESOURCE ( resources ); #ifndef SERVER_ONLY - //### TEST: BEGIN ###// - // activate the following line to activate the test bench, - // CTestbench Testbench ( "127.0.0.1", DEFAULT_PORT_NUMBER ); - //### TEST: END ###// + // ### TEST: BEGIN ###// + // activate the following line to activate the test bench, + // CTestbench Testbench ( "127.0.0.1", DEFAULT_PORT_NUMBER ); + // ### TEST: END ###// #endif #ifdef NO_JSON_RPC @@ -877,7 +875,7 @@ int main ( int argc, char** argv ) qWarning() << "No JSON-RPC support in this build."; } #else - CRpcServer* pRpcServer = nullptr; + CRpcServer* pRpcServer = nullptr; if ( iJsonRpcPortNumber != INVALID_PORT ) { @@ -924,18 +922,7 @@ int main ( int argc, char** argv ) { CClientSettings tmpSettings ( nullptr, strIniFileName ); tmpSettings.Load ( CommandLineOptions ); - - strMIDISetup = QString ( "%1;f%2*%3;p%4*%5;s%6*%7;m%8*%9;o%10" ) - .arg ( tmpSettings.midiChannel ) - .arg ( tmpSettings.midiFaderOffset ) - .arg ( tmpSettings.midiFaderCount ) - .arg ( tmpSettings.midiPanOffset ) - .arg ( tmpSettings.midiPanCount ) - .arg ( tmpSettings.midiSoloOffset ) - .arg ( tmpSettings.midiSoloCount ) - .arg ( tmpSettings.midiMuteOffset ) - .arg ( tmpSettings.midiMuteCount ) - .arg ( tmpSettings.midiMuteMyself ); + strMIDISetup = tmpSettings.GetMIDIMapString(); } CClient Client ( iPortNumber, diff --git a/src/settings.cpp b/src/settings.cpp index 100a7a67b0..0dd767b2fb 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -517,17 +517,17 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // custom directories - //### TODO: BEGIN ###// - // compatibility to old version (< 3.6.1) + // ### TODO: BEGIN ###// + // compatibility to old version (< 3.6.1) QString strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", "centralservaddr", "" ); - //### TODO: END ###// + // ### TODO: END ###// for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) { - //### TODO: BEGIN ###// - // compatibility to old version (< 3.8.2) + // ### TODO: BEGIN ###// + // compatibility to old version (< 3.8.2) strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", QString ( "centralservaddr%1" ).arg ( iIdx ), strDirectoryAddress ); - //### TODO: END ###// + // ### TODO: END ###// vstrDirectoryAddress[iIdx] = GetIniSetting ( IniXMLDocument, "client", QString ( "directoryaddress%1" ).arg ( iIdx ), strDirectoryAddress ); strDirectoryAddress = ""; @@ -535,9 +535,9 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // directory type - //### TODO: BEGIN ###// - // compatibility to old version (<3.4.7) - // only the case that "centralservaddr" was set in old ini must be considered + // ### TODO: BEGIN ###// + // 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; @@ -547,7 +547,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, { eDirectoryType = static_cast ( iValue ); } - //### TODO: END ###// + // ### TODO: END ###// else if ( GetNumericIniSet ( IniXMLDocument, "client", "directorytype", 0, static_cast ( AT_CUSTOM ), iValue ) ) { @@ -824,6 +824,21 @@ void CClientSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument, bool is WriteFaderSettingsToXML ( IniXMLDocument ); } +QString CClientSettings::GetMIDIMapString() const +{ + return QString ( "%1;f%2*%3;p%4*%5;s%6*%7;m%8*%9;o%10" ) + .arg ( midiChannel ) + .arg ( midiFaderOffset ) + .arg ( midiFaderCount ) + .arg ( midiPanOffset ) + .arg ( midiPanCount ) + .arg ( midiSoloOffset ) + .arg ( midiSoloCount ) + .arg ( midiMuteOffset ) + .arg ( midiMuteCount ) + .arg ( midiMuteMyself ); +} + void CClientSettings::WriteFaderSettingsToXML ( QDomDocument& IniXMLDocument ) { int iIdx; @@ -912,10 +927,10 @@ void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // Server GUI defaults to "" QString directoryAddress = ""; - //### TODO: BEGIN ###// - // compatibility to old version < 3.8.2 + // ### TODO: BEGIN ###// + // compatibility to old version < 3.8.2 directoryAddress = GetIniSetting ( IniXMLDocument, "server", "centralservaddr", directoryAddress ); - //### TODO: END ###// + // ### TODO: END ###// directoryAddress = GetIniSetting ( IniXMLDocument, "server", "directoryaddress", directoryAddress ); @@ -935,20 +950,20 @@ void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, } else { - //### TODO: BEGIN ###// - // compatibility to old version < 3.4.7 + // ### TODO: BEGIN ###// + // compatibility to old version < 3.4.7 if ( GetFlagIniSet ( IniXMLDocument, "server", "defcentservaddr", bValue ) ) { directoryType = bValue ? AT_DEFAULT : AT_CUSTOM; } else { - //### TODO: END ###// + // ### TODO: END ###// // if "directorytype" itself is set, use it (note "AT_NONE", "AT_DEFAULT" and "AT_CUSTOM" are min/max directory type here) - //### TODO: BEGIN ###// - // compatibility to old version < 3.8.2 + // ### TODO: BEGIN ###// + // compatibility to old version < 3.8.2 if ( GetNumericIniSet ( IniXMLDocument, "server", "centservaddrtype", @@ -958,7 +973,7 @@ void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, { directoryType = static_cast ( iValue ); } - //### TODO: END ###// + // ### TODO: END ###// else { @@ -974,14 +989,14 @@ void CServerSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, } } - //### TODO: BEGIN ###// - // compatibility to old version < 3.9.0 - // override type to AT_NONE if servlistenabled exists and is false + // ### TODO: BEGIN ###// + // 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; } - //### TODO: END ###// + // ### TODO: END ###// } pServer->SetDirectoryType ( directoryType ); diff --git a/src/settings.h b/src/settings.h index 8cde0f15df..59c3b86473 100644 --- a/src/settings.h +++ b/src/settings.h @@ -60,12 +60,7 @@ class CSettings : public QObject if ( pGApp != nullptr ) { # ifndef QT_NO_SESSIONMANAGER - QObject::connect ( - pGApp, - &QGuiApplication::saveStateRequest, - this, - [=] ( QSessionManager& ) { Save ( false ); }, - Qt::DirectConnection ); + QObject::connect ( pGApp, &QGuiApplication::saveStateRequest, this, [=] ( QSessionManager& ) { Save ( false ); }, Qt::DirectConnection ); # endif QObject::connect ( pGApp, &QGuiApplication::applicationStateChanged, this, [=] ( Qt::ApplicationState state ) { @@ -202,16 +197,17 @@ class CClientSettings : public CSettings bool bOwnFaderFirst; // MIDI settings - int midiChannel = 0; // Default MIDI channel 0 - int midiMuteMyself = 0; - int midiFaderOffset = 70; - int midiFaderCount = 0; - int midiPanOffset = 0; - int midiPanCount = 0; - int midiSoloOffset = 0; - int midiSoloCount = 0; - int midiMuteOffset = 0; - int midiMuteCount = 0; + int midiChannel = 0; // Default MIDI channel 0 + int midiMuteMyself = 0; + int midiFaderOffset = 70; + int midiFaderCount = 0; + int midiPanOffset = 0; + int midiPanCount = 0; + int midiSoloOffset = 0; + int midiSoloCount = 0; + int midiMuteOffset = 0; + int midiMuteCount = 0; + QString GetMIDIMapString() const; protected: virtual void WriteSettingsToXML ( QDomDocument& IniXMLDocument, bool isAboutToQuit ) override; From 6c2dc140d4313dcc9d5bd8af64b5a66ddef1d45e Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Wed, 28 May 2025 11:58:27 +0200 Subject: [PATCH 07/24] Clang-format errors corrected --- src/clientsettingsdlg.h | 2 +- src/main.cpp | 10 ++++++---- src/settings.h | 7 ++++++- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 6c90b9d8e2..d9cc5c31d2 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -60,7 +60,7 @@ class CClientSettingsDlg : public CBaseDlg, private Ui_CClientSettingsDlgBase void UpdateDisplay(); void UpdateSoundDeviceChannelSelectionFrame(); - void SetEnableFeedbackDetection ( bool enable ); + void SetEnableFeedbackDetection ( bool enable ); protected: void UpdateJitterBufferFrame(); diff --git a/src/main.cpp b/src/main.cpp index 7bc81a4893..d011c36916 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -662,12 +662,14 @@ int main ( int argc, char** argv ) if ( !strServerListFileName.isEmpty() ) { - qInfo() << "Note:" << "Server list persistence file will only take effect when running as a directory."; + qInfo() << "Note:" + << "Server list persistence file will only take effect when running as a directory."; } if ( !strServerListFilter.isEmpty() ) { - qInfo() << "Note:" << "Server list filter will only take effect when running as a directory."; + qInfo() << "Note:" + << "Server list filter will only take effect when running as a directory."; } } else @@ -819,7 +821,7 @@ int main ( int argc, char** argv ) bIsClient = true; // Client only - TODO: maybe a switch in interface to change to server? // bUseMultithreading = true; - QApplication* pApp = new QApplication ( argc, argv ); + QApplication* pApp = new QApplication ( argc, argv ); # else QCoreApplication* pApp = bUseGUI ? new QApplication ( argc, argv ) : new QCoreApplication ( argc, argv ); # endif @@ -875,7 +877,7 @@ int main ( int argc, char** argv ) qWarning() << "No JSON-RPC support in this build."; } #else - CRpcServer* pRpcServer = nullptr; + CRpcServer* pRpcServer = nullptr; if ( iJsonRpcPortNumber != INVALID_PORT ) { diff --git a/src/settings.h b/src/settings.h index 59c3b86473..0db6dde0e1 100644 --- a/src/settings.h +++ b/src/settings.h @@ -60,7 +60,12 @@ class CSettings : public QObject if ( pGApp != nullptr ) { # ifndef QT_NO_SESSIONMANAGER - QObject::connect ( pGApp, &QGuiApplication::saveStateRequest, this, [=] ( QSessionManager& ) { Save ( false ); }, Qt::DirectConnection ); + QObject::connect ( + pGApp, + &QGuiApplication::saveStateRequest, + this, + [=] ( QSessionManager& ) { Save ( false ); }, + Qt::DirectConnection ); # endif QObject::connect ( pGApp, &QGuiApplication::applicationStateChanged, this, [=] ( Qt::ApplicationState state ) { From a2c06dbc3f445e1d20fd33a9877096ddcab8c906 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Wed, 28 May 2025 12:34:25 +0200 Subject: [PATCH 08/24] Set autoDefault to false for Learn buttons --- src/clientsettingsdlgbase.ui | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index bfdc2575ba..d76acd0112 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -1445,7 +1445,7 @@ Learn
- true + false @@ -1528,7 +1528,7 @@ Learn - true + false @@ -1653,7 +1653,7 @@ Learn - true + false @@ -1778,7 +1778,7 @@ Learn - true + false @@ -1903,7 +1903,7 @@ Learn - true + false From d4e1e585324e16f84e8a63f645871d77b9182e64 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Wed, 28 May 2025 16:26:49 +0200 Subject: [PATCH 09/24] SImplify learn button setup, add error handling, initialise learn buttons as a member variable. --- src/clientsettingsdlg.cpp | 20 ++++++++++++++------ src/clientsettingsdlg.h | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 4ad0d33604..0932aef359 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -28,7 +28,8 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSetP, QWidget* parent ) : CBaseDlg ( parent, Qt::Window ), // use Qt::Window to get min/max window buttons pClient ( pNCliP ), - pSettings ( pNSetP ) + pSettings ( pNSetP ), + midiLearnTarget ( None ) { setupUi ( this ); @@ -841,11 +842,11 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet midiLearnButtons[3] = butLearnSoloOffset; midiLearnButtons[4] = butLearnMuteOffset; - QObject::connect ( butLearnMuteMyself, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); - QObject::connect ( butLearnFaderOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); - QObject::connect ( butLearnPanOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); - QObject::connect ( butLearnSoloOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); - QObject::connect ( butLearnMuteOffset, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); + for ( QPushButton* button : midiLearnButtons ) + { + QObject::connect ( button, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); + } + // Connect MIDI CC signal from sound engine QObject::connect ( pClient, &CClient::MidiCCReceived, this, &CClientSettingsDlg::OnMidiCCReceived ); @@ -1385,6 +1386,13 @@ void CClientSettingsDlg::OnMidiCCReceived ( int ccNumber ) if ( midiLearnTarget == None ) return; + // Validate MIDI CC number is within valid range (0-127) + if ( ccNumber < 0 || ccNumber > 127 ) + { + qWarning() << "CClientSettingsDlg::OnMidiCCReceived: Invalid MIDI CC number received:" << ccNumber; + return; + } + switch ( midiLearnTarget ) { case Fader: diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index d9cc5c31d2..71373200f8 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -131,7 +131,7 @@ public slots: Solo, Mute }; - MidiLearnTarget midiLearnTarget = None; + MidiLearnTarget midiLearnTarget; QPushButton* midiLearnButtons[5]; void SetMidiLearnTarget ( MidiLearnTarget target, QPushButton* activeButton ); From a36bc3803a3ff71dc77addbe15ee2fa176147a2e Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Thu, 29 May 2025 12:37:37 +0200 Subject: [PATCH 10/24] Add comment --- src/settings.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/settings.cpp b/src/settings.cpp index 0dd767b2fb..7cf45dde23 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -289,7 +289,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, bEnableAudioAlerts = bValue; } - // --- MIDI settings (do NOT require pClient) --- + // MIDI settings if ( GetNumericIniSet ( IniXMLDocument, "client", "midichannel", 0, 16, iValue ) ) { midiChannel = iValue; @@ -339,7 +339,8 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, midiMuteMyself = iValue; } - // All code that uses pClient must be guarded + // Code that uses pClient is guarded. MIDI settings in particular need to be decoupled from + // the Client state to enable opening the MIDI-in port without the --ctrlmidich pàrameter. if ( pClient ) { // name From 03900bdfa3edfe038ef98ec687790ca26ff294e1 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Mon, 2 Jun 2025 10:19:15 +0200 Subject: [PATCH 11/24] Add runtime MIDI in port toggle, remove pClient guards --- src/android/JamulusMidiManager.java | 0 src/audiomixerboard.cpp | 12 + src/client.h | 4 + src/clientdlg.cpp | 11 + src/clientdlg.h | 1 + src/clientsettingsdlg.cpp | 32 ++- src/clientsettingsdlg.h | 1 + src/clientsettingsdlgbase.ui | 77 +++++- src/main.cpp | 19 +- src/settings.cpp | 361 ++++++++++++++-------------- src/settings.h | 3 +- src/sound/asio/sound.cpp | 27 +++ src/sound/asio/sound.h | 7 + src/sound/coreaudio-mac/sound.cpp | 118 +++++++-- src/sound/coreaudio-mac/sound.h | 12 +- src/sound/jack/sound.cpp | 69 ++++-- src/sound/jack/sound.h | 4 + src/sound/midi-win/midi.cpp | 39 ++- src/sound/midi-win/midi.h | 4 +- src/sound/oboe/sound.cpp | 18 ++ src/sound/oboe/sound.h | 4 + src/sound/soundbase.cpp | 10 +- src/sound/soundbase.h | 2 + 23 files changed, 595 insertions(+), 240 deletions(-) create mode 100644 src/android/JamulusMidiManager.java diff --git a/src/android/JamulusMidiManager.java b/src/android/JamulusMidiManager.java new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/audiomixerboard.cpp b/src/audiomixerboard.cpp index f463088dfc..6031ecf146 100644 --- a/src/audiomixerboard.cpp +++ b/src/audiomixerboard.cpp @@ -226,6 +226,14 @@ void CChannelFader::SetGUIDesign ( const EGUIDesign eNewDesign ) pcbSolo->setText ( tr ( "SOLO" ) ); strGroupBaseText = tr ( "GRP" ); iInstrPicMaxWidth = INVALID_INDEX; // no instrument picture scaling + + // Use StyleSheet to set font + pPanLabel->setStyleSheet ( "font-family: 'IDroid'; font-size: 10pt;" ); + pcbMute->setStyleSheet ( "font-family: 'IDroid'; font-size: 10pt;" ); + pcbSolo->setStyleSheet ( "font-family: 'IDroid'; font-size: 10pt;" ); + pPanLabel->setCursor ( Qt::PointingHandCursor ); + pcbMute->setCursor ( Qt::PointingHandCursor ); + pcbSolo->setCursor ( Qt::PointingHandCursor ); break; case GD_SLIMFADER: @@ -611,6 +619,10 @@ void CChannelFader::UpdateGroupIDDependencies() // the fader tag border color is set according to the selected group SetupFaderTag ( cReceivedChanInfo.eSkillLevel ); + + // Set font: + pcbGroup->setFont ( QFont ( "IDroid", 10 ) ); + pcbGroup->setCursor ( Qt::PointingHandCursor ); } void CChannelFader::OnGroupStateChanged ( int ) diff --git a/src/client.h b/src/client.h index 7412582618..fc1f0966b8 100644 --- a/src/client.h +++ b/src/client.h @@ -291,6 +291,10 @@ class CClient : public QObject CProtocol* getConnLessProtocol() { return &ConnLessProtocol; } //### TODO: END ###// + // MIDI control + void EnableMIDI ( bool bEnable ) { Sound.EnableMIDI ( bEnable ); } + bool IsMIDIEnabled() const { return Sound.IsMIDIEnabled(); } + // settings CChannelCoreInfo ChannelInfo; QString strClientName; diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 7ae940a0bf..c480e4ad0d 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -537,6 +537,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP, QObject::connect ( &ClientSettingsDlg, &CClientSettingsDlg::NumMixerPanelRowsChanged, this, &CClientDlg::OnNumMixerPanelRowsChanged ); + QObject::connect ( &ClientSettingsDlg, &CClientSettingsDlg::MIDIControllerUsageChanged, this, &CClientDlg::OnMIDIControllerUsageChanged ); + QObject::connect ( this, &CClientDlg::SendTabChange, &ClientSettingsDlg, &CClientSettingsDlg::OnMakeTabChange ); QObject::connect ( MainMixerBoard, &CAudioMixerBoard::ChangeChanGain, this, &CClientDlg::OnChangeChanGain ); @@ -1519,3 +1521,12 @@ void CClientDlg::SetPingTime ( const int iPingTime, const int iOverallDelayMs, c } void CClientDlg::OnOpenMidiSettings() { ShowGeneralSettings ( SETTING_TAB_MIDI ); } + +void CClientDlg::OnMIDIControllerUsageChanged ( bool bEnabled ) +{ + // Update the mixer board's MIDI flag to trigger proper user numbering display + MainMixerBoard->SetMIDICtrlUsed ( bEnabled ); + + // Enable/disable runtime MIDI via the sound interface through the public CClient interface + pClient->EnableMIDI ( bEnabled ); +} diff --git a/src/clientdlg.h b/src/clientdlg.h index a3910a18f0..52aa968cd7 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -244,6 +244,7 @@ public slots: void accept() { close(); } // introduced by pljones void OnOpenMidiSettings(); + void OnMIDIControllerUsageChanged ( bool bEnabled ); signals: void SendTabChange ( int iTabIdx ); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 0932aef359..995fd10f9c 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -401,9 +401,9 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet // MIDI settings QString strMidiSettings = "" + tr ( "MIDI controller settings" ) + ": " + tr ( "There is one global MIDI channel parameter (1-16) and two parameters you can set " - "for each item controlled: offset and consecutive CC numbers (count). First set the " + "for each item controlled: First MIDI CC and consecutive CC numbers (count). First set the " "channel you want Jamulus to listen on (0 for all channels). Then, for each item " - "you want to control (volume fader, pan, solo, mute), set the offset (CC number " + "you want to control (volume fader, pan, solo, mute), set the first MIDI CC (CC number " "to start from) and number of consecutive CC numbers (count). There is one " "exception that does not require establishing consecutive CC numbers which is " "the “Mute Myself” parameter - it only requires a single CC number as it is only " @@ -835,6 +835,19 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet ApplyMIDIMappingFromSettings(); } ); +// Connect MIDI controller checkbox +QObject::connect(chbUseMIDIController, &QCheckBox::toggled, this, [this](bool checked) { + pSettings->bUseMIDIController = checked; + + if (checked) { + pClient->ApplyMIDIMapping(pSettings->GetMIDIMapString()); + } else { + pClient->ApplyMIDIMapping(""); + } + + emit MIDIControllerUsageChanged(checked); +}); + // MIDI Learn buttons midiLearnButtons[0] = butLearnMuteMyself; midiLearnButtons[1] = butLearnFaderOffset; @@ -886,6 +899,7 @@ void CClientSettingsDlg::showEvent ( QShowEvent* event ) spnSoloCount->setValue ( pSettings->midiSoloCount ); spnMuteOffset->setValue ( pSettings->midiMuteOffset ); spnMuteCount->setValue ( pSettings->midiMuteCount ); + chbUseMIDIController->setChecked ( pSettings->bUseMIDIController ); QDialog::showEvent ( event ); } @@ -1331,7 +1345,19 @@ void CClientSettingsDlg::OnAudioPanValueChanged ( int value ) UpdateAudioFaderSlider(); } -void CClientSettingsDlg::ApplyMIDIMappingFromSettings() { pClient->ApplyMIDIMapping ( pSettings->GetMIDIMapString() ); } +void CClientSettingsDlg::ApplyMIDIMappingFromSettings() +{ + // Only apply MIDI mapping if the controller is enabled + if ( pSettings->bUseMIDIController ) + { + pClient->ApplyMIDIMapping ( pSettings->GetMIDIMapString() ); + } + else + { + // If disabled, ensure no MIDI mapping is applied + pClient->ApplyMIDIMapping ( "" ); + } +} void CClientSettingsDlg::ResetMidiLearn() { diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 71373200f8..3f436dbce5 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -120,6 +120,7 @@ public slots: void AudioChannelsChanged(); void CustomDirectoriesChanged(); void NumMixerPanelRowsChanged ( int value ); + void MIDIControllerUsageChanged ( bool bEnabled ); private: enum MidiLearnTarget diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index d76acd0112..a9c1779430 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -1341,6 +1341,13 @@ MIDI + + + + Enable MIDI-in port + + + @@ -1377,12 +1384,18 @@ - Channel + MIDI Channel + + + 55 + 0 + + 50 @@ -1422,6 +1435,12 @@ + + + 65 + 0 + + 50 @@ -1499,12 +1518,18 @@ - Offset + First MIDI CC + + + 65 + 0 + + 50 @@ -1560,6 +1585,12 @@ + + + 65 + 0 + + 50 @@ -1624,12 +1655,18 @@ - Offset + First MIDI CC + + + 65 + 0 + + 50 @@ -1685,6 +1722,12 @@ + + + 65 + 0 + + 50 @@ -1749,12 +1792,18 @@ - Offset + First MIDI CC + + + 65 + 0 + + 50 @@ -1810,6 +1859,12 @@ + + + 65 + 0 + + 50 @@ -1874,12 +1929,18 @@ - Offset + First MIDI CC + + + 65 + 0 + + 50 @@ -1935,6 +1996,12 @@ + + + 65 + 0 + + 50 diff --git a/src/main.cpp b/src/main.cpp index d011c36916..3bc65a27e6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -920,17 +920,11 @@ int main ( int argc, char** argv ) #ifndef SERVER_ONLY if ( bIsClient ) { - if ( strMIDISetup.isEmpty() ) - { - CClientSettings tmpSettings ( nullptr, strIniFileName ); - tmpSettings.Load ( CommandLineOptions ); - strMIDISetup = tmpSettings.GetMIDIMapString(); - } - + // Create client with empty MIDI string initially (safer initialization) CClient Client ( iPortNumber, iQosNumber, strConnOnStartupAddress, - strMIDISetup, + "", // Always start with empty MIDI bNoAutoJackConnect, strClientName, bEnableIPv6, @@ -940,6 +934,7 @@ int main ( int argc, char** argv ) CClientSettings Settings ( &Client, strIniFileName ); Settings.Load ( CommandLineOptions ); + // Parse command line MIDI parameters if provided if ( !strMIDISetup.isEmpty() ) { QStringList slMIDIParams = strMIDISetup.split ( ";" ); @@ -992,6 +987,14 @@ int main ( int argc, char** argv ) } } } + + // Enable MIDI controller and apply settings when command line MIDI is provided + Settings.bUseMIDIController = true; + Client.ApplyMIDIMapping( Settings.GetMIDIMapString() ); + } + else if ( Settings.bUseMIDIController ) + { + Client.ApplyMIDIMapping( Settings.GetMIDIMapString() ); } # ifndef NO_JSON_RPC diff --git a/src/settings.cpp b/src/settings.cpp index 7cf45dde23..bab77d88b7 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -289,246 +289,247 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, bEnableAudioAlerts = bValue; } - // MIDI settings - if ( GetNumericIniSet ( IniXMLDocument, "client", "midichannel", 0, 16, iValue ) ) + // name + pClient->ChannelInfo.strName = FromBase64ToString ( + GetIniSetting ( IniXMLDocument, "client", "name_base64", ToBase64 ( QCoreApplication::translate ( "CMusProfDlg", "No Name" ) ) ) ); + + // instrument + if ( GetNumericIniSet ( IniXMLDocument, "client", "instrument", 0, CInstPictures::GetNumAvailableInst() - 1, iValue ) ) { - midiChannel = iValue; + pClient->ChannelInfo.iInstrument = iValue; } - if ( GetNumericIniSet ( IniXMLDocument, "client", "midifaderoffset", 0, 127, iValue ) ) + // country + if ( GetNumericIniSet ( IniXMLDocument, "client", "country", 0, static_cast ( QLocale::LastCountry ), iValue ) ) { - midiFaderOffset = iValue; + pClient->ChannelInfo.eCountry = CLocale::WireFormatCountryCodeToQtCountry ( iValue ); } - - if ( GetNumericIniSet ( IniXMLDocument, "client", "midifadercount", 0, 127, iValue ) ) + else { - midiFaderCount = iValue; + // if no country is given, use the one from the operating system + pClient->ChannelInfo.eCountry = QLocale::system().country(); } - if ( GetNumericIniSet ( IniXMLDocument, "client", "midipanoffset", 0, 127, iValue ) ) + // city + pClient->ChannelInfo.strCity = FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "city_base64" ) ); + + // skill level + if ( GetNumericIniSet ( IniXMLDocument, "client", "skill", 0, 3 /* SL_PROFESSIONAL */, iValue ) ) { - midiPanOffset = iValue; + pClient->ChannelInfo.eSkillLevel = static_cast ( iValue ); } - if ( GetNumericIniSet ( IniXMLDocument, "client", "midipancount", 0, 127, iValue ) ) + // audio fader + if ( GetNumericIniSet ( IniXMLDocument, "client", "audfad", AUD_FADER_IN_MIN, AUD_FADER_IN_MAX, iValue ) ) { - midiPanCount = iValue; + pClient->SetAudioInFader ( iValue ); } - if ( GetNumericIniSet ( IniXMLDocument, "client", "midisolooffset", 0, 127, iValue ) ) + + // reverberation level + if ( GetNumericIniSet ( IniXMLDocument, "client", "revlev", 0, AUD_REVERB_MAX, iValue ) ) { - midiSoloOffset = iValue; + pClient->SetReverbLevel ( iValue ); } - if ( GetNumericIniSet ( IniXMLDocument, "client", "midisolocount", 0, 127, iValue ) ) + // reverberation channel assignment + if ( GetFlagIniSet ( IniXMLDocument, "client", "reverblchan", bValue ) ) { - midiSoloCount = iValue; + pClient->SetReverbOnLeftChan ( bValue ); } - if ( GetNumericIniSet ( IniXMLDocument, "client", "midimuteoffset", 0, 127, iValue ) ) + // sound card selection + const QString strError = pClient->SetSndCrdDev ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "auddev_base64", "" ) ) ); + + if ( !strError.isEmpty() ) { - midiMuteOffset = iValue; +# 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 } - if ( GetNumericIniSet ( IniXMLDocument, "client", "midimutecount", 0, 127, iValue ) ) + // sound card channel mapping settings: make sure these settings are + // set AFTER the sound card device is set, otherwise the settings are + // overwritten by the defaults + // + // sound card left input channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) { - midiMuteCount = iValue; + pClient->SetSndCrdLeftInputChannel ( iValue ); } - if ( GetNumericIniSet ( IniXMLDocument, "client", "midimutemyself", 0, 127, iValue ) ) + // sound card right input channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) { - midiMuteMyself = iValue; + pClient->SetSndCrdRightInputChannel ( iValue ); } - // Code that uses pClient is guarded. MIDI settings in particular need to be decoupled from - // the Client state to enable opening the MIDI-in port without the --ctrlmidich pàrameter. - if ( pClient ) + // sound card left output channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) { - // name - pClient->ChannelInfo.strName = FromBase64ToString ( - GetIniSetting ( IniXMLDocument, "client", "name_base64", ToBase64 ( QCoreApplication::translate ( "CMusProfDlg", "No Name" ) ) ) ); + pClient->SetSndCrdLeftOutputChannel ( iValue ); + } - // instrument - if ( GetNumericIniSet ( IniXMLDocument, "client", "instrument", 0, CInstPictures::GetNumAvailableInst() - 1, iValue ) ) - { - pClient->ChannelInfo.iInstrument = iValue; - } + // sound card right output channel mapping + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + { + pClient->SetSndCrdRightOutputChannel ( iValue ); + } - // country - if ( GetNumericIniSet ( IniXMLDocument, "client", "country", 0, static_cast ( QLocale::LastCountry ), iValue ) ) - { - pClient->ChannelInfo.eCountry = CLocale::WireFormatCountryCodeToQtCountry ( iValue ); - } - else + // sound card preferred buffer size index + if ( GetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", FRAME_SIZE_FACTOR_PREFERRED, FRAME_SIZE_FACTOR_SAFE, iValue ) ) + { + // additional check required since only a subset of factors are + // defined + if ( ( iValue == FRAME_SIZE_FACTOR_PREFERRED ) || ( iValue == FRAME_SIZE_FACTOR_DEFAULT ) || ( iValue == FRAME_SIZE_FACTOR_SAFE ) ) { - // if no country is given, use the one from the operating system - pClient->ChannelInfo.eCountry = QLocale::system().country(); + pClient->SetSndCrdPrefFrameSizeFactor ( iValue ); } + } - // city - pClient->ChannelInfo.strCity = FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "city_base64" ) ); - - // skill level - if ( GetNumericIniSet ( IniXMLDocument, "client", "skill", 0, 3 /* SL_PROFESSIONAL */, iValue ) ) - { - pClient->ChannelInfo.eSkillLevel = static_cast ( iValue ); - } + // automatic network jitter buffer size setting + if ( GetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", bValue ) ) + { + pClient->SetDoAutoSockBufSize ( bValue ); + } - // audio fader - if ( GetNumericIniSet ( IniXMLDocument, "client", "audfad", AUD_FADER_IN_MIN, AUD_FADER_IN_MAX, iValue ) ) - { - pClient->SetAudioInFader ( iValue ); - } + // network jitter buffer size + if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbuf", MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) + { + pClient->SetSockBufNumFrames ( iValue ); + } - // reverberation level - if ( GetNumericIniSet ( IniXMLDocument, "client", "revlev", 0, AUD_REVERB_MAX, iValue ) ) - { - pClient->SetReverbLevel ( iValue ); - } + // network jitter buffer size for server + if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbufserver", MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) + { + pClient->SetServerSockBufNumFrames ( iValue ); + } - // reverberation channel assignment - if ( GetFlagIniSet ( IniXMLDocument, "client", "reverblchan", bValue ) ) - { - pClient->SetReverbOnLeftChan ( bValue ); - } + // enable OPUS64 setting + if ( GetFlagIniSet ( IniXMLDocument, "client", "enableopussmall", bValue ) ) + { + pClient->SetEnableOPUS64 ( bValue ); + } - // sound card selection - const QString strError = pClient->SetSndCrdDev ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "auddev_base64", "" ) ) ); + // GUI design + if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", 0, 2 /* GD_SLIMFADER */, iValue ) ) + { + pClient->SetGUIDesign ( static_cast ( iValue ) ); + } - if ( !strError.isEmpty() ) + // MeterStyle + if ( GetNumericIniSet ( IniXMLDocument, "client", "meterstyle", 0, 4 /* MT_LED_ROUND_BIG */, iValue ) ) + { + pClient->SetMeterStyle ( static_cast ( iValue ) ); + } + else + { + // if MeterStyle is not found in the ini, set it based on the GUI design + if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", 0, 2 /* GD_SLIMFADER */, iValue ) ) { -# 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 - } + switch ( iValue ) + { + case GD_STANDARD: + pClient->SetMeterStyle ( MT_BAR_WIDE ); + break; - // sound card channel mapping settings: make sure these settings are - // set AFTER the sound card device is set, otherwise the settings are - // overwritten by the defaults - // - // sound card left input channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) - { - pClient->SetSndCrdLeftInputChannel ( iValue ); - } + case GD_ORIGINAL: + pClient->SetMeterStyle ( MT_LED_STRIPE ); + break; - // sound card right input channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) - { - pClient->SetSndCrdRightInputChannel ( iValue ); - } + case GD_SLIMFADER: + pClient->SetMeterStyle ( MT_BAR_NARROW ); + break; - // sound card left output channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) - { - pClient->SetSndCrdLeftOutputChannel ( iValue ); + default: + pClient->SetMeterStyle ( MT_LED_STRIPE ); + break; + } } + } - // sound card right output channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) - { - pClient->SetSndCrdRightOutputChannel ( iValue ); - } + // audio channels + if ( GetNumericIniSet ( IniXMLDocument, "client", "audiochannels", 0, 2 /* CC_STEREO */, iValue ) ) + { + pClient->SetAudioChannels ( static_cast ( iValue ) ); + } - // sound card preferred buffer size index - if ( GetNumericIniSet ( IniXMLDocument, "client", "prefsndcrdbufidx", FRAME_SIZE_FACTOR_PREFERRED, FRAME_SIZE_FACTOR_SAFE, iValue ) ) - { - // additional check required since only a subset of factors are - // defined - if ( ( iValue == FRAME_SIZE_FACTOR_PREFERRED ) || ( iValue == FRAME_SIZE_FACTOR_DEFAULT ) || ( iValue == FRAME_SIZE_FACTOR_SAFE ) ) - { - pClient->SetSndCrdPrefFrameSizeFactor ( iValue ); - } - } + // audio quality + if ( GetNumericIniSet ( IniXMLDocument, "client", "audioquality", 0, 2 /* AQ_HIGH */, iValue ) ) + { + pClient->SetAudioQuality ( static_cast ( iValue ) ); + } - // automatic network jitter buffer size setting - if ( GetFlagIniSet ( IniXMLDocument, "client", "autojitbuf", bValue ) ) - { - pClient->SetDoAutoSockBufSize ( bValue ); - } + // MIDI settings + if ( GetNumericIniSet ( IniXMLDocument, "client", "midichannel", 0, 16, iValue ) ) + { + midiChannel = iValue; + } - // network jitter buffer size - if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbuf", MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) - { - pClient->SetSockBufNumFrames ( iValue ); - } + if ( GetNumericIniSet ( IniXMLDocument, "client", "midifaderoffset", 0, 127, iValue ) ) + { + midiFaderOffset = iValue; + } - // network jitter buffer size for server - if ( GetNumericIniSet ( IniXMLDocument, "client", "jitbufserver", MIN_NET_BUF_SIZE_NUM_BL, MAX_NET_BUF_SIZE_NUM_BL, iValue ) ) - { - pClient->SetServerSockBufNumFrames ( iValue ); - } + if ( GetNumericIniSet ( IniXMLDocument, "client", "midifadercount", 0, 127, iValue ) ) + { + midiFaderCount = iValue; + } - // enable OPUS64 setting - if ( GetFlagIniSet ( IniXMLDocument, "client", "enableopussmall", bValue ) ) - { - pClient->SetEnableOPUS64 ( bValue ); - } + if ( GetNumericIniSet ( IniXMLDocument, "client", "midipanoffset", 0, 127, iValue ) ) + { + midiPanOffset = iValue; + } - // GUI design - if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", 0, 2 /* GD_SLIMFADER */, iValue ) ) - { - pClient->SetGUIDesign ( static_cast ( iValue ) ); - } + if ( GetNumericIniSet ( IniXMLDocument, "client", "midipancount", 0, 127, iValue ) ) + { + midiPanCount = iValue; + } - // MeterStyle - if ( GetNumericIniSet ( IniXMLDocument, "client", "meterstyle", 0, 4 /* MT_LED_ROUND_BIG */, iValue ) ) - { - pClient->SetMeterStyle ( static_cast ( iValue ) ); - } - else - { - // if MeterStyle is not found in the ini, set it based on the GUI design - if ( GetNumericIniSet ( IniXMLDocument, "client", "guidesign", 0, 2 /* GD_SLIMFADER */, iValue ) ) - { - switch ( iValue ) - { - case GD_STANDARD: - pClient->SetMeterStyle ( MT_BAR_WIDE ); - break; + if ( GetNumericIniSet ( IniXMLDocument, "client", "midisolooffset", 0, 127, iValue ) ) + { + midiSoloOffset = iValue; + } - case GD_ORIGINAL: - pClient->SetMeterStyle ( MT_LED_STRIPE ); - break; + if ( GetNumericIniSet ( IniXMLDocument, "client", "midisolocount", 0, 127, iValue ) ) + { + midiSoloCount = iValue; + } - case GD_SLIMFADER: - pClient->SetMeterStyle ( MT_BAR_NARROW ); - break; + if ( GetNumericIniSet ( IniXMLDocument, "client", "midimuteoffset", 0, 127, iValue ) ) + { + midiMuteOffset = iValue; + } - default: - pClient->SetMeterStyle ( MT_LED_STRIPE ); - break; - } - } - } + if ( GetNumericIniSet ( IniXMLDocument, "client", "midimutecount", 0, 127, iValue ) ) + { + midiMuteCount = iValue; + } - // audio channels - if ( GetNumericIniSet ( IniXMLDocument, "client", "audiochannels", 0, 2 /* CC_STEREO */, iValue ) ) - { - pClient->SetAudioChannels ( static_cast ( iValue ) ); - } + if ( GetNumericIniSet ( IniXMLDocument, "client", "midimutemyself", 0, 127, iValue ) ) + { + midiMuteMyself = iValue; + } - // audio quality - if ( GetNumericIniSet ( IniXMLDocument, "client", "audioquality", 0, 2 /* AQ_HIGH */, iValue ) ) - { - pClient->SetAudioQuality ( static_cast ( iValue ) ); - } + if ( GetFlagIniSet ( IniXMLDocument, "client", "usemidicontroller", bValue ) ) + { + bUseMIDIController = bValue; } // custom directories - // ### TODO: BEGIN ###// - // compatibility to old version (< 3.6.1) + //### TODO: BEGIN ###// + // compatibility to old version (< 3.6.1) QString strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", "centralservaddr", "" ); - // ### TODO: END ###// + //### TODO: END ###// for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) { - // ### TODO: BEGIN ###// - // compatibility to old version (< 3.8.2) + //### TODO: BEGIN ###// + // compatibility to old version (< 3.8.2) strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", QString ( "centralservaddr%1" ).arg ( iIdx ), strDirectoryAddress ); - // ### TODO: END ###// + //### TODO: END ###// vstrDirectoryAddress[iIdx] = GetIniSetting ( IniXMLDocument, "client", QString ( "directoryaddress%1" ).arg ( iIdx ), strDirectoryAddress ); strDirectoryAddress = ""; @@ -536,9 +537,9 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // directory type - // ### TODO: BEGIN ###// - // compatibility to old version (<3.4.7) - // only the case that "centralservaddr" was set in old ini must be considered + //### TODO: BEGIN ###// + // 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; @@ -548,7 +549,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, { eDirectoryType = static_cast ( iValue ); } - // ### TODO: END ###// + //### TODO: END ###// else if ( GetNumericIniSet ( IniXMLDocument, "client", "directorytype", 0, static_cast ( AT_CUSTOM ), iValue ) ) { @@ -603,7 +604,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, } // selected Settings Tab - if ( GetNumericIniSet ( IniXMLDocument, "client", "settingstab", 0, 3, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "settingstab", 0, 2, iValue ) ) { iSettingsTab = iValue; } @@ -611,7 +612,6 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // fader settings ReadFaderSettingsFromXML ( IniXMLDocument ); } - void CClientSettings::ReadFaderSettingsFromXML ( const QDomDocument& IniXMLDocument ) { int iIdx; @@ -820,6 +820,7 @@ void CClientSettings::WriteSettingsToXML ( QDomDocument& IniXMLDocument, bool is SetNumericIniSet ( IniXMLDocument, "client", "midimuteoffset", midiMuteOffset ); SetNumericIniSet ( IniXMLDocument, "client", "midimutecount", midiMuteCount ); SetNumericIniSet ( IniXMLDocument, "client", "midimutemyself", midiMuteMyself ); + SetFlagIniSet ( IniXMLDocument, "client", "usemidicontroller", bUseMIDIController ); // fader settings WriteFaderSettingsToXML ( IniXMLDocument ); diff --git a/src/settings.h b/src/settings.h index 0db6dde0e1..79fce1e5b5 100644 --- a/src/settings.h +++ b/src/settings.h @@ -204,7 +204,7 @@ class CClientSettings : public CSettings // MIDI settings int midiChannel = 0; // Default MIDI channel 0 int midiMuteMyself = 0; - int midiFaderOffset = 70; + int midiFaderOffset = 0; int midiFaderCount = 0; int midiPanOffset = 0; int midiPanCount = 0; @@ -212,6 +212,7 @@ class CClientSettings : public CSettings int midiSoloCount = 0; int midiMuteOffset = 0; int midiMuteCount = 0; + bool bUseMIDIController = false; QString GetMIDIMapString() const; protected: diff --git a/src/sound/asio/sound.cpp b/src/sound/asio/sound.cpp index 74044315c4..e61b9ccac7 100644 --- a/src/sound/asio/sound.cpp +++ b/src/sound/asio/sound.cpp @@ -629,6 +629,33 @@ bool CSound::CheckSampleTypeSupportedForCHMixing ( const ASIOSampleType SamType return ( ( SamType == ASIOSTInt16LSB ) || ( SamType == ASIOSTInt24LSB ) || ( SamType == ASIOSTInt32LSB ) ); } +void CSound::EnableMIDI(bool bEnable) +{ + if (bEnable) + { + // Enable MIDI only if it's not already enabled + if (!bMidiEnabled && iCtrlMIDIChannel != INVALID_MIDI_CH) + { + Midi.MidiStart(); + bMidiEnabled = true; + } + } + else + { + // Disable MIDI only if it's currently enabled + if (bMidiEnabled) + { + Midi.MidiStop(); + bMidiEnabled = false; + } + } +} + +bool CSound::IsMIDIEnabled() const +{ + return bMidiEnabled; +} + void CSound::bufferSwitch ( long index, ASIOBool ) { int iCurSample; diff --git a/src/sound/asio/sound.h b/src/sound/asio/sound.h index 12caaf0aa3..1801ffdd21 100644 --- a/src/sound/asio/sound.h +++ b/src/sound/asio/sound.h @@ -82,6 +82,10 @@ class CSound : public CSoundBase virtual float GetInOutLatencyMs() { return fInOutLatencyMs; } + // MIDI port toggle + virtual void EnableMIDI(bool bEnable); + virtual bool IsMIDIEnabled() const; + protected: virtual QString LoadAndInitializeDriver ( QString strDriverName, bool bOpenDriverSetup ); virtual void UnloadCurrentDriver(); @@ -138,4 +142,7 @@ class CSound : public CSoundBase // Windows native MIDI support CMidi Midi; + +private: + bool bMidiEnabled = false; // Tracks the runtime state of MIDI }; diff --git a/src/sound/coreaudio-mac/sound.cpp b/src/sound/coreaudio-mac/sound.cpp index 5fc793424c..99cb7aed59 100644 --- a/src/sound/coreaudio-mac/sound.cpp +++ b/src/sound/coreaudio-mac/sound.cpp @@ -31,6 +31,7 @@ CSound::CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* const bool, const QString& ) : CSoundBase ( "CoreAudio", fpNewProcessCallback, arg, strMIDISetup ), + midiClient ( static_cast ( NULL ) ), midiInPortRef ( static_cast ( NULL ) ) { // Apple Mailing Lists: Subject: GUI Apps should set kAudioHardwarePropertyRunLoop @@ -60,24 +61,12 @@ CSound::CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* iSelInputRightChannel = 0; iSelOutputLeftChannel = 0; iSelOutputRightChannel = 0; +} - // Optional MIDI initialization -------------------------------------------- - if ( iCtrlMIDIChannel != INVALID_MIDI_CH ) - { - // create client and ports - MIDIClientRef midiClient = static_cast ( NULL ); - MIDIClientCreate ( CFSTR ( APP_NAME ), NULL, NULL, &midiClient ); - MIDIInputPortCreate ( midiClient, CFSTR ( "Input port" ), callbackMIDI, this, &midiInPortRef ); - - // open connections from all sources - const int iNMIDISources = MIDIGetNumberOfSources(); - - for ( int i = 0; i < iNMIDISources; i++ ) - { - MIDIEndpointRef src = MIDIGetSource ( i ); - MIDIPortConnectSource ( midiInPortRef, src, NULL ); - } - } +CSound::~CSound() +{ + // Ensure MIDI resources are properly cleaned up + DestroyMIDIPort(); } void CSound::GetAvailableInOutDevices() @@ -718,6 +707,101 @@ void CSound::Stop() CSoundBase::Stop(); } +void CSound::EnableMIDI ( bool bEnable ) +{ + if ( bEnable && ( iCtrlMIDIChannel != INVALID_MIDI_CH ) ) + { + // Create MIDI port if we have valid MIDI channel and no port exists + if ( midiInPortRef == static_cast ( NULL ) ) + { + CreateMIDIPort(); + } + } + else + { + // Destroy MIDI port if it exists + if ( midiInPortRef != static_cast ( NULL ) ) + { + DestroyMIDIPort(); + } + } +} + +bool CSound::IsMIDIEnabled() const +{ + return ( midiInPortRef != static_cast ( NULL ) ); +} + +void CSound::CreateMIDIPort() +{ + if ( midiClient == static_cast ( NULL ) ) + { + // Create MIDI client + OSStatus result = MIDIClientCreate ( CFSTR ( APP_NAME ), NULL, NULL, &midiClient ); + if ( result != noErr ) + { + qWarning() << "Failed to create CoreAudio MIDI client. Error code:" << result; + return; + } + } + + if ( midiInPortRef == static_cast ( NULL ) ) + { + // Create MIDI input port + OSStatus result = MIDIInputPortCreate ( midiClient, CFSTR ( "Input port" ), callbackMIDI, this, &midiInPortRef ); + if ( result != noErr ) + { + qWarning() << "Failed to create CoreAudio MIDI input port. Error code:" << result; + return; + } + + // Connect to all available MIDI sources + const int iNMIDISources = MIDIGetNumberOfSources(); + for ( int i = 0; i < iNMIDISources; i++ ) + { + MIDIEndpointRef src = MIDIGetSource ( i ); + MIDIPortConnectSource ( midiInPortRef, src, NULL ); + } + + qInfo() << "CoreAudio MIDI port created and connected to" << iNMIDISources << "sources"; + } +} + +void CSound::DestroyMIDIPort() +{ + if ( midiInPortRef != static_cast ( NULL ) ) + { + // Disconnect from all sources before disposing + const int iNMIDISources = MIDIGetNumberOfSources(); + for ( int i = 0; i < iNMIDISources; i++ ) + { + MIDIEndpointRef src = MIDIGetSource ( i ); + MIDIPortDisconnectSource ( midiInPortRef, src ); + } + + // Dispose of the MIDI input port + OSStatus result = MIDIPortDispose ( midiInPortRef ); + if ( result != noErr ) + { + qWarning() << "Failed to dispose CoreAudio MIDI input port. Error code:" << result; + } + midiInPortRef = static_cast ( NULL ); + + qInfo() << "CoreAudio MIDI port destroyed"; + } + + // Only dispose client if no ports are using it + if ( midiClient != static_cast ( NULL ) && midiInPortRef == static_cast ( NULL ) ) + { + OSStatus result = MIDIClientDispose ( midiClient ); + if ( result != noErr ) + { + qWarning() << "Failed to dispose CoreAudio MIDI client. Error code:" << result; + } + midiClient = static_cast ( NULL ); + } +} + int CSound::Init ( const int iNewPrefMonoBufferSize ) { UInt32 iActualMonoBufferSize; diff --git a/src/sound/coreaudio-mac/sound.h b/src/sound/coreaudio-mac/sound.h index 9ba70a01fd..5bab7ac254 100644 --- a/src/sound/coreaudio-mac/sound.h +++ b/src/sound/coreaudio-mac/sound.h @@ -43,6 +43,8 @@ class CSound : public CSoundBase const QString& strMIDISetup, const bool, const QString& ); + + virtual ~CSound(); virtual int Init ( const int iNewPrefMonoBufferSize ); virtual void Start(); @@ -63,6 +65,10 @@ class CSound : public CSoundBase virtual int GetLeftOutputChannel() { return iSelOutputLeftChannel; } virtual int GetRightOutputChannel() { return iSelOutputRightChannel; } + // MIDI functions + virtual void EnableMIDI ( const bool bEnable ); + virtual bool IsMIDIEnabled() const; + // these variables/functions should be protected but cannot since we want // to access them from the callback function CVector vecsTmpAudioSndCrdStereo; @@ -108,6 +114,9 @@ class CSound : public CSoundBase bool ConvertCFStringToQString ( const CFStringRef stringRef, QString& sOut ); + void CreateMIDIPort(); + void DestroyMIDIPort(); + // callbacks static OSStatus deviceNotification ( AudioDeviceID, UInt32, const AudioObjectPropertyAddress* inAddresses, void* inRefCon ); @@ -126,7 +135,8 @@ class CSound : public CSoundBase AudioDeviceIOProcID audioInputProcID; AudioDeviceIOProcID audioOutputProcID; - MIDIPortRef midiInPortRef; + MIDIClientRef midiClient; + MIDIPortRef midiInPortRef; QString sChannelNamesInput[MAX_NUM_IN_OUT_CHANNELS]; QString sChannelNamesOutput[MAX_NUM_IN_OUT_CHANNELS]; diff --git a/src/sound/jack/sound.cpp b/src/sound/jack/sound.cpp index e53dbece42..efcb8e18e0 100644 --- a/src/sound/jack/sound.cpp +++ b/src/sound/jack/sound.cpp @@ -85,21 +85,7 @@ void CSound::OpenJack ( const bool bNoAutoJackConnect, const char* jackClientNam } // optional MIDI initialization - if ( iCtrlMIDIChannel != INVALID_MIDI_CH ) - { - input_port_midi = jack_port_register ( pJackClient, "input midi", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); - - if ( input_port_midi == nullptr ) - { - throw CGenErr ( QString ( tr ( "The JACK port registration failed. This is probably an error with JACK. Please stop %1 and JACK. " - "Afterwards, check if another MIDI program can connect to JACK." ) ) - .arg ( APP_NAME ) ); - } - } - else - { - input_port_midi = nullptr; - } + input_port_midi = nullptr; // tell the JACK server that we are ready to roll if ( jack_activate ( pJackClient ) ) @@ -192,6 +178,59 @@ void CSound::Stop() CSoundBase::Stop(); } +void CSound::EnableMIDI ( bool bEnable ) +{ + if ( bEnable && ( iCtrlMIDIChannel != INVALID_MIDI_CH ) ) + { + // Create MIDI port if we have valid MIDI channel and no port exists + if ( input_port_midi == nullptr ) + { + CreateMIDIPort(); + } + } + else + { + // Destroy MIDI port if it exists + if ( input_port_midi != nullptr ) + { + DestroyMIDIPort(); + } + } +} + +bool CSound::IsMIDIEnabled() const +{ + return ( input_port_midi != nullptr ); +} + +void CSound::CreateMIDIPort() +{ + if ( pJackClient != nullptr && input_port_midi == nullptr ) + { + input_port_midi = jack_port_register ( pJackClient, "input midi", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); + + if ( input_port_midi == nullptr ) + { + qWarning() << "Failed to create JACK MIDI port at runtime"; + } + } +} + +void CSound::DestroyMIDIPort() +{ + if ( pJackClient != nullptr && input_port_midi != nullptr ) + { + if ( jack_port_unregister ( pJackClient, input_port_midi ) == 0 ) + { + input_port_midi = nullptr; + } + else + { + qWarning() << "Failed to destroy JACK MIDI port"; + } + } +} + int CSound::Init ( const int /* iNewPrefMonoBufferSize */ ) { diff --git a/src/sound/jack/sound.h b/src/sound/jack/sound.h index 55b922ef7d..ea4e01ff5d 100644 --- a/src/sound/jack/sound.h +++ b/src/sound/jack/sound.h @@ -87,6 +87,8 @@ class CSound : public CSoundBase virtual void Stop(); virtual float GetInOutLatencyMs() { return fInOutLatencyMs; } + virtual void EnableMIDI ( bool bEnable ) override; + virtual bool IsMIDIEnabled() const override; // these variables should be protected but cannot since we want // to access them from the callback function @@ -105,6 +107,8 @@ class CSound : public CSoundBase void OpenJack ( const bool bNoAutoJackConnect, const char* jackClientName ); void CloseJack(); + void CreateMIDIPort(); + void DestroyMIDIPort(); // callbacks static int process ( jack_nframes_t nframes, void* arg ); diff --git a/src/sound/midi-win/midi.cpp b/src/sound/midi-win/midi.cpp index 654b05760d..ee083c4d7e 100644 --- a/src/sound/midi-win/midi.cpp +++ b/src/sound/midi-win/midi.cpp @@ -39,6 +39,11 @@ extern CSound* pSound; void CMidi::MidiStart() { + if (m_bIsActive) + { + return; // MIDI is already active, no need to start again + } + QString selMIDIDevice = pSound->GetMIDIDevice(); /* Get the number of MIDI In devices in this computer */ @@ -49,15 +54,15 @@ void CMidi::MidiStart() // open all connected MIDI devices and set the callback function to handle incoming messages for ( int i = 0; i < iMidiDevs; i++ ) { - HMIDIIN hMidiIn; // windows handle - MIDIINCAPS mic; // device name and capabilities + HMIDIIN hMidiIn; // Windows handle + MIDIINCAPS mic; // Device name and capabilities MMRESULT result = midiInGetDevCaps ( i, &mic, sizeof ( MIDIINCAPS ) ); if ( result != MMSYSERR_NOERROR ) { qWarning() << qUtf8Printable ( QString ( "! Failed to identify MIDI input device %1. Error code: %2" ).arg ( i ).arg ( result ) ); - continue; // try next device, if any + continue; // Try next device, if any } QString midiDev ( mic.szPname ); @@ -65,7 +70,7 @@ void CMidi::MidiStart() if ( !selMIDIDevice.isEmpty() && selMIDIDevice != midiDev ) { qInfo() << qUtf8Printable ( QString ( " %1: %2 (ignored)" ).arg ( i ).arg ( midiDev ) ); - continue; // try next device, if any + continue; // Try next device, if any } qInfo() << qUtf8Printable ( QString ( " %1: %2" ).arg ( i ).arg ( midiDev ) ); @@ -75,7 +80,7 @@ void CMidi::MidiStart() if ( result != MMSYSERR_NOERROR ) { qWarning() << qUtf8Printable ( QString ( "! Failed to open MIDI input device %1. Error code: %2" ).arg ( i ).arg ( result ) ); - continue; // try next device, if any + continue; // Try next device, if any } result = midiInStart ( hMidiIn ); @@ -84,22 +89,40 @@ void CMidi::MidiStart() { qWarning() << qUtf8Printable ( QString ( "! Failed to start MIDI input device %1. Error code: %2" ).arg ( i ).arg ( result ) ); midiInClose ( hMidiIn ); - continue; // try next device, if any + continue; // Try next device, if any } - // success, add it to list of open handles + // Success, add it to the list of open handles vecMidiInHandles.append ( hMidiIn ); } + + if (!vecMidiInHandles.isEmpty()) + { + m_bIsActive = true; // Set active state if at least one device was started + } } void CMidi::MidiStop() { - // stop MIDI if running + if (!m_bIsActive) + { + return; // MIDI is already stopped, no need to stop again + } + + // Stop MIDI if running for ( int i = 0; i < vecMidiInHandles.size(); i++ ) { midiInStop ( vecMidiInHandles.at ( i ) ); midiInClose ( vecMidiInHandles.at ( i ) ); } + + vecMidiInHandles.clear(); // Clear the list of handles + m_bIsActive = false; // Set active state to false +} + +bool CMidi::IsActive() const +{ + return m_bIsActive; } // See https://learn.microsoft.com/en-us/previous-versions//dd798460(v=vs.85) diff --git a/src/sound/midi-win/midi.h b/src/sound/midi-win/midi.h index 3ce01f9ced..31f023954f 100644 --- a/src/sound/midi-win/midi.h +++ b/src/sound/midi-win/midi.h @@ -31,16 +31,18 @@ class CMidi { public: - CMidi() {} + CMidi() : m_bIsActive(false) {} virtual ~CMidi() {} void MidiStart(); void MidiStop(); + bool IsActive() const; protected: int iMidiDevs; QVector vecMidiInHandles; // windows handles + bool m_bIsActive; // Tracks if MIDI is currently active static void CALLBACK MidiCallback ( HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ); }; diff --git a/src/sound/oboe/sound.cpp b/src/sound/oboe/sound.cpp index 6fe3a5dceb..5af91c3ff3 100644 --- a/src/sound/oboe/sound.cpp +++ b/src/sound/oboe/sound.cpp @@ -354,3 +354,21 @@ void CSound::Stats::log() const << "frames_in: " << frames_in << ",frames_out: " << frames_out << ",frames_filled_out: " << frames_filled_out << ",in_callback_calls: " << in_callback_calls << ",out_callback_calls: " << out_callback_calls << ",ring_overrun: " << ring_overrun; } + +// MIDI functions implementation for Android/Oboe +void CSound::EnableMIDI ( bool bEnable ) +{ + // Android/Oboe MIDI support is not currently implemented + // This is a placeholder implementation to maintain API compatibility + Q_UNUSED ( bEnable ); + + // TODO: Add Android MIDI implementation using Android MIDI API + // See: https://developer.android.com/reference/android/media/midi/package-summary +} + +bool CSound::IsMIDIEnabled() const +{ + // Android/Oboe MIDI support is not currently implemented + // Return false to indicate MIDI is not available + return false; +} diff --git a/src/sound/oboe/sound.h b/src/sound/oboe/sound.h index 3045b079cc..c2058aeb25 100644 --- a/src/sound/oboe/sound.h +++ b/src/sound/oboe/sound.h @@ -50,6 +50,10 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback virtual void Start(); virtual void Stop(); + // MIDI control - required virtual functions from CSoundBase + virtual void EnableMIDI ( bool bEnable ) override; + virtual bool IsMIDIEnabled() const override; + // Call backs for Oboe virtual oboe::DataCallbackResult onAudioReady ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ); virtual void onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result result ); diff --git a/src/sound/soundbase.cpp b/src/sound/soundbase.cpp index d517219bc6..770206509f 100644 --- a/src/sound/soundbase.cpp +++ b/src/sound/soundbase.cpp @@ -422,4 +422,12 @@ void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) } } -void CSoundBase::SetMIDIMapping ( const QString& strMIDISetup ) { ParseCommandLineArgument ( strMIDISetup ); } +void CSoundBase::SetMIDIMapping ( const QString& strMIDISetup ) +{ + // Parse the MIDI mapping + ParseCommandLineArgument ( strMIDISetup ); + + // Enable/disable MIDI port based on whether mapping is empty + bool bShouldEnable = !strMIDISetup.isEmpty(); + EnableMIDI ( bShouldEnable ); +} diff --git a/src/sound/soundbase.h b/src/sound/soundbase.h index 26188161d7..773e7879ec 100644 --- a/src/sound/soundbase.h +++ b/src/sound/soundbase.h @@ -119,6 +119,8 @@ class CSoundBase : public QThread // this needs to be public so that it can be called from CMidi void ParseMIDIMessage ( const CVector& vMIDIPaketBytes ); void SetMIDIMapping ( const QString& strMIDISetup ); + virtual void EnableMIDI ( bool /* bEnable */ ) {} // Default empty implementation + virtual bool IsMIDIEnabled() const { return false; } // Default false protected: virtual QString LoadAndInitializeDriver ( QString, bool ) { return ""; } From b6d9f12a560e19dda58836d54bf65c59666d692b Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Mon, 2 Jun 2025 11:22:38 +0200 Subject: [PATCH 12/24] clang-format and remove android MIDI changes --- src/android/JamulusMidiManager.java | 0 src/audiomixerboard.cpp | 12 ------------ src/client.h | 6 +++--- src/clientdlg.cpp | 10 +++++----- src/clientsettingsdlg.cpp | 25 ++++++++++++++----------- src/main.cpp | 14 ++++++-------- src/settings.cpp | 20 ++++++++++---------- src/sound/asio/sound.cpp | 25 +++++++++++-------------- src/sound/asio/sound.h | 2 +- src/sound/coreaudio-mac/sound.cpp | 9 +++------ src/sound/coreaudio-mac/sound.h | 2 +- src/sound/jack/sound.cpp | 25 +++++++++++-------------- src/sound/jack/sound.h | 4 ++-- src/sound/midi-win/midi.cpp | 23 ++++++++++------------- src/sound/midi-win/midi.h | 4 ++-- src/sound/oboe/sound.cpp | 18 ------------------ src/sound/oboe/sound.h | 4 ---- src/sound/soundbase.h | 6 +++--- 18 files changed, 82 insertions(+), 127 deletions(-) delete mode 100644 src/android/JamulusMidiManager.java diff --git a/src/android/JamulusMidiManager.java b/src/android/JamulusMidiManager.java deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/audiomixerboard.cpp b/src/audiomixerboard.cpp index 6031ecf146..f463088dfc 100644 --- a/src/audiomixerboard.cpp +++ b/src/audiomixerboard.cpp @@ -226,14 +226,6 @@ void CChannelFader::SetGUIDesign ( const EGUIDesign eNewDesign ) pcbSolo->setText ( tr ( "SOLO" ) ); strGroupBaseText = tr ( "GRP" ); iInstrPicMaxWidth = INVALID_INDEX; // no instrument picture scaling - - // Use StyleSheet to set font - pPanLabel->setStyleSheet ( "font-family: 'IDroid'; font-size: 10pt;" ); - pcbMute->setStyleSheet ( "font-family: 'IDroid'; font-size: 10pt;" ); - pcbSolo->setStyleSheet ( "font-family: 'IDroid'; font-size: 10pt;" ); - pPanLabel->setCursor ( Qt::PointingHandCursor ); - pcbMute->setCursor ( Qt::PointingHandCursor ); - pcbSolo->setCursor ( Qt::PointingHandCursor ); break; case GD_SLIMFADER: @@ -619,10 +611,6 @@ void CChannelFader::UpdateGroupIDDependencies() // the fader tag border color is set according to the selected group SetupFaderTag ( cReceivedChanInfo.eSkillLevel ); - - // Set font: - pcbGroup->setFont ( QFont ( "IDroid", 10 ) ); - pcbGroup->setCursor ( Qt::PointingHandCursor ); } void CChannelFader::OnGroupStateChanged ( int ) diff --git a/src/client.h b/src/client.h index fc1f0966b8..3bfa3f0139 100644 --- a/src/client.h +++ b/src/client.h @@ -286,10 +286,10 @@ class CClient : public QObject Channel.GetBufErrorRates ( vecErrRates, dLimit, dMaxUpLimit ); } - //### TODO: BEGIN ###// - // Refactor this to use signal/slot mechanism. https://github.com/jamulussoftware/jamulus/pull/3479/files#r1976382416 + // ### TODO: BEGIN ###// + // Refactor this to use signal/slot mechanism. https://github.com/jamulussoftware/jamulus/pull/3479/files#r1976382416 CProtocol* getConnLessProtocol() { return &ConnLessProtocol; } - //### TODO: END ###// + // ### TODO: END ###// // MIDI control void EnableMIDI ( bool bEnable ) { Sound.EnableMIDI ( bEnable ); } diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index c480e4ad0d..18423b771d 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -1289,11 +1289,11 @@ void CClientDlg::Disconnect() TimerDetectFeedback.stop(); bDetectFeedback = false; - //### TODO: BEGIN ###// - // is this still required??? - // immediately update status bar + // ### TODO: BEGIN ###// + // is this still required??? + // immediately update status bar OnTimerStatus(); - //### TODO: END ###// + // ### TODO: END ###// // reset LEDs ledBuffers->Reset(); @@ -1526,7 +1526,7 @@ void CClientDlg::OnMIDIControllerUsageChanged ( bool bEnabled ) { // Update the mixer board's MIDI flag to trigger proper user numbering display MainMixerBoard->SetMIDICtrlUsed ( bEnabled ); - + // Enable/disable runtime MIDI via the sound interface through the public CClient interface pClient->EnableMIDI ( bEnabled ); } diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 995fd10f9c..99a8838601 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -835,18 +835,21 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet ApplyMIDIMappingFromSettings(); } ); -// Connect MIDI controller checkbox -QObject::connect(chbUseMIDIController, &QCheckBox::toggled, this, [this](bool checked) { - pSettings->bUseMIDIController = checked; - - if (checked) { - pClient->ApplyMIDIMapping(pSettings->GetMIDIMapString()); - } else { - pClient->ApplyMIDIMapping(""); - } + // Connect MIDI controller checkbox + QObject::connect ( chbUseMIDIController, &QCheckBox::toggled, this, [this] ( bool checked ) { + pSettings->bUseMIDIController = checked; + + if ( checked ) + { + pClient->ApplyMIDIMapping ( pSettings->GetMIDIMapString() ); + } + else + { + pClient->ApplyMIDIMapping ( "" ); + } - emit MIDIControllerUsageChanged(checked); -}); + emit MIDIControllerUsageChanged ( checked ); + } ); // MIDI Learn buttons midiLearnButtons[0] = butLearnMuteMyself; diff --git a/src/main.cpp b/src/main.cpp index 3bc65a27e6..039a510ac9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -662,14 +662,12 @@ int main ( int argc, char** argv ) if ( !strServerListFileName.isEmpty() ) { - qInfo() << "Note:" - << "Server list persistence file will only take effect when running as a directory."; + qInfo() << "Note:" << "Server list persistence file will only take effect when running as a directory."; } if ( !strServerListFilter.isEmpty() ) { - qInfo() << "Note:" - << "Server list filter will only take effect when running as a directory."; + qInfo() << "Note:" << "Server list filter will only take effect when running as a directory."; } } else @@ -821,7 +819,7 @@ int main ( int argc, char** argv ) bIsClient = true; // Client only - TODO: maybe a switch in interface to change to server? // bUseMultithreading = true; - QApplication* pApp = new QApplication ( argc, argv ); + QApplication* pApp = new QApplication ( argc, argv ); # else QCoreApplication* pApp = bUseGUI ? new QApplication ( argc, argv ) : new QCoreApplication ( argc, argv ); # endif @@ -877,7 +875,7 @@ int main ( int argc, char** argv ) qWarning() << "No JSON-RPC support in this build."; } #else - CRpcServer* pRpcServer = nullptr; + CRpcServer* pRpcServer = nullptr; if ( iJsonRpcPortNumber != INVALID_PORT ) { @@ -990,11 +988,11 @@ int main ( int argc, char** argv ) // Enable MIDI controller and apply settings when command line MIDI is provided Settings.bUseMIDIController = true; - Client.ApplyMIDIMapping( Settings.GetMIDIMapString() ); + Client.ApplyMIDIMapping ( Settings.GetMIDIMapString() ); } else if ( Settings.bUseMIDIController ) { - Client.ApplyMIDIMapping( Settings.GetMIDIMapString() ); + Client.ApplyMIDIMapping ( Settings.GetMIDIMapString() ); } # ifndef NO_JSON_RPC diff --git a/src/settings.cpp b/src/settings.cpp index bab77d88b7..8f4748a7fe 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -519,17 +519,17 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // custom directories - //### TODO: BEGIN ###// - // compatibility to old version (< 3.6.1) + // ### TODO: BEGIN ###// + // compatibility to old version (< 3.6.1) QString strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", "centralservaddr", "" ); - //### TODO: END ###// + // ### TODO: END ###// for ( iIdx = 0; iIdx < MAX_NUM_SERVER_ADDR_ITEMS; iIdx++ ) { - //### TODO: BEGIN ###// - // compatibility to old version (< 3.8.2) + // ### TODO: BEGIN ###// + // compatibility to old version (< 3.8.2) strDirectoryAddress = GetIniSetting ( IniXMLDocument, "client", QString ( "centralservaddr%1" ).arg ( iIdx ), strDirectoryAddress ); - //### TODO: END ###// + // ### TODO: END ###// vstrDirectoryAddress[iIdx] = GetIniSetting ( IniXMLDocument, "client", QString ( "directoryaddress%1" ).arg ( iIdx ), strDirectoryAddress ); strDirectoryAddress = ""; @@ -537,9 +537,9 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, // directory type - //### TODO: BEGIN ###// - // compatibility to old version (<3.4.7) - // only the case that "centralservaddr" was set in old ini must be considered + // ### TODO: BEGIN ###// + // 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; @@ -549,7 +549,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, { eDirectoryType = static_cast ( iValue ); } - //### TODO: END ###// + // ### TODO: END ###// else if ( GetNumericIniSet ( IniXMLDocument, "client", "directorytype", 0, static_cast ( AT_CUSTOM ), iValue ) ) { diff --git a/src/sound/asio/sound.cpp b/src/sound/asio/sound.cpp index e61b9ccac7..a8bec59418 100644 --- a/src/sound/asio/sound.cpp +++ b/src/sound/asio/sound.cpp @@ -311,7 +311,7 @@ int CSound::GetActualBufferSize ( const int iDesiredBufferSizeMono ) // query the usable buffer sizes ASIOGetBufferSize ( &HWBufferInfo.lMinSize, &HWBufferInfo.lMaxSize, &HWBufferInfo.lPreferredSize, &HWBufferInfo.lGranularity ); - //### TEST: BEGIN ###// + // ### TEST: BEGIN ###// /* #include QMessageBox::information ( 0, "APP_NAME", QString("lMinSize: %1, lMaxSize: %2, lPreferredSize: %3, lGranularity: %4"). @@ -319,12 +319,12 @@ int CSound::GetActualBufferSize ( const int iDesiredBufferSizeMono ) ); _exit(1); */ - //### TEST: END ###// + // ### TEST: END ###// - //### TODO: BEGIN ###// - // see https://github.com/EddieRingle/portaudio/blob/master/src/hostapi/asio/pa_asio.cpp#L1654 - // (SelectHostBufferSizeForUnspecifiedUserFramesPerBuffer) - //### TODO: END ###// + // ### TODO: BEGIN ###// + // see https://github.com/EddieRingle/portaudio/blob/master/src/hostapi/asio/pa_asio.cpp#L1654 + // (SelectHostBufferSizeForUnspecifiedUserFramesPerBuffer) + // ### TODO: END ###// // calculate "nearest" buffer size and set internal parameter accordingly // first check minimum and maximum values @@ -629,12 +629,12 @@ bool CSound::CheckSampleTypeSupportedForCHMixing ( const ASIOSampleType SamType return ( ( SamType == ASIOSTInt16LSB ) || ( SamType == ASIOSTInt24LSB ) || ( SamType == ASIOSTInt32LSB ) ); } -void CSound::EnableMIDI(bool bEnable) +void CSound::EnableMIDI ( bool bEnable ) { - if (bEnable) + if ( bEnable ) { // Enable MIDI only if it's not already enabled - if (!bMidiEnabled && iCtrlMIDIChannel != INVALID_MIDI_CH) + if ( !bMidiEnabled && iCtrlMIDIChannel != INVALID_MIDI_CH ) { Midi.MidiStart(); bMidiEnabled = true; @@ -643,7 +643,7 @@ void CSound::EnableMIDI(bool bEnable) else { // Disable MIDI only if it's currently enabled - if (bMidiEnabled) + if ( bMidiEnabled ) { Midi.MidiStop(); bMidiEnabled = false; @@ -651,10 +651,7 @@ void CSound::EnableMIDI(bool bEnable) } } -bool CSound::IsMIDIEnabled() const -{ - return bMidiEnabled; -} +bool CSound::IsMIDIEnabled() const { return bMidiEnabled; } void CSound::bufferSwitch ( long index, ASIOBool ) { diff --git a/src/sound/asio/sound.h b/src/sound/asio/sound.h index 1801ffdd21..7259be2e38 100644 --- a/src/sound/asio/sound.h +++ b/src/sound/asio/sound.h @@ -83,7 +83,7 @@ class CSound : public CSoundBase virtual float GetInOutLatencyMs() { return fInOutLatencyMs; } // MIDI port toggle - virtual void EnableMIDI(bool bEnable); + virtual void EnableMIDI ( bool bEnable ); virtual bool IsMIDIEnabled() const; protected: diff --git a/src/sound/coreaudio-mac/sound.cpp b/src/sound/coreaudio-mac/sound.cpp index 99cb7aed59..cfbbe78f00 100644 --- a/src/sound/coreaudio-mac/sound.cpp +++ b/src/sound/coreaudio-mac/sound.cpp @@ -727,10 +727,7 @@ void CSound::EnableMIDI ( bool bEnable ) } } -bool CSound::IsMIDIEnabled() const -{ - return ( midiInPortRef != static_cast ( NULL ) ); -} +bool CSound::IsMIDIEnabled() const { return ( midiInPortRef != static_cast ( NULL ) ); } void CSound::CreateMIDIPort() { @@ -762,7 +759,7 @@ void CSound::CreateMIDIPort() MIDIEndpointRef src = MIDIGetSource ( i ); MIDIPortConnectSource ( midiInPortRef, src, NULL ); } - + qInfo() << "CoreAudio MIDI port created and connected to" << iNMIDISources << "sources"; } } @@ -786,7 +783,7 @@ void CSound::DestroyMIDIPort() qWarning() << "Failed to dispose CoreAudio MIDI input port. Error code:" << result; } midiInPortRef = static_cast ( NULL ); - + qInfo() << "CoreAudio MIDI port destroyed"; } diff --git a/src/sound/coreaudio-mac/sound.h b/src/sound/coreaudio-mac/sound.h index 5bab7ac254..96f2e397ce 100644 --- a/src/sound/coreaudio-mac/sound.h +++ b/src/sound/coreaudio-mac/sound.h @@ -43,7 +43,7 @@ class CSound : public CSoundBase const QString& strMIDISetup, const bool, const QString& ); - + virtual ~CSound(); virtual int Init ( const int iNewPrefMonoBufferSize ); diff --git a/src/sound/jack/sound.cpp b/src/sound/jack/sound.cpp index efcb8e18e0..50f7324201 100644 --- a/src/sound/jack/sound.cpp +++ b/src/sound/jack/sound.cpp @@ -198,10 +198,7 @@ void CSound::EnableMIDI ( bool bEnable ) } } -bool CSound::IsMIDIEnabled() const -{ - return ( input_port_midi != nullptr ); -} +bool CSound::IsMIDIEnabled() const { return ( input_port_midi != nullptr ); } void CSound::CreateMIDIPort() { @@ -234,13 +231,13 @@ void CSound::DestroyMIDIPort() int CSound::Init ( const int /* iNewPrefMonoBufferSize */ ) { - //### TODO: BEGIN ###// - // try setting buffer size seems not to work! -> no audio after this operation! - // Doesn't this give an infinite loop? The set buffer size function will call our - // registered callback which calls "EmitReinitRequestSignal()". In that function - // this CSound::Init() function is called... - // jack_set_buffer_size ( pJackClient, iNewPrefMonoBufferSize ); - //### TODO: END ###// + // ### TODO: BEGIN ###// + // try setting buffer size seems not to work! -> no audio after this operation! + // Doesn't this give an infinite loop? The set buffer size function will call our + // registered callback which calls "EmitReinitRequestSignal()". In that function + // this CSound::Init() function is called... + // jack_set_buffer_size ( pJackClient, iNewPrefMonoBufferSize ); + // ### TODO: END ###// // without a Jack server, Jamulus makes no sense to run, throw an error message if ( bJackWasShutDown ) @@ -344,10 +341,10 @@ int CSound::process ( jack_nframes_t nframes, void* arg ) // copy packet and send it to the MIDI parser - //### TODO: BEGIN ###// - // do not call malloc in real-time callback + // ### TODO: BEGIN ###// + // do not call malloc in real-time callback CVector vMIDIPaketBytes ( in_event.size ); - //### TODO: END ###// + // ### TODO: END ###// for ( i = 0; i < static_cast ( in_event.size ); i++ ) { diff --git a/src/sound/jack/sound.h b/src/sound/jack/sound.h index ea4e01ff5d..2008452135 100644 --- a/src/sound/jack/sound.h +++ b/src/sound/jack/sound.h @@ -87,8 +87,8 @@ class CSound : public CSoundBase virtual void Stop(); virtual float GetInOutLatencyMs() { return fInOutLatencyMs; } - virtual void EnableMIDI ( bool bEnable ) override; - virtual bool IsMIDIEnabled() const override; + virtual void EnableMIDI ( bool bEnable ) override; + virtual bool IsMIDIEnabled() const override; // these variables should be protected but cannot since we want // to access them from the callback function diff --git a/src/sound/midi-win/midi.cpp b/src/sound/midi-win/midi.cpp index ee083c4d7e..0fbf27cfcf 100644 --- a/src/sound/midi-win/midi.cpp +++ b/src/sound/midi-win/midi.cpp @@ -39,7 +39,7 @@ extern CSound* pSound; void CMidi::MidiStart() { - if (m_bIsActive) + if ( m_bIsActive ) { return; // MIDI is already active, no need to start again } @@ -54,15 +54,15 @@ void CMidi::MidiStart() // open all connected MIDI devices and set the callback function to handle incoming messages for ( int i = 0; i < iMidiDevs; i++ ) { - HMIDIIN hMidiIn; // Windows handle - MIDIINCAPS mic; // Device name and capabilities + HMIDIIN hMidiIn; // windows handle + MIDIINCAPS mic; // device name and capabilities MMRESULT result = midiInGetDevCaps ( i, &mic, sizeof ( MIDIINCAPS ) ); if ( result != MMSYSERR_NOERROR ) { qWarning() << qUtf8Printable ( QString ( "! Failed to identify MIDI input device %1. Error code: %2" ).arg ( i ).arg ( result ) ); - continue; // Try next device, if any + continue; // try next device, if any } QString midiDev ( mic.szPname ); @@ -70,7 +70,7 @@ void CMidi::MidiStart() if ( !selMIDIDevice.isEmpty() && selMIDIDevice != midiDev ) { qInfo() << qUtf8Printable ( QString ( " %1: %2 (ignored)" ).arg ( i ).arg ( midiDev ) ); - continue; // Try next device, if any + continue; // try next device, if any } qInfo() << qUtf8Printable ( QString ( " %1: %2" ).arg ( i ).arg ( midiDev ) ); @@ -80,7 +80,7 @@ void CMidi::MidiStart() if ( result != MMSYSERR_NOERROR ) { qWarning() << qUtf8Printable ( QString ( "! Failed to open MIDI input device %1. Error code: %2" ).arg ( i ).arg ( result ) ); - continue; // Try next device, if any + continue; // try next device, if any } result = midiInStart ( hMidiIn ); @@ -89,14 +89,14 @@ void CMidi::MidiStart() { qWarning() << qUtf8Printable ( QString ( "! Failed to start MIDI input device %1. Error code: %2" ).arg ( i ).arg ( result ) ); midiInClose ( hMidiIn ); - continue; // Try next device, if any + continue; // try next device, if any } // Success, add it to the list of open handles vecMidiInHandles.append ( hMidiIn ); } - if (!vecMidiInHandles.isEmpty()) + if ( !vecMidiInHandles.isEmpty() ) { m_bIsActive = true; // Set active state if at least one device was started } @@ -104,7 +104,7 @@ void CMidi::MidiStart() void CMidi::MidiStop() { - if (!m_bIsActive) + if ( !m_bIsActive ) { return; // MIDI is already stopped, no need to stop again } @@ -120,10 +120,7 @@ void CMidi::MidiStop() m_bIsActive = false; // Set active state to false } -bool CMidi::IsActive() const -{ - return m_bIsActive; -} +bool CMidi::IsActive() const { return m_bIsActive; } // See https://learn.microsoft.com/en-us/previous-versions//dd798460(v=vs.85) // for the definition of the MIDI input callback function. diff --git a/src/sound/midi-win/midi.h b/src/sound/midi-win/midi.h index 31f023954f..a87d5a5688 100644 --- a/src/sound/midi-win/midi.h +++ b/src/sound/midi-win/midi.h @@ -31,7 +31,7 @@ class CMidi { public: - CMidi() : m_bIsActive(false) {} + CMidi() : m_bIsActive ( false ) {} virtual ~CMidi() {} @@ -42,7 +42,7 @@ class CMidi protected: int iMidiDevs; QVector vecMidiInHandles; // windows handles - bool m_bIsActive; // Tracks if MIDI is currently active + bool m_bIsActive; // Tracks if MIDI is currently active static void CALLBACK MidiCallback ( HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 ); }; diff --git a/src/sound/oboe/sound.cpp b/src/sound/oboe/sound.cpp index 5af91c3ff3..6fe3a5dceb 100644 --- a/src/sound/oboe/sound.cpp +++ b/src/sound/oboe/sound.cpp @@ -354,21 +354,3 @@ void CSound::Stats::log() const << "frames_in: " << frames_in << ",frames_out: " << frames_out << ",frames_filled_out: " << frames_filled_out << ",in_callback_calls: " << in_callback_calls << ",out_callback_calls: " << out_callback_calls << ",ring_overrun: " << ring_overrun; } - -// MIDI functions implementation for Android/Oboe -void CSound::EnableMIDI ( bool bEnable ) -{ - // Android/Oboe MIDI support is not currently implemented - // This is a placeholder implementation to maintain API compatibility - Q_UNUSED ( bEnable ); - - // TODO: Add Android MIDI implementation using Android MIDI API - // See: https://developer.android.com/reference/android/media/midi/package-summary -} - -bool CSound::IsMIDIEnabled() const -{ - // Android/Oboe MIDI support is not currently implemented - // Return false to indicate MIDI is not available - return false; -} diff --git a/src/sound/oboe/sound.h b/src/sound/oboe/sound.h index c2058aeb25..3045b079cc 100644 --- a/src/sound/oboe/sound.h +++ b/src/sound/oboe/sound.h @@ -50,10 +50,6 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback virtual void Start(); virtual void Stop(); - // MIDI control - required virtual functions from CSoundBase - virtual void EnableMIDI ( bool bEnable ) override; - virtual bool IsMIDIEnabled() const override; - // Call backs for Oboe virtual oboe::DataCallbackResult onAudioReady ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ); virtual void onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result result ); diff --git a/src/sound/soundbase.h b/src/sound/soundbase.h index 773e7879ec..1f054835ee 100644 --- a/src/sound/soundbase.h +++ b/src/sound/soundbase.h @@ -117,9 +117,9 @@ class CSoundBase : public QThread void EmitReinitRequestSignal ( const ESndCrdResetType eSndCrdResetType ) { emit ReinitRequest ( eSndCrdResetType ); } // this needs to be public so that it can be called from CMidi - void ParseMIDIMessage ( const CVector& vMIDIPaketBytes ); - void SetMIDIMapping ( const QString& strMIDISetup ); - virtual void EnableMIDI ( bool /* bEnable */ ) {} // Default empty implementation + void ParseMIDIMessage ( const CVector& vMIDIPaketBytes ); + void SetMIDIMapping ( const QString& strMIDISetup ); + virtual void EnableMIDI ( bool /* bEnable */ ) {} // Default empty implementation virtual bool IsMIDIEnabled() const { return false; } // Default false protected: From d5dd70bdb12423a319433ef96e072400691970f2 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Mon, 2 Jun 2025 11:50:28 +0200 Subject: [PATCH 13/24] Add whats this for enable MIDI, correct tab order --- src/clientsettingsdlg.cpp | 5 +++++ src/clientsettingsdlgbase.ui | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 99a8838601..187fefc5bd 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -399,6 +399,10 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet chbAudioAlerts->setAccessibleName ( tr ( "Audio Alerts check box" ) ); // MIDI settings + chbUseMIDIController->setWhatsThis ( "" + tr ( "Enable/disable MIDI-in port" ) + " " ) ); + + chbUseMIDIController->setAccessibleName ( tr ( "Enable or disable MIDI-in port check box" ) ); + QString strMidiSettings = "" + tr ( "MIDI controller settings" ) + ": " + tr ( "There is one global MIDI channel parameter (1-16) and two parameters you can set " "for each item controlled: First MIDI CC and consecutive CC numbers (count). First set the " @@ -412,6 +416,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet tr ( "You can either type in the MIDI CC values or use the \"Learn\" button: click on " "\"Learn\", move the fader/knob on your MIDI controller, and the MIDI CC number " "will be saved." ); + lblChannel->setWhatsThis ( strMidiSettings ); lblMuteMyself->setWhatsThis ( strMidiSettings ); faderGroup->setWhatsThis ( strMidiSettings ); diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index a9c1779430..a17b4d34c2 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -2062,8 +2062,8 @@ butDriverSetup cbxLInChan cbxRInChan - cbxLOutChan cbxROutChan + cbxLOutChan cbxAudioChannels cbxAudioQuality rbtBufferDelayPreferred @@ -2079,6 +2079,7 @@ cbxInputBoost chbDetectFeedback sldAudioPan + chbUseMIDIController spnChannel spnMuteMyself butLearnMuteMyself @@ -2094,6 +2095,7 @@ spnMuteOffset butLearnMuteOffset spnMuteCount + tabSettings From 455d06264a6e8f2e7fafb350ac2cb31581405b8f Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Mon, 2 Jun 2025 12:13:27 +0200 Subject: [PATCH 14/24] Fix tabbing and whats this --- src/clientsettingsdlg.cpp | 2 +- src/clientsettingsdlgbase.ui | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 187fefc5bd..6d130126fd 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -399,7 +399,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet chbAudioAlerts->setAccessibleName ( tr ( "Audio Alerts check box" ) ); // MIDI settings - chbUseMIDIController->setWhatsThis ( "" + tr ( "Enable/disable MIDI-in port" ) + " " ) ); + chbUseMIDIController->setWhatsThis ( tr ( "Enable/disable MIDI-in port" ) ); chbUseMIDIController->setAccessibleName ( tr ( "Enable or disable MIDI-in port check box" ) ); diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index a17b4d34c2..fc6e64fbbd 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -2095,7 +2095,6 @@ spnMuteOffset butLearnMuteOffset spnMuteCount - tabSettings From b7faa037ffecc4db7e8eb927a1b87b17af27db53 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Mon, 2 Jun 2025 12:23:39 +0200 Subject: [PATCH 15/24] Make clang-format happy --- src/main.cpp | 10 ++++++---- src/settings.h | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 039a510ac9..2904ee67f8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -662,12 +662,14 @@ int main ( int argc, char** argv ) if ( !strServerListFileName.isEmpty() ) { - qInfo() << "Note:" << "Server list persistence file will only take effect when running as a directory."; + qInfo() << "Note:" + << "Server list persistence file will only take effect when running as a directory."; } if ( !strServerListFilter.isEmpty() ) { - qInfo() << "Note:" << "Server list filter will only take effect when running as a directory."; + qInfo() << "Note:" + << "Server list filter will only take effect when running as a directory."; } } else @@ -819,7 +821,7 @@ int main ( int argc, char** argv ) bIsClient = true; // Client only - TODO: maybe a switch in interface to change to server? // bUseMultithreading = true; - QApplication* pApp = new QApplication ( argc, argv ); + QApplication* pApp = new QApplication ( argc, argv ); # else QCoreApplication* pApp = bUseGUI ? new QApplication ( argc, argv ) : new QCoreApplication ( argc, argv ); # endif @@ -875,7 +877,7 @@ int main ( int argc, char** argv ) qWarning() << "No JSON-RPC support in this build."; } #else - CRpcServer* pRpcServer = nullptr; + CRpcServer* pRpcServer = nullptr; if ( iJsonRpcPortNumber != INVALID_PORT ) { diff --git a/src/settings.h b/src/settings.h index 79fce1e5b5..3499194fc8 100644 --- a/src/settings.h +++ b/src/settings.h @@ -202,16 +202,16 @@ class CClientSettings : public CSettings bool bOwnFaderFirst; // MIDI settings - int midiChannel = 0; // Default MIDI channel 0 - int midiMuteMyself = 0; - int midiFaderOffset = 0; - int midiFaderCount = 0; - int midiPanOffset = 0; - int midiPanCount = 0; - int midiSoloOffset = 0; - int midiSoloCount = 0; - int midiMuteOffset = 0; - int midiMuteCount = 0; + int midiChannel = 0; // Default MIDI channel 0 + int midiMuteMyself = 0; + int midiFaderOffset = 0; + int midiFaderCount = 0; + int midiPanOffset = 0; + int midiPanCount = 0; + int midiSoloOffset = 0; + int midiSoloCount = 0; + int midiMuteOffset = 0; + int midiMuteCount = 0; bool bUseMIDIController = false; QString GetMIDIMapString() const; From d694d1458a6f1cf8e541a4c8c5266a6be8eb21cb Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Mon, 2 Jun 2025 22:08:28 +0200 Subject: [PATCH 16/24] Fix prepend user with number --- src/audiomixerboard.cpp | 3 +++ src/clientsettingsdlg.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/audiomixerboard.cpp b/src/audiomixerboard.cpp index f463088dfc..6045cd8a2c 100644 --- a/src/audiomixerboard.cpp +++ b/src/audiomixerboard.cpp @@ -1334,6 +1334,9 @@ void CAudioMixerBoard::ApplyNewConClientList ( CVector& vecChanInf } Mutex.unlock(); // release mutex + // Ensure MIDI state is applied to faders during the connection process + SetMIDICtrlUsed(pSettings->bUseMIDIController); + // sort the channels according to the selected sorting type ChangeFaderOrder ( eChSortType ); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 6d130126fd..074acfc2d6 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -909,6 +909,9 @@ void CClientSettingsDlg::showEvent ( QShowEvent* event ) spnMuteCount->setValue ( pSettings->midiMuteCount ); chbUseMIDIController->setChecked ( pSettings->bUseMIDIController ); + // Emit MIDIControllerUsageChanged signal to propagate MIDI state at startup + emit MIDIControllerUsageChanged(chbUseMIDIController->isChecked()); + QDialog::showEvent ( event ); } From 280a3724ae937992afa31bd8d2301350d013f83f Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Mon, 2 Jun 2025 22:12:59 +0200 Subject: [PATCH 17/24] Clang-format --- src/audiomixerboard.cpp | 2 +- src/clientsettingsdlg.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audiomixerboard.cpp b/src/audiomixerboard.cpp index 6045cd8a2c..5f171d63ca 100644 --- a/src/audiomixerboard.cpp +++ b/src/audiomixerboard.cpp @@ -1335,7 +1335,7 @@ void CAudioMixerBoard::ApplyNewConClientList ( CVector& vecChanInf Mutex.unlock(); // release mutex // Ensure MIDI state is applied to faders during the connection process - SetMIDICtrlUsed(pSettings->bUseMIDIController); + SetMIDICtrlUsed ( pSettings->bUseMIDIController ); // sort the channels according to the selected sorting type ChangeFaderOrder ( eChSortType ); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 074acfc2d6..f67792d5d2 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -910,7 +910,7 @@ void CClientSettingsDlg::showEvent ( QShowEvent* event ) chbUseMIDIController->setChecked ( pSettings->bUseMIDIController ); // Emit MIDIControllerUsageChanged signal to propagate MIDI state at startup - emit MIDIControllerUsageChanged(chbUseMIDIController->isChecked()); + emit MIDIControllerUsageChanged ( chbUseMIDIController->isChecked() ); QDialog::showEvent ( event ); } From 09cd62591f19ee357f8b85754fd19936adcad6e6 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Tue, 3 Jun 2025 09:48:37 +0200 Subject: [PATCH 18/24] Fix show last opened tab --- src/clientsettingsdlg.cpp | 8 ++++---- src/settings.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index f67792d5d2..1dece606af 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -785,10 +785,6 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet QObject::connect ( pcbxSkill, static_cast ( &QComboBox::activated ), this, &CClientSettingsDlg::OnSkillActivated ); - QObject::connect ( tabSettings, &QTabWidget::currentChanged, this, &CClientSettingsDlg::OnTabChanged ); - - tabSettings->setCurrentIndex ( pSettings->iSettingsTab ); - // MIDI tab QObject::connect ( spnChannel, static_cast ( &QSpinBox::valueChanged ), this, [this] ( int v ) { pSettings->midiChannel = v; @@ -871,6 +867,10 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet // Connect MIDI CC signal from sound engine QObject::connect ( pClient, &CClient::MidiCCReceived, this, &CClientSettingsDlg::OnMidiCCReceived ); + QObject::connect ( tabSettings, &QTabWidget::currentChanged, this, &CClientSettingsDlg::OnTabChanged ); + + tabSettings->setCurrentIndex ( pSettings->iSettingsTab ); + // Timers ------------------------------------------------------------------ // start timer for status bar TimerStatus.start ( DISPLAY_UPDATE_TIME ); diff --git a/src/settings.cpp b/src/settings.cpp index 8f4748a7fe..825b2282eb 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -604,7 +604,7 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, } // selected Settings Tab - if ( GetNumericIniSet ( IniXMLDocument, "client", "settingstab", 0, 2, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "settingstab", 0, 3, iValue ) ) { iSettingsTab = iValue; } From e6defeceefa6da05d05fa4d443cfd5385a325231 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Tue, 3 Jun 2025 22:38:42 +0200 Subject: [PATCH 19/24] Add curly braces --- src/clientsettingsdlg.cpp | 11 +++++++++++ src/sound/soundbase.cpp | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 1dece606af..65ad4d6254 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -1404,16 +1404,27 @@ void CClientSettingsDlg::OnLearnButtonClicked() QPushButton* sender = qobject_cast ( QObject::sender() ); MidiLearnTarget target = None; + if ( sender == butLearnMuteMyself ) + { target = MuteMyself; + } else if ( sender == butLearnFaderOffset ) + { target = Fader; + } else if ( sender == butLearnPanOffset ) + { target = Pan; + } else if ( sender == butLearnSoloOffset ) + { target = Solo; + } else if ( sender == butLearnMuteOffset ) + { target = Mute; + } SetMidiLearnTarget ( target, sender ); } diff --git a/src/sound/soundbase.cpp b/src/sound/soundbase.cpp index 770206509f..ee84a516a4 100644 --- a/src/sound/soundbase.cpp +++ b/src/sound/soundbase.cpp @@ -239,7 +239,9 @@ void CSoundBase::ParseCommandLineArgument ( const QString& strMIDISetup ) { // Clear all previous MIDI mappings for ( int i = 0; i < aMidiCtls.size(); ++i ) + { aMidiCtls[i] = { None, 0 }; + } int iMIDIOffsetFader = 70; // Behringer X-TOUCH: offset of 0x46 From 2970f5e62e340de47c33eda22297418fad583501 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Wed, 4 Jun 2025 11:24:16 +0200 Subject: [PATCH 20/24] Fix MIDI-in port toggle misbehaving when launched enabled (macOS) --- src/sound/coreaudio-mac/sound.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/sound/coreaudio-mac/sound.cpp b/src/sound/coreaudio-mac/sound.cpp index cfbbe78f00..910f0f220c 100644 --- a/src/sound/coreaudio-mac/sound.cpp +++ b/src/sound/coreaudio-mac/sound.cpp @@ -66,7 +66,18 @@ CSound::CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* CSound::~CSound() { // Ensure MIDI resources are properly cleaned up - DestroyMIDIPort(); + DestroyMIDIPort(); // This will destroy the port if it exists + + // Explicitly destroy the client if it exists + if ( midiClient != static_cast ( NULL ) ) + { + OSStatus result = MIDIClientDispose ( midiClient ); + if ( result != noErr ) + { + qWarning() << "Failed to dispose CoreAudio MIDI client in destructor. Error code:" << result; + } + midiClient = static_cast ( NULL ); + } } void CSound::GetAvailableInOutDevices() @@ -786,17 +797,6 @@ void CSound::DestroyMIDIPort() qInfo() << "CoreAudio MIDI port destroyed"; } - - // Only dispose client if no ports are using it - if ( midiClient != static_cast ( NULL ) && midiInPortRef == static_cast ( NULL ) ) - { - OSStatus result = MIDIClientDispose ( midiClient ); - if ( result != noErr ) - { - qWarning() << "Failed to dispose CoreAudio MIDI client. Error code:" << result; - } - midiClient = static_cast ( NULL ); - } } int CSound::Init ( const int iNewPrefMonoBufferSize ) From 1909b62e5892e3834d3d03cd8ffd6cf706f35b02 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Thu, 5 Jun 2025 19:48:37 +0200 Subject: [PATCH 21/24] Rename tab, improve layout and correct whats this --- src/clientdlg.cpp | 2 +- src/clientsettingsdlg.cpp | 4 +- src/clientsettingsdlgbase.ui | 112 +++++++++++++++++------------------ 3 files changed, 59 insertions(+), 59 deletions(-) diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index 18423b771d..6fa0c9f049 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -400,7 +400,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP, pSettingsMenu->addAction ( tr ( "A&dvanced Settings..." ), this, SLOT ( OnOpenAdvancedSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_D ) ); - pSettingsMenu->addAction ( tr ( "&MIDI Settings..." ), this, SLOT ( OnOpenMidiSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_M ) ); + pSettingsMenu->addAction ( tr ( "&MIDI Control Settings..." ), this, SLOT ( OnOpenMidiSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_M ) ); // Main menu bar ----------------------------------------------------------- QMenuBar* pMenu = new QMenuBar ( this ); diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 65ad4d6254..c80006bbcf 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -414,8 +414,8 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet "applied to one’s own audio stream." ) + "
" + tr ( "You can either type in the MIDI CC values or use the \"Learn\" button: click on " - "\"Learn\", move the fader/knob on your MIDI controller, and the MIDI CC number " - "will be saved." ); + "\"Learn\", actuate the fader/knob/button on your MIDI controller, and the MIDI CC " + "number will be detected and saved." ); lblChannel->setWhatsThis ( strMidiSettings ); lblMuteMyself->setWhatsThis ( strMidiSettings ); diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index fc6e64fbbd..16ddbdbdcb 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -6,8 +6,8 @@ 0 0 - 622 - 647 + 524 + 569
@@ -24,7 +24,7 @@ - + 0 0 @@ -1338,7 +1338,7 @@ - MIDI + MIDI Control @@ -1484,6 +1484,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -1496,19 +1509,6 @@ Fader - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -1621,6 +1621,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -1633,19 +1646,6 @@ Pan - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -1758,6 +1758,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -1770,19 +1783,6 @@ Solo - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -1895,6 +1895,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -1907,19 +1920,6 @@ Mute - - - - Qt::Horizontal - - - - 40 - 20 - - - - From e35023d6d7dc25b965f8b83595c24c8d711a5271 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Mon, 9 Jun 2025 20:15:31 +0200 Subject: [PATCH 22/24] Make check box text consistent --- src/clientsettingsdlg.cpp | 5 +---- src/clientsettingsdlgbase.ui | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index c80006bbcf..2c0f3bc82b 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -400,8 +400,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet // MIDI settings chbUseMIDIController->setWhatsThis ( tr ( "Enable/disable MIDI-in port" ) ); - - chbUseMIDIController->setAccessibleName ( tr ( "Enable or disable MIDI-in port check box" ) ); + chbUseMIDIController->setAccessibleName ( tr ( "MIDI-in port check box" ) ); QString strMidiSettings = "" + tr ( "MIDI controller settings" ) + ": " + tr ( "There is one global MIDI channel parameter (1-16) and two parameters you can set " @@ -836,7 +835,6 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet ApplyMIDIMappingFromSettings(); } ); - // Connect MIDI controller checkbox QObject::connect ( chbUseMIDIController, &QCheckBox::toggled, this, [this] ( bool checked ) { pSettings->bUseMIDIController = checked; @@ -864,7 +862,6 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet QObject::connect ( button, &QPushButton::clicked, this, &CClientSettingsDlg::OnLearnButtonClicked ); } - // Connect MIDI CC signal from sound engine QObject::connect ( pClient, &CClient::MidiCCReceived, this, &CClientSettingsDlg::OnMidiCCReceived ); QObject::connect ( tabSettings, &QTabWidget::currentChanged, this, &CClientSettingsDlg::OnTabChanged ); diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index 16ddbdbdcb..aa04a2a812 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -1344,7 +1344,7 @@ - Enable MIDI-in port + MIDI-in port From f718efaf8da95415aa5264e0f638ac2668e58501 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Wed, 11 Jun 2025 15:59:28 +0200 Subject: [PATCH 23/24] Grey out MIDI controls when disabled --- src/clientsettingsdlg.cpp | 5 + src/clientsettingsdlg.h | 1 + src/clientsettingsdlgbase.ui | 1398 +++++++++++++++++----------------- 3 files changed, 709 insertions(+), 695 deletions(-) diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 2c0f3bc82b..8d039b3fb8 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -837,6 +837,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet QObject::connect ( chbUseMIDIController, &QCheckBox::toggled, this, [this] ( bool checked ) { pSettings->bUseMIDIController = checked; + SetMIDIControlsEnabled ( checked ); if ( checked ) { @@ -906,6 +907,8 @@ void CClientSettingsDlg::showEvent ( QShowEvent* event ) spnMuteCount->setValue ( pSettings->midiMuteCount ); chbUseMIDIController->setChecked ( pSettings->bUseMIDIController ); + SetMIDIControlsEnabled ( chbUseMIDIController->isChecked() ); + // Emit MIDIControllerUsageChanged signal to propagate MIDI state at startup emit MIDIControllerUsageChanged ( chbUseMIDIController->isChecked() ); @@ -1377,6 +1380,8 @@ void CClientSettingsDlg::ResetMidiLearn() } } +void CClientSettingsDlg::SetMIDIControlsEnabled ( bool enabled ) { midiControlsContainer->setEnabled ( enabled ); } + void CClientSettingsDlg::SetMidiLearnTarget ( MidiLearnTarget target, QPushButton* activeButton ) { if ( midiLearnTarget == target ) diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index 3f436dbce5..822c100317 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -137,6 +137,7 @@ public slots: QPushButton* midiLearnButtons[5]; void SetMidiLearnTarget ( MidiLearnTarget target, QPushButton* activeButton ); void ResetMidiLearn(); + void SetMIDIControlsEnabled ( bool enabled ); private slots: void OnLearnButtonClicked(); diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index aa04a2a812..cd0324ecd5 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -6,7 +6,7 @@ 0 0 - 524 + 543 569 @@ -14,7 +14,7 @@ Settings - + :/png/main/res/fronticon.png:/png/main/res/fronticon.png @@ -1340,700 +1340,711 @@ MIDI Control - - + + + + + + + + + + + + 50 + false + + + + Mute + + + + + + + 50 + false + + + + First MIDI CC + + + + + + + + 65 + 0 + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Count + + + + + + + + 65 + 0 + + + + + 50 + false + + + + 1 + + + 127 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 50 + false + + + + Solo + + + + + + + 50 + false + + + + First MIDI CC + + + + + + + + 65 + 0 + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Count + + + + + + + + 65 + 0 + + + + + 50 + false + + + + 1 + + + 127 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 50 + false + + + + Pan + + + + + + + 50 + false + + + + First MIDI CC + + + + + + + + 65 + 0 + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Count + + + + + + + + 65 + 0 + + + + + 50 + false + + + + 1 + + + 127 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 75 + true + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + MIDI Channel + + + + + + + + 55 + 0 + + + + + 50 + false + + + + 16 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Mute Myself + + + + + + + + 65 + 0 + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 50 + false + + + + Fader + + + + + + + 50 + false + + + + First MIDI CC + + + + + + + + 65 + 0 + + + + + 50 + false + + + + 127 + + + + + + + + 50 + false + + + + Learn + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 50 + false + + + + Count + + + + + + + + 65 + 0 + + + + + 50 + false + + + + 1 + + + 127 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + 0 + 0 + + - MIDI-in port + MIDI-in - - - - - - - 75 - true - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - false - - - - MIDI Channel - - - - - - - - 55 - 0 - - - - - 50 - false - - - - 16 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - false - - - - Mute Myself - - - - - - - - 65 - 0 - - - - - 50 - false - - - - 127 - - - - - - - - 50 - false - - - - Learn - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 50 - false - - - - Fader - - - - - - - 50 - false - - - - First MIDI CC - - - - - - - - 65 - 0 - - - - - 50 - false - - - - 127 - - - - - - - - 50 - false - - - - Learn - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - false - - - - Count - - - - - - - - 65 - 0 - - - - - 50 - false - - - - 1 - - - 127 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 50 - false - - - - Pan - - - - - - - 50 - false - - - - First MIDI CC - - - - - - - - 65 - 0 - - - - - 50 - false - - - - 127 - - - - - - - - 50 - false - - - - Learn - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - false - - - - Count - - - - - - - - 65 - 0 - - - - - 50 - false - - - - 1 - - - 127 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 50 - false - - - - Solo - - - - - - - 50 - false - - - - First MIDI CC - - - - - - - - 65 - 0 - - - - - 50 - false - - - - 127 - - - - - - - - 50 - false - - - - Learn - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - false - - - - Count - - - - - - - - 65 - 0 - - - - - 50 - false - - - - 1 - - - 127 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 50 - false - - - - Mute - - - - - - - 50 - false - - - - First MIDI CC - - - - - - - - 65 - 0 - - - - - 50 - false - - - - 127 - - - - - - - - 50 - false - - - - Learn - - - false - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 50 - false - - - - Count - - - - - - - - 65 - 0 - - - - - 50 - false - - - - 1 - - - 127 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - -
@@ -2062,8 +2073,8 @@ butDriverSetup cbxLInChan cbxRInChan - cbxROutChan cbxLOutChan + cbxROutChan cbxAudioChannels cbxAudioQuality rbtBufferDelayPreferred @@ -2080,9 +2091,6 @@ chbDetectFeedback sldAudioPan chbUseMIDIController - spnChannel - spnMuteMyself - butLearnMuteMyself spnFaderOffset butLearnFaderOffset spnFaderCount From 1e1759c7572dd5e07873830e7b3143dfec7e4817 Mon Sep 17 00:00:00 2001 From: ignotus666 Date: Wed, 11 Jun 2025 16:07:53 +0200 Subject: [PATCH 24/24] Correct tab order --- src/clientsettingsdlgbase.ui | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index cd0324ecd5..ac08206a73 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -14,7 +14,7 @@ Settings - + :/png/main/res/fronticon.png:/png/main/res/fronticon.png @@ -2091,6 +2091,9 @@ chbDetectFeedback sldAudioPan chbUseMIDIController + spnChannel + spnMuteMyself + butLearnMuteMyself spnFaderOffset butLearnFaderOffset spnFaderCount