juce-framework / JUCE

JUCE is an open-source cross-platform C++ application framework for desktop and mobile applications, including VST, VST3, AU, AUv3, LV2 and AAX audio plug-ins.
https://juce.com
Other
6.38k stars 1.69k forks source link

Jack midi support #952

Open dromer opened 2 years ago

dromer commented 2 years ago

Currently JUCE supports jack audio ports fairly well, but there is no jack midi support at all. This makes sequencing JUCE applications really difficult, having to create virtual midi ports and routing signals via these. It's quite the hassle.

Please consider adding jack midi support to make life a little easier on those that use this system.

It would just need to expose an input port and let the user take care of routing signals to the application.

fpesari commented 2 years ago

Pipewire bypasses this problem, by the way.

dromer commented 2 years ago

Kind of. It's still nice to be able to control the graph from outside the juce application, which is not possible if juce "latches on" without any regard to routing rules.

Also many cases where running pipewire is not wanted and dedicated jack configuration is needed.

fpesari commented 2 years ago

With Pipewire you can use any JACK routing program (such as Qjackctl) to route audio and MIDI connections written originally for ALSA, Pulseaudio and JACK.

dromer commented 2 years ago

Yes, but any routing logic from within the JUCE app doesn't make any sense then. There would still need to be some sort of "just expose midi port" ability, without any routing on the JUCE side.

PulseAudio does not have a midi api afaik.

denilsonsa commented 10 months ago

I'd like to add that this specific issue is about MIDI support, not audio. Indeed, with PipeWire I can route the two output audio channels to whatever audio sink I want, but no MIDI input is available for JUCE applications.

For comparison, look at how amsynth behaves, when compared to a JUCE-based application (odin2). qpwgraph shows native JACK/PipeWire MIDI input, and even the correct application name with an application icon:

Screenshot of qpwgraph

As well as basic ALSA MIDI input:

Screenshot of qpwgraph

And both can be re-routed easily via PipeWire using qpwgraph. However, any JUCE-based application only shows two audio output channels and zero MIDI input channels. Thus, it's impossible to route MIDI to JUCE applications.

It is extremely annoying to have to hunt down for the configuration screen of each application and then toggle some checkboxes over there. It negates the patchbay saving/loading feature of qpwgraph. It is also frustrating to press some MIDI keys and not know which application is handling those and generating audio.

This issue affects several projects:

Some potentially related issues:

If anyone wants to tackle this issue, this is probably a good start: juce_Midi_linux.cpp

kmatheussen commented 10 months ago

On Fri, Sep 8, 2023 at 1:41 PM Denilson Sá Maia @.***> wrote:

I'd like to add that this specific issue is about MIDI support, not audio. Indeed, with PipeWire I can route the two output audio channels to whatever audio sink I want, but no MIDI input is available for JUCE applications.

Here's the rough fix I'm using: https://github.com/kmatheussen/radium/blob/e115ad6ec6fb9dfa9348a716efe706aa740a38ad/pluginhost/midi_linux.diff#L32

Problem is that this is disabled by default instead of enabled by default, for some reason. I just force it to always be enabled. (I think I submitted a patch around 10 years ago, but it wasn't accepted)

Message ID: @.***>

denilsonsa commented 10 months ago

@kmatheussen, that is very helpful!

As a quick-and-dirty fix, we can have a single-line patch!

--- juce_Midi_linux.cpp 2023-09-08 16:19:43.865931328 +0200
+++ juce_Midi_linux.cpp 2023-09-08 16:09:52.193231251 +0200
@@ -469,7 +469,7 @@
             {
                 if (portID != -1)
                 {
-                    port = client.createPort (portName, forInput, false);
+                    port = client.createPort (portName, forInput, true);
                     jassert (port->isValid());
                     port->connectWith (sourceClient, portID);
                     break;

With just that change, I've managed to recompile OPL and it indeed shows up in qpwgraph:

Screenshot of qpwgraph

Changes in the settings screen seem to be mostly reflected into qpwgraph (but I can't uncheck my main controller, for whatever reason). Changes from PipeWire are not reflected in the application, so changes can get out-of-sync. MIDI playback still works, anyway, even if the application checkboxes are out-of-sync compared to the real PipeWire connections.

Unfortunately, the ports have the name copied from the devices, which gets confusing. Even more confusing because all ports end up duplicated in the PipeWire Midi-Bridge box.

Another screenshot of qpwgraph

In this screenshot, I'm running VMPK and connecting it to OPL (a JUCE application), together with a hardware device. I'm also running amsynth, but unconnected. This leads to many duplicated ports in many boxes (and some of that is inherent to how PipeWire's Midi-Bridge works); but it's still better than not being able to route anything.

Please note that kmatheussen's patch does a few more changes, one of them is related to the port name. I haven't tested those changes.


My suggestion for next steps:

  1. Apply the simple one-line patch.
  2. Apply additional changes for better port naming in JUCE.
  3. Figure out how to keep the checkboxes in-sync with the real connections.
  4. Write a brand-new MIDI driver for linux, targeting JACK/PipeWire instead of plain ALSA. All input and output ports should show up in the same box (in qpwgraph), and the box should have the application name and possibly the application icon.
  5. At some point, send PRs to many applications so they can use the newer JUCE version with the MIDI fixes. These can happen as early as step one.

I'm sorry, but I'm not going to implement those. These are just my suggestions (from easiest to hardest). I'll leave those to whoever is more familiar with JUCE.

kmatheussen commented 10 months ago

i actually just meant to refer to line 32 of that patch, which is also a one-liner. :)

On Fri, 8 Sep 2023 at 16:44, Denilson Sá Maia @.***> wrote:

@kmatheussen https://github.com/kmatheussen, that is very helpful!

As a quick-and-dirty fix, we can have a single-line patch https://github.com/juce-framework/JUCE/blob/22df0d2266007bccb25d6ed52b9907f60d04e971/modules/juce_audio_devices/native/juce_Midi_linux.cpp#L472 !

--- juce_Midi_linux.cpp 2023-09-08 16:19:43.865931328 +0200+++ juce_Midi_linux.cpp 2023-09-08 16:09:52.193231251 +0200@@ -469,7 +469,7 @@ { if (portID != -1) {- port = client.createPort (portName, forInput, false);+ port = client.createPort (portName, forInput, true); jassert (port->isValid()); port->connectWith (sourceClient, portID); break;

With just that change, I've managed to recompile OPL https://github.com/reales/OPL and it indeed shows up in qpwgraph:

[image: Screenshot of qpwgraph] https://user-images.githubusercontent.com/121676/266639643-0ff3294d-00eb-477a-b64a-40372bc6f188.png

Changes in the settings screen seem to be mostly reflected into qpwgraph (but I can't uncheck my main controller, for whatever reason). Changes from PipeWire are not reflected in the application, so changes can get out-of-sync. MIDI playback still works, anyway, even if the application checkboxes are out-of-sync compared to the real PipeWire connections.

Unfortunately, the ports have the name copied from the devices, which gets confusing. Even more confusing because all ports end up duplicated in the PipeWire Midi-Bridge box.

[image: Another screenshot of qpwgraph] https://user-images.githubusercontent.com/121676/266644555-0f2f2e07-19a5-490a-97da-ece8265bc83c.png

In this screenshot, I'm running VMPK http://vmpk.sourceforge.net and connecting it to OPL https://github.com/reales/OPL (a JUCE application), together with a hardware device. I'm also running amsynth https://amsynth.github.io/, but unconnected. This leads to many duplicated ports in many boxes (and some of that is inherent to how PipeWire's Midi-Bridge works); but it's still better than not being able to route anything.

Please note that kmatheussen's patch https://github.com/kmatheussen/radium/blob/e115ad6ec6fb9dfa9348a716efe706aa740a38ad/pluginhost/midi_linux.diff does a few more changes, one of them is related to the port name. I haven't tested those changes.

My suggestion for next steps:

  1. Apply the simple one-line patch.
  2. Apply additional changes for better port naming in JUCE.
  3. Figure out how to keep the checkboxes in-sync with the real connections.
  4. Write a brand-new MIDI driver for linux, targeting JACK/PipeWire instead of plain ALSA.
  5. At some point, send PRs to many applications so they can use the newer JUCE version with the MIDI fixes. These can happen as early as step one.

I'm sorry, but I'm not going to implement those. These are just my suggestions (from easiest to hardest). I'll leave those to whoever is more familiar with JUCE.

— Reply to this email directly, view it on GitHub https://github.com/juce-framework/JUCE/issues/952#issuecomment-1711787509, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAIX3J57FJ2KXO4NQZCY3TDXZMVM3ANCNFSM5ETMXVIQ . You are receiving this because you were mentioned.Message ID: @.***>

denilsonsa commented 10 months ago

By just looking at git history, I guess this behavior was introduced in 2017 (6 years ago), in this commit, which was a large refactor with hundreds of lines. That commit added the enableSubscription parameter, as previously that flag was always set.

The commit message mentions:

  • Fixed a bug when getDeivces() would return devices created by the application itself
  • Only ports created with createNewDevice() are allowed to be subscribed, other ports (created by openDevice()) doesn't allow subscription

As a workaround for this issue, if your JUCE application is also built and installed as LV2 plugin, then you can use Jalv as a simple standalone LV2 plugin host. It will behave properly, having one box for the whole application, with proper audio out and midi in channels, with proper name, and even including the icon.

image

Compare Odin2 running inside Jalv (above) with Odin2 standalone binary (below):

image

Of course, you can also run other more complete plugin hosts, like Carla (which also shows up properly just like Jalv).

dromer commented 10 months ago

Except most (extensive) standalone JUCE applications don't run as a plugin at all.