Commit Graph
10 Commits
Author SHA1 Message Date
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
EdnunpandClaude Opus 4.8 55bdcde0af Release v3.6: rebuild the self-updater (in-app, rollback-safe); warnings to front
Replace the generated cmd.exe + robocopy update helper — which silently
failed on some machines — with an in-app C# installer:
  * Stage the new version to a per-user temp folder off the install and run
    the new RemSound.exe from there, so nothing in the install is locked by
    the updater itself.
  * Wait for the old process to fully exit (real WaitForExit), then
    back-up-and-swap files in C# with retry + rename-aside; roll the install
    back to the previous version on any failure, so a failed update can never
    leave a half-installed RemSound.
  * Log every step to updater.log; clean up old stages and legacy batch
    artefacts on launch. Removed the old BuildInstallScript batch generator.

Route the "RemSound is already running" dialog and its follow-up message
through ForegroundDialog so they surface in front from a background-relaunched
copy, matching the earlier post-update fix.

Docs: rewrite the readme update sections for the new mechanism, regenerate
MANUAL.md, refresh the About-box changelog and RELEASE_NOTES.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 13:37:19 +01:00
EdnunpandClaude Opus 4.8 4dbe9a47a0 Release v3.5: USB-card recovery, per-card adaptive buffer, one user folder, audit fixes
- Recover an unplugged/replugged output sound card automatically (issue #5):
  detect the dead WASAPI device and remember the receive-output selection so it
  re-ticks and re-opens when the card returns.
- Adaptive per-card WASAPI buffer target sized to each card's pull chunk, held
  stable so it never flits about under CPU/network load.
- Consolidate all per-user data (config, profiles, logs, sounds) into one
  "user settings and logs" folder; migrate every older layout; exclude it from
  the updater so custom cue sounds now survive updates.
- Mic-privacy detector: warn once when a Windows-blocked mic is switched on, or a
  profile loads with one already on.
- All warning/notice dialogs now come to the foreground even when minimised.
- Apply volume + mute on profile load (were saved but not restored).
- Crash-safe (atomic) profile/config saves.
- Fix two resource leaks (push-mode capture MMDevice; UPnP DeviceFound handler).
- Remove dead code (baseline-diff machinery, dead ASIO probes, no-op stubs).
- Docs: readme.html, MANUAL.md, About-box changelog and release notes for v3.5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 11:34:17 +01:00
EdnunpandClaude Opus 4.8 946e6f4be4 v3.4 refinements: WASAPI drift correction, startup-dialog sequencing, quick-switch polish, docs
Builds on the v3.4 freeze (dd70613) with the fixes and tuning from live testing,
plus the v3.4 documentation pass.

Audio (receiver):
- Per-device WASAPI drift correction in MultiOutputPlayout. A pull-side resampler
  (mirroring SessionPlayout's proven corrector) holds each output device's buffer at
  a fixed low depth, cancelling the slow clock drift that made WASAPI peers "lag
  apart" over long sessions. Feed-forward clock-ratio measurement plus a gentle
  depth-restoring term; the first measurement window is discarded because WASAPI
  start-up priming poisons it. ASIO already self-corrected; this brings WASAPI level.
- Output device buffer requested at 5 ms (WASAPI clamps it up to the device's minimum
  period, ~10 ms) instead of 15 ms, since the corrector keeps it fed — a free saving.

UI / accessibility (MainForm, Program):
- Startup notices (what's-new About box, Realtek warning) now run one at a time via a
  single sequence instead of separate BeginInvokes, so they no longer stack into
  nested modals that couldn't be closed. The loading splash is skipped for a
  tray-bound quick switch.
- Quick profile switch keeps RemSound in the tray if it was there, and plays the
  switch cue immediately on click.
- Profile-switch cue now plays on click for every switch path (recent menu, quick
  switch, File > Open) and no longer on a fresh start into the first profile. It was
  also previously dead on the rebuilt form (pendingProfile was nulled first).
- Realtek ASIO toggle's accessible name now reads "Enable"/"Disable" to match the
  visible text, instead of "Toggle" (screen reader read the wrong word).

Docs (plain English):
- RELEASE_NOTES.md: v3.4 entry.
- About dialog: v3.4 "what's new".
- readme.html (the canonical bundled manual): quick switch, the hotkey read-outs, the
  new profile-menu-open cue, Realtek auto-detect/disable, and the config-folder path.
- MANUAL.md regenerated from readme.html via sync-manual.py so the two stay in sync.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 15:31:32 +01:00
EdnunpandClaude Opus 4.8 959720f54d Release v3.3: end-to-end encrypted audio, plus cue and reliability fixes
Headline: all audio is now encrypted (AES-256-GCM), keyed by a per-profile
password. Mandatory — v3.3 only interoperates with v3.3+.

Encryption
- RemSoundCrypto (Core): PBKDF2 key derivation, AES-GCM encrypt/decrypt
  (low-alloc, into-span), password fingerprint, light on-disk obfuscation.
- Wire: SenderLane encrypts the audio payload (PCM split across parts when the
  +28 overhead crosses MTU); AudioReceiver/StreamSession decrypt via a shared
  single-thread AudioDecryptor. Fingerprint piggybacks on the Format packet
  (offset 36, backward-compatible) so a peer can detect a password mismatch.
- Profile.Password (scrambled), carried through BuildCurrentProfile; MainForm
  derives + pushes the key/fingerprint to sender + receiver (RecomputeAudioCrypto).
- UX: ask-for-password on profile create; File -> Change this profile's password
  (ProfilePasswordDialog); Options -> Profile passwords (manager); a gate that
  prompts before streaming without a password; and a clear "passwords don't
  match" / "peer needs to update" message driven by the fingerprint.

Cue fixes
- CuePlayer (NAudio) replaces System.Media.SoundPlayer, which silently failed
  on the 96 kHz/24-bit cue WAVs (and any custom file) — cues now play reliably,
  resampled to 48 kHz/16-bit. Also fixes the Preferences preview button.
- Connect/disconnect cues now audio-gated with hysteresis: connected when audio
  flows OR heartbeat healthy; lost only when audio stops AND heartbeat
  unreachable. Kills false disconnects and the receive-only "no cues" case.
- Honest cue logging (played / muted / not loaded).

Smaller
- Endpoint stickiness: keep the audio target pinned to the heartbeat-proven
  address instead of chasing a multi-homed peer's other (unreachable) address.
- "Online/offline" label now audio+heartbeat aware, not discovery-only.
- "Show what's new after each update" preference (on by default).

Docs: About v3.3 block, RELEASE_NOTES, README (encryption as a headline),
manual section 12 "Passwords and encryption" (+ renumber), MANUAL.md regenerated.
Version 3.2.0 -> 3.3.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 23:57:32 +01:00
EdnunpandClaude Opus 4.8 cdcac859c4 Release v3.2: update sound cue + single-instance bring-to-front fix
- New "Update sound" cue (CueId.Update / update.wav). Plays just before an
  update starts installing, on every path (manual, background-silent,
  startup-silent), so a silent background update gives an audible heads-up
  before RemSound closes to restart. Per-profile mute + custom-sound override
  in Preferences, same infrastructure as the other cues:
    * Profile.EnableUpdateCue + RemSoundSettingsStore Load/Save + round-trip
    * MainForm updateSound field, TryLoadCueSound, play in InstallUpdateAsync
      gated by LoadEnableUpdateCue
    * PreferencesDialog "Update sound" CueRow + ResolveCueFilePath mapping
    * update.wav shipped in sounds\ via csproj Content
- Single-instance "switch to the running copy" now actually brings the window
  to the front. The second copy grants the running copy foreground rights via
  AllowSetForegroundWindow before signalling, and lingers briefly so it can
  raise itself before we exit — without this, Windows' foreground lock left
  the running window only flashing in the taskbar (Ed's report).
- Docs: About box v3.2 block, RELEASE_NOTES.md, manual cue section (now seven
  cues, with the update cue described), MANUAL.md regenerated.
- Version bumped 3.1.3 -> 3.2.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 17:58:25 +01:00
EdnunpandClaude Opus 4.8 9ab0a1a20a Bump to v3.1.3: single-instance lock + update/read-only reliability fixes
Fixes the chained-fault runaway Andre hit after an update (multiple copies
stacking, audio climbing to deafening, terminal kill to recover).

- Single-instance lock (SingleInstanceCoordinator + SingleInstanceDialog,
  wired in Program.Main). A named mutex makes two copies impossible. A second
  launch offers: switch to the running copy (default; surfaces it from the
  tray via a named activation event), or force the running copy closed and
  start fresh (Process.Kill, retried elevated if the target is elevated).
  This is the structural fix that makes the stacking runaway impossible.
- Prompt-free update exit. InstallUpdateAsync sets updatingInProgress before
  Application.Exit(); the close path's skipPrompt now honours it, so no
  unsaved-changes dialog (whose default button is Cancel) can abort the
  update's restart.
- Read-only persistence fix. BuildCurrentProfile now carries
  currentProfileReadOnly into the saved snapshot. Previously a deliberate
  save of a locked profile wrote ReadOnly=false, silently unlocking it on
  disk — which re-armed the save prompt that then blocked the update.
- In-process double-install guard. updateInstallStarted stops the ~4 s
  startup check and the background poll both staging an install + helper.
- Docs: About box, RELEASE_NOTES.md, readme.html + MANUAL.md ("Only one copy
  of RemSound runs at a time").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 17:33:11 +01:00
EdnunpandClaude Opus 4.8 baf50baf73 Bump to v3.1.2: tray icon re-stamps on state change, recording shows as a flag
- Tray icon now re-registers itself (hide+re-show) whenever the meaningful
  state changes — peer connect/drop, send/receive toggle, recording
  start/stop — so the name a screen reader announces stays in step with
  the live state. Fixes the persistent "no peers" then "1 peer" double
  announcement that the old "show window then minimise again" trick used
  to clear by hand. Same root cause as the v3.1.1 stuck-tooltip fix, just
  exposed when the state changes a moment after the icon appears.
- Recording shows as a plain "recording" flag in the tray rather than a
  live timer. A ticking timer would have either flickered the icon once a
  second or left a screen reader announcing a stale time next to the live
  one. Removed the now-dead FormatRecordingElapsed helper.
- Bundles the earlier Win7 updater fix (6cbde0d): a failed secure
  connection is no longer mislabelled as "you're up to date".
- About box, RELEASE_NOTES.md, readme.html and MANUAL.md updated to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 23:34:44 +01:00
EdnunpandClaude Opus 4.7 70c2669d70 Bump to v3.1.0: new audio cues (save, profile-switch), per-profile custom cue paths, redesigned tray menu
Two new cues join the existing connect / disconnect / record-start /
record-stop set:
  * profile-save cue (sounds\save.wav by default) fires in
    SaveProfileTo after a successful Save or Save As. Honours the
    Profile.EnableSaveCue per-profile flag.
  * profile-switch cue (sounds\profile.wav by default) fires in the
    MainForm Shown handler after a profile finishes loading - covers
    both startup-with-profile and mid-session profile switches.
    Honours the Profile.EnableProfileSwitchCue flag. Because cue
    loading runs AFTER settings.ApplyProfile in the ctor (line 542),
    the profile being entered determines which sound plays - not the
    one being left, exactly as Ed asked.

The audio cue UI in PreferencesDialog stays as a CheckedListBox (up /
down navigates, Space toggles) with TWO action buttons below that
operate on whichever cue is selected:
  * Play [cue name] (Alt+P) - previews via SoundPlayer.Play on the
    resolved path (custom override if set, default in sounds\
    otherwise). Independent of the tick state.
  * Browse for [cue name]... (Alt+B) - opens a WAV picker. If the
    user picks a file inside RemSound's own sounds\ folder, it's
    treated as "use default" and the override is cleared - avoids
    pinning the user to a specific shipped default that a future
    release might replace. Right-click "Use default sound" reverts.

Custom cue paths AND enable flags are per-profile. Lives on
Profile.CustomCuePaths (Dictionary<string,string>) and the per-cue
EnableXxxCue bool? properties. Cache mirror in
RemSoundSettingsStore.Settings.

All default WAVs moved from the install-root flat layout into a
sounds\ subfolder (csproj Content rules updated). save.wav and
profile.wav are bundled defaults.

Tray menu rewritten (MainFormTrayController) per Ed's spec:
  * Show RemSound (W) - now uses Win32 SetForegroundWindow after the
    standard Activate() because WinForms Activate is blocked by the
    foreground-lock when invoked from a tray-menu click, which left
    screen-reader users having to Alt+Tab to reach the restored
    window.
  * Enable sending (S) / Enable receiving (R) - tickable, reflect
    current state, TOGGLE rather than always-on.
  * Profiles (P) - submenu populated from AppConfig.RecentProfiles
    with the same &1..&5 mnemonics the File menu uses. Pre-populated
    once at construction so WinForms recognises it as a submenu and
    fires DropDownOpening - originally I relied entirely on the
    open event, which the framework skipped for items with no
    DropDownItems, producing the "Profiles does nothing" bug.
  * Exit (X).

Tray tooltip now built dynamically from snapshot tick (1 Hz):
"RemSound - [recording for MM:SS,] N peer(s), sending (lane),
receiving (lane)". Recording timer only included while
RecordingController.IsRecording is true (added
RecordingStartedUtc accessor for the elapsed calculation). Lane is
derived from which device-list ticks are active, not just the audio-
mode setting, so a BothIndependent user with only WASAPI inputs ticked
honestly reads as "sending (WASAPI)".

Fixes:
  * Initial tooltip "RemSound" produced a "RemSound RemSound" read on
    NVDA because the process name and tooltip matched. Set to
    "RemSound - starting up" so the duplicate disappears.
  * Recent profile menu items no longer carry a "Recent profile N:"
    AccessibleName prefix in either the tray submenu or the File
    menu's Recent profiles - now just the profile name. Number-key
    mnemonics (&1..&5) untouched.

Manual (readme.html) updated: new section 17 "Audio cue sounds"
documents all six cues, the Play/Browse buttons, the right-click
"Use default sound", and the per-profile semantics. Sections 17-21
renumbered to 18-22. New "System tray icon and its menu" subsection
inside section 4 documents the redesigned right-click menu and the
hover tooltip. MANUAL.md regenerated via sync-manual.py. About box
gets a v3.1 block at the top. RELEASE_NOTES.md fully rewritten for
v3.1.

No wire format change - v3.1 talks to other v3.0.x machines exactly
as before.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 14:24:34 +01:00
EdnunpandClaude Opus 4.7 0189b12668 Docs: rewrite README.md for users, add MANUAL.md, sync from readme.html on every release
People landing on the repo page were having to install RemSound just to read what
it does and how to use it. Two doc changes fix that:

1) README.md rewritten as a plain-English landing page. Drops the developer-focused
   highlights / build-from-source / project-layout sections in favour of what
   RemSound is, who it's for, how to install it, and a prominent link to the manual.
   No jargon, no command lines, no NuGet / SDK / ASIO-protocol talk. The dev-side
   information that used to live here (build commands, source layout, relay setup)
   is still discoverable for anyone who wants it — the source itself is on the same
   page, and the relay docs are under server/README.md.

2) MANUAL.md added at the repo root as the GitHub-rendered version of the F1 help.
   Markdown derived directly from readme.html via sync-manual.py (new), so visitors
   can read the manual inline on the repo page with no download. readme.html stays
   exactly where it was (bundled inside RemSound, opened by F1) — it remains the
   canonical source of the manual content; MANUAL.md is auto-generated from it.

The sync-manual.py script is invoked automatically from build-release.ps1 as step 0,
before any other release work. It regenerates MANUAL.md from readme.html and then
checks `git diff` on MANUAL.md — if the file changed, the release is paused with a
message asking the user to commit the updated MANUAL.md alongside the release commit.
That makes it structurally impossible to ship a release with a stale GitHub-facing
manual: forgetting to commit MANUAL.md after editing the bundled help triggers a
deliberate release-time stop.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 23:48:35 +01:00