feat: multicast tosc feedback to clients

Replace single ~tosc NetAddr with ~toscClients Dictionary
(keyed by ip) so feedback OSC reaches all connected clients
(web bridge + iPad) simultaneously. ~toscTouch deduplicates
by IP and logs only on first connection. ~toscSend iterates
the dictionary; no-op when empty.

Test: add assertion that two distinct IPs produce size==2.
Balance: P:0 B:0 on both files. Headless TEST PASS.
This commit is contained in:
L'électron rare
2026-06-28 14:02:41 +02:00
parent 67fd253de5
commit 2213b4e53d
2 changed files with 16 additions and 9 deletions
@@ -30,13 +30,18 @@ pass = pass and: { ~toscSend.notNil };
pass = pass and: { ~toscTouch.notNil };
pass = pass and: { ~toscArmedPush.notNil };
// Calling ~toscArmedPush.() with ~tosc nil must not raise
// (~toscSend nil-guards via !? so no OSC is actually sent)
// Calling ~toscArmedPush.() with no clients must not raise
// (~toscSend iterates an empty dictionary -> no-op, no OSC sent)
try { ~toscArmedPush.() } { |e|
pass = false;
("EXCEPTION in ~toscArmedPush: " ++ e.messageString).postln;
};
// Assert multicast set grows with distinct IPs
~toscTouch.(NetAddr("127.0.0.1", 0));
~toscTouch.(NetAddr("10.0.0.9", 0));
pass = pass and: { ~toscClients.size == 2 };
// Stop the background Routine so the process can exit cleanly
~toscFeedbackRoutine !? { ~toscFeedbackRoutine.stop };
+9 -7
View File
@@ -20,21 +20,23 @@
(
// -- Port + address init --
~toscPort = ~toscPort ? 9000;
~tosc = ~tosc; // nil on first load, preserved on reload
~toscPort = ~toscPort ? 9000;
~toscClients = ~toscClients ? Dictionary.new;
// Refresh the target NetAddr when a control message arrives from the surface
// Add / refresh a client entry when a control message arrives from the surface.
// Keyed by ip String so the same host always maps to one NetAddr (dedup).
// Logs only when a NEW ip is first seen.
~toscTouch = { |addr|
var ip = addr.ip;
(~tosc.isNil or: { ~tosc.ip != ip }).if({
~tosc = NetAddr(ip, ~toscPort);
~toscClients[ip].isNil.if({
~toscClients[ip] = NetAddr(ip, ~toscPort);
("[touchosc] client -> " ++ ip ++ ":" ++ ~toscPort.asString).postln;
});
};
// Send an OSC message to the surface, silently if ~tosc not yet known
// Send an OSC message to all known clients; no-op when none connected
~toscSend = { |path ...args|
~tosc !? { ~tosc.sendMsg(path, *args) };
~toscClients.values.do { |dest| dest.sendMsg(path, *args) };
};
// Canonical pad name order (16 pads in grid order, matches the surface layout)