Files
RemSound/pi server/pi server update.md
T
EdnunpandClaude Opus 4.8 033edd776f Volume/pan/EQ tab overhaul: one master switch, peer checklist, 16-band parametric EQ
Reworks the per-peer shaping tab (held for next release):

 * Renamed the tab to "Volume, pan and EQ for peers"; the Preferences toggle now defaults ON.
 * Collapsed the two master switches (Enable EQ / Enable pan) into ONE: "Enable volume, pan and
   EQ for all peers" (Alt+E). Volume now obeys it too. PeerDspChain.Build takes a single enabled
   flag; Profile.EnableAllPeerShaping replaces the two bools (old ones kept for load-migration).
 * Peer picker is now a CheckedListBox: ticking a peer shapes them (per-peer bypass via new
   PeerShaping.Enabled, default true); the focused row is the one the controls edit. Effective
   shaping = master switch AND that peer's tick. Letter-nav suppressed so keys never toggle a tick.
 * Three EQ modes, renamed: "3 band simple EQ", "12 band advanced graphic EQ", and the new
   "16 band parametric EQ" (PeerEqMode.Parametric16Band).
 * Parametric EQ: up to 16 user bands, each a boost/cut across a start->end range (PeerShaping
   .ParametricBands; ParametricToPeaking maps range -> peaking centre+Q, shared by DSP and curve).
   Add band dialog (spin-or-type, numeric-only, live preview, OK/Escape); Bands list sorted
   bass->treble reading "X Hz to Y Hz, plus/minus N dB"; Delete key / Delete button, multi-select.
   Set peer EQ to default clears the parametric list too.
 * dB now spoken as words ("plus 3 dB" / "minus 6 dB" / "flat") on the graphic sliders and the
   parametric list, since NVDA users typically have punctuation off and never hear a "+".
 * New unbound machine-wide global shortcut "Toggle volume, pan and EQ for all peers" (not stored
   in any profile) via the hotkey controller + settings store.
 * Renamed the Inputs/outputs "Set volume for all received audio" to "Master receive volume".
 * Added EqCurveControl: a purely-visual EQ response graph (not focusable, invisible to NVDA).
 * Full manual sweep (readme.html + regenerated MANUAL.md).

Build clean; --selftest passes. Deployed to both test folders. Held for next release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 23:21:18 +01:00

7.9 KiB

RemSound Pi Server — Handover

Status: server side COMPLETE. Built, released, deployed, verified — 15 May 2026.

This document records the upgrade of the RemSound relay server from the original two-peer reflector to a dual-protocol (v1 pairwise + v2 lobby) relay with a GitHub-based auto-updater. It is written for the RemSound thread, for Andre, and for any future maintainer.

The work was done from the Pi thread (the one that manages Ed's Raspberry Pi). The design it was built from is the companion file remsound server update.md.


1. What was built

A single relay binary, remsound-relay.py, that handles two protocol versions concurrently on the same UDP listener (port 47830):

  • v1 (pairwise) — the original two-slot reflector, unchanged. First two UDP endpoints to send a valid RemSound v1 packet claim the slots; their traffic is mirrored to each other. Existing RemSound clients (v1.x) keep working against the new server with no changes.
  • v2 (lobby) — a multi-peer lobby, up to 10 peers (configurable), keyed on a per-instance CLIENT_ID (UUID). Every packet from one client is fanned out to every other client in the lobby. NAT rebinds and "two clients behind one NAT" stop being special cases because identity is the CLIENT_ID, not the endpoint. Periodic LobbyRoster packets keep clients informed of membership.

The server inspects byte 4 of each packet (the version byte) and routes v1 vs v2 accordingly. A v1 client and a v2 client cannot share a lobby in this release — that is a deliberate scope cut (see the design doc).

Also built:

  • Auto-updaterremsound-relay-update.sh, run by a systemd timer. It polls the GitHub Releases API hourly for tags beginning server-, and if a newer one exists, downloads it, backs up the current install, swaps the files, restarts the service, health-checks it, and rolls back automatically if the service fails to come up.
  • systemd unitsremsound-relay-update.service (one-shot) + remsound-relay-update.timer (boot + 2 min, then hourly with 10-min jitter).
  • Installer / uninstaller / smoke-testinstall.sh, uninstall.sh, smoke-test.sh, updated to wire in the auto-updater.
  • VERSION — the tag the bundle represents.

2. Where everything is

Thing Location
GitHub releases (what the auto-updater pulls) github.com/Ednunp/RemSound/releases — tags server-v2.0server-v2.3
GitHub source github.com/Ednunp/RemSoundserver/ folder
Deployed and running Ed's Raspberry Pi (Pi5), server-v2.3, UDP 47830
Deployable bundle (this archive) the files alongside this document
Design / spec remsound server update.md (alongside this document)

Note for the RemSound thread: the repo's server/ folder was updated by the Pi thread on 15 May 2026 directly via the GitHub API. The local checkout at D:\proj\remsound\server\ may therefore be behind the remote — do a git pull to sync before doing any work there, and do not overwrite server/ with older server code.


3. The releases

Tag What it is
server-v2.0 Initial dual-protocol release
server-v2.1 No-op test release (used to validate the auto-updater's upgrade path)
server-v2.2 Bug fix — see section 6
server-v2.3 No-op test release (validated the fixed updater)

The Pi runs server-v2.3. The auto-updater always picks the highest version, so the test releases don't interfere. Future real releases should be server-v2.4 and upward.


4. IMPORTANT — what is still left, and whose job it is

The server is finished. What remains is client-side work in the RemSound application, and that is the RemSound thread's job, not the Pi thread's.

Per the design doc, sections 5 and 9, the client work is:

  1. Profile / AppConfig: a new ClientId (Guid) field, generated once and persisted in remsound.config.json.
  2. RemPacket: header read/write learns the v2 format (28-byte header with the 16-byte CLIENT_ID). v1 read/write stays.
  3. AudioSender: send one stream to the lobby server; drop the per-peer fan-out.
  4. AudioReceiver / StreamSession: re-key sessions on (CLIENT_ID, streamId) instead of (IPEndPoint, streamId).
  5. Connectivity tab: a "Lobby" section showing the current LobbyRoster.

None of this blocks anything: v1 clients keep working against the new server, so the client work can land in its own release whenever convenient (the design doc suggests v1.5 or v2.0 of the client).


5. Wire format (v1 vs v2)

v1 header — 12 bytes, unchanged:

0   4   MAGIC = 'RMND'
4   1   VERSION = 1
5   1   TYPE       (Format=1, Audio=2, KeepAlive=3, Heartbeat=4, Control=5)
6   2   STREAM_ID  (LE)
8   4   SEQUENCE   (LE)
12      payload...

v2 header — 28 bytes:

0   4   MAGIC = 'RMND'
4   1   VERSION = 2
5   1   TYPE       (1-5 as v1, plus LobbyHello=6, LobbyRoster=7,
                    LobbyFull=8, LobbyBye=9)
6   2   STREAM_ID  (LE)
8   4   SEQUENCE   (LE)
12  16  CLIENT_ID  (UUID, RFC 4122 binary form)
28      payload...

v2 packet types the server originates use a zero CLIENT_ID (00000000-0000-0000-0000-000000000000) so clients can recognise "from server".


6. The bug we hit (for the record)

During release validation, the auto-updater (remsound-relay-update.sh) was found to exit with status 1 after a successful upgrade. Cause: the EXIT trap referenced a variable (work) that had been declared local inside a function; by the time bash fired the EXIT trap (after the function returned) the variable was out of scope, so set -u raised unbound variable and bash exited 1. systemd then marked the one-shot service as failed even though the upgrade had completed correctly.

Fixed in server-v2.2: the working-directory variable was promoted to script scope (WORK_DIR) and the cleanup trap registered at script-global level. The clean upgrade path was re-verified on server-v2.2server-v2.3.


7. Cutting a future server release

# 1. Edit the bundle source. Bump VERSION to the new tag, e.g. "server-v2.4".
# 2. Tar it with the correct internal directory name:
tar -czf /tmp/remsound-server-v2.4.tar.gz \
    --transform 's,^<srcdir>,remsound-server-v2.4,' <srcdir>
# 3. Publish the release:
gh release create server-v2.4 /tmp/remsound-server-v2.4.tar.gz \
    --repo Ednunp/RemSound --title "Server v2.4 — ..." --notes "..."
# 4. Within ~1 hour every running relay's auto-updater picks it up,
#    installs it, restarts, and rolls back automatically if it fails.

The asset must be named remsound-server-*.tar.gz and must contain a single top-level folder. The updater finds the highest server-* tag, so version numbers must keep climbing.


8. Installing on a fresh box (e.g. Andre's Linux server)

The deployable files sit alongside this document. On the target machine:

sudo ./install.sh      # sets up the relay AND the auto-updater
sudo ./smoke-test.sh   # confirms it's alive
# then open UDP 47830 in the firewall / router toward this host

After that the box auto-updates from GitHub — no manual intervention ever again. To pin a version: sudo systemctl disable --now remsound-relay-update.timer. To remove everything: sudo ./uninstall.sh.

Full operational detail is in the bundle's own README.md.


9. Verification done

  • Initial install on the Pi via install.sh — smoke test all green.
  • v1 synthetic packet accepted (logged peer_joined); v2 synthetic packet accepted (logged client_joined).
  • Auto-updater "up to date" path — clean exit.
  • Auto-updater upgrade path — server-v2.0v2.1v2.2v2.3, each step verified, post-fix runs exit 0/SUCCESS, version stamp updates, rollback snapshot in place.
  • The relay survived a whole-house power cut on 15 May 2026 and came back on server-v2.3 automatically.