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>
This commit is contained in:
Ednunp
2026-05-28 14:24:34 +01:00
co-authored by Claude Opus 4.7
parent 0189b12668
commit 70c2669d70
18 changed files with 1158 additions and 128 deletions
+26 -13
View File
@@ -14,7 +14,7 @@
tag_name on the latest GitHub release; bump it on every public release. The
AssemblyVersion / FileVersion default to this value, and Assembly.GetName().Version
is what the About dialog and the updater both read. -->
<Version>3.0.2</Version>
<Version>3.1.0</Version>
</PropertyGroup>
<ItemGroup>
@@ -57,24 +57,37 @@
</ItemGroup>
<ItemGroup>
<!-- Peer-state notification sounds. Copied next to RemSound.exe so SoundPlayer can find
them via AppContext.BaseDirectory. -->
<Content Include="..\..\connect.wav">
<Link>connect.wav</Link>
<!-- All cue sounds live in a sounds\ subfolder under the publish output (moved out of
the install-root flat layout 2026-05-28). Default cue WAVs ship inside that folder;
user-supplied custom cue paths set in Preferences override the defaults at runtime.
Filenames containing a space (e.g. "record start.wav") are preserved verbatim on
copy so the load-by-filename path in TryLoadCueSound finds them exactly as written. -->
<Content Include="..\..\sounds\connect.wav">
<Link>sounds\connect.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\disconnect.wav">
<Link>disconnect.wav</Link>
<Content Include="..\..\sounds\disconnect.wav">
<Link>sounds\disconnect.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- Recording start/stop cues. Filenames contain a space — preserve on copy so the
load-by-filename path in TryLoadCueSound finds them exactly as written. -->
<Content Include="..\..\record start.wav">
<Link>record start.wav</Link>
<Content Include="..\..\sounds\record start.wav">
<Link>sounds\record start.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\record stop.wav">
<Link>record stop.wav</Link>
<Content Include="..\..\sounds\record stop.wav">
<Link>sounds\record stop.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- New cues introduced 2026-05-28: save.wav fires after a successful Save / Save As;
profile.wav fires immediately after a profile finishes loading. Both default WAVs
to be supplied by the project owner; absent files are gracefully ignored at load
time (the cue just doesn't play) so the build is fine without them. -->
<Content Include="..\..\sounds\save.wav" Condition="Exists('..\..\sounds\save.wav')">
<Link>sounds\save.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="..\..\sounds\profile.wav" Condition="Exists('..\..\sounds\profile.wav')">
<Link>sounds\profile.wav</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<!-- User manual. F1 anywhere in the app opens this via the user's default browser