diff --git a/MANUAL.md b/MANUAL.md
index 468778b..cce0681 100644
--- a/MANUAL.md
+++ b/MANUAL.md
@@ -866,7 +866,7 @@ Toggle| What it does
## 18. Audio cue sounds
-RemSound plays a short sound at moments where you might want an audible confirmation that something just happened. These are called **cue sounds**. Fifteen events have a cue:
+RemSound plays a short sound at moments where you might want an audible confirmation that something just happened. These are called **cue sounds**. Sixteen kinds of event have a cue:
Cue| Plays when
---|---
@@ -883,6 +883,7 @@ Cue| Plays when
**Receive turned on / off sound**| You turn receiving audio on or off — from the **Receive audio** box or its mute shortcut. Again, a separate sound for on and for off.
**Minimise (hide) sound**| RemSound's window minimises to the notification area (hides).
**Restore (show) sound**| RemSound's window is brought back from the notification area (shows).
+**Checkbox ticked / unticked sound**| You tick or untick _any_ checkbox anywhere in RemSound — a click for ticked, a different one for unticked. This gives instant feedback on which way a box just went, which is especially handy in the busy inputs and outputs lists. There's a separate sound for ticking and for unticking.
All of these cues play through your default Windows sound output, which is separate from the audio RemSound is sending or receiving. They don't appear in a normal recording. (The exception: if your sending side is capturing the very output device the cues play through, then they get captured along with everything else from that device.)
diff --git a/readme.html b/readme.html
index 9bb6219..ff94ec4 100644
--- a/readme.html
+++ b/readme.html
@@ -919,7 +919,7 @@ Use whatever key combinations you prefer (for example Ctrl+Shift+Up / Ctrl+Shift
18. Audio cue sounds
-RemSound plays a short sound at moments where you might want an audible confirmation that something just happened. These are called cue sounds. Fifteen events have a cue:
+RemSound plays a short sound at moments where you might want an audible confirmation that something just happened. These are called cue sounds. Sixteen kinds of event have a cue:
| Cue | Plays when |
@@ -936,6 +936,7 @@ Use whatever key combinations you prefer (for example Ctrl+Shift+Up / Ctrl+Shift
| Receive turned on / off sound | You turn receiving audio on or off — from the Receive audio box or its mute shortcut. Again, a separate sound for on and for off. |
| Minimise (hide) sound | RemSound's window minimises to the notification area (hides). |
| Restore (show) sound | RemSound's window is brought back from the notification area (shows). |
+| Checkbox ticked / unticked sound | You tick or untick any checkbox anywhere in RemSound — a click for ticked, a different one for unticked. This gives instant feedback on which way a box just went, which is especially handy in the busy inputs and outputs lists. There's a separate sound for ticking and for unticking. |
All of these cues play through your default Windows sound output, which is separate from the audio RemSound is sending or receiving. They don't appear in a normal recording. (The exception: if your sending side is capturing the very output device the cues play through, then they get captured along with everything else from that device.)
diff --git a/run-tests.ps1 b/run-tests.ps1
index ed03535..6e6878e 100644
--- a/run-tests.ps1
+++ b/run-tests.ps1
@@ -48,7 +48,7 @@ $soundsPath = Join-Path $publishDir 'sounds'
$wavCount = @(Get-ChildItem -LiteralPath $soundsPath -Filter *.wav -ErrorAction SilentlyContinue).Count
if ($wavCount -ge 3) { Pass "cue sounds bundled ($wavCount .wav)" } else { Fail "cue sounds missing (found $wavCount) - this is the bug that shipped v3.9 with no sounds" }
# Cues ship as numbered variants ("connect 1.wav", ...); each required cue needs at least one.
-foreach ($base in @('connect', 'disconnect', 'start up', 'send on', 'send off', 'recieve on', 'recieve off', 'minimise', 'maximise')) {
+foreach ($base in @('connect', 'disconnect', 'start up', 'send on', 'send off', 'recieve on', 'recieve off', 'minimise', 'maximise', 'check', 'uncheck')) {
$variants = @(Get-ChildItem -LiteralPath $soundsPath -Filter "$base*.wav" -ErrorAction SilentlyContinue)
if ($variants.Count -gt 0) { Pass "'$base' cue has $($variants.Count) sound variant(s)" } else { Fail "no sound variant for the '$base' cue" }
}
diff --git a/sounds/check 1.wav b/sounds/check 1.wav
new file mode 100644
index 0000000..1210e19
Binary files /dev/null and b/sounds/check 1.wav differ
diff --git a/sounds/check 2.wav b/sounds/check 2.wav
new file mode 100644
index 0000000..fa2e53b
Binary files /dev/null and b/sounds/check 2.wav differ
diff --git a/sounds/uncheck 1.wav b/sounds/uncheck 1.wav
new file mode 100644
index 0000000..375b39b
Binary files /dev/null and b/sounds/uncheck 1.wav differ
diff --git a/sounds/uncheck 2.wav b/sounds/uncheck 2.wav
new file mode 100644
index 0000000..1b7de63
Binary files /dev/null and b/sounds/uncheck 2.wav differ
diff --git a/src/RemSound.App/AccessibleCheckBox.cs b/src/RemSound.App/AccessibleCheckBox.cs
index 52db1a5..a6ea60c 100644
--- a/src/RemSound.App/AccessibleCheckBox.cs
+++ b/src/RemSound.App/AccessibleCheckBox.cs
@@ -60,6 +60,9 @@ internal sealed class AccessibleCheckBox : CheckBox
if (Focused)
{
NotifyWinEvent(EVENT_OBJECT_FOCUS, Handle, OBJID_CLIENT, CHILDID_SELF);
+ // Audible tick/untick feedback. Gated on Focused so it fires for a genuine user toggle
+ // (click or spacebar) but stays silent for the bulk programmatic checking on profile load.
+ CheckSoundService.Play(Checked);
}
}
}
diff --git a/src/RemSound.App/CheckSoundService.cs b/src/RemSound.App/CheckSoundService.cs
new file mode 100644
index 0000000..d64e674
--- /dev/null
+++ b/src/RemSound.App/CheckSoundService.cs
@@ -0,0 +1,47 @@
+using RemSound.Core;
+
+namespace RemSound.App;
+
+///
+/// Plays a short tick / untick sound whenever the user checks or unchecks a checkbox anywhere in
+/// RemSound - instant audible feedback of which way a box just went, which matters most in the busy
+/// inputs/outputs lists. Two normal machine-wide cues: CheckboxOn (check.wav) and CheckboxOff
+/// (uncheck.wav), each with numbered variants + an off option in Preferences, like any other cue.
+///
+/// Wired from and the device LiveCheckedListBox, both gated
+/// on the control being focused - so a genuine user toggle clicks, but the bulk programmatic
+/// checking done on profile load or by "uncheck all" stays silent.
+///
+internal static class CheckSoundService
+{
+ private static CuePlayer? checkSound;
+ private static CuePlayer? uncheckSound;
+
+ /// (Re)load the tick/untick sounds from the current cue configuration. Call at startup
+ /// and whenever cue settings change.
+ public static void Reload()
+ {
+ var cfg = AppConfig.Load();
+ checkSound = LoadCue(MainForm.CueId.CheckboxOn, "check.wav", cfg);
+ uncheckSound = LoadCue(MainForm.CueId.CheckboxOff, "uncheck.wav", cfg);
+ }
+
+ public static void Play(bool isChecked)
+ {
+ var cfg = AppConfig.Load();
+ if (isChecked) { if (cfg.EnableCheckboxOnCue) checkSound?.Play(); }
+ else { if (cfg.EnableCheckboxOffCue) uncheckSound?.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; }
+ }
+}
diff --git a/src/RemSound.App/MainForm.cs b/src/RemSound.App/MainForm.cs
index 08341a2..8b45a4a 100644
--- a/src/RemSound.App/MainForm.cs
+++ b/src/RemSound.App/MainForm.cs
@@ -776,8 +776,8 @@ public sealed class MainForm : Form
// "Uncheck all inputs and outputs on all soundcards" — clears every device tick in one
// press. Button owns its own &-mnemonic (Alt+U), so AccessibleName stays clean per Ed's
// mnemonic convention.
- uncheckAllDevicesButton.Text = "Uncheck all inputs and outputs on all soundcards (Alt+&U)";
- uncheckAllDevicesButton.AccessibleName = "Uncheck all inputs and outputs on all soundcards";
+ uncheckAllDevicesButton.Text = "Uncheck all inputs and outputs on all soundcards and set ASIO driver to none (Alt+&U)";
+ uncheckAllDevicesButton.AccessibleName = "Uncheck all inputs and outputs on all soundcards and set ASIO driver to none";
uncheckAllDevicesButton.Click += (_, _) => UncheckAllDevices();
// Populate ASIO driver list at startup. Discovers all ASIO drivers via NAudio + a
@@ -4572,6 +4572,11 @@ public sealed class MainForm : Form
}
finally { suppressDeviceCheckChange = false; }
+ // Also reset the ASIO driver to "(none)" (row 0): the button now does a full reset to a
+ // clean WASAPI-only, nothing-selected state. Setting the index fires the driver-change
+ // handler, which saves the change and rebuilds the engine in WASAPI-only mode.
+ if (asioDriverBox.SelectedIndex > 0) asioDriverBox.SelectedIndex = 0;
+
ApplyAudioRuntime();
ApplyReceiveDevices();
MarkProfileDirty();
@@ -6512,6 +6517,9 @@ public sealed class MainForm : Form
public const string ReceiveOff = "receive-off";
public const string Hide = "hide";
public const string Show = "show";
+ // Played on every checkbox tick/untick across the whole app (CheckSoundService).
+ public const string CheckboxOn = "checkbox-on";
+ public const string CheckboxOff = "checkbox-off";
}
/// Load one cue sound. Resolution order:
@@ -6589,6 +6597,8 @@ public sealed class MainForm : Form
TryLoadCueSound(CueId.ReceiveOff, "recieve off.wav", out receiveOffSound);
TryLoadCueSound(CueId.Hide, "minimise.wav", out hideSound);
TryLoadCueSound(CueId.Show, "maximise.wav", out showSound);
+ // The app-wide checkbox tick/untick sounds live in their own service; keep them in step.
+ CheckSoundService.Reload();
}
///
@@ -7209,6 +7219,13 @@ public sealed class MainForm : Form
private void WireCheckedListAccessibility(CheckedListBox list, Label statusLabel, string itemKind)
{
+ // Tick/untick sound for the inputs/outputs lists. Gated on the list being focused so a real
+ // user click/spacebar clicks, but the bulk programmatic (un)checking done on profile load or
+ // by "uncheck all" (focus is on the button, not the list) stays silent.
+ list.ItemCheck += (_, e) =>
+ {
+ if (list.Focused) CheckSoundService.Play(e.NewValue == CheckState.Checked);
+ };
list.SelectedIndexChanged += (_, _) =>
{
if (list.SelectedIndex >= 0) lastFocusedListIndices[list] = list.SelectedIndex;
diff --git a/src/RemSound.App/PreferencesDialog.cs b/src/RemSound.App/PreferencesDialog.cs
index ca9f53c..299d14b 100644
--- a/src/RemSound.App/PreferencesDialog.cs
+++ b/src/RemSound.App/PreferencesDialog.cs
@@ -194,6 +194,11 @@ internal sealed class PreferencesDialog : Form
c => c.EnableHideCue, (c, v) => c.EnableHideCue = v),
MachineRow("Restore (show) sound", MainForm.CueId.Show, "maximise.wav",
c => c.EnableShowCue, (c, v) => c.EnableShowCue = v),
+ // Played on every checkbox tick / untick across the whole app (CheckSoundService).
+ MachineRow("Checkbox ticked sound", MainForm.CueId.CheckboxOn, "check.wav",
+ c => c.EnableCheckboxOnCue, (c, v) => c.EnableCheckboxOnCue = v),
+ MachineRow("Checkbox unticked sound", MainForm.CueId.CheckboxOff, "uncheck.wav",
+ c => c.EnableCheckboxOffCue, (c, v) => c.EnableCheckboxOffCue = v),
];
}
diff --git a/src/RemSound.App/Program.cs b/src/RemSound.App/Program.cs
index 026ebdf..e3dcc16 100644
--- a/src/RemSound.App/Program.cs
+++ b/src/RemSound.App/Program.cs
@@ -137,6 +137,10 @@ internal static class Program
KeyClickService.Initialize(AppConfig.Load().EnableKeyboardClicks);
Application.ApplicationExit += (_, _) => KeyClickService.Shutdown();
+ // Tick/untick sounds for checkbox toggles app-wide (CheckSoundService). Loaded here; reloaded
+ // by MainForm.ReloadAllCueSounds whenever cue settings change in Preferences.
+ CheckSoundService.Reload();
+
// One-time "your settings moved" notice — only the launch that actually relocated files
// shows it (idempotent migration ⇒ MovedAnything is false on every later launch). Shown
// here, after the guard and before the profile picker, so the user reads it once up front.
diff --git a/src/RemSound.App/SelfTest.cs b/src/RemSound.App/SelfTest.cs
index 2ac80cf..564d891 100644
--- a/src/RemSound.App/SelfTest.cs
+++ b/src/RemSound.App/SelfTest.cs
@@ -307,6 +307,7 @@ internal static class SelfTest
{
"connect.wav", "disconnect.wav", "start up.wav",
"send on.wav", "send off.wav", "recieve on.wav", "recieve off.wav", "minimise.wav", "maximise.wav",
+ "check.wav", "uncheck.wav",
})
{
Check(CueSounds.Variants(cue).Count > 0,
diff --git a/src/RemSound.Core/AppConfig.cs b/src/RemSound.Core/AppConfig.cs
index a047bf6..bfafca9 100644
--- a/src/RemSound.Core/AppConfig.cs
+++ b/src/RemSound.Core/AppConfig.cs
@@ -106,6 +106,9 @@ public sealed class AppConfig
public bool EnableReceiveOffCue { get; set; } = true;
public bool EnableHideCue { get; set; } = true;
public bool EnableShowCue { get; set; } = true;
+ /// Tick / untick sounds played on every checkbox toggle anywhere in the app.
+ public bool EnableCheckboxOnCue { get; set; } = true;
+ public bool EnableCheckboxOffCue { get; set; } = true;
/// Custom WAV overrides for the machine-wide cues above, keyed by cue id. The
/// equivalent of but machine-wide, since these cues don't