* New "Opus, live latency" codec mode: 2.5 ms frames (120 samples/ch at 48 kHz)
via the float-input encode path. End-to-end codec delay drops to ~5 ms (vs
~12.5 ms at standard 10 ms Opus). Test on LAN: 400 pps/lane, zero missed /
reordered / duplicate packets, ~15 ms one-way saved end-to-end.
* Wire-format change: AudioFormatInfo.FrameDurationMilliseconds renamed to
FrameSamplesPerChannel (int sample-count at announced sample rate). Removes
the lossy 48000*ms/1000 conversion that couldn't represent 2.5 ms. v3 <-> v3
exact; v3 <-> v2 still passes audio (Opus decoder is self-describing from
packet TOC) but v2 side over-sizes its buffer wildly. v2.x profiles auto-
migrate via <120 sentinel rule in RemSoundSettingsStore (anything below 120
is treated as legacy ms and multiplied by 48). Profile JSON key kept as
OpusFrameMilliseconds via [JsonPropertyName] so old profile files still load.
* Codec dropdown rebuilt with use-case names: "PCM 48K 24 bit - uncompressed",
"Opus, broadcast quality - loss tolerant", "Opus, live latency - for jamming
and monitoring". Middle 10 ms option retired; saved 480-sample profiles
collapse to broadcast quality (safer-side default).
* Profile auto-resume after self-update: RemSoundUpdater writes a one-shot
_resume-after-update.txt sentinel containing the active profile title before
exit; Program.Main reads + deletes it on next start and silently loads that
profile, skipping the picker. Helper batch's robocopy /XF excludes the
sentinel and the failure-branch cleans it up if the install aborts. Falls
through to normal startup behaviour (StartWithProfileTitle or picker) if the
sentinel is missing, empty, or names a profile that no longer exists.
* Read-only profile saves now go through on explicit Ctrl+S / File -> Save
with a one-time TaskDialog warning ("Save anyway" / "Cancel" + Do-not-show-
again). Lock continues to suppress the automatic unsaved-changes prompt on
close / profile switch (its main job). AppConfig.SaveOnReadOnlyMessageSuppressed
renamed to SaveOnReadOnlyWarningSuppressed; v2.x suppression flag is silently
discarded since the behaviour changed and the user needs to see the warning
once on each machine.
* Manual (readme.html) updated: codec table rewritten with the three new
choices and corrected bandwidth figures, send-rate description updated, new
sections "The same profile picks up automatically after an update" and
"Saving on purpose while a profile is locked".
* Subsumes the never-separately-released v2.2 work: native Opus encoder
(~97% less per-second memory churn on Opus send path via Concentus.Native),
efficiency tidy-ups (item 4 ASIO probe rate, item 6 WaitHandle, item 7
snapshot cache, items 14/16 heartbeat + discovery), legacy cleanup
(items 30/34/35/36: KeepAlive infrastructure, drift drop/repeat/accumulator
fields, fan-out cache stat). New diagnostic columns cpu/memMB/wsMB/
allocKBps/captureMs/sendMs/recvMs/renderMs gated on Enable-logs.
About dialog updated with v3.0 block at top; v2.2 block retained for the
subsumed work.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Headline features:
* Automatic router port opening (UPnP / NAT-PMP / PCP). Opt-in via
Preferences; surfaces external address + carrier-grade NAT detection.
* Lock profile (read-only). New File-menu tick that makes a profile
load-only — session changes don't persist, no save prompt on close.
Unblocks unattended shutdowns (NVDA gone, remote dropped, hibernate)
where the existing save prompt could deadlock.
* Check for updates on startup (default on) + brief countdown notice
before silent updates install, so a launch-time update doesn't make
the app silently vanish.
* "Cue sounds" -> "Audio cue sounds" label clarification.
Bug fixes:
* No sound after the computer wakes from sleep. PowerResumeHandler
rebuilds the audio backend automatically on resume; brief
"Reconnecting to audio driver" splash during the rebuild.
* Receiver audio silent after waking from hibernate. RefreshAudioDeviceLists
now treats a transient ASIO probe failure (returns -1/-1 because the
driver is mid-teardown / mid-reinit) as "retry next tick" instead of
clearing the user's tick selection.
Diagnostic-only changes (gated on the existing Enable-logs checkbox,
zero cost when off):
* AudioStepProbe split into cross-buffer vs within-buffer maxes so log
inspection can tell a real-content sharp transient apart from a
pipeline-boundary glitch. Plumbed through every probe owner.
* New rxNetGapMs + gc0/gc1/gc2 delta columns in the diag log to split
receive-side jitter into network-layer vs managed-runtime causes.
Files touched: RELEASE_NOTES.md + readme.html + 24 source files across
RemSound.Core / RemSound.Sender / RemSound.Receiver / RemSound.App.
Three new app files: PowerResumeHandler, RouterPortMapper,
UpdateInstallNoticeDialog.
Wire format and audio pipeline unchanged from v1.5 onward — v1.5
through v2.1 peers interoperate.
Bug fixes:
* BothIndependent recording was double-tapped — when both WASAPI and
ASIO outputs were ticked, recordings came out garbled and ~2x the
expected duration. AudioRecorder now uses four per-lane rings
(sent-wasapi, sent-asio, recv-wasapi, recv-asio) plus a writer-
thread mix step that drains min(wasapi, asio) frames and sum-mixes
with the soft-tanh limiter. Tap signatures gained a RenderRoute
parameter; Mixed maps to the wasapi slot.
* A peer announcing on the WASAPI lane was inaudible when the
receiver only had an ASIO output ticked (and vice versa). The
session opened, samples flowed into the SessionPlayout ring, but
ReadForRoute(AsioLane) skipped any session whose Route was
WasapiLane so nothing drained the ring. PlayoutEngine now tracks
per-lane "is this lane backed by a device" via volatile bools
settable through SetLaneActive(RenderRoute, bool), called from
CompositeRenderBackend.SetOutputDevices whenever the device split
changes. ReadForRoute admits orphan sessions when the other lane
is inactive.
Menu reorganisation:
* New Options menu (Alt+O) holds Recording settings (Alt+S), Keyboard
shortcuts (Alt+K, Ctrl+K), Startup behaviour (Alt+T), Preferences
(Alt+P, Ctrl+P). Pre-v1.5 these were scattered across File menu,
Record menu, and inside the Preferences dialog itself.
* Record menu mnemonic moved 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 Receive audio.
* File menu — new Recent profiles submenu (Alt+F, R) listing the
five most-recently-opened profiles. Press 1..5 inside the submenu
to jump to a slot. Missing files are skipped from the menu but
kept in storage.
* Rename current profile moves to Alt+M (was R), Minimise to tray
moves to Alt+N (was M).
* Lock to audio clock was Alt+K, now Alt+D.
UX additions:
* Ctrl+O = Open profile (matches the menu chord).
* New global hotkey: Start / Stop recording. Pickable from Options
-> Keyboard shortcuts. Unbound by default. Works system-wide.
Wire format and audio pipeline unchanged from v1.4 — v1.4 and v1.5
peers interoperate.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>