diff --git a/src/client.h b/src/client.h
index 7412582618..3bfa3f0139 100644
--- a/src/client.h
+++ b/src/client.h
@@ -286,10 +286,14 @@ 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 ); }
+ bool IsMIDIEnabled() const { return Sound.IsMIDIEnabled(); }
// settings
CChannelCoreInfo ChannelInfo;
diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp
index 7ae940a0bf..18423b771d 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 );
@@ -1287,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();
@@ -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..6d130126fd 100644
--- a/src/clientsettingsdlg.cpp
+++ b/src/clientsettingsdlg.cpp
@@ -399,11 +399,15 @@ 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: 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 "
@@ -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 );
@@ -835,6 +840,22 @@ 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 +907,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 +1353,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..fc6e64fbbd 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
@@ -1995,8 +2062,8 @@
butDriverSetup
cbxLInChan
cbxRInChan
- cbxLOutChan
cbxROutChan
+ cbxLOutChan
cbxAudioChannels
cbxAudioQuality
rbtBufferDelayPreferred
@@ -2012,6 +2079,7 @@
cbxInputBoost
chbDetectFeedback
sldAudioPan
+ chbUseMIDIController
spnChannel
spnMuteMyself
butLearnMuteMyself
diff --git a/src/main.cpp b/src/main.cpp
index d011c36916..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 )
{
@@ -920,17 +918,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 +932,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 +985,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..8f4748a7fe 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -289,231 +289,232 @@ 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
@@ -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..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,6 +629,30 @@ 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..7259be2e38 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..cfbbe78f00 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,98 @@ 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..96f2e397ce 100644
--- a/src/sound/coreaudio-mac/sound.h
+++ b/src/sound/coreaudio-mac/sound.h
@@ -44,6 +44,8 @@ class CSound : public CSoundBase
const bool,
const QString& );
+ virtual ~CSound();
+
virtual int Init ( const int iNewPrefMonoBufferSize );
virtual void Start();
virtual void Stop();
@@ -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..50f7324201 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,16 +178,66 @@ 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 */ )
{
- //### 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 )
@@ -305,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 55b922ef7d..2008452135 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..0fbf27cfcf 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 */
@@ -87,21 +92,36 @@ void CMidi::MidiStart()
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)
// for the definition of the MIDI input callback function.
void CALLBACK CMidi::MidiCallback ( HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
diff --git a/src/sound/midi-win/midi.h b/src/sound/midi-win/midi.h
index 3ce01f9ced..a87d5a5688 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/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..1f054835ee 100644
--- a/src/sound/soundbase.h
+++ b/src/sound/soundbase.h
@@ -117,8 +117,10 @@ 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 );
+ 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 ""; }