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>
This commit is contained in:
Ednunp
2026-06-13 11:57:19 +01:00
co-authored by Claude Opus 4.8
parent 0b7ad49021
commit a408d2b56e
61 changed files with 548 additions and 139 deletions
+47
View File
@@ -0,0 +1,47 @@
using RemSound.Core;
namespace RemSound.App;
/// <summary>
/// Plays a short cue whenever the user switches between tabs anywhere in RemSound - the main
/// window's tab strip and every tabbed dialog (Preferences, Recording settings, ...). Fired from
/// <see cref="QuietTabControl"/> on a selection change, gated on the tab control actually containing
/// focus, so a genuine user switch (arrow keys / Ctrl+Tab) clicks but the programmatic selection
/// done while building or restoring a window stays silent.
///
/// Machine-wide cue (<see cref="AppConfig.EnableTabSwitchCue"/>), shipped as numbered variants
/// ("tab switch 1.wav", ...) and configured in Preferences exactly like the other cues. Default on.
/// </summary>
internal static class TabSwitchSoundService
{
private static CuePlayer? switchSound;
/// <summary>When true, <see cref="Play"/> is a no-op. Reserved for bulk programmatic tab changes
/// the focus gate doesn't already cover; mirrors <see cref="CheckSoundService.Suppressed"/>.</summary>
public static bool Suppressed { get; set; }
/// <summary>(Re)load the cue from the current cue configuration. Call at startup and whenever cue
/// settings change, alongside <see cref="CheckSoundService.Reload"/>.</summary>
public static void Reload()
{
switchSound = LoadCue(MainForm.CueId.TabSwitch, "tab switch.wav", AppConfig.Load());
}
public static void Play()
{
if (Suppressed) return;
if (AppConfig.Load().EnableTabSwitchCue) switchSound?.Play();
}
private static CuePlayer? LoadCue(string cueId, string defaultFile, AppConfig cfg)
{
try
{
string? path = cfg.MachineCueCustomPaths.TryGetValue(cueId, out var custom) && File.Exists(custom)
? custom
: CueSounds.ResolveDefaultPath(cueId, defaultFile, cfg);
return path is not null && File.Exists(path) ? new CuePlayer(path) : null;
}
catch { return null; }
}
}