Skip to content

Add MIDI GUI tab and learn function#3502

Draft
ignotus666 wants to merge 1 commit intojamulussoftware:mainfrom
ignotus666:midi-gui-learn
Draft

Add MIDI GUI tab and learn function#3502
ignotus666 wants to merge 1 commit intojamulussoftware:mainfrom
ignotus666:midi-gui-learn

Conversation

@ignotus666
Copy link
Member

@ignotus666 ignotus666 commented May 22, 2025

Adds a MIDI tab to the Settings menu and exposes the MIDI parameters so they can be changed via the GUI. MIDI CC numbers can be automatically set using "learn" buttons that when pressed, listen for an incoming MIDI message and store it. Starting Jamulus with --ctrlmidich command line arguments overrides and replaces those set via the GUI.

The MIDI-in port can be enabled/disabled at runtime instead of it being determined at launch by the --ctrlmidich parameter or by making it permanently open. This means that user names are prepended by a number depending on whether the MIDI in port has been enabled or not (as is the case now).

I've also ditched the "Offset" term, at least for the GUI. It's a hangover from the days when that's what it was, a value hard-coded for a specific Behringer device, but in the current context it no longer makes sense.

EDIT: Added JSONRPC support.

MIDI_GUI

CHANGELOG: Client: Added MIDI tab to Settings GUI exposing MIDI parameters. MIDI Learn feature also added.

Context: Fixes an issue?

Does this change need documentation? What needs to be documented and how?

Needs to be documented.

Status of this Pull Request

Working on Linux, Windows and macOS but should be tested more thoroughly.

What is missing until this pull request can be merged?

Further testing and code review.

Checklist

  • I've verified that this Pull Request follows the general code principles
  • I tested my code and it does what I want
  • My code follows the style guide
  • I waited some time after this Pull Request was opened and all GitHub checks completed without errors.
  • I've filled all the content above

@softins softins added this to the Release 4.0.0 milestone May 22, 2025
@ignotus666 ignotus666 marked this pull request as draft May 22, 2025 14:07
@softins
Copy link
Member

softins commented May 28, 2025

Thanks for this contribution. It looks like a useful feature.

I will be happy to test it in due course, on Linux, Windows and Mac, but at the moment most of my kit is packed away for a pending house move.

@ignotus666
Copy link
Member Author

@softins thanks - no worries, I guess there will be plenty of time for testing before this ever makes it in (if it does).

Hope the house move goes smoothly.

@ann0see
Copy link
Member

ann0see commented May 31, 2025

Nice!

I'd like to open a discussion:

  1. Do you think a separate MIDI tab is useful or does it add clutter?
  2. Is there a better word instead of "Learn" for the detector? How can we make the feature clear to the user? Would a dynamic text field showing currently pressed buttons be better? (I'm unsure as this could be more complicated)

@ignotus666
Copy link
Member Author

@ann0see

  1. Given the number of MIDI settings, I don't see how it would fit in somewhere else without cluttering up that section in a worse way. I think it merits its own tab.
  2. "Learn" is a standard term for this feature. I see and use it in many other MIDI applications. Maybe "MIDI learn"? But I think that might make the button too large.

In any event, this is still under development as I'm finding that some of my implementations are far from perfect. E.g. the way the MIDI in port is currently kept open I think has been done wrong, so I'm working on that. I'm testing a version where the MIDI port can be enabled/disabled at runtime, as among other things it affects whether numbers are prepended to user names in the mixer board - if you're not using MIDI you won't want that.

The main purpose for opening this PR was to generate builds for other platforms for testing, but for now it's just a PoC that's far from finished, so any suggestions are welcome.

@ignotus666
Copy link
Member Author

ignotus666 commented Jun 2, 2025

Tested on Linux, Windows (no JACK) and macOS (legacy and non-legacy), and it seems to be working on these three platforms at least.

@ignotus666 ignotus666 added the needs documentation PRs requiring documentation changes or additions label Jul 1, 2025
@pljones pljones added this to Tracking Jan 25, 2026
@github-project-automation github-project-automation bot moved this to Triage in Tracking Jan 25, 2026
@pljones
Copy link
Collaborator

pljones commented Jan 25, 2026

OK just stumbled onto this. As this is a new feature, the following need to be adhered to:

  • All updates in the UI should only work by emitting a signal to the main application, where state is held -- no state to be held in the UI (I've not reviewed the code yet)
  • Any UI enhancement should be mirrored by an enhancement to the JSONRPC API, if the feature is missing there

(The "learn" feature for JSONRPC would just be the eventual "set X to Y" update, the visual behaviour would be for the JSONRPC user to supply - it just needs to be able to send the same update to the core code as the Client UI.)

Copy link
Collaborator

@pljones pljones left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Get rid of all the unnecessary changes in spacing and resubmit the PR. As it stands, I can't review it.

@github-project-automation github-project-automation bot moved this from Triage to Waiting externally in Tracking Jan 25, 2026
@ignotus666
Copy link
Member Author

ignotus666 commented Jan 25, 2026

Get rid of all the unnecessary changes in spacing and resubmit the PR. As it stands, I can't review it.

Done. Those keep getting added in by clang.

All updates in the UI should only work by emitting a signal to the main application, where state is held -- no state to be held in the UI (I've not reviewed the code yet)

It's been almost a year since I did this, but as far as I recall it updates the main application state via pSettings and pClient.

@ann0see
Copy link
Member

ann0see commented Jan 25, 2026

I believe this should be rebased and squashed into one commit.

@pljones
Copy link
Collaborator

pljones commented Jan 26, 2026

It's been almost a year since I did this, but as far as I recall it updates the main application state via pSettings and pClient.

It looks pretty good -- as @ann0see says, please rebase and squash. Then check the resultant diff for anything obvious 😄 - it looks mostly okay to me but there's a few places I'm not sure what was being intended, so I might have some more questions.

One thing I might ask is for the command line parsing code to be moved into soundbase.cpp (as a static), just to remove a bit of clutter.

@ignotus666 ignotus666 force-pushed the midi-gui-learn branch 2 times, most recently from fe82247 to 7097b29 Compare January 27, 2026 08:21
@ignotus666
Copy link
Member Author

as @ann0see says, please rebase and squash.

Done.

One thing I might ask is for the command line parsing code to be moved into soundbase.cpp (as a static), just to remove a bit of clutter.

Done. I don't know what clang is whining about - I can't use it because it reformats a ton of stuff that shouldn't be.

@ann0see
Copy link
Member

ann0see commented Jan 27, 2026

I don't know what clang is whining about - I can't use it because it reformats a ton of stuff that shouldn't be.

Known issue. We should really bump the clang format version. Otherwise be sure to use the same clang format version as used in the CI

@ignotus666
Copy link
Member Author

  • Any UI enhancement should be mirrored by an enhancement to the JSONRPC API, if the feature is missing there

I've been working on this - should the UI immediately reflect changes from JSONRPC commands sent? It doesn't seem to do this for the existing commands but just to confirm.

@ignotus666
Copy link
Member Author

Known issue. We should really bump the clang format version. Otherwise be sure to use the same clang format version as used in the CI

The problem is that in the TODO blocks, the TODO lines don't have a space after //, but all other comments do. I couldn't find a way to preserve that distinction other than by adding CommentPragmas: '^ TODO:|^### TODO:' to clang-format. It works (I'll use it myself for now), but maybe there's a better way.

@ignotus666 ignotus666 force-pushed the midi-gui-learn branch 2 times, most recently from 26f4096 to 1f2f394 Compare January 29, 2026 16:02
int& iMidiMuteCount,
int& iMidiMuteMyself,
bool& bUseMIDIController,
QString* strMIDIDevice )
Copy link
Collaborator

@pljones pljones Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a QString& so it's safely settable? I can't remember the syntax well enough to be clear but writing to a pointer feels dangerous. Of course, if this is just the old code moved... I guess it works...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When used it's checked before dereferencing:

if ( cType == 'd' )
{
    if ( strMIDIDevice != nullptr )  // Check before writing
    {
        *strMIDIDevice = sParm.mid ( 1 );
    }
    continue;
}

So it should be safe.

Copy link
Collaborator

@pljones pljones Feb 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm more worried about:

QString str = QString("abc");

QString* _str = &str;

*_str = QString("really long string");

That's like trying QString("abc") = QString("really long string") isn't it?

Saying

QString str = QString("abc");

QString& _str = &str;

&_str = QString("really long string");

gets you str == QString("really long string"), I think. But, like I say, I'd have to run it to check (or see what the compiler says).

{
if ( GetNumericIniSet ( IniXMLDocument, "client", "midichannel", 0, 16, iValue ) )
iMidiChannel = iValue;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why doesn't the MIDI device round trip to settings?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean why isn't it saved? There's no way to set it other than by using it on the command line, it's a per-session option.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah... right, so as there's only the command line and that prevents reading or writing the settings, it's not there.

Mmm, I really would like it to be in the UI in that case, with a drop-down... But then I'd want it cross-platform... and that's not for this change. So OK, this'll do for now.

Please feel free to do more work here 😄 !

@softins
Copy link
Member

softins commented Feb 11, 2026

Starting Jamulus with --ctrlmidich command line arguments overrides and replaces those set via the GUI.

Would need to make sure that specifying --ctrlmidich 1;dDevicename still allowed loading of the other MIDI settings that were set via the GUI and saved.

Sorry, I don't agree with this at this time. This would unnecessarily complicate the already complicated explanation we have of how things work.

OK fair enough. In that case, this GUI implementation would need to provide the equivalent of dDevicename (for Windows), and the settings from the ini file would need to be applied before the MIDI devices were opened. I haven't looked to see whether that is already the case or not.

@ignotus666
Copy link
Member Author

this GUI implementation would need to provide the equivalent of dDevicename (for Windows), and the settings from the ini file would need to be applied before the MIDI devices were opened

Settings are applied before opening MIDI devices.

I've come up with this (not finished yet but mostly working):

  • A MIDI device list dropdown that's only visible in the non-Jack Windows version. I think if it's shown for any other platform (but doesn't really do anything) it will just lead to confusion.
  • If no devices are connected it will show a warning message and disable the MIDI-in checkbox. If a device was previously connected and saved, but is not connected at launch, a message will pop up warning of this and that it will attempt to connect to "All devices". If none are connected, the "Failed to connect to MIDI port" message will appear and disable MIDI settings.
  • It's possible to pick a different device at runtime. It will disconnect and reconnect to the chosen device. If a MIDI device is connected during runtime (where none were connected before), upon enabling the MIDI-in checkbox it will rescan for devices and list those available.
MIDI_devices

Is this something you guys would agree to, or how should I proceed?

@softins
Copy link
Member

softins commented Feb 12, 2026

Is this something you guys would agree to, or how should I proceed?

Looks good to me. I'll be happy to compile it and try it out when it's ready to do that.

@ignotus666
Copy link
Member Author

@softins you can find builds here if you like - or if you prefer you can compile the midi-gui-learn2 branch from my fork. I think it's pretty much done, just a few things like tooltips and other minor stuff need to be addressed.

@pljones
Copy link
Collaborator

pljones commented Feb 12, 2026

Yeah but as I say, if it's Windows only... I might prefer to split out the work on the UI bit and have it given the UI and cross-platform support. I'm torn... but probably more towards getting cross-platform support.

@ignotus666
Copy link
Member Author

ignotus666 commented Feb 13, 2026

I know you suggested adding it in a future update but I went ahead and had a go at adding MIDI device discovery/selection for Linux and macOS too (not to this PR yet - you can find builds here).

  • Unlike in the non-Jack Windows version, it won't fail to create/open a MIDI port if no devices are found (not usually the case since they have system ports anyway), nor will it connect to "all devices" since that would create a mess.
  • If no devices have been connected/saved yet, or the saved one is not detected, the dropdown will simply revert to a "No device connected" option (and will clear the saved device string if there was one). This option can also be used to disconnect from any currently connected devices.
  • Device discovery/rescanning is triggered by clicking on the dropdown. Otherwise hot-plugging a device during runtime would require toggling the MIDI-in port or restarting Jamulus, which isn't very user-friendly.
  • JSONRPC has been updated to support MIDI device list retrieval and setting a device.
  • I'm probably forgetting something or other but my brain is toast.

I've been testing on Linux and Windows (non-Jack), but it'd be great if someone could do tests on macOS.

@pljones
Copy link
Collaborator

pljones commented Feb 13, 2026

Wow. Nice! I'll try to get time over the weekend.

@ignotus666
Copy link
Member Author

@pljones here's what I came up with - not exactly what you proposed but closer I think:

new_midi_tab

@pljones
Copy link
Collaborator

pljones commented Feb 13, 2026

That's actually closer to what I had in mind -- Markdown wasn't letting me get everything right.

@pljones
Copy link
Collaborator

pljones commented Feb 13, 2026

Here's what it looks like on Windows 11 from the jamulus_3.11.0dev-707e68f_win.exe install.

That's not your branch, though, that's the last one on the PR. So I'll see if the next one looks better. It might need the "First CC" and "Count" turned into column headings or something.

image

That's not going to work -- the padding around each section is too much compared with the actual controls on screen.

You can see how busy the Audio/Network Setup tab is in comparison, yet it all fits:
image

@ignotus666
Copy link
Member Author

You can check out the latest version here. I just pushed it so it'll take a while to build though.

@pljones
Copy link
Collaborator

pljones commented Feb 14, 2026

Could you make sure you've got the latest upstream and rebase -- I'm concerned over the iOS failure.

@pljones
Copy link
Collaborator

pljones commented Feb 14, 2026

Ah, that's great!
image
And it picked up the command line correctly. I'm running with -ctrlmidich "1;f85*3;p94*3;dnanoKONTROL" -- the only thing is there doesn't appear to be a "disable". CC0 is still valid, so any CC0 message will affect my Solo and Mute settings and Mute Myself...

https://midi.org/midi-1-0-control-change-messages

@pljones
Copy link
Collaborator

pljones commented Feb 14, 2026

It shouldn't fail on the PR.

I can't see why it fails on yours, though... Try clearing any worker caches you've got in your repo.

@pljones
Copy link
Collaborator

pljones commented Feb 14, 2026

Ugh - this definitely isn't down to you....

image

What have they done to the tabs... They're trying to do what Firefox did and turn them into floating buttons! I think it's dreadful. I had to use userChrome on Firefox to fix it.

@ignotus666
Copy link
Member Author

the only thing is there doesn't appear to be a "disable". CC0 is still valid, so any CC0 message will affect my Solo and Mute settings and Mute Myself...

I just went with the old behaviour - aside from the default 70 value for faders, the rest were 0. I see what you mean though, so maybe we can make the default "disabled" (represented by "--" or something similar to fit)?

@ignotus666
Copy link
Member Author

I can't see why it fails on yours, though... Try clearing any worker caches you've got in your repo.

I've been holding back on adding the latest MIDI device selection code to this PR pending a go-ahead by you guys. If it's ok by you I'll add it and I'm fairly confident the iOS build won't fail.

@pljones
Copy link
Collaborator

pljones commented Feb 14, 2026

I think the default "count" was also zero, though? Anyway, please push what's here to the PR (do rebase and squash if needed). I don't think you can put "--" into the spinner (it would look odd) but on the heading line you could add a checkbox (it doesn't need a visible label) and have that enable the settings for that control? (Breaks your panel-based approach, though.) EDIT: Mmm, bold labelled checkbox with "Fader", or whatever as the label, rather than the panel border title? If you can get a checkbox in as the title, even better!

If you do change the layout, you could change "MIDI-in" to "MIDI Control" and move the device selector next to it, labelled "Device", perhaps, to save a bit of space. (Or are MIDI device names long sometimes?)

@ignotus666
Copy link
Member Author

ignotus666 commented Feb 14, 2026

I'm pretty certain "count" was 1 as default, but what about this:

Make the default count zero, which means that the affected item is disabled in practice ("zero controls in this category will react to a MIDI message"). This saves us cluttering up the UI with more items. It could be explained in the tooltip and manual - "Setting 'count' to zero will disable that control".

@pljones
Copy link
Collaborator

pljones commented Feb 14, 2026

Mute Myself doesn't have a count though. Somehow it's not active with the --ctrlmidich method unless specified, as far as I remember.

(And it's bad UX to conflate concepts like "count" and "active".)

... even if it's stored in the settings as a count of zero (and we store a dummy count for Mute Myself), it should display separately.

@ignotus666
Copy link
Member Author

And it's bad UX to conflate concepts like "count" and "active"

My reasoning is that a "count" of zero is simply that - zero controls will react to it. I don't think it undermines the logic attached to the "count" concept and provides a way to "disable" controls (set to whatever note) that is far easier to implement than having a bool flag and checkbox for every control... As for Mute Myself: make the default CC -1. That way it doesn't get mapped and won't react to anything.

@pljones
Copy link
Collaborator

pljones commented Feb 14, 2026

I know it makes the work easier.

@gilgongo, as our resident UI/UX arbitrator, what do you reckon? Is it okay to go with this?

@pljones
Copy link
Collaborator

pljones commented Feb 14, 2026

Here's what Gemini made of it:

UX by Gemini.pdf

Full text of prompt:

Attached is a screen shot of a new Settings tab being developed for Jamulus. It allows the user to configure through the UI settings previously only available from the command line (the --ctrlmidich values). The current design has all controls enabled by default, sharing CC0 with a count of 1. Two proposals have been made:

  • add an explicit "control active" checkbox (e.g. change the label for the panel to be a checkbox with the same label as the panel title)
  • use an "invalid value" to indicate the control isn't active (-1 for Mute Myself and a Count of 0 for the others)

Which would the majority of users find easier to understand?

It sounds like Gemini thinks there's a "Checkable Groupbox" -- but AI can make mistakes (and I wouldn't trust Gemini with coding, to be honest).

The winning point for me is one I didn't think of, though -- if I disable a control temporarily, I don't want to have to go through the process of setting the value again to get it working.


Surprisingly fast response by one of my local LLMs under Ollama:

UX by devstral-small-2_24b_local.pdf

@ignotus666
Copy link
Member Author

ignotus666 commented Feb 14, 2026

Fair enough, it makes good points. Will get to work on it, and yes, checkable groupboxes are a thing. I'll make one for Mute Myself too to make it consistent.

iOS build succeeded BTW.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs documentation PRs requiring documentation changes or additions

Projects

Status: Waiting externally

Development

Successfully merging this pull request may close these issues.

4 participants