Bump to v1.6.0: peer-address recovery, reconnect crash, long-run memory/CPU leak

Three reliability fixes. Wire format and audio pipeline unchanged from
v1.4 / v1.5 — all interoperate.

Peer address recovery:
* When a tracked peer goes Unreachable (its resolved address — often a
  stale DNS / Pi-hole record, or a peer that rebooted onto a new IP) but
  the same peer is still heartbeat-pinging us from a different address,
  RemSound now adopts the live address instead of transmitting to a dead
  one. HeartbeatService records untracked ping sources; MainForm's
  TryAdoptLiveHeartbeatAddress (1 Hz) re-points the sender, heartbeat
  tracking and receiver allow-list. Conservative: fires only on the
  unambiguous one-unreachable-and-one-source case, private-range (RFC1918)
  addresses only so a relay can't hijack the sender, 10 s cooldown.

Reconnect crash:
* Fixed IndexOutOfRangeException in MainForm.SyncConnectedList. A churny
  peer-list rebuild (peer reboot) left SelectedIndex pointing past the
  rebuilt item array; the 1 Hz status timer read SelectedItem and crashed
  the app. New SafeSelectedItem bounds-checks the index; applied to all
  three timer-driven sync methods. The status tick is also wrapped in
  try/catch so a transient WinForms hiccup logs instead of crashing.

Long-run memory / CPU leak:
* A receiver left running for hours grew to gigabytes and climbing CPU.
  Decoder sessions orphaned by peer reconnects were not reaped — every
  reconnect mints a fresh (endpoint, streamId) key, and PruneIdleSessions
  silently skipped sessions whose PlayoutEngine lookup missed. Rewrote it
  to reap on each session's own LastWriteUtc (no cross-dictionary lookup),
  added a hard MaxLiveSessions cap as a backstop, and a "stream sessions
  live: N" diagnostic line. Bounds both memory and render-thread CPU.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-05-18 21:32:22 +01:00
co-authored by Claude Opus 4.7
parent 9ceff8bcc1
commit 918ca6cac0
8 changed files with 232 additions and 47 deletions
+9 -21
View File
@@ -1,36 +1,24 @@
# RemSound v1.5
# RemSound v1.6
Menu reorganisation plus two real bug fixes for BothIndependent mode. Wire format and audio pipeline are unchanged from v1.4 v1.4 and v1.5 peers interoperate.
Three reliability fixes — peer-address recovery, a reconnect crash, and a long-run memory/CPU leak. Wire format and audio pipeline are unchanged from v1.4 / v1.5 — all three releases interoperate.
## Bug fixes
- **BothIndependent recording**: when both WASAPI and ASIO output devices were ticked, recordings came out garbled and roughly double the expected duration. The recording taps fired from both lanes independently and the writer thread appended both streams into a single ring as if they were sequential audio. The recorder now has per-lane rings and mixes them in the writer thread before handing the resulting per-direction stream to the file writer.
- **A peer announcing on the WASAPI lane was inaudible when the receiver only had an ASIO output ticked** (and vice versa). The session was opened but its decoded samples sat in the SessionPlayout ring with nothing draining them. Sessions whose announced lane has no active output device now fall through to whichever lane IS being read — so a single ASIO output plays everyone, regardless of how each peer announced their stream.
## Menu reorganisation
- **New Options menu (Alt+O)** holds *Recording settings*, *Keyboard shortcuts*, *Startup behaviour*, and *Preferences*. Pre-v1.5 these were scattered across the File menu (Keyboard shortcuts, Preferences), the Record menu (Recording settings), and a button inside the Preferences dialog itself (Startup behaviour).
- **Record menu mnemonic moves from Alt+O to Alt+K**. Rendered as "Record (Alt+K)" so the chord is visible despite K not being a letter in "Record". Alt+R is taken by the Receive audio checkbox; Alt+O has the natural home on the Options menu now.
- **File menu — new Recent profiles submenu (Alt+F, R)**. Lists the five most-recently-opened profiles, newest first. Press 1..5 while the submenu is open to jump straight to a slot; arrow + Enter also works. Missing files (e.g. external drive unmounted) are skipped from the menu but kept in storage in case they come back.
- **File menu — small mnemonic shuffle** to make room for Recent profiles: *Rename current profile* moves from Alt+M to Alt+M (was R), and *Minimise to tray* moves from M to **N**.
- **Lock to audio clock** (Audio profile tab) was Alt+K; now **Alt+D** (the D from "au**d**io"). Top-level menus win Alt-letter dispatch on a Form, so Record taking Alt+K bumped the checkbox.
## UX additions
- **Ctrl+O** is now bound to Open profile (matches the menu chord). Previously the menu had no global shortcut.
- **New global hotkey: Start / Stop recording.** Pickable from Options → Keyboard shortcuts. Unbound by default so it doesn't collide with anything on a fresh install. Works system-wide — RemSound doesn't need keyboard focus.
- **Peer address recovery.** If the address you connected to stops responding — a peer rebooted onto a new IP, or a computer name resolved (via DNS / a router or Pi-hole record) to a stale address — RemSound now follows the peer to the live address it is still heartbeating from, instead of sending audio to a dead address indefinitely. Recovery is automatic within a few seconds. It only adopts private-network (LAN) addresses, so a relay's public address can never be mistaken for a moved peer.
- **Reconnect crash fixed.** When a peer reconnected — typically after rebooting — the Connectivity tab's peer list could be read mid-rebuild with a stale list index, throwing an `IndexOutOfRangeException` from the 1-second status timer and bringing the app down with a crash dialog. The list reads are now bounds-checked, and the status tick is wrapped so a transient UI hiccup is logged instead of fatal.
- **Long-run memory and CPU leak fixed.** A receiver left running for hours could grow to several gigabytes of memory and climbing CPU. Decoder sessions orphaned by peer reconnects (each reconnect creates a fresh stream identity) were not being reliably reclaimed — they piled up, each holding a multi-megabyte playout buffer and costing render-thread time on every audio callback. Idle sessions are now reaped on their own activity timer, with a hard cap on live sessions as a backstop, so memory and CPU stay bounded however long RemSound runs.
## Install
1. Download `RemSound-v1.5.zip` from this release.
1. Download `RemSound-v1.6.zip` from this release.
2. Extract somewhere with write permission (e.g. `C:\RemSound\`, `Documents\RemSound\`). Avoid `Program Files` unless you grant write permission so the self-updater can replace files in place.
3. Run `RemSound.exe`. Allow on private networks when Windows Firewall prompts.
4. Press F1 (or use the Help menu) for the user manual.
Requires the .NET 10 Desktop Runtime. If it's missing, Windows offers to fetch it on first launch.
## Upgrading from v1.3 / v1.4
## Upgrading
v1.3 / v1.4 / v1.5 users on any install location can use **Help → Check for updates** — the hardened updater pulls v1.6 cleanly.
If you installed RemSound inside a Dropbox-synced (or other file-sync) folder and your install is v1.0 / v1.1 / v1.2, see the [v1.3 release notes](https://github.com/Ednunp/RemSound/releases/tag/v1.3) for one-time manual install steps. From v1.3 onward Check-for-updates handles Dropbox correctly.
v1.3 and v1.4 users on any install location can use **Help → Check for updates** — the hardened updater in v1.3+ pulls v1.5 cleanly.