Amidiminder utility

Alas, sometimes USB cords get pulled accidentally, or devices get switched off and on. In the middle of a set, I don’t want to have to aconnect the MIDI ports again!

amidiauto takes care of this for may set ups, but not mine: I have a more complex set up, with specific devices connected to specific places.

Enter amidiminder: This little app simply keeps track of all the ALSA MIDI connections made (whether via aconnect or via other software, like SuperCollider) - and remembers them by name. If a MIDI device goes away and then later comes back, amidiminder will connect it back up as it was before. It works even if devices are reconnected in a different order.

This project is working and available on github: https://github.com/mzero/amidiminder
It’s pretty early going, and you’ll have to build it yourself. Open to issues, bug reports, pull requests, and discussion.

Perhaps there is some natural merging of the functionality of this and amidiauto… seems likely to me, but it wasn’t immediately obvious how to meld it, and the undocumented file feature in amidiauto

9 Likes

I’ve been thinking more about how to merge amidiminder with amidiauto. The things amidiauto was missing for me:

  1. Handle clients & devices with more than one port in each direction. For example, Launchpad Pro has three bidirectional ports. SuperCollider can have many more.

  2. Observe software that makes connections, and maintain them. aconnect, Patchage, and SuperCollider can all make connections - but don’t “reconnect” if the devices disconnect and reconnect.

On the other hand, amidiauto offers a way to declare specific connections you want, and ensure those get made. And it can do this by “class” of port and device.


I can see how to extend amidiauto's file format to support port numbers and names. I can also see how to incorporate both connections made via the rules, and connections observed from other software. However… I feel like I’m not clear on how amidiauto is currently used:

By default it will just connect all hardware ports to all software ports. This works for simple cases: “just connect my midi note controller to the current running software thing” - but fails for larger setups where you might a devices with multiple ports, controllers that are knob/fader boxes going to one software or one port on a program, and note contollers going to different ports. Nor does it handle external synths well when there are more than one if you need more control over them than just MIDI channels

To handle these more complex cases, amidiauto has a file rule format… but it isn’t documented in the man page. What isn’t clear to me is how do people use that file? Is having just one file in /etc enough? Do you need one per “scenario” and away to change between them? Does anyone use the [deny] feature, and if so how? There are some internal subtle logic around rule “priority” that is hidden from the file… does that work well?

If anyone uses amidiauto's file feature - I’d like understand how you use it - and what your set up is like.

1 Like

Looks like a very useful utility. These are very good ideas. :slight_smile:

Indeed, it’d probably make sense to merge the functionalities into one process. Maybe amidiauto may be monitoring the connect/disconnect events and update its rules file, whenever the user or some other software make changes to the virtual MIDI connections in any of the other software. This would also spare amidiauto from eventually needing a GUI. :slight_smile:

Regarding /etc/amidiauto.conf - we allow Patchbox Modules to have their own amidiauto.conf versions. So it allows having different setups for different modules. But still, everyone’s setup is different, so editing it for custom setups would be necessary.

For the rules of the config file - there’s 3 levels of possible rule strengths - very specific, vague (when one side is named, the other is a wildcard ‘*’), and weakest - wildcard on both sides. amidiauto puts ports into ‘software’ and ‘hardware’ categories. When considering a port, a wildcard always matches the ports of the opposite type, in order to do the software and hardware port connections. When evaluating whether particular ports must be connected together, the strongest and most explicit rule wins. Also, it’s possible to have wildcard allow rules, but explicitly deny connections in the [deny] section. The default rule for amidiauto is * <-> *.

Major update to amidiminder pushed to github today:

Rules files

It now reads a file of rules at startup /etc/amidiminder.rules that describes connections, and will automatically make those connections once the devices and software shows up.

# Rules for connecting up controllers to software MIDI looper called "bicycle":

nanoKEY2 --> bicycle
nanoKONTROL --> bicycle

bicycle <-- Launchpad Pro MK3
bicycle <-- Launchpad Pro MK3:2

bicycle:synths --> Circuit

Rule format is intentionally similar to amidiauto, but with some notable differences:

  • Ports default to 0, since many devices have but one
  • Ports can be specified by number or by name
  • Clients and ports are matched by partial string matching, unless the name is given in double quotes.
  • * is supported… but not yet really useful as it isn’t restricted to HW or SW (yet)
  • There is no [disallow] section - I have future plays for negative rules. There is no [allow] section header.

Connection monitoring

It still also continues to monitor connections made by aconnect and other software - and if the devices go away but later reconnect, those connections will be remade as well.

Service

The project builds a .deb file that installs with a systemd service file - and so it’ll just start and run all the time.

Misc

There are command line flags - and there is a “rules” checking mode so you can double check
your file editing before you restart the service.


Note: I’m don’t think amidiauto and amidiminder would get along. You probably only want to have one service or the othe running.

2 Likes

Looks good :slight_smile: It’d be great to have a single utility that’d do everything.

It looks like it’s not far off - essentially it’d just need to support the automatic connection between hardware and software ports, if there’s a rule asking for it in the rules file.

What do you have in mind for the disallow rules? I think they’re mostly useful in case of having the wildcard rules which connect hw and sw together, to block certain unwanted connections.

With those 2 things sorted out, I think this could be merged into a single utility package.

1 Like

For hardware/software, I’m thinking of using something like:

[hw] <-> [sw]

The brackets would indicate a “property” of the client or port. ALSA offers a number of these:

  • Client: user | kernel
  • Port:
    • applicaiton, hardware, software
    • midi generic, midi gm, midi gm2, midi gs, midi mt32, midi xg
    • direct sample, port, sample, specific, synth, synthesizer

I’m not sure if those type flags are really used all that much, other than the client and first port set. I’m planning on seeing what all my gear and software does to see what might be useful.

For disallow, I’m thinking of explicit negative rules:

TouchOSC Bridge  <->  [sw]
TouchOSC Bridge <-x-> [hw]
# other negative rule operators:  -x-> <-x-

The question is should the rules be evaluated in the order in the file (so you could have exceptions to negative rules with positive ones that follow) - or is it easier to just sort all the negative rules to the end. I’m leaning “file order”…

One last thing, I don’t yet implement the logic that wild cards connect the first port with the appropriate direction capability. I need to check if that is important for SuperCollider or Pd… but Imagine all HW ports are already bidirectional.

Ok, sounds good. I also don’t think the Port Type gets much use out there. It seems the only useful distinction are the hw and sw port types.

As for the rule order, it might make sense to do file order, but it might also lead to user mistakes where you could place a specific disallow rule somewhere, and then further down, add a rule that allows connection to any hardware port.

The approach in amidiauto was to evaluate the strength of rules that apply to whether the connection between 2 particular ports should be made. The strength depends on how explicit the rule is, so [hw] ↔ [sw] rule would be considered a weak one, while OP-1 ↔ [sw] would be stronger and OP-1 ↔ Pisound would be the strongest one.

In addition to amidiauto rules logic, file order could be used so that the last rule would override the rule, in case a conflicting one is specified. Example:

OP-1 <-> [hw]
...
OP-1 <-x-> [hw] # This rule would override the one above

Using <-x-> (and related) and <-> (and related) would allow to get rid of [allow] and [disallow] sections altogether in the rules file.

There can definitely be one directional hardware ports.

My survey of everything I could get my hands on turned up this:

  • The only port properties that are set with any consistency are application and hardware.
  • The property software is only set by the kernel on the through port.

My inclination would be to say a port is [hw] if has the port type hardware, and [sw] if it has application. This would nicely exclude the system ports, and the kernel through port from a generic [hw] <-> [sw] rule.

There are some ports I’m just not clear about:

Touchosc seems to have different clients? There is a client touchosc that declares its port as hardware, so that one would do the right thing. There is another set of clients RtMidiIn and RtMidiOut - not sure what these are. Also, the second client has one bizarre port name!

I don’t understand what the client and port pisound-ctl is for.

Painoteq doesn’t make its ports subscribable… so it can’t be “helped” by either amidiminder or amidiauto. Sigh.

There seems to be several conflicting approaches to MIDI port handling by applications:

  • Some create ports and let the user connect externally to the application. Like Pd

  • Some let the user pick a MIDI port from within the application only. Ex.: Pianoteq. Then the app’s decision of what is an acceptable port limits you: I can’t pick the output of my looper application to go into Pianoteq! Moddep does something similar… but via the indirection of Jack’s re-exported ALSA Seq ports… it is even harder to get some software port into it!

  • Some create matching, separate in & out ports for each other port in the system - like SuperCollider.

I’m going to have to add the logic of defaulting to the first port of the appropriate direction when the port isn’t specified… Need to brood a bit on this one…

The RtMidiIn and RtMidiOut ports get created by rtmidi library that touchosc2midi uses. The touchosc ports are actually created using amidithru program to solve the port naming and port type for touchosc and possibly other ports which should fall in the ‘hardware’ group. I’ve looked into modifying touchosc2midi to avoid that, but the MIDI library it uses does not give enough control over the name or port type.

pisound-ctl is for the Pisound App, it should also be considered a hardware port. It’s hosted by osc2midi

The PDs use of MIDI ports seem to be the right way for software on Linux to do it.

The Pianoteq actually mimics how the ports work on other major platforms, but as far as I can remember, it has a setting which makes it act like PD does, where you can hook up the connections yourself.

Does SuperCollider really make a new port for each other port? Sounds very inefficient :smiley: Does it make the connections automatically too? I was looking into its MIDI use, I noticed it had many ports, but I didn’t think it depended on the count of other ports in the system.

amidiminder is finally ready for early adopter testing:

This release supports:

  • simple rules: nanoKEY2 ---> Circuit
  • complex rules: Launchpad Pro:3 <-- bicycle:synths
  • port type rules: .hw <-- nanoKey2
  • blocking rules: PreenFM <-x- nanoKey2
  • see the file amidiminder.rules file for full details

The release page has a built debian package for Raspberry Pi, built on and for Patchbox OS. You can install it with:

sudo systemctl stop amidiauto
sudo systemctl disable amidiauto
sudo dpkg -i amidiminder_0.70_armhf.deb

Installing the debian package file will set up a service to run by default, and start it immediately. The service will read a rules file placed in /etc/amidiminder.rules. If you edit that file, restart the service:

sudo systemctl restart amidiminder

It is recommended that you disable the amidiauto service if you have it, as this package contains all of that functionality, and more… and they’ll probably fight if both running.

The system is pretty verbose at the moment, see the logs with

journalctl -f amidiminder

The default rules file has these rules:

.hw <---> .app
  # Interconnect all hardware and application ports.
  # For simple set ups, this is often all you need.
  # Comment this out if you need to be more explicit about what is connected
  # to what.

RtMidiIn Client <-x- *
RtMidiOut Client -x-> *
  # Don't auto connect anything to these ports. They are generic client
  # names used by various applications, and could mean anything.

That should be equivalent to the logic that amidiauto was using.

5 Likes

That’s great! :slight_smile: We’ll look into moving to amidiminder.

I assume it still supports specifying the port number, in case a device has multiple available? It’s not mentioned in the latest information and it’d be great to explicitly mention it and the syntax in the readme/help for the program, currently it’s only visible in an example. :slight_smile:

Btw, does it update the rules file as you make connections manually using aconnect, or does it store that state somewhere else?

It does support both port numbers and port names. But yes, I need to work on the README / help / man page.

Currently it only stores observed rules in memory, not on disk. If you restart it, however, it’ll “reobserve” them, since it does a scan at start up. Nonetheless, perhaps it should store them so that such things survive restart.

Things I think it needs before a 1.0 release:

  • reload signal (SIGHUP) support (& service file support)
  • watching the rules file and auto-reloading when written
  • writing out observed rules, and possibly reloading at start (somewhere in /var/run)
  • man page / readme / help – or all three…

Anything else?

I’m very curious to see how well this works for other people in practice. I look forward to feedback.

1 Like

That’d be great - then you rarely would even have to manually edit the rules file, as you could rely on tools like aconnectgui to specify direct connections yourself, and rely on them. I guess you’d have to be careful to let the user override connections that are not initially allowed by the rules file.

What m I doing wrong? aconnectgui does not give me any connections…

patch@patchbox:~/amidiminder $ sudo amidiminder -C
reading rules from /etc/amidiminder.rules
adding rule Deluge MIDI 1:* --> Midihub MH-2KMG9CN MIDI 1:*
adding rule Deluge MIDI 1:* --> NDLR MIDI 1:*
adding rule NDLR MIDI 2:* --> Midihub MH-2KMG9CN MIDI 2:*
patch@patchbox:~/amidiminder $ sudo amidiminder -f amidiminder.rules
reading rules from amidiminder.rules
adding rule 24:0 --> 32:0
adding rule 24:0 --> 28:0
adding rule 28:1 --> 32:1
^C
patch@patchbox:~/amidiminder $ sudo systemctl restart amidiminder
patch@patchbox:~/amidiminder $ aconnectgui

Which rules file is the correct one?

When you do sudo systemctl restart amidiminder, it restarts amidiminder, and it most likely reverts to reading from /etc/amidiminder.rules.

You may double check what it’s doing by running:

sudo systemctl status amidiminder
sudo journalctl -u amidiminder

Thx for your help, I ve got to learn a lot about directories and rights as I am a linux noob

patch@patchbox:~ $ sudo sytemctl status amidiminder
sudo: sytemctl: command not found
patch@patchbox:~ $ sudo journalctl -u amidiminder
– Logs begin at Mon 2021-01-04 10:48:27 GMT, end at Mon 2021-01-04 13:35:43 GMT. –
Jan 04 12:23:43 patchbox systemd[1]: Stopping ALSA MIDI minder daemon…
Jan 04 12:23:43 patchbox systemd[1]: amidiminder.service: Main process exited, code=killed, status=1
Jan 04 12:23:43 patchbox systemd[1]: amidiminder.service: Succeeded.
Jan 04 12:23:43 patchbox systemd[1]: Stopped ALSA MIDI minder daemon.
Jan 04 12:23:44 patchbox systemd[1]: Started ALSA MIDI minder daemon.
Jan 04 12:23:44 patchbox amidiminder[2276]: reading rules from /etc/amidiminder.rules
Jan 04 12:23:44 patchbox amidiminder[2276]: adding rule Deluge MIDI 1:* --> Midihub MH-2KMG9CN MIDI
Jan 04 12:23:44 patchbox amidiminder[2276]: adding rule Deluge MIDI 1:* --> NDLR MIDI 1:*
Jan 04 12:23:44 patchbox amidiminder[2276]: adding rule NDLR MIDI 2:* --> Midihub MH-2KMG9CN MIDI 2:
Jan 04 12:27:50 patchbox systemd[1]: Stopping ALSA MIDI minder daemon…
Jan 04 12:27:50 patchbox systemd[1]: amidiminder.service: Main process exited, code=killed, status=1
Jan 04 12:27:50 patchbox systemd[1]: amidiminder.service: Succeeded.
Jan 04 12:27:50 patchbox systemd[1]: Stopped ALSA MIDI minder daemon.
Jan 04 12:27:51 patchbox systemd[1]: Started ALSA MIDI minder daemon.
Jan 04 12:27:51 patchbox amidiminder[2335]: reading rules from /etc/amidiminder.rules
Jan 04 12:27:51 patchbox amidiminder[2335]: adding rule Deluge MIDI 1:* --> Midihub MH-2KMG9CN MIDI
Jan 04 12:27:51 patchbox amidiminder[2335]: adding rule Deluge MIDI 1:* --> NDLR MIDI 1:*
Jan 04 12:27:51 patchbox amidiminder[2335]: adding rule NDLR MIDI 2:* --> Midihub MH-2KMG9CN MIDI 2:
Jan 04 12:29:51 patchbox systemd[1]: Stopping ALSA MIDI minder daemon…
Jan 04 12:29:51 patchbox systemd[1]: amidiminder.service: Main process exited, code=killed, status=1
Jan 04 12:29:51 patchbox systemd[1]: amidiminder.service: Succeeded.
Jan 04 12:29:51 patchbox systemd[1]: Stopped ALSA MIDI minder daemon.
lines 1-23…skipping…

There was a typo in sudo systemctl status command.

Btw, hit ‘end’ key to go to the very latest lines in journalctl.

1 Like

Reinstalled everything (one should mention that ‘make’ can take minutes to finish :slight_smile:
Summary: Restarted amidiminder and checked status: All ports are added but not connected by the rules.

(The journalctl is to long for a post and besides I find it kind of embarrassing to paste it here)

Just to be clear: If you are going to have the system service run amidiminder (rather than start it by hand all the time), then the rules you want to edit are in /etc/amidiminder.rules.

Looking at the output:

These rules look wrong. The format for a connection end point is device:port, so I think the rules you want are:

Deluge:MIDI 1 --> Midihub:MIDI 1
Deluge:MIDI 1 --> NDLR:MIDI 1
NDLR:MIDI 2 --> Midihub:MIDI 2

Notice that you can abbreviate the device names. Also, USB MIDI devices under linux don’t get useful names¹. So you can just use port numbers (which start at 0):

Deluge:0 --> Midihub:0
Deluge:0 --> NDLR:0
NDLR:1 --> Midihub:1

1: Why, ALSA, why??? The port names are in the USB descriptors the devices provide!!

2 Likes

Yes…yes…after days…Devices are connected and survived disconnect and restart!!

Big Thx for your help!!

Many things to do on my Zero/W: Speeding up the patching (why can´t it start that quick like the MIDIHUB does?), setting a temporary hot spot connection, and of course supporting the amidiminder project!

But first: Return to makin music…so happy!

2 Likes