69 Commits
Author SHA1 Message Date
EdnunpandClaude Opus 5 1ee510bf58 v5.9: the latency control governs the streams it's supposed to, and arrives in seconds
Confirmed working on Ed's two machines before shipping (his log: slider 500 with 17ms buffered
at 07:10:31 -> 214ms at +3s -> 445 at +9s -> settled ~490 by +15s; auto-tune then walked it down
in 5ms steps with the buffer tracking; a drop to 20ms took effect immediately). Both directions,
auto-tune on and off.

Third fix in this batch, found while answering "is ASIO testing essential before we ship": the
ASIO slider only exists in BothIndependent, but MainForm pushes its persisted value at startup in
EVERY mode (and its auto-tune can tick). Now that all routes resolve to the shared value in
single-slider mode, that write would silently overwrite the visible slider with a hidden control's
number. SetMaxLatencyMs now ignores AsioLane writes when !independentLanes — in single-slider mode
the ASIO box governs nothing. Gate covers it, plus the ASIO half of the wiring (a stream on the
ASIO lane reads the ASIO slider; moving one lane doesn't disturb the other) — provable without an
ASIO device, since the change is about WHICH value a lane reads, not the driver path (untouched).

Docs: manual gains an honest paragraph on changing latency mid-listen (immediate down, a few
seconds up, the slight stretch is the change happening, no gap or click); About + RELEASE_NOTES
for 5.9; MANUAL regenerated. Version 5.9. Gate 74/74 + relay 7.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 07:15:31 +01:00
EdnunpandClaude Fable 5 72ebfb50a8 About box shows only the newest 5 releases (screen-reader crash fix)
The About box's release notes had accumulated the ENTIRE version history back to v1.0 —
~70 KB in a single TextBox — and reading a control value that size crashes some screen
readers (user reports, 2026-08-11). The box now displays only the newest five version
blocks via a pure trim (TrimToLastVersions), ending with a plain pointer to the full
history on the GitHub releases page. The full constant stays in source as the archive.

Gate: new step pins the trim logic on synthetic notes AND the real shipped text (exactly
5 versions, size well under the crashing range — 5,516 chars vs ~70,000) so the crash
can't quietly return as releases accumulate. 73/73. Readme About section updated + MANUAL
regenerated; 5.8 notes mention the change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 09:35:13 +01:00
EdnunpandClaude Fable 5 e0dc2fec94 v5.8: service-folder repair — fix the 5.6 file-wedging ACL bug + record the real user through elevation
Root cause found while REPRODUCING the suspected wrong-owner bug in a gate test (the repro
failed in a way the theory couldn't explain, and the empirical icacls run showed why): the
5.6 hardening's /T sweep applied (OI)(CI) grants to FILES, where such ACEs are inherit-only
— they grant the file itself NOTHING. Every file existing at harden time was left with
/inheritance:r + inherit-only ACEs = an effectively EMPTY ACL: unreadable/unwritable by the
user, admins, even SYSTEM. That is the Jonathan report (2026-08-06) end to end: profile save
"access denied" even elevated, logs unreadable in Notepad, his own icacls /T "fix" adding
useless inherit-only ACEs (137 processed, nothing healed), new files fine (echo test), and
the service failing to start (SYSTEM can't read a wedged profile).

The fix, in layers:
- BuildServiceDirAclArgs no longer sweeps /T: the lockdown applies to the FOLDER only.
  Existing children are rebuilt by a new /reset pass (BuildResetChildrenArgs) as purely-
  inherited from the hardened folder ACL — real file access again, stale/planted explicit
  ACEs removed, and it HEALS files wedged by 5.6. Regression-pinned in the gate (the folder
  args must never contain /T again).
- Second bug fixed in the same area: the elevated helper recorded ITS OWN token as the
  "installing user" — under over-the-shoulder elevation that's the separate admin account
  whose password was typed, not the person at the keyboard. Elevated verbs now carry
  --as-user <SID> from the non-elevated app (validated: real user SIDs only — service
  identities and builtin groups rejected) and install/repair re-record it, so a stale wrong
  owner can't persist through reinstalls or self-update re-hardens.
- Self-heal everywhere: new --repair-service-access verb (re-record owner + re-harden);
  "Repair service folder access" in the Service menu; a startup write-probe that offers the
  repair when the folder is broken (settled startup sequence, ForegroundDialog, skipped on
  --silent); the profile-save UnauthorizedAccessException catch offers it at the exact wall
  users hit; and the service self-update's existing re-harden now runs the FIXED sequence,
  so wedged fleet machines heal automatically when 5.8 rolls out — no user action needed.
- Logs readable again: Users get read-only on service\logs (inheritable, no /T needed —
  propagation covers existing files) and on service-events.log. The profile stays locked
  (it holds the obfuscated password). GrantUsersWriteToBin deleted — the folder ACL's
  inherited user-Modify covers bin, and the reset wiped its explicit grants anyway.
- DoStart/DoStop no longer swallow the reason: exception recorded to service events, and
  distinct exit codes (6 timeout, 7 SCM refused, 9 repair-didn't-stick) let the dialog say
  what happened instead of the bare "(code 1)" that cost this diagnosis a day.

Gate: new "Service folder repair" step reproduces BOTH bugs for real in a scratch folder —
wedges a file with the exact 5.6 spec (proves reads genuinely die), locks the folder to the
wrong owner, asserts the app's probe reports broken, runs the exact shipped repair sequence
(ApplyServiceDirAcl), and asserts folder writable + wedged file readable again. Plus a
SID pass-through step (validation, parse, arg-building, logs-grant shape). 72/72 + relay 7.

Docs: Service-menu repair item + troubleshooting entry in readme; About + RELEASE_NOTES
rewritten for 5.8; MANUAL regenerated. Version 5.8. NOT released — awaiting Ed's test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 09:24:44 +01:00
EdnunpandClaude Fable 5 2cf4948bfb v5.7: back out the enforced stronger passwords (restore cross-port compatibility)
The v5.6 password change went down badly and broke the other ports: raising PBKDF2 to
600k changed the key recipe, so the SAME password produced a DIFFERENT key on 5.6 vs
every other build (the iOS/TestFlight app, older desktops) — same password, no audio.
Ed: back it out so the other ports can use it again; a password is still required, just
suggest a strong one.

Reverted:
- PBKDF2 100k again (RemSoundCrypto) — the key recipe now matches the other ports, so
  the same password derives the same key and audio flows again. Pinned by a gate check
  so an accidental change can't silently re-break cross-port compat.
- ForPlainPassword no longer refuses a weak password: ANY non-empty password derives a
  key (encryption stays mandatory — empty still means no audio). Strength is not enforced.
- Removed every enforcement/nag: the streaming-tick force-strengthen, the startup
  weak-password dialog, the status-line weak warning, the app's weak-SERVICE-password
  launch + live nags, and the service-profile save-time block. The password dialogs just
  suggest a strong password in their hint now; PasswordStrength.cs deleted (now unused).

Someone who set a strong password because of 5.6 feels no difference on update — their
password still loads and works, no prompt (Ed's requirement).

Kept (separate from "stronger passwords", not backed out): signed updates, password-
sealed remote volume, service startup volume, the update time-window, the nonce widening,
and the relay address-proof. NOTE: sealed remote-volume still needs both ends on 5.6+ —
ordinary audio does not. Flagging in case full remote-volume interop with old ports is
wanted too.

Docs: About + readme + release notes rewritten for v5.7 (plain English, positive framing,
no "must update"). MANUAL.md regenerated. Version 5.7. Gate 70/70 (dropped the now-moot
streaming-strengthening step) + 7 relay tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 14:01:12 +01:00
EdnunpandClaude Fable 5 e82268f925 Weak SERVICE password: warn from the app, since the headless service can't ask
Ed's point: the send-only service auto-updates itself to 5.6, and a pre-existing weak
service-profile password then leaves it silently not streaming — the service is
headless (SYSTEM, no window) so it can't prompt, and nobody watches its log. Users
would never know. Closed from the two places a user WILL see:

1. App launch (MaybeWarnWeakServicePassword, in the settled RunStartupNotices
   sequence): if a service is installed and its profile password is set-but-weak, a
   readable, focus-clean dialog warns and offers to open the service settings to fix
   it. Self-resolving — stops once the service password is strengthened. The person
   running the service almost always has the app (that's how it's configured), and
   the app updates around the same time the service does, so this fires right when it
   matters. Decision extracted to the pure ServicePasswordNeedsStrengthening (tested:
   installed+weak warns; not-installed / unset / strong don't).

2. Saving the service profile (ServiceProfileDialog): Save now validates first — a
   weak password pops a warning offering to set a stronger one inline (requireStrong),
   Save-anyway, or Cancel. The save button no longer auto-closes; it validates then
   closes explicitly.

readme: a sentence in the service section explaining the service password follows the
same rule and how RemSound surfaces a weak one. Gate 71/71 + 7 relay tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 12:03:49 +01:00
EdnunpandClaude Fable 5 93bf1d86eb Fix: service startup-volume re-punching on every restart + misleading suspend log
Ed reported the service pulling his volume to 20% repeatedly. Diagnosis from the
service logs: NOT a runaway timer - one service process ran untouched for 36 hours,
proving there's no periodic restart. The volume-to-20 punches came from GENUINE
service restarts (deploys, the one self-update, profile saves, handovers during
active use), each re-applying because the mode was "every service restart". Two real
faults found and fixed:

1. Re-apply burst guard. "Every service restart" (and boot-only as belt-and-braces)
   now skips a re-apply within ReapplyCooldown (5 min) of the last successful apply,
   persisted as startup-volume-last.txt. This kills the double-apply we saw in the
   log at 08:20:48 then 08:21:02 (a self-update restart immediately followed by a
   follow-on start, 14s apart, both punching the volume), and any rapid churn from
   self-update / profile-save / deploy. ShouldApply gains lastApplied+now params;
   future-dated stamp (clock moved back) can't wedge it.

2. Misleading log. ServiceSendHost.Suspend() hard-coded "interactive app present" on
   EVERY suspend, including plain shutdown - which sent this very investigation
   chasing phantom app-handovers. Suspend now takes a reason; the run-loop-ending
   path says so, only a real app yield says "interactive app present".

readme: recommend "first start after boot" as the set-and-forget mode and explain
that "every restart" also fires on routine internal restarts (and is now burst-
guarded). Self-test extended: cooldown skip in both modes, 14s double-apply guard,
future-stamp safety. Gate 71/71 + 7 relay tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 10:36:54 +01:00
EdnunpandClaude Fable 5 6c53fe54d1 v5.6 batch: signed releases + stronger passwords enforced + relay address-proof
The everyone-must-update release. Four coordinated changes, each from the security
discussion Ed approved 2026-07-27, plus the remembered-apps polish:

1. SIGNED RELEASES. build-release.ps1 now signs the release zip (ECDSA P-256 /
   SHA-256, --sign-update verb) with a private key that lives ONLY at Ed's chosen
   location outside the repo; the matching public key is embedded (UpdateSignature)
   and the updater REFUSES any release whose .sig asset is missing or does not
   verify - a compromised GitHub account can no longer ship code to users. The
   signing verb self-checks against the embedded key so a key/embed mismatch fails
   the pipeline, and the gate proves the on-disk key matches the embed when present.

2. STRONGER PASSWORDS, ENFORCED (BREAKING). PBKDF2 raised 100k -> 600k (both peers
   must derive the same key, so 5.6 cannot stream with pre-5.6 AT ALL - release
   notes lead with it). New PasswordStrength rule (>= 8 chars, not an infamous
   password) enforced at EVERY door: both password dialogs block weak NEW entries
   with concrete plain-English advice; the streaming gate walks an existing weak
   password through strengthening; and ForPlainPassword - the single derivation
   choke-point shared with the service - refuses weak outright, so no path streams
   on a guessable password. Headless service logs the why. Per Ed: painful once,
   and this coordinated-update release is the cheapest moment it will ever have.

3. RELAY ADDRESS-PROOF (watch-only). The relay sends every new client address a
   random cookie and marks it verified when echoed - a forged source address can
   never echo, killing the reflection attack. 5.6 clients echo automatically
   (AddrCheck type 10, verbatim, self-limiting); the relay ships watch-only
   (logs would-blocks) until the fleet updates, then one flag (--require-addr-check)
   enforces. Per-IP entry cap (4) enforced immediately. Relay changes are committed
   but NOT deployed to the Pi - they ride the v5.6 release moment.

4. Remembered-apps empty state teaches its lifecycle + manual sentence; About/
   release notes written; version bumped to 5.6.

New gate steps: signing round-trip/tamper/wrong-key/embed-match; password rules incl.
the exact "Games" case; AddrCheck verbatim echo. Gate 69/69.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 08:19:20 +01:00
EdnunpandClaude Fable 5 9574d9d08b Update install window: automatic updates only within a chosen daily time range
Feature (requested 2026-07-26): Preferences' update settings gain 'Only install
updates within this time range' - a checkbox plus Start/End time lists in 15-minute
steps (00:00-23:45, default 01:00-06:00). RemSound streams live audio, so an
automatic update mid-session kills someone's sound; with the range on, the startup
check and the background poll DEFER an available update until the range opens (a
one-shot timer retries right at the range start, so a 24-hourly poll can't keep
missing the window for days). Manual 'Check for updates now' is deliberately never
gated - asking by hand means now. End at-or-before start wraps past midnight
(22:00-06:00); start minute inclusive, end exclusive; an equal start/end means
unrestricted rather than a silent never-install trap. The service inherits the
window for free: its self-update follows the app's install.

readme: the two new Preferences rows, the service startup-volume option, and a
security note on password-locked remote volume (both ends need 5.6).

New gate step pins the window maths (same-day, wraparound, boundaries, empty-range
rule, retry arithmetic). Dialog audit covers the new controls. Gate 66/66.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 23:52:21 +01:00
EdnunpandClaude Fable 5 b010525bce Review bug batch: Delete key on remembered apps (#26), phantom service cues, suppress-flag guards
- Remembered applications list now supports the Delete key (issue #26): deleting an
  entry forgets it machine-wide, mirroring the remembered-peers list exactly (same
  focus-next-item behaviour). Logic lives in RemoveRememberedApplication so the
  self-test drives the real path; the send-app semantics test now covers deletion.
  Manual documents Delete on BOTH remembered lists (neither was documented).
- Service dialog's Additional options no longer offers connect/disconnect cue
  checkboxes: the headless service never plays cues (nothing in it touches CuePlayer,
  and a logged-out session couldn't render them), so the switches did nothing.
  Manual sentence updated to match.
- Three re-entrancy suppress flags (send-mode reset x2, streaming password gate) now
  clear in try/finally - a throw can no longer leave list events suppressed for the
  rest of the session.

Gate 47/47.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:47:38 +01:00
EdnunpandClaude Opus 4.8 0102aa51f4 Service: offer to start it right after installing (uninstall already stops first)
- After a successful install, both entry points now ask "start it now?" (Yes/No):
  the app's Service menu (RunServiceVerbAsync reused for the follow-up start) and the
  app installer's optional service step. Without this the service only comes up at the
  next boot, so a first-time user saw nothing happen after installing it.
- Uninstall already stops the service before deleting (DoUninstall -> DoStop, which
  waits up to 15s for Stopped), so nothing to change there - confirmed, not added.
- Manual: note the start-now prompt in the service section.

No new automated test: these are UI Yes/No prompts layered on the already-tested
start/stop verbs. Gate 46/46.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:02:04 +01:00
EdnunpandClaude Opus 4.8 5fc1b5bd75 "Use Windows default" is now exclusive: it clears + locks out the specific cards
Ed: ticking "Use Windows default audio device" should uncheck the other cards and stop
them being ticked again until the default is turned back off - in both the app and the
service. This replaces the old optional "shall I untick the others?" prompt with a hard
rule.

Behaviour (all follower lists: app received-outputs, outputs-to-send and inputs-to-send;
service outputs):
- Ticking the follower unticks every specific card in that list.
- While the follower is on, trying to tick a specific card is refused (the ItemCheck is
  vetoed straight back to unticked) - you're either following the Windows default or
  picking cards, never both.
- Unticking the follower frees the cards again.
- Enforced on load too, so a hand-edited/legacy profile carrying both comes up clean.

Shared, not duplicated: the veto + clear + follower-checked helpers live in
AudioDefaultFollower and are used by MainForm and the service dialog alike.

Removed the now-obsolete soft prompt and everything that hung off it:
- MainForm.MaybeUntickOthersForDefault + ResetDefaultAudioDevicePrompt.
- The "Reset the default audio device prompt" Options item (and its Alt+O,R).
- AppConfig.UntickOthersWhenUsingDefaultOutput/Input.
- Manual + About-box references to the prompt / reset item; the manual now documents
  the exclusive rule and lists all three follower lists (the outputs-to-send follower
  had been undocumented).

Test: "Default follower exclusivity" self-test - follower on clears + locks specific
cards, follower off frees them, unticking and the follower entry itself are never
vetoed. Gate 45/45.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 23:26:38 +01:00
EdnunpandClaude Opus 4.8 4a86a82ec8 Service: offer "Use Windows default output" (Christopher's request), reusing the app's follower
The service profile could only pick named output devices, so it couldn't "just send
whatever this machine plays and follow the Windows default". The main app already had
exactly that (its DefaultLoopbackSendFollower + ResolveDefaultDeviceId) - so rather
than invent a parallel mechanism (which would silently diverge), this pulls the shared
piece out and has the service reuse it.

- New AudioDefaultFollower: one home for the loopback-send sentinel
  ("__use-default-loopback-send__"), the follower list entry, and the default-endpoint
  resolver. MainForm now references it (its DefaultLoopbackSendFollower and
  ResolveDefaultDeviceId delegate to it) so there is a single definition.
- Service config dialog: the Audio send tab lists "Use Windows default audio device,
  follows Windows changes" as the first output choice. Ticking it persists the same
  sentinel the app uses.
- ServiceSendHost.BuildSendSpecs resolves that sentinel to the LIVE default render
  endpoint (with de-dup against explicitly-ticked devices), never passing the raw
  sentinel through. Because the service re-applies its profile on every device-change
  notification - and OnDefaultDeviceChanged is one of them - it FOLLOWS the default:
  change Windows' default output and the service switches to it within a beat.
- Manual: documents the new option in the service section.

Test: new "Default-output follower" self-test - follower is flagged + shares the app's
sentinel, and the service resolves it to the current Windows default render endpoint
(never leaks the raw sentinel into a capture spec). Gate 44/44.

The ASIO "Rea" devices Ed noticed are real registry drivers (Realtek ASIO + REAPER's
ReaRoute), not injected dummies - we only ever list HKLM\SOFTWARE\ASIO. No code change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 23:08:55 +01:00
EdnunpandClaude Opus 4.8 d002130402 Cleanup + security narrowing + manual updates (post-review)
Security (narrow the no-admin grants to one account):
- The service's no-admin start/stop and bin-write grants went to Authenticated Users /
  BUILTIN\Users - together that was a one-step local privilege escalation for ANY
  account (overwrite the SYSTEM-run binary, then stop/start it). Now both grants go to
  the INSTALLING user's SID only (the elevated install runs as that interactive user).
  Same effortless workflow for that user; the any-account escalation surface is gone.
  AddUserStartStopAce takes the SID; self-test asserts it's scoped, not AU.

Dead-code removal:
- --probe-apploopback diagnostic verb + ProbeAppLoopback.cs + the ProcessLoopbackCapture
  .Diagnostic hook (all scaffolding for the now-fixed activation bug).
- --update-service verb + ServiceControl.DoUpdate (the "Update service" menu item is
  gone; auto-update via ServiceUpdate.RestartSelf replaced it). Verb gate now lists five.

Manual (readme.html):
- New "Sending specific applications" section (the How-to-send chooser + the two app
  lists) and the Alt+6/8/9 shortcuts - the whole per-app feature was undocumented.
- Documented the two "Clear remembered ... list" buttons on Preferences > General.

Gate: 42/42.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:37:13 +01:00
EdnunpandClaude Opus 4.8 984bcd042e Review sweep: fix real bugs found across the service + per-app + settings changes
Parallel code review of this session's changes surfaced several real bugs; fixed the
substantive ones (judgment/cleanup calls held for Ed):

- HIGH Clearing "remembered peers" was resurrected on the next launch: the per-profile
  -> global migration re-ran every startup and re-unioned the profile file's stale copy.
  Added a one-time AppConfig.RememberedPeersMigrated marker so migration runs once and a
  cleared list stays cleared. Self-test pins the clear-then-reload scenario.
- MED PushModeWasapiBackend.Start rethrew on a device-open failure; nothing up the stack
  wraps it, so device churn (a push-eligible single WASAPI source unplugged mid-open)
  could crash the app. Now logs and stays stopped like MixingEngine/ASIO; the device
  watcher / self-heal re-open when a device returns.
- MED Service self-heal: (a) the re-open "no send sources" path left PerformanceMode ON
  and presence up while streaming nothing - now releases cleanly; (b) the 3-attempt
  ladder never refunded, so 3 hiccups over a days-long stint meant permanent silence -
  now refunds when real audio is heard, and resets on a device hot-plug.
- MED ApplyProfile resolved peers (DNS) and enumerated devices INSIDE the gate lock -
  a boot-time DNS hang as SYSTEM stalled Suspend()/yield/self-heal. Moved outside the lock.
- LOW AudioSessionStartWatcher leaked the AudioSessionManager on every Rehook (the WASAPI
  handle-leak fingerprint) - now disposed. New lifecycle self-test.
- LOW stale docstrings (send-all master toggle; ServiceUpdate in-place scheme; Profile
  .SendAllApplications "neither reads nor writes").

Gate: 42/42 (added peers-migration + session-watcher-lifecycle tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:49:01 +01:00
EdnunpandClaude Opus 4.8 6e84f491e8 Service: manual section + menu-list sweep
Local checkpoint - NOT for public release.

- New manual section "24. The lock-screen service (send only)": what it is, the
  send-only/WASAPI-only limits, the yield-to-the-app behaviour, the Service menu
  (configure/install/start/stop), and the good-to-know notes (direct peers, firewall,
  updates picked up on next restart, install both ends for two-way).
- Swept the two stale "Menus (File, Record, Options, Help)" references to include
  Service; added a Service-menu pointer at the end of the Menus section.
- Updater needs NO change: UpdateApplier renames the old exe aside (survives the
  running service's lock) then copies the new one in, so the service keeps running the
  previous version until its next restart and never blocks a swap. Documented.

MANUAL.md regenerates from readme.html at release time (sync-manual.py), so untouched.
Gate 20/20.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 15:53:54 +01:00
EdnunpandClaude Opus 4.8 7038fef67c v5.1: install RemSound as a proper Windows app, plus smaller sounds
New Options -> Install / Uninstall RemSound on this PC: a per-user self-installer
(%LOCALAPPDATA%\Programs\RemSound, no admin) with optional desktop + Start-menu
shortcuts, login auto-start (reuses StartupAutoStart), Windows Installed-apps
registration, and copy-across of profiles+config, recordings and logs. Install
state is decided by a marker file, not a folder-path guess; the post-install
relaunch hands over foreground via AllowSetForegroundWindow so the installed copy
comes to the front; uninstall uses a batch remover (no PowerShell) and confirms
with two independent tick-boxes. All new dialogs use the house accessible controls
(AccessibleCheckBox, Theme.Heading).

Also: iOS (TestFlight) companion link alongside Android in README + manual;
slimmed-down default cue WAVs; About/RELEASE_NOTES/manual updated; version -> 5.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 15:45:21 +01:00
EdnunpandClaude Opus 4.8 a92928d357 Docs: add the iOS (TestFlight beta) companion app alongside the Android one
Jonathan Schuster's iOS client (iPhone/iPad) is in TestFlight beta — linked in the GitHub README and
the bundled manual next to the existing Android app, framed the same way (separate community project,
speaks the same protocol, not maintained by us).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 11:18:02 +01:00
EdnunpandClaude Opus 4.8 849a0b4c73 v5 manual: deep audit sweep, About-box changelog, version bump
Full manual audit before release: fixed the General→Appearance toggle location (3 places), the
Connectivity section-6 tab order (Add-by-IP after Remembered), a new Appearance subsection (theme /
tab order / list toggles) resolving the #appearance links, standardised the recording-source names
and default, added the split-recording friendly-name precedence, the two recording tickboxes to the
Options-menu summary, Alt-keys on the pan/EQ table, Alt+L and Alt+O,N to the shortcut tables, fixed
the glossary (hotkeys not per-profile; remote-control cross-ref) and the section-4 tab-count wording.
About-box v5.0 changelog and version 5.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 18:22:59 +01:00
EdnunpandClaude Opus 4.8 fcc3cdc793 Ctrl+1..9 to jump to a tab by its current position (main window + Preferences)
Ctrl and a number selects the Nth tab as it currently appears — positions are live, so they follow
the user's tab reordering and the pan/EQ tab's show/hide. Handled in ProcessCmdKey on both the main
window and the Preferences dialog; focuses the tab strip afterwards so NVDA reads the new tab (like
Ctrl+Tab). Matches Andre's readout app. Manual updated.

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-08 14:31:22 +01:00
EdnunpandClaude Opus 4.8 597b002f01 Clearer wording for the tab-order list: "Tab order, press the move up and move down buttons to reorder"
Renamed from "Main tab order" and reworded the accessible name so NVDA reads a clear instruction
instead of the confusing "main tab order, then move up / move down".

Held for next release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 13:31:47 +01:00
EdnunpandClaude Opus 4.8 78684f29f1 New Preferences "Appearance" tab: theme + show-pan/EQ moved in, tab reordering, peer-list toggles
Adds an Appearance tab (after General) and moves the colour theme and "show volume/pan/EQ tab" into
it, per Ed. New on that tab:
 * Main tab order — a list of the four main-window tabs (all shown, even when the pan/EQ tab is
   hidden) with Move up / Move down buttons to reorder them. Saved to AppConfig.MainTabOrder and
   applied by the new ApplyMainTabLayout (rebuilds the tab strip in order, dropping pan/EQ when off,
   preserving selection). Replaces RefreshPanEqTabVisibility.
 * Enable discovered / remembered peers lists on the Connectivity tab (AppConfig.ShowDiscoveredPeers
   / ShowRememberedPeers, both default on). RefreshConnectivityListVisibility hides the row's label
   and its list wrapper when off. Row labels are now captured for this.

All apply when Preferences closes. Manual updated. 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-08 12:56:06 +01:00
EdnunpandClaude Opus 4.8 e35f2f318a Testing-feedback fixes: tab order, label wording, parametric editing, recording default
* Connectivity tab order: Add peer by IP now sits right after Rename peer (before the Discovered
   list), grouped with the connected-peer actions, per Ed's requested order. Dropped the second
   section header (only the lock toggle was under it).
 * Audio I/O labels: "Set volume for all received audio" → "Master volume for received audio" (the
   code never got this rename, only the manual had). Device lists now say "audio": "...for received
   audio" and "WASAPI/ASIO audio inputs/outputs to send". Labels + AccessibleNames updated together.
 * Add EQ band dialog: the spin/edit boxes now select-all on focus, so a typed value REPLACES what's
   there instead of being inserted next to it and reverting (typing 2.5 over 4.0 now works).
 * Parametric bands list: Left/Right arrow nudge the selected band's gain by half a dB, live —
   up/down still move between bands. NVDA re-reads the band's new dB.
 * Recording settings: source list reordered to Both (top, now the default) / Received / Sent;
   display order decoupled from the RecordingSource enum. Default RecordingSettings.Source = Both.
 * Preferences: Colour theme is now first in the General tab order.

Manual updated. 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-08 11:36:02 +01:00
EdnunpandClaude Opus 4.8 3c449d78b3 Named-peers registry + Options → Manage named peers dialog
Turns the flat friendly-name map into a proper machine-wide book (AppConfig.NamedPeers), and adds a
management dialog. Held for next release.

 * New NamedPeer record (machine name, friendly name, last address, last-seen UTC). AppConfig gains
   NamedPeers; the legacy PeerFriendlyNames map is migrated into it once on load, then no longer
   written. The book stays machine-wide and profile-independent (it always was — this just enriches it).
 * Only deliberately-renamed peers are recorded (per Ed) so the list can't balloon. A named peer's
   last address / last-seen are updated in memory each tick while connected; persisted on address
   change and on app close (timestamp-only changes don't thrash the disk).
 * Options → Manage named peers (Alt+O, N): lists each named peer as "friendly — machine — last seen
   date, address"; Rename (Alt+R / F2 / double-click) reuses RenamePeerDialog; Delete (Alt+D / Del)
   forgets the name. Edits refresh the connected/discovered lists, the volume/pan/EQ list and details
   box live.
 * ApplyFriendlyName now records machine name + last address alongside the name.

Manual updated (readme.html + MANUAL.md). Build clean; --selftest passes; deployed to both test folders.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:20:31 +01:00
EdnunpandClaude Opus 4.8 289bb2b15c F2 renames the highlighted connected peer (Windows Explorer idiom); manual note
Held for next release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:08:47 +01:00
EdnunpandClaude Opus 4.8 f35f6c3c28 Peer rename + details box on Connectivity tab; Add band dialog fixes
Add band dialog (from testing feedback):
 * Tab order is now Start, End, Gain, OK, Cancel (OK was after Cancel).
 * Gain box takes decimals like 1.5 (half-dB steps); parametric list shows one decimal.
 * The two frequency boxes start empty — nothing prepopulated to mislead; both required on OK.

New Connectivity-tab feature (held for next release):
 * Rename peer (Alt+M) opens a dialog to give a peer a friendly name, with a Clear custom name
   button. Names are keyed by the peer's MACHINE NAME (stable across restarts, IP changes and
   networks), stored machine-wide in AppConfig.PeerFriendlyNames, and resolved everywhere a peer
   shows: both peer lists (via PeerListItem.DisplayNameProvider), the volume/pan/EQ list, the
   status line and split-recording filenames. Manual-by-IP peers with no announced name fall back
   to keying by address.
 * Peer details (Alt+E): a read-only box for the highlighted connected peer showing name, machine
   name, IP, connected-for, link health + ping, what they're sending, and whether they're
   receiving our audio. "Sending: 2 devices on ASIO at 48 kHz, Opus" is derived from the live
   receive streams — each stream is one device and its lane gives WASAPI vs ASIO — so NO protocol
   change and no new privacy exposure. AudioReceiver.ActiveFormatsFromAddress added for this.

Manual updated (readme.html + 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:56:42 +01:00
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
EdnunpandClaude Opus 4.8 eaae76d015 Manual: document Pan and EQ, multi-track/raw recording, new naming, and the tab
Full sweep of readme.html (regenerated MANUAL.md) for the held batch:
 * New section 9 "Pan and EQ tab" — per-peer volume/pan/EQ, the two master switches, the peer
   picker, 3-band and 12-band (31/63/80/125/250/500 Hz, 1/2/4/6/8/16 kHz) EQ, reset button,
   real-time, saved per profile; shown via the "Show the Pan and EQ tab" Preferences checkbox.
   Sections 9-24 renumbered to 10-25; TOC entry added.
 * Recording section: the two new tickboxes (split into per-peer tracks; bypass pan/EQ for raw),
   and the new date-folder / "<time> RemSound recording <machine>" / multi-track-folder naming.
 * Section 4 tab list, keyboard-shortcuts (Pan and EQ tab: Alt+E/P/U/L/N/Q/M), and a Preferences
   mention. Replaced a stale old-format recording-filename example.

Held for next release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 20:43:23 +01:00
EdnunpandClaude Opus 4.8 c7d422e3ba Add per-profile "Lock to exact peer addresses" setting (#17, held for next release)
Feature request from the same singer: a way to pin a profile to exact peer addresses
so RemSound never matches the other computer by its advertised name and never switches
to a different address it discovers — for a machine reachable at two addresses at once
(VPN + LAN), they want only the one they chose, and would rather the connection die than
wander. The logical end of the v4.7/v4.8 direction.

New per-profile Profile.LockPeerAddresses (default false), round-tripped through
RemSoundSettingsStore (ApplyProfile/CopyTo + Load/SaveLockPeerAddresses), mirroring the
PriorityMode pattern. New AccessibleCheckBox on the Connectivity tab ("Lock to these exact
peer addresses, no matter what — never follow names or switch", Alt+L), saved with the
profile, marks the profile dirty on change. When set, RefreshKnownPeers early-returns
before the discovered-peer merge and the address-follow, so the profile's peers stay
exactly as set (the allow-list is still pushed). Off = unchanged behaviour.

Manual: rewrote the "Connecting to one specific IP" section (which over-promised that
add-by-IP "can never drift" — the merge/follow could) into an unambiguous "Locking a
profile to one exact address" section, plus a Connectivity-tab control-table row.

Held for the next release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 15:31:50 +01:00
EdnunpandClaude Opus 4.8 0df2a576fc Release v4.8: fix rare crash from endpoint thrash on a dual-address peer (#16)
The same singer hit a rare crash: their transmitter (COMP3) was reachable at two
addresses at once — the VPN address 10.8.0.1 they chose and that machine's wireless
192.168.3.245 — and the discovery-driven endpoint-follow ping-ponged the connection
between the two (the log shows four moves in 58 ms) right where the process died with
no shutdown line, no managed exception, no dialog: a hard crash from the receiver
audio-session teardown/rebuild churn the thrash caused.

Fix: the follow loop now never moves off an endpoint that's still answering heartbeats,
only follows once the current one has been unreachable for a sustained grace period
(6 s), only to an address that is itself answering, and never more than once per cooldown
(15 s) — so it can't thrash, and a peer reached on a working address stays put (honours
the singer's "just stay on 10.8.0.1"). New endpointUnreachableSinceUtc + lastEndpointMoveUtc
state; genuine DHCP/network moves are still followed a few seconds later.

Also: a global crash handler (Program.WriteCrashReport on AppDomain.UnhandledException +
TaskScheduler.UnobservedTaskException) writes a crash-*.txt into the logs folder, so a
future "RemSound just vanished" report leaves a stack behind. Removed a stale doc comment
left over from the v4.7 adoption removal. Manual gains a "RemSound closed unexpectedly"
troubleshooting entry. Version 4.8; About + RELEASE_NOTES updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 22:57:49 +01:00
EdnunpandClaude Opus 4.8 64d5cb5a86 Release v4.7: keep trying your configured peer after a reboot (#15)
Removes the heartbeat "adopt a live address" feature (added v1.6). On a LAN with
more than one RemSound machine it could latch a receiver onto an unrelated sender
that happened to be pinging the audio port — then never recover to the real peer,
needing a manual restart (the singer's #15, with log). The feature guessed peer
identity from an untracked ping source with no way to verify it was the same peer;
on the stable VPN/LAN addresses RemSound is actually used with, it only ever caused
harm, since same-address reconnect already works via the continuous heartbeat.
Removed TryAdoptLiveHeartbeatAddress + IsPrivateLanAddress (MainForm) and
GetUntrackedPingSources + recentPingSources (HeartbeatService); the identity-safe
discovery-based following (by verified peer ID) stays. Manual troubleshooting entry
rewritten to match.

Also bundles the held changes since v4.6: status reads line-by-line with GB totals
and double-press-to-copy, CPU/memory in the status, the Install Scripts folder, and
the what's-new-after-failed-update fix. Version 4.7; About + RELEASE_NOTES updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 22:41:43 +01:00
EdnunpandClaude Opus 4.8 843be9b7fb Speak-status: drop the double-press-copy Preferences toggle
Andre confirmed (via NVDA speech history) that holding the speak-status hotkey
doesn't repeat or spam — so the defensive toggle that let users disable the
double-press-to-copy was needless configurability. Removed the Preferences
checkbox and the DoublePressStatusToCopy setting; double-press-to-copy (Andre's
own idea) stays, now simply always on. There was never any debounce/anti-spam
code to remove — the only timing is the 600 ms double-tap detection window, which
is the feature itself. Manual updated to drop the toggle line.

Held for the next release (the toggle never shipped).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:55:03 +01:00
EdnunpandClaude Opus 4.8 6c36369806 Docs: correct RemSound's origin story (true remote-audio-monitoring use)
The manual intro and the repo README both carried an invented origin (a guitarist
and singer playing together, "built by a sound designer"). RemSound was actually
built to hear the audio from a powerful computer while working remotely from a
lighter one — other programs can move audio between PCs, but none did it quite the
way Ed wanted. Reworded the manual opening (readme.html) and the README audience
and "Who made this" lines to tell one true, consistent story in Ed's voice. The
music-together and podcast uses stay as the genuine secondary use cases they are.

readme.html ships to users on the next release; README/MANUAL update on push.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 16:04:31 +01:00
EdnunpandClaude Opus 4.8 771a358a87 Status box: show RemSound's own CPU and memory usage (held for next release)
New last status line "CPU usage 2% and memory 184 MB" — CPU as a share of the whole machine
(Task Manager style: process CPU-time delta / wall-clock / logical-core count), memory as the
working set via the same MB-to-GB formatter the totals use. Sampled on the existing once-a-second
status tick with a cached Process handle. Shows on screen and reads/copies with the rest of the
status (Ed's idea).

Held for the next release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 15:22:40 +01:00
EdnunpandClaude Opus 4.8 be32a060e7 Add "double-press to copy status" toggle + document the spoken-status changes (held for next release)
Preferences General-tab checkbox "Double-press the speak-status hotkey to copy the status to the
clipboard" (Alt+C), machine-wide (AppConfig.DoublePressStatusToCopy), on by default. When off, a
double press just reads the status again. SpeakStatusLine reads the flag only when the timing already
qualifies, so a normal single press never touches the config. Manual's speak-status section now
covers the line-by-line read, GB totals, and the double-press copy + its toggle.

Held for the next release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 14:47:02 +01:00
EdnunpandClaude Opus 4.8 f9b3ed9e67 Add Install Scripts (winget .NET 10 install) for the no-runtime case (held for next release)
RemSound is a .NET 10 app, so it can't install its own runtime — it can't start without it. A
plain .cmd/.ps1 (which run on what's already in Windows) sidesteps that: double-click
"Install Scripts\Install .NET for RemSound.cmd" and it winget-installs the .NET 10 Desktop Runtime
(Microsoft.DotNet.DesktopRuntime.10), falling back to opening Microsoft's download page if winget
is absent. Modelled on Andre Louis's accessible-sensor-readout Install_Scripts (his install .NET
Framework 4.8 because SR is Framework; ours installs the .NET 10 Desktop Runtime RemSound needs).

Bundled into the build + release zip via csproj Content + EnsureInstallScriptsPublished target.
Manual Quick-start note points users here if RemSound won't start. Held for the next release.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 14:24:20 +01:00
EdnunpandClaude Opus 4.8 36431d6d39 Release v4.6: follow the Windows default audio device (output + input)
- New "Use Windows default audio device, follows Windows changes" entry at the top of the
  received-output and send-input WASAPI lists. Resolves the current Windows default live,
  re-routes automatically when the default device changes, works alongside specific devices,
  and persists across launches (a follower can't go stale). Optional "untick the others?"
  prompt with a remembered "don't ask again", reset via a new Options item "Reset the default
  audio device prompt".
- Manual updated for the feature, plus a sweep that corrected the now-stale Options-menu
  section (retired Startup-behaviour item, four->five Preferences tabs, missing entries).
  About changelog and RELEASE_NOTES updated; version 4.6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 11:41:58 +01:00
EdnunpandClaude Opus 4.8 962f35ab80 Release v4.5: import shortcuts on pre-4.4 upgrade + clearer shortcuts dialog
- Pre-v4.4 upgraders are offered a one-time dialog to copy their keyboard shortcuts
  from one of their profiles (still readable in the profile files) instead of being
  reset. Users who already went through v4.4's reset are deliberately NOT re-offered
  (gated on KeyboardShortcutsGlobalNoticeShown). New KeyboardShortcutImportDialog +
  AppConfig.KeyboardShortcutsImportOffered + MainFormHotkeyController.ReloadAndReRegisterAll.
- Keyboard shortcuts dialog: new "Clear this shortcut" button; Delete inside the
  capture box leaves a shortcut unassigned.
- Relabelled the three RemSound-app remote rows to "Send remote RemSound volume/..."
  to distinguish them from the Windows-global ones.
- Manual (readme.html + MANUAL.md), About changelog, RELEASE_NOTES updated; version 4.5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 06:36:00 +01:00
EdnunpandClaude Opus 4.8 8a1bc0c813 Release v4.4: keyboard shortcuts are now machine-wide, not per-profile
Fixes issue #14: shortcuts were stored on each Profile, so one set on profile A
didn't apply on profile B and seemed to vanish on switch. They now live in
AppConfig (one set shared by every profile). RemSoundSettingsStore's Load*/Save*
hotkey methods re-point to AppConfig (callers unchanged); the per-profile cache
fields, profile load/save plumbing, and the HotkeySetting helper class are removed.
Profile's HotkeyRecord fields stay only for back-compat deserialization.

On upgrade, shortcuts reset to defaults (there's no single correct set to carry over
since profiles could hold different/partial sets). A one-time startup notice tells
upgraders to re-set them; fresh installs are silently marked done (nothing to reset).

Manual, About changelog and RELEASE_NOTES updated; csproj <Version> 4.4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 23:11:53 +01:00
EdnunpandClaude Opus 4.8 fd5c31740a Release v4.3: speak status line (Tolk) + Logging tab with housekeeping
- New screen-reader hotkey "Speak the RemSound status information" (issue #13):
  reads the status line aloud through the active screen reader via Tolk, fires from
  anywhere (system-wide), unset by default. Built behind an IScreenReaderOutput seam
  so a future build can swap Tolk for Prism on Windows 10+ without touching callers.
  Tolk DLLs vendored under tolk/ and shipped next to the exe.
- New Logging tab in Preferences: Enable logs + Write logs now moved there, plus
  opt-in startup "warn if logs folder exceeds N MB" and "delete logs older than N days",
  and a "Delete all logs" button (Yes/No confirm). New LogMaintenance helper + AppConfig
  settings drive it.
- Manual (readme.html + regenerated MANUAL.md), About changelog and RELEASE_NOTES
  updated in plain English; csproj <Version> bumped to 4.3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 13:09:06 +01:00
EdnunpandClaude Opus 4.8 a408d2b56e v4.0: full audio-cue system, cause-aware auto-tune, four-tab Preferences, install-side default sounds
Audio cues
- Cues for send/receive on-off, minimise/restore, checkbox tick/untick, and tab switch
- Soft keyboard clicks while typing, with a distinct passkey sound on password fields
- Per-cue "Choose sound" variant picker; "(none)" silences a cue; front-most missing-sound warning
- Send/receive cues take priority over the generic checkbox sound; programmatic ticks stay silent

Preferences
- Redesigned into four tabs (General, Audio cues, Startup behaviour, Update settings)
- Startup behaviour moved in from the Options menu
- NVDA now announces the dialog on open (focus a real named control, not the quiet tab control)

Auto-tune
- Cause-aware: tells device render-callback stalls (more buffer can't fix) apart from genuine
  network/buffer starvation, so it no longer pins latency high on chunky onboard cards
- Lowering the target eases the buffer down (glide) instead of trimming it, so no clicks while tuning

Sounds layout
- Shipped defaults moved out of the per-user folder into an install-side "default sounds" folder,
  so updates can refresh them; user customs are Browse-picked file paths and are left untouched
- Startup migration removes both legacy sound folders; verified from oldest (v1.0-v3.3) and v3.4 layouts

Quiet automated launches
- New --silent launch flag mutes all cue sounds and suppresses the startup dialogs (migration notice,
  update check, Realtek/mic/missing-sound warnings) so test launches never disturb the user
- run-tests / build-release / SelfTest repointed to the new "default sounds" layout

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 11:57:19 +01:00
EdnunpandClaude Opus 4.8 2eaf7da184 Manual: document the Preferences tabs, the cue "(none)" model, and the missing-file warning
Audio cues section rewritten for the redesign: the cue list is a plain list you arrow through to
hear each cue, the "Choose sound" list's "(none)" entry turns a cue off (no more tickboxes), and
a new note covers the front-most "couldn't find the sound file" warning + auto-disable. Startup
behaviour section and the Menus -> Preferences entry updated for the four-tab Preferences dialog
(General / Audio cues / Startup behaviour / Update settings) and the retired Options-menu item.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 06:47:57 +01:00
EdnunpandClaude Opus 4.8 aed555cc2e Reset-ASIO button + checkbox tick/untick sounds (parts 1-2 of the cue/preferences overhaul)
Part 1 - "Uncheck all inputs and outputs" button now also resets the ASIO driver to "(none)":
renamed to say so, and UncheckAllDevices sets asioDriverBox to row 0 for a full clean
WASAPI-only, nothing-selected state.

Part 2 - checkbox tick/untick sounds: every checkbox toggle anywhere in RemSound now plays a
short cue (check.wav on tick, uncheck.wav on untick) - instant feedback on which way a box
went, especially in the inputs/outputs lists. New CheckSoundService + two machine-wide cues
(CheckboxOn/Off) with the usual numbered-variant + Preferences treatment. Hooked from
AccessibleCheckBox.OnCheckedChanged and the device lists' WireCheckedListAccessibility, both
gated on the control being Focused so a genuine user toggle clicks but bulk programmatic
(un)checking (profile load, "uncheck all") stays silent. Reloaded at startup and on cue change.

Tests + manual updated; .sfk byproducts cleared.

Remaining for the overhaul (next): tabbed Preferences (General / Audio cues / Startup behaviour /
Update settings), the cue-list redesign with a "none" option replacing per-cue checkboxes, moving
Startup behaviour out of the Options menu, and a front-most "missing sound file" error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:42:41 +01:00
EdnunpandClaude Opus 4.8 8763470037 Audio cues: add send/receive on-off and minimise/restore cues (2 sounds each), drop .sfk byproducts
Six new machine-wide cues, each with the same numbered-variant + Preferences treatment as
the others (enable tickbox, Choose default sound picker, Play/Browse):

- Send turned on / off, Receive turned on / off: fire from OnStreamingCheckboxChanged, so
  they sound whether the user clicked the in-window tickbox or pressed the mute shortcut
  (the hotkey flips .Checked, which routes through the same handler). Suppressed during
  profile load by the existing password-gate guard, so loading a profile doesn't blast them.
- Minimise (hide) / Restore (show): fire from the tray controller's Minimize()/Restore() on a
  genuine visibility transition (guarded against no-op / startup-minimise).

Enable flags + custom-WAV overrides for these six live machine-wide in AppConfig
(EnableSendOnCue.., MachineCueCustomPaths) - they're app-level feedback, not per-profile
audio - so no Profile/settings-cache plumbing. TryLoadCueSound now also honours the
machine-wide custom path. PreferencesDialog gains a MachineRow helper + the six rows.

Sounds: shipped via the existing sounds\*.wav wildcard. Fixed an obvious typo in the
supplied files ("rcieve off 1.wav" -> "recieve off 1.wav") so receive-off has both variants.
Renamed the old single-name cue WAVs to Ed's numbered-variant set; added key/passkey and the
new cue sounds.

build-release.ps1: new step deletes the SoundForge .sfk peak-file byproducts from sounds\
before packaging (they never shipped - build is *.wav only - this just keeps the tree tidy).

Tests + manual updated for the six new cues.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:18:09 +01:00
EdnunpandClaude Opus 4.8 e526014e6a Audio cues: per-cue default-sound picker, keyboard-click typing feedback, passkey on password fields
Cue sounds now ship as numbered variants ("connect 1.wav", "connect 2.wav", ...); the
count is never hard-coded so more can be added with no code change.

- CueSounds.cs: discovers a cue's "<base> <n>.wav" variants (case-insensitive) and
  resolves the active default: per-profile custom WAV > machine-wide chosen variant >
  first variant > silent. Wired into MainForm.TryLoadCueSound, the startup cue in
  Program.cs, and PreferencesDialog.ResolveCueFilePath.
- AppConfig: DefaultCueSounds (machine-wide cueId -> chosen filename) and
  EnableKeyboardClicks (on by default).
- Preferences: a "Choose default sound" listbox under the cue checklist - it lists the
  selected cue's variants, arrowing it previews each sound and makes it that cue's
  default. Plus a "Play keyboard clicks when typing into any edit field" checkbox.
- KeyClickService.cs: an app-wide WM_CHAR message filter + a low-latency NAudio mixer.
  Typing into any edit field plays a random key click (key 1..N.wav); password fields
  also play passkey.wav at the same instant. On/off live from the Preferences toggle.
  Inert if the sounds are missing or the device won't open; never consumes the keystroke.
- csproj: ship every sounds\*.wav via a wildcard (variants, key clicks, passkey, future
  additions) instead of stale per-file canonical names.
- Tests: resource checks (self-test + run-tests.ps1) now verify each cue has >=1 variant
  and that key 1.wav / passkey.wav are present. Accessibility audit still green with the
  new Preferences controls (Alt+D, Alt+K - no mnemonic clashes).
- Manual: variant picker, keyboard clicks, and the new sound-file naming documented.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 00:06:07 +01:00
EdnunpandClaude Opus 4.8 ad66fe5364 Test suite: richer diagnostics, headless accessibility audit, perf/leak sanity
Andre's three "bigger ideas" from RemSound-smoke-test-agent-brief.md:

- Richer diagnostics: --diagnostics now includes a live localhost audio self-check
  (PCM + Opus, with packet/underrun/drop/buffer/latency counters), the most recent
  session snapshot parsed from the log (codec, send/receive state, buffer, drops,
  heartbeat), and a recent-warnings/errors digest from the log. BuildDiagnosticsReport
  gained a runLiveAudioProbe flag so the self-test's privacy check stays fast.

- Headless accessibility audit: new --selftest step constructs the dialogs that can be
  built without hardware (Startup behaviour, Recording settings, Preferences) and checks
  every actionable control announces a name and that Alt-key mnemonics are unique within
  a container. MainForm is out of scope (its constructor opens audio/hotkeys/sockets).
  Dialogs that won't construct are skipped, not failed. Currently audits 3, no violations.

- Perf/leak sanity: new --perftest command runs several audio-loopback cycles and reports
  whether handle/memory/thread counts stay bounded (handles ratcheting up cycle-on-cycle is
  the leak fingerprint, given RemSound's handle-leak history). Lenient thresholds; logs the
  numbers for build-to-build comparison. Wired into run-tests.ps1.

- Shared AudioLoopback helper (used by the self-test, diagnostics and perf test) so all
  three exercise the identical real capture/encode/network/decode path on test port 47929.

- csproj: the four previously-unconditional cue Content items are now Exists-guarded like
  the rest, so a mid-edit sounds\ folder doesn't break the dev build; the gate still
  enforces the required cues before release.

Help + manual updated (--perftest, --smoke-test, --config-dir, richer --selftest).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 23:45:08 +01:00
EdnunpandClaude Opus 4.8 832ed40bf7 Test suite: add --config-dir isolation, --smoke-test alias, and a cold-start/close gate step
Adopted from Andre's RemSound-smoke-test-agent-brief.md - the gaps our pack didn't
already cover:

- --config-dir <folder>: redirect ALL user state (config, profiles, logs, cue
  sounds) to an explicit folder for this process only, applied at the very start of
  Program.Main before the layout migration runs. Lets a test exercise a real build
  without touching the user's live settings (the brief's safety rule 1). Works with
  every command. AppConfig gains SetUserDataDirectoryOverride / an override on
  UserDataDirectory; CommandLine.TryGetConfigDir parses it early.
- --smoke-test / --smoketest: alias for --selftest, matching the brief's vocabulary.
- run-tests.ps1: a cold-start + clean-close smoke (brief baseline steps 3-4) -
  launches the GUI minimized against an isolated --config-dir, confirms it stays up,
  that it used the isolated folder (real settings untouched), and that --close shuts
  it down with no orphan process. SKIPs cleanly if a RemSound instance is already
  running (machine-wide single-instance lock).

Manual + --help updated for both switches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 23:12:22 +01:00
EdnunpandClaude Opus 4.8 141c5e8ce1 Add build-and-test suite (in-app self-test + publish gate); fix release-zip missing sounds
The test suite, modelled on Andre's Sensor Readout (an in-app self-test + a build
script), runnable as one step before every publish.

Part 1 - in-app multi-step self-test (SelfTest.cs), run by --selftest:
  audio round-trip (PCM + Opus over localhost, dedicated test port so it never
  clashes with a running instance), encryption right/wrong-password + fingerprint,
  packet framing + malformed rejection, client<->server wire-format compatibility,
  settings save/reload, profile save/reload (temp folder), diagnostics-report
  privacy (never leaks a password), and bundled-resources present. Each step is
  timed and reported PASS/FAIL/SKIP; exit 0 only if nothing failed. Replaces the
  old single-shot --selftest. RunDiagnostics refactored to expose
  BuildDiagnosticsReport(AppConfig) for the privacy step.

Part 2 - run-tests.ps1: builds, then checks the package (sounds, readme, native
  opus, framework-dependent, dll version == csproj), the About-box changelog, the
  client/server wire contract (relay magic/version/port still match RemPacket),
  the CLI surface, and runs --selftest. build-release.ps1 now runs this gate first
  and aborts the release if it fails.

Bug caught + fixed: the published release zip carried ZERO cue sounds (startup
  sound + connect/disconnect/etc.) - MSBuild's incremental Content-copy marker
  skipped sounds\ on a fresh publish. Added an AfterTargets=Publish copy in the
  csproj that lands every cue WAV in the published sounds\ folder regardless of
  the marker. Verified: a staging publish now contains all 9 cue WAVs.

Manual/help: --selftest description updated (readme.html + MANUAL.md).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 23:01:40 +01:00
EdnunpandClaude Opus 4.8 05b218825d Release v3.9: silent-mic fix, buffer drain, idle-send guard, start-up sound, command-line
- Receiver: a plain (Mixed) stream now renders on an active lane when the
  receiver is in two-lane (ASIO) mode, instead of being decoded into a ring
  nothing reads. Fixes one-way silence ("my mic works for me but not for them").
- Receiver: drift resampler gains a buffer-depth correction term so a bloated
  standing buffer eases back to the latency target over a long session.
- App: don't send audio until a peer is genuinely reachable (issue #8); status
  no longer shows phantom send traffic with nobody connected.
- App: start-up cue sound (machine-wide toggle + custom path in Preferences).
- App: command-line options (CommandLine.cs) -- --devices, --selftest,
  --diagnostics, --log, --close, --profile, --connect, --minimized, --version,
  --help. New "Command-line options" manual section (readme.html + MANUAL.md).
- Version 3.9; About-dialog and RELEASE_NOTES updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 19:57:36 +01:00
EdnunpandClaude Opus 4.8 04b17ff1ab Release v3.8: New profile (blank template renamed), password-mismatch warning stays up, IP-pinning docs
New profile: a File-menu item + Ctrl+N that loads a fresh blank template as a
new unsaved session via a LoadBlankTemplateNext handoff to Program.cs's relaunch
loop — reachable even when "start with a specific profile" boots past the picker
(issue #6). Offers to save the current profile first if dirty; deliberately
silent (no profile-switch cue).

Renamed the user-facing "blank template" to "New profile": the picker's synthetic
entry (now a distinct marker TYPE, collision-safe against a real profile named
"New profile"), the window title ("RemSound — New profile"), and the manual.

Fixed the password-mismatch warning flashing away: it's raised from the 1 Hz
statusTimer, which kept firing into the modal loop and rebuilt the peer lists
(SyncAllPeerLists) under the dialog, knocking it out of the foreground. Now the
tick is frozen while it's up, it's routed through ForegroundDialog, and a
re-entry guard ensures one warning that stays put. Audited: it was the only
popup raised from a recurring timer.

Docs: manual section "Connecting to one specific IP address (and only that one)"
explaining by-name vs by-fixed-IP and that a profile saves the exact address
(issue #7 — functionality already existed); About box + RELEASE_NOTES for v3.8;
MANUAL.md regenerated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 21:38:38 +01:00
EdnunpandClaude Fable 5 9e247112cb Release v3.7: device-change smoothing, auto-tune spike rejection, wider mic detector, forensic logging
Coalesce capture-engine rebuilds (CompositeCaptureBackend): a swap-triggering
source change now arms a 250ms debounce timer and re-arms on each further
change, so a flap or quick reconfiguration produces ONE rebuild to the final
state instead of a burst (Andre's 16:40 four-rebuilds-in-33s crackle). In-place
updates still apply immediately; a pending rebuild whose target flaps back is
cancelled.

Auto-tune (TickRoute) now keys off the SECOND-highest arrival-gap/render-gap
second in the lookback window instead of the single worst, so a lone ~1s
OS/driver stall no longer balloons the buffer to the 200ms cap (the 16:51
trim burst); sustained jitter still reacts at full speed. Logs both gap-max
(true peak) and gap-used (value acted on).

Mic-privacy detector widened: also catches a per-app Deny aimed at this exe
under ConsentStore\microphone\NonPackaged\<exe>, the HKLM NonPackaged gate,
and the Group-Policy/MDM force-deny (AppPrivacy LetAppsAccessMicrophone=2) —
the block shapes that silence WASAPI capture while ASIO sails past, and that
the old three-value check missed.

Forensic instrumentation so the next log proves what happened: capPeak=
(loudest pre-encode sample, per lane, on the diag line), mic-privacy verdict
logged at startup, ui: capture tick/untick events, and device-event: lines for
Windows endpoint changes.

Docs: mic-privacy + auto-tune sections updated in readme.html, MANUAL.md
regenerated, About-box changelog and RELEASE_NOTES for v3.7.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 22:44:55 +01:00