Start rtpMIDI if it is not running; it also restarts itself if it crashes

- Hold MIDI on "LIGHT PC MIDI GONE" starts the rtpMIDI service, then its tray
  app, only if they are not running (a restart drops every session).
- The Lighting PC's rtpMIDI service now has Windows recovery actions
  (restart after 5 s, 5 s, 30 s); windows-setup.ps1 sets the same.
- coremidi.disconnect_peer (Disconnect in Audio MIDI Setup), used to test the
  alarms: all six links dropped and came back (four by themselves, two by
  the hold).
- Audio MIDI Setup need not be open: quitting it on NA Ingest left the
  session connected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 16:16:31 -04:00
co-authored by Claude Opus 5.5
parent 1d05412359
commit 288c1e6e1a
5 changed files with 47 additions and 8 deletions
+28 -5
View File
@@ -134,16 +134,25 @@ def with_peer(sess, peer_name, address):
return new
def connect_peer(session_name, peer_name, address):
"""Ask macOS to connect `peer_name` at `address` ("10.1.10.206:5004") to the named session,
like Audio MIDI Setup's Connect. Returns "already" (it is in the session) or "asked"."""
def without_peer(sess, peer_name):
"""The session settings without this peer, or None if it is not in the session."""
peers = list(sess.get("peers", []))
keep = [p for p in peers if p.get("name") != peer_name]
if len(keep) == len(peers):
return None
new = dict(sess)
new["peers"] = keep
return new
def _edit_session(session_name, edit):
with _lock:
for ent, sess in _session_entities():
if (sess.get("dns-sd-name") or _string_prop(ent, "name")) != session_name:
continue
new = with_peer(sess, peer_name, address)
new = edit(sess)
if new is None:
return "already"
return "unchanged"
err = _set_dict_prop(ent, "apple.midirtp.session", new)
if err:
raise OSError("MIDIObjectSetDictionaryProperty failed: %d" % err)
@@ -151,6 +160,20 @@ def connect_peer(session_name, peer_name, address):
raise LookupError("no network MIDI session named %r on this Mac" % session_name)
def connect_peer(session_name, peer_name, address):
"""Ask macOS to connect `peer_name` at `address` ("10.1.10.206:5004") to the named session,
like Audio MIDI Setup's Connect. Returns "already" (it is in the session) or "asked"."""
result = _edit_session(session_name, lambda sess: with_peer(sess, peer_name, address))
return "already" if result == "unchanged" else result
def disconnect_peer(session_name, peer_name):
"""Drop a peer from the named session, like Audio MIDI Setup's Disconnect (to test the alarms).
Returns "not connected" or "asked"."""
result = _edit_session(session_name, lambda sess: without_peer(sess, peer_name))
return "not connected" if result == "unchanged" else result
def sessions():
"""{session name: {"port": int, "peers": [{"name", "address", "state"}]}}"""
found = {}