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>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
962f35ab80
commit
36431d6d39
@@ -262,14 +262,16 @@ Item| Shortcut| What it does
|
||||
|
||||
### Options menu
|
||||
|
||||
The Options menu gathers everything you might want to configure about the app — recording settings, keyboard shortcuts, startup behaviour, and general preferences.
|
||||
The Options menu gathers everything you might want to configure about the app — recording settings, keyboard shortcuts, profile passwords, and general preferences.
|
||||
|
||||
Item| Shortcut| What it does
|
||||
---|---|---
|
||||
**Recording settings …**| Alt+O, S| Opens the Recording settings dialog. Up to five lists: _Recording source_ (Alt+S), _File format_ (Alt+F), _Audio format attributes_ (Alt+A), _FLAC compression level_ (Alt+L — only shown when FLAC is chosen), and _Channels_ (Alt+C). The attributes list changes to match the format you pick. OK saves to the current profile; Cancel discards.
|
||||
**Keyboard shortcuts …**| Ctrl+K, or Alt+O, K| Opens the global hotkey dialog (mute, volume, show/hide window, start/stop recording, remote-control commands).
|
||||
**Startup behaviour …**| Alt+O, T| Opens the Startup behaviour dialog. Choose whether to launch automatically with Windows, which profile to load by default, and whether to start hidden in the tray.
|
||||
**Preferences …**| Ctrl+P, or Alt+O, P| Opens the Preferences dialog, organised into four tabs (move between them with Ctrl+Tab, or the arrow keys when the tab names have focus): **General** — the profiles folder, accept remote volume commands, UPnP router opening, and keep-logs / write-logs-now; **Audio cues** — the cue list and its sounds (see Audio cue sounds); **Startup behaviour** — start minimised / with Windows / with a specific profile; and **Update settings** — the update checks and install options. Esc or the Close button dismisses it.
|
||||
**Keyboard shortcuts …**| Ctrl+K, or Alt+O, K| Opens the global hotkey dialog (mute, volume, show/hide window, start/stop recording, remote-control commands, speak the status line).
|
||||
**Profile passwords …**| Alt+O, W| Lists every profile alongside its password, so you can view or change any of them in one place.
|
||||
**Reset the default audio device prompt**| Alt+O, R| Brings back the “use only the default audio device?” question if you previously ticked “Don't ask me this again” on it. See Following the Windows default audio device.
|
||||
**Enable / Disable Realtek ASIO**| —| Only shown if a Realtek ASIO driver is installed. Lets you reverse the choice RemSound offered about disabling that driver (Realtek's generic ASIO driver tends to grab the wrong device and clash with your screen reader).
|
||||
**Preferences …**| Ctrl+P, or Alt+O, P| Opens the Preferences dialog, organised into five tabs (move between them with Ctrl+Tab, or the arrow keys when the tab names have focus): **General** — the profiles folder, accept remote volume commands, and UPnP router opening; **Audio cues** — the cue list and its sounds (see Audio cue sounds); **Startup behaviour** — start minimised / with Windows / with a specific profile; **Update settings** — the update checks and install options; and **Logging** — enable logs, write logs now, and the log-folder housekeeping (see Logs and diagnostics). Esc or the Close button dismisses it.
|
||||
|
||||
### Help menu
|
||||
|
||||
@@ -311,6 +313,14 @@ Control| Shortcut| What it does
|
||||
|
||||
All the device lists are checkable lists — tick or untick an item to include or exclude that device. Profiles save which devices are ticked; a new profile starts with everything unticked.
|
||||
|
||||
### Following the Windows default audio device
|
||||
|
||||
At the very top of the **WASAPI outputs for received sound** list and the **WASAPI inputs to send** list there's a special entry: **Use Windows default audio device, follows Windows changes**. Tick it and RemSound uses whatever Windows currently treats as the default — the default speakers for received sound, the default microphone for sending — and, the useful part, it **follows that default on its own**. Make a headset your default and RemSound switches to it; unplug it and RemSound moves back, all without you touching the list.
|
||||
|
||||
It works alongside the specific devices: tick the default entry _and_ particular cards, and the sound plays out of (or is captured from) all of them at once. When you first tick it, RemSound asks whether you'd like to untick the other devices in that list, so you can choose to use only the default — with a “Don't ask me this again” option. If you tick that and later want the question back, use **Options → Reset the default audio device prompt**.
|
||||
|
||||
Unlike the specific device ticks (which start fresh each session), the “use default audio device” choice is remembered between launches. It's safe to remember because it can never point at the wrong card — it always resolves to whatever Windows is using right now.
|
||||
|
||||
### Receiving
|
||||
|
||||
To receive sound you need two things: **Receive audio** ticked, and at least one output device ticked. Without an output device, even when sound arrives there is nowhere for it to go.
|
||||
@@ -751,7 +761,8 @@ Alt+F, M| Rename the current profile
|
||||
Alt+F, N| Minimise to tray
|
||||
Alt+O, S| Recording settings
|
||||
Alt+O, K| Keyboard shortcuts
|
||||
Alt+O, T| Startup behaviour
|
||||
Alt+O, W| Profile passwords
|
||||
Alt+O, R| Reset the default audio device prompt
|
||||
Alt+O, P| Preferences
|
||||
Alt+F, X| Exit
|
||||
|
||||
@@ -903,7 +914,7 @@ All of these cues play through your default Windows sound output, which is separ
|
||||
|
||||
### The Audio cues tab
|
||||
|
||||
Open **File → Preferences** (or Ctrl+P) and go to the **Audio cues** tab. (Preferences is now organised into four tabs — General, Audio cues, Startup behaviour and Update settings — which you move between with Ctrl+Tab, or with the arrow keys when the row of tab names has focus.)
|
||||
Open **File → Preferences** (or Ctrl+P) and go to the **Audio cues** tab. (Preferences is organised into five tabs — General, Audio cues, Startup behaviour, Update settings and Logging — which you move between with Ctrl+Tab, or with the arrow keys when the row of tab names has focus.)
|
||||
|
||||
The **Audio cue sounds (Alt+N)** list shows every cue by name. Use the up and down arrow keys to move between them — as you land on each cue, RemSound plays its current sound, so you can hear what's set just by arrowing through.
|
||||
|
||||
@@ -1004,7 +1015,7 @@ You'll see the window close, then reopen on the new version within a second or t
|
||||
|
||||
When the install finishes and RemSound reopens, it loads the same profile that was running just before the update — you don't see the profile picker, and your devices, peer list, codec and latency settings all come back exactly as they were. This means a silent update in the middle of a session drops the audio briefly while the install finishes, then your session reconnects on its own. You don't have to be at the computer when it happens.
|
||||
|
||||
This is a one-shot, just-after-the-update behaviour. The very next time you launch RemSound manually (from the desktop, the Start menu, or the tray icon), it follows your normal startup choice — the picker if that's how you've set it, or your chosen startup profile if you've picked one in Options → Startup behaviour.
|
||||
This is a one-shot, just-after-the-update behaviour. The very next time you launch RemSound manually (from the desktop, the Start menu, or the tray icon), it follows your normal startup choice — the picker if that's how you've set it, or your chosen startup profile if you've picked one in Preferences → Startup behaviour.
|
||||
|
||||
If the profile that was running can't be found after the update (you'd renamed or moved it during the session, for example), RemSound falls back to your normal startup behaviour rather than getting stuck.
|
||||
|
||||
|
||||
+9
-14
@@ -1,34 +1,29 @@
|
||||
# RemSound v4.5
|
||||
# RemSound v4.6
|
||||
|
||||
Keyboard shortcuts get easier to manage — and people upgrading from before 4.4 no longer lose theirs.
|
||||
Follow your Windows default audio device — for both the sound you receive and the mic you send.
|
||||
|
||||
## Bring your shortcuts across when you upgrade
|
||||
## Use the Windows default audio device
|
||||
|
||||
In version 4.4, keyboard shortcuts moved from being saved per-profile to one shared set for the whole app. If you're updating **from before 4.4**, RemSound now offers — the first time you run it — to **bring your shortcuts across** from one of your existing profiles: it lists your profiles, you pick the one you set your shortcuts up in, or choose to start fresh. (If you already updated to 4.4, nothing changes for you here.)
|
||||
Both WASAPI device lists now have a new entry at the very top — **"Use Windows default audio device, follows Windows changes"** — in the outputs for received sound, and in the inputs to send.
|
||||
|
||||
## A clearer Keyboard shortcuts dialog
|
||||
Tick it and RemSound uses whatever Windows currently treats as the default (the default speakers for received sound, the default microphone for sending), and — the useful part — it **follows that default automatically**. Make a headset your default and RemSound switches to it on its own; unplug it and it moves back, all without touching the list.
|
||||
|
||||
- A new **Clear this shortcut** button next to Close, so clearing a shortcut is discoverable — you no longer have to know the Del key.
|
||||
- When you're setting a shortcut, pressing **Delete** in that box now leaves it unassigned.
|
||||
|
||||
## Clearer remote-control labels
|
||||
|
||||
The three remote-control rows that adjust a peer's **RemSound** volume now say so — "Send remote RemSound volume up / down to peers" and "Send remote RemSound receive mute toggle to peers" — so they're easy to tell apart from the "Windows global volume" rows.
|
||||
It works alongside the specific devices: tick the default entry *and* particular cards, and the sound plays out of (or is captured from) all of them at once. When you first turn it on, RemSound offers to untick the other devices so you use only the default — with a "Don't ask me this again" option. If you hide that question and later want it back, use **Options → Reset the default audio device prompt**.
|
||||
|
||||
## Compatibility
|
||||
|
||||
**v4.5 talks to v3.3 through v4.4 with no trouble** — the over-the-network format is unchanged, so you don't have to update both ends at once. (Everyone still needs **v3.3 or newer**, where end-to-end encryption came in.)
|
||||
**v4.6 talks to v3.3 through v4.5 with no trouble** — the over-the-network format is unchanged, so you don't have to update both ends at once. (Everyone still needs **v3.3 or newer**, where end-to-end encryption came in.)
|
||||
|
||||
## Install
|
||||
|
||||
1. Download `RemSound-v4.5.zip` from this release.
|
||||
1. Download `RemSound-v4.6.zip` from this release.
|
||||
2. Close RemSound.
|
||||
3. Extract the zip **over your existing RemSound folder**, overwriting program files when prompted. The zip is program files only — it won't touch your settings, profiles, logs or recordings.
|
||||
4. Run `RemSound.exe`.
|
||||
|
||||
## Upgrading
|
||||
|
||||
**From v3.6 or newer:** Help → Check for updates installs v4.5 with the in-app updater — and if it can't finish, it puts your old version back exactly as it was.
|
||||
**From v3.6 or newer:** Help → Check for updates installs v4.6 with the in-app updater — and if it can't finish, it puts your old version back exactly as it was.
|
||||
|
||||
**From v1.9–v3.5:** Check for updates works, but uses your current version's older updater for this one hop. If auto-update has been failing on your machine, install by hand using the steps above.
|
||||
|
||||
|
||||
+18
-7
@@ -296,14 +296,16 @@ ul, ol { padding-left: 1.4em; }
|
||||
|
||||
<h3>Options menu</h3>
|
||||
|
||||
<p>The Options menu gathers everything you might want to configure about the app — recording settings, keyboard shortcuts, startup behaviour, and general preferences.</p>
|
||||
<p>The Options menu gathers everything you might want to configure about the app — recording settings, keyboard shortcuts, profile passwords, and general preferences.</p>
|
||||
|
||||
<table>
|
||||
<tr><th>Item</th><th>Shortcut</th><th>What it does</th></tr>
|
||||
<tr><td><strong>Recording settings…</strong></td><td>Alt+O, S</td><td>Opens the Recording settings dialog. Up to five lists: <em>Recording source</em> (Alt+S), <em>File format</em> (Alt+F), <em>Audio format attributes</em> (Alt+A), <em>FLAC compression level</em> (Alt+L — only shown when FLAC is chosen), and <em>Channels</em> (Alt+C). The attributes list changes to match the format you pick. OK saves to the current profile; Cancel discards.</td></tr>
|
||||
<tr><td><strong>Keyboard shortcuts…</strong></td><td>Ctrl+K, or Alt+O, K</td><td>Opens the global hotkey dialog (mute, volume, show/hide window, start/stop recording, remote-control commands).</td></tr>
|
||||
<tr><td><strong>Startup behaviour…</strong></td><td>Alt+O, T</td><td>Opens the Startup behaviour dialog. Choose whether to launch automatically with Windows, which profile to load by default, and whether to start hidden in the tray.</td></tr>
|
||||
<tr><td><strong>Preferences…</strong></td><td>Ctrl+P, or Alt+O, P</td><td>Opens the Preferences dialog, organised into four tabs (move between them with Ctrl+Tab, or the arrow keys when the tab names have focus): <strong>General</strong> — the profiles folder, accept remote volume commands, UPnP router opening, and keep-logs / write-logs-now; <strong>Audio cues</strong> — the cue list and its sounds (see <a href="#audio-cues">Audio cue sounds</a>); <strong>Startup behaviour</strong> — start minimised / with Windows / with a specific profile; and <strong>Update settings</strong> — the update checks and install options. Esc or the Close button dismisses it.</td></tr>
|
||||
<tr><td><strong>Keyboard shortcuts…</strong></td><td>Ctrl+K, or Alt+O, K</td><td>Opens the global hotkey dialog (mute, volume, show/hide window, start/stop recording, remote-control commands, speak the status line).</td></tr>
|
||||
<tr><td><strong>Profile passwords…</strong></td><td>Alt+O, W</td><td>Lists every profile alongside its password, so you can view or change any of them in one place.</td></tr>
|
||||
<tr><td><strong>Reset the default audio device prompt</strong></td><td>Alt+O, R</td><td>Brings back the “use only the default audio device?” question if you previously ticked “Don't ask me this again” on it. See <a href="#tab-io">Following the Windows default audio device</a>.</td></tr>
|
||||
<tr><td><strong>Enable / Disable Realtek ASIO</strong></td><td>—</td><td>Only shown if a Realtek ASIO driver is installed. Lets you reverse the choice RemSound offered about disabling that driver (Realtek's generic ASIO driver tends to grab the wrong device and clash with your screen reader).</td></tr>
|
||||
<tr><td><strong>Preferences…</strong></td><td>Ctrl+P, or Alt+O, P</td><td>Opens the Preferences dialog, organised into five tabs (move between them with Ctrl+Tab, or the arrow keys when the tab names have focus): <strong>General</strong> — the profiles folder, accept remote volume commands, and UPnP router opening; <strong>Audio cues</strong> — the cue list and its sounds (see <a href="#audio-cues">Audio cue sounds</a>); <strong>Startup behaviour</strong> — start minimised / with Windows / with a specific profile; <strong>Update settings</strong> — the update checks and install options; and <strong>Logging</strong> — enable logs, write logs now, and the log-folder housekeeping (see <a href="#logs">Logs and diagnostics</a>). Esc or the Close button dismisses it.</td></tr>
|
||||
</table>
|
||||
|
||||
<h3>Help menu</h3>
|
||||
@@ -349,6 +351,14 @@ ul, ol { padding-left: 1.4em; }
|
||||
|
||||
<p>All the device lists are checkable lists — tick or untick an item to include or exclude that device. Profiles save which devices are ticked; a new profile starts with everything unticked.</p>
|
||||
|
||||
<h3>Following the Windows default audio device</h3>
|
||||
|
||||
<p>At the very top of the <strong>WASAPI outputs for received sound</strong> list and the <strong>WASAPI inputs to send</strong> list there's a special entry: <strong>Use Windows default audio device, follows Windows changes</strong>. Tick it and RemSound uses whatever Windows currently treats as the default — the default speakers for received sound, the default microphone for sending — and, the useful part, it <strong>follows that default on its own</strong>. Make a headset your default and RemSound switches to it; unplug it and RemSound moves back, all without you touching the list.</p>
|
||||
|
||||
<p>It works alongside the specific devices: tick the default entry <em>and</em> particular cards, and the sound plays out of (or is captured from) all of them at once. When you first tick it, RemSound asks whether you'd like to untick the other devices in that list, so you can choose to use only the default — with a “Don't ask me this again” option. If you tick that and later want the question back, use <strong>Options → Reset the default audio device prompt</strong>.</p>
|
||||
|
||||
<p>Unlike the specific device ticks (which start fresh each session), the “use default audio device” choice is remembered between launches. It's safe to remember because it can never point at the wrong card — it always resolves to whatever Windows is using right now.</p>
|
||||
|
||||
<h3>Receiving</h3>
|
||||
|
||||
<p>To receive sound you need two things: <strong>Receive audio</strong> ticked, and at least one output device ticked. Without an output device, even when sound arrives there is nowhere for it to go.</p>
|
||||
@@ -792,7 +802,8 @@ Audient USB Audio ASIO Driver — Pair 3 (channels 5/6): Loop-back 1 (L) / L
|
||||
<tr><td>Alt+F, N</td><td>Minimise to tray</td></tr>
|
||||
<tr><td>Alt+O, S</td><td>Recording settings</td></tr>
|
||||
<tr><td>Alt+O, K</td><td>Keyboard shortcuts</td></tr>
|
||||
<tr><td>Alt+O, T</td><td>Startup behaviour</td></tr>
|
||||
<tr><td>Alt+O, W</td><td>Profile passwords</td></tr>
|
||||
<tr><td>Alt+O, R</td><td>Reset the default audio device prompt</td></tr>
|
||||
<tr><td>Alt+O, P</td><td>Preferences</td></tr>
|
||||
<tr><td>Alt+F, X</td><td>Exit</td></tr>
|
||||
</table>
|
||||
@@ -957,7 +968,7 @@ Use whatever key combinations you prefer (for example Ctrl+Shift+Up / Ctrl+Shift
|
||||
|
||||
<h3>The Audio cues tab</h3>
|
||||
|
||||
<p>Open <strong>File → Preferences</strong> (or Ctrl+P) and go to the <strong>Audio cues</strong> tab. (Preferences is now organised into four tabs — General, Audio cues, Startup behaviour and Update settings — which you move between with Ctrl+Tab, or with the arrow keys when the row of tab names has focus.)</p>
|
||||
<p>Open <strong>File → Preferences</strong> (or Ctrl+P) and go to the <strong>Audio cues</strong> tab. (Preferences is organised into five tabs — General, Audio cues, Startup behaviour, Update settings and Logging — which you move between with Ctrl+Tab, or with the arrow keys when the row of tab names has focus.)</p>
|
||||
|
||||
<p>The <strong>Audio cue sounds (Alt+N)</strong> list shows every cue by name. Use the up and down arrow keys to move between them — as you land on each cue, RemSound plays its current sound, so you can hear what's set just by arrowing through.</p>
|
||||
|
||||
@@ -1061,7 +1072,7 @@ Use whatever key combinations you prefer (for example Ctrl+Shift+Up / Ctrl+Shift
|
||||
|
||||
<p>When the install finishes and RemSound reopens, it loads the same profile that was running just before the update — you don't see the profile picker, and your devices, peer list, codec and latency settings all come back exactly as they were. This means a silent update in the middle of a session drops the audio briefly while the install finishes, then your session reconnects on its own. You don't have to be at the computer when it happens.</p>
|
||||
|
||||
<p>This is a one-shot, just-after-the-update behaviour. The very next time you launch RemSound manually (from the desktop, the Start menu, or the tray icon), it follows your normal startup choice — the picker if that's how you've set it, or your chosen startup profile if you've picked one in Options → Startup behaviour.</p>
|
||||
<p>This is a one-shot, just-after-the-update behaviour. The very next time you launch RemSound manually (from the desktop, the Start menu, or the tray icon), it follows your normal startup choice — the picker if that's how you've set it, or your chosen startup profile if you've picked one in Preferences → Startup behaviour.</p>
|
||||
|
||||
<p>If the profile that was running can't be found after the update (you'd renamed or moved it during the session, for example), RemSound falls back to your normal startup behaviour rather than getting stuck.</p>
|
||||
|
||||
|
||||
@@ -20,6 +20,25 @@ internal sealed class AboutDialog : Form
|
||||
/// updates" path.</summary>
|
||||
private const string ReleaseNotes =
|
||||
"""
|
||||
RemSound v4.6
|
||||
|
||||
Follow your Windows default audio device.
|
||||
|
||||
Both device lists — the outputs for received
|
||||
sound and the inputs to send — now have a "Use
|
||||
Windows default audio device, follows Windows
|
||||
changes" entry at the top. Tick it and RemSound
|
||||
uses whatever Windows is currently using, and
|
||||
switches with it on its own: make a headset your
|
||||
default and RemSound follows, no clicking. You
|
||||
can still tick specific devices alongside it.
|
||||
|
||||
When you turn it on, RemSound offers to untick
|
||||
the other devices so you use only the default;
|
||||
if you hide that question with "Don't ask
|
||||
again", Options → Reset the default audio device
|
||||
prompt brings it back.
|
||||
|
||||
RemSound v4.5
|
||||
|
||||
Keyboard shortcuts get easier to manage.
|
||||
|
||||
+266
-11
@@ -400,6 +400,20 @@ public sealed class MainForm : Form
|
||||
// wired in the constructor; disposed in FormClosing (which unregisters the COM callback).
|
||||
private AudioDeviceChangeNotifier? deviceChangeNotifier;
|
||||
|
||||
// The "Use Windows default ..." follower entries sit at the top of the receive-output and
|
||||
// send-input lists. Ticked, the current Windows default device is resolved live (and re-resolved
|
||||
// when Windows' default changes). A synthetic sentinel DeviceId that can never collide
|
||||
// with a real endpoint id (which looks like "{0.0.0.00000000}.{guid}").
|
||||
private static readonly AudioDeviceChoice DefaultOutputFollower =
|
||||
new("Use Windows default audio device, follows Windows changes", "__use-default-output__", CaptureKind.Loopback) { IsDefaultFollower = true };
|
||||
private static readonly AudioDeviceChoice DefaultInputFollower =
|
||||
new("Use Windows default audio device, follows Windows changes", "__use-default-input__", CaptureKind.Input) { IsDefaultFollower = true };
|
||||
// The Windows-default device id we last routed to while following, per direction. A default-device
|
||||
// change doesn't change the device SET, so the list-sync wouldn't catch it — we compare against
|
||||
// these to spot it and re-route (see ReapplyIfFollowedDefaultChanged).
|
||||
private string? lastFollowedDefaultOutputId;
|
||||
private string? lastFollowedDefaultInputId;
|
||||
|
||||
// Receive-output device IDs the user/profile selected — kept even while a device is unplugged,
|
||||
// so a card that returns is silently re-ticked and re-opened (issue #5: recover after USB
|
||||
// unplug). Receive-only: the send lists deliberately don't persist selection (AudioDeviceCatalog).
|
||||
@@ -1042,15 +1056,25 @@ public sealed class MainForm : Form
|
||||
receiveOutputDevicesList.ItemCheck += (_, e) =>
|
||||
{
|
||||
if (suppressDeviceCheckChange) return;
|
||||
// Track the user's intent so a card that's later unplugged is re-ticked + re-opened when
|
||||
// it returns (issue #5). See ReapplyRememberedReceiveOutputs.
|
||||
if (receiveOutputDevicesList.Items[e.Index] is AudioDeviceChoice c && c.DeviceId is { } rid)
|
||||
if (receiveOutputDevicesList.Items[e.Index] is AudioDeviceChoice c)
|
||||
{
|
||||
if (c.IsDefaultFollower)
|
||||
{
|
||||
// Machine-wide preference (AppConfig), not part of the profile.
|
||||
var on = e.NewValue == CheckState.Checked;
|
||||
PersistUseDefaultDevice(output: true, on);
|
||||
if (on) BeginInvoke(new Action(() => MaybeUntickOthersForDefault(receiveOutputDevicesList, output: true)));
|
||||
}
|
||||
else if (c.DeviceId is { } rid)
|
||||
{
|
||||
// Track the user's intent so a card that's later unplugged is re-ticked + re-opened
|
||||
// when it returns (issue #5). See ReapplyRememberedReceiveOutputs.
|
||||
if (e.NewValue == CheckState.Checked) rememberedReceiveOutputIds.Add(rid);
|
||||
else rememberedReceiveOutputIds.Remove(rid);
|
||||
MarkProfileDirty();
|
||||
}
|
||||
}
|
||||
BeginInvoke(ApplyReceiveDevices);
|
||||
MarkProfileDirty();
|
||||
};
|
||||
WireCheckedListAccessibility(sendOutputDevicesList, sendOutputDevicesStatusLabel, "output device");
|
||||
WireCheckedListAccessibility(sendInputDevicesList, sendInputDevicesStatusLabel, "input device");
|
||||
@@ -1060,6 +1084,15 @@ public sealed class MainForm : Form
|
||||
if (suppressDeviceCheckChange) return;
|
||||
logFile.Event($"ui: capture (WASAPI mic) '{sendInputDevicesList.Items[args.Index]}' {(args.NewValue == CheckState.Checked ? "ticked" : "unticked")}");
|
||||
BeginInvoke(ApplyAudioRuntime);
|
||||
if (sendInputDevicesList.Items[args.Index] is AudioDeviceChoice { IsDefaultFollower: true })
|
||||
{
|
||||
// Machine-wide preference (AppConfig), not part of the profile — and no mic-block check
|
||||
// (the default mic could be anything; the runtime apply handles a blocked default).
|
||||
var on = args.NewValue == CheckState.Checked;
|
||||
PersistUseDefaultDevice(output: false, on);
|
||||
if (on) BeginInvoke(new Action(() => MaybeUntickOthersForDefault(sendInputDevicesList, output: false)));
|
||||
return;
|
||||
}
|
||||
MarkProfileDirty();
|
||||
// Heads-up when the user ticks a WASAPI mic ON but Windows is blocking desktop-app
|
||||
// microphone access — capture would open but silently send nothing. Deferred so the
|
||||
@@ -1826,6 +1859,15 @@ public sealed class MainForm : Form
|
||||
};
|
||||
profilePasswordsItem.Click += (_, _) => OpenProfilePasswordManager();
|
||||
|
||||
// Reset the one-time "untick the other devices?" prompt that appears when you turn on a
|
||||
// "Use Windows default audio device" box — clears the remembered Yes/No so it asks again.
|
||||
// Mirrors the Realtek toggle's "let the user reverse a one-time decision" pattern.
|
||||
var resetDefaultDeviceItem = new ToolStripMenuItem("&Reset the default audio device prompt")
|
||||
{
|
||||
AccessibleName = "Reset the default audio device prompt",
|
||||
};
|
||||
resetDefaultDeviceItem.Click += (_, _) => ResetDefaultAudioDevicePrompt();
|
||||
|
||||
// Realtek ASIO enable/disable toggle — only present when a Realtek ASIO driver is actually
|
||||
// installed. Lets the user reverse the disable decision (or disable a driver they kept).
|
||||
ToolStripMenuItem? realtekToggle = null;
|
||||
@@ -1844,6 +1886,7 @@ public sealed class MainForm : Form
|
||||
recordingSettingsItem,
|
||||
keyboardItem,
|
||||
profilePasswordsItem,
|
||||
resetDefaultDeviceItem,
|
||||
};
|
||||
if (realtekToggle is not null) optionItems.Add(realtekToggle);
|
||||
optionItems.Add(new ToolStripSeparator());
|
||||
@@ -3932,10 +3975,25 @@ public sealed class MainForm : Form
|
||||
{
|
||||
if (item.DeviceId is { } id) specs.Add(new CaptureSourceSpec(id, CaptureKind.Loopback, item.Name));
|
||||
}
|
||||
var addedInputIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
string? followedDefaultInput = null;
|
||||
foreach (var item in sendInputDevicesList.CheckedItems.OfType<AudioDeviceChoice>())
|
||||
{
|
||||
if (item.DeviceId is { } id) specs.Add(new CaptureSourceSpec(id, CaptureKind.Input, item.Name));
|
||||
if (item.IsDefaultFollower)
|
||||
{
|
||||
// Resolve the current Windows default capture device live.
|
||||
followedDefaultInput = ResolveDefaultDeviceId(NAudio.CoreAudioApi.DataFlow.Capture);
|
||||
if (!string.IsNullOrEmpty(followedDefaultInput) && addedInputIds.Add(followedDefaultInput))
|
||||
{
|
||||
specs.Add(new CaptureSourceSpec(followedDefaultInput, CaptureKind.Input, "Windows default audio device"));
|
||||
}
|
||||
}
|
||||
else if (item.DeviceId is { } id && addedInputIds.Add(id))
|
||||
{
|
||||
specs.Add(new CaptureSourceSpec(id, CaptureKind.Input, item.Name));
|
||||
}
|
||||
}
|
||||
lastFollowedDefaultInputId = followedDefaultInput;
|
||||
foreach (var item in asioSendDevicesList.CheckedItems.OfType<AudioDeviceChoice>())
|
||||
{
|
||||
// ASIO channels have no Loopback/Input distinction — Kind is irrelevant for ASIO
|
||||
@@ -3963,8 +4021,12 @@ public sealed class MainForm : Form
|
||||
// The user re-ticks once per session, avoiding the "wrong-device-still-selected"
|
||||
// failure mode after a card unplug or ID change.
|
||||
sendOutputDevicesSignature = SyncDeviceCheckedListBox(sendOutputDevicesList, outputs);
|
||||
sendInputDevicesSignature = SyncDeviceCheckedListBox(sendInputDevicesList, inputs);
|
||||
receiveOutputDevicesSignature = SyncDeviceCheckedListBox(receiveOutputDevicesList, outputs);
|
||||
sendInputDevicesSignature = SyncDeviceCheckedListBox(sendInputDevicesList, WithDefaultFollower(inputs, DefaultInputFollower));
|
||||
receiveOutputDevicesSignature = SyncDeviceCheckedListBox(receiveOutputDevicesList, WithDefaultFollower(outputs, DefaultOutputFollower));
|
||||
// Re-tick the "Use Windows default" followers from the saved preference. They don't ride the
|
||||
// per-session "all unticked" rule above — a follower can never go stale (it always resolves
|
||||
// to the current default), so persisting it is the whole point of the feature.
|
||||
RestoreDefaultFollowerChecks();
|
||||
|
||||
// Ground-truth log so we can definitively see the device list and initial check state
|
||||
// each launch — diagnoses any "device was checked at startup" mystery.
|
||||
@@ -4066,8 +4128,8 @@ public sealed class MainForm : Form
|
||||
}
|
||||
|
||||
var sendOutputChanged = MaybeSyncList(sendOutputDevicesList, wasapiOutputs, ref sendOutputDevicesSignature);
|
||||
var sendInputChanged = MaybeSyncList(sendInputDevicesList, wasapiInputs, ref sendInputDevicesSignature);
|
||||
var receiveOutputChanged = MaybeSyncList(receiveOutputDevicesList, wasapiOutputs, ref receiveOutputDevicesSignature);
|
||||
var sendInputChanged = MaybeSyncList(sendInputDevicesList, WithDefaultFollower(wasapiInputs, DefaultInputFollower), ref sendInputDevicesSignature);
|
||||
var receiveOutputChanged = MaybeSyncList(receiveOutputDevicesList, WithDefaultFollower(wasapiOutputs, DefaultOutputFollower), ref receiveOutputDevicesSignature);
|
||||
bool asioSendChanged;
|
||||
bool asioReceiveChanged;
|
||||
if (asioProbeAttemptedAndFailed)
|
||||
@@ -4094,6 +4156,9 @@ public sealed class MainForm : Form
|
||||
{
|
||||
ApplyReceiveDevices();
|
||||
}
|
||||
// A default-device change leaves the device SET unchanged, so the syncs above don't fire — but
|
||||
// if we're following the Windows default, the target device just moved. Catch that and re-route.
|
||||
ReapplyIfFollowedDefaultChanged();
|
||||
}
|
||||
|
||||
private void ClearAsioProbeCache()
|
||||
@@ -4494,17 +4559,207 @@ public sealed class MainForm : Form
|
||||
// Combine WASAPI device-ids and ASIO synthetic-ids into one list. The
|
||||
// CompositeRenderBackend splits them internally and feeds each child the right subset.
|
||||
var ids = new List<string>();
|
||||
var added = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
string? followedDefault = null;
|
||||
foreach (var c in receiveOutputDevicesList.CheckedItems.OfType<AudioDeviceChoice>())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(c.DeviceId)) ids.Add(c.DeviceId);
|
||||
if (c.IsDefaultFollower)
|
||||
{
|
||||
// Resolve the current Windows default render device live.
|
||||
followedDefault = ResolveDefaultDeviceId(NAudio.CoreAudioApi.DataFlow.Render);
|
||||
if (!string.IsNullOrEmpty(followedDefault) && added.Add(followedDefault)) ids.Add(followedDefault);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(c.DeviceId) && added.Add(c.DeviceId))
|
||||
{
|
||||
ids.Add(c.DeviceId);
|
||||
}
|
||||
}
|
||||
lastFollowedDefaultOutputId = followedDefault;
|
||||
foreach (var c in asioReceiveOutputDevicesList.CheckedItems.OfType<AudioDeviceChoice>())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(c.DeviceId)) ids.Add(c.DeviceId);
|
||||
if (!string.IsNullOrEmpty(c.DeviceId) && added.Add(c.DeviceId)) ids.Add(c.DeviceId);
|
||||
}
|
||||
receiver.SetOutputDevices(ids);
|
||||
}
|
||||
|
||||
// ===================== "Use Windows default device" follower support =====================
|
||||
|
||||
/// <summary>Returns a new list with <paramref name="follower"/> at the top, followed by
|
||||
/// <paramref name="devices"/>. Used to put the "Use Windows default ..." entry first in the
|
||||
/// receive-output and send-input lists.</summary>
|
||||
private static IReadOnlyList<AudioDeviceChoice> WithDefaultFollower(IReadOnlyList<AudioDeviceChoice> devices, AudioDeviceChoice follower)
|
||||
{
|
||||
var list = new List<AudioDeviceChoice>(devices.Count + 1) { follower };
|
||||
list.AddRange(devices);
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>The id of the current Windows default endpoint for the given direction, or null if
|
||||
/// there isn't one / it can't be read. Best-effort.</summary>
|
||||
private static string? ResolveDefaultDeviceId(NAudio.CoreAudioApi.DataFlow flow)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var enumerator = new NAudio.CoreAudioApi.MMDeviceEnumerator();
|
||||
if (!enumerator.HasDefaultAudioEndpoint(flow, NAudio.CoreAudioApi.Role.Multimedia)) return null;
|
||||
using var device = enumerator.GetDefaultAudioEndpoint(flow, NAudio.CoreAudioApi.Role.Multimedia);
|
||||
return device.ID;
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
private static bool IsFollowerChecked(CheckedListBox list) =>
|
||||
list.CheckedItems.OfType<AudioDeviceChoice>().Any(c => c.IsDefaultFollower);
|
||||
|
||||
/// <summary>When following the Windows default and that default device changes, the device SET is
|
||||
/// unchanged so the list-sync doesn't fire — so re-route here. Called from OnAudioEndpointsChanged,
|
||||
/// which the change-notifier raises (among other things) on a default-device change.</summary>
|
||||
private void ReapplyIfFollowedDefaultChanged()
|
||||
{
|
||||
if (IsFollowerChecked(receiveOutputDevicesList)
|
||||
&& ResolveDefaultDeviceId(NAudio.CoreAudioApi.DataFlow.Render) != lastFollowedDefaultOutputId)
|
||||
{
|
||||
ApplyReceiveDevices();
|
||||
}
|
||||
if (IsFollowerChecked(sendInputDevicesList)
|
||||
&& ResolveDefaultDeviceId(NAudio.CoreAudioApi.DataFlow.Capture) != lastFollowedDefaultInputId)
|
||||
{
|
||||
ApplySendSources();
|
||||
}
|
||||
}
|
||||
|
||||
private static void PersistUseDefaultDevice(bool output, bool on)
|
||||
{
|
||||
try
|
||||
{
|
||||
var c = AppConfig.Load();
|
||||
if (output) c.UseDefaultOutputDevice = on; else c.UseDefaultInputDevice = on;
|
||||
c.Save();
|
||||
}
|
||||
catch { /* harmless — choice just won't survive a restart */ }
|
||||
}
|
||||
|
||||
private void RestoreDefaultFollowerChecks()
|
||||
{
|
||||
AppConfig cfg;
|
||||
try { cfg = AppConfig.Load(); }
|
||||
catch { return; }
|
||||
SetFollowerChecked(receiveOutputDevicesList, cfg.UseDefaultOutputDevice);
|
||||
SetFollowerChecked(sendInputDevicesList, cfg.UseDefaultInputDevice);
|
||||
}
|
||||
|
||||
private void SetFollowerChecked(CheckedListBox list, bool on)
|
||||
{
|
||||
if (!on) return;
|
||||
suppressDeviceCheckChange = true;
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < list.Items.Count; i++)
|
||||
{
|
||||
if (list.Items[i] is AudioDeviceChoice { IsDefaultFollower: true })
|
||||
{
|
||||
list.SetItemChecked(i, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally { suppressDeviceCheckChange = false; }
|
||||
}
|
||||
|
||||
/// <summary>Offered when the user ticks a "Use Windows default" follower: optionally untick the
|
||||
/// other devices in that list so only the default is used. Remembers the answer if the user ticks
|
||||
/// "Don't ask again".</summary>
|
||||
private void MaybeUntickOthersForDefault(CheckedListBox list, bool output)
|
||||
{
|
||||
if (IsDisposed) return;
|
||||
AppConfig cfg;
|
||||
try { cfg = AppConfig.Load(); }
|
||||
catch { return; }
|
||||
var remembered = output ? cfg.UntickOthersWhenUsingDefaultOutput : cfg.UntickOthersWhenUsingDefaultInput;
|
||||
bool untick;
|
||||
if (remembered is bool r)
|
||||
{
|
||||
untick = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
var verification = new TaskDialogVerificationCheckBox("Don't ask me this again");
|
||||
var page = new TaskDialogPage
|
||||
{
|
||||
Caption = "RemSound",
|
||||
Heading = "Use only the default audio device?",
|
||||
Text = output
|
||||
? "You've turned on “Use Windows default audio device”. Do you want to untick the other outputs in this list, so received sound plays only to whatever Windows is using?\n\nChoose No to keep playing to those outputs as well as the default audio device."
|
||||
: "You've turned on “Use Windows default audio device”. Do you want to untick the other inputs in this list, so only the Windows default audio device is captured?\n\nChoose No to keep capturing those inputs as well as the default audio device.",
|
||||
Icon = TaskDialogIcon.Information,
|
||||
Buttons = { TaskDialogButton.Yes, TaskDialogButton.No },
|
||||
DefaultButton = TaskDialogButton.No,
|
||||
Verification = verification,
|
||||
};
|
||||
untick = TaskDialog.ShowDialog(this, page) == TaskDialogButton.Yes;
|
||||
if (verification.Checked)
|
||||
{
|
||||
try
|
||||
{
|
||||
var c2 = AppConfig.Load();
|
||||
if (output) c2.UntickOthersWhenUsingDefaultOutput = untick; else c2.UntickOthersWhenUsingDefaultInput = untick;
|
||||
c2.Save();
|
||||
}
|
||||
catch { /* harmless */ }
|
||||
}
|
||||
}
|
||||
if (untick) UntickAllExceptDefaultFollower(list, output);
|
||||
}
|
||||
|
||||
private void UntickAllExceptDefaultFollower(CheckedListBox list, bool output)
|
||||
{
|
||||
var anyChanged = false;
|
||||
suppressDeviceCheckChange = true;
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < list.Items.Count; i++)
|
||||
{
|
||||
if (list.Items[i] is AudioDeviceChoice c && !c.IsDefaultFollower && list.GetItemChecked(i))
|
||||
{
|
||||
list.SetItemChecked(i, false);
|
||||
if (output && c.DeviceId is { } id) rememberedReceiveOutputIds.Remove(id);
|
||||
anyChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally { suppressDeviceCheckChange = false; }
|
||||
if (!anyChanged) return;
|
||||
if (output) ApplyReceiveDevices(); else ApplyAudioRuntime();
|
||||
}
|
||||
|
||||
/// <summary>Options-menu action: clear the remembered answer to the "untick the other devices?"
|
||||
/// prompt (for both output and input), so it asks again next time the user turns on a "Use Windows
|
||||
/// default audio device" box. The discoverable way back from the prompt's "Don't ask again".</summary>
|
||||
private void ResetDefaultAudioDevicePrompt()
|
||||
{
|
||||
try
|
||||
{
|
||||
var c = AppConfig.Load();
|
||||
c.UntickOthersWhenUsingDefaultOutput = null;
|
||||
c.UntickOthersWhenUsingDefaultInput = null;
|
||||
c.Save();
|
||||
logFile.Event("default audio device: reset the untick-others prompt (will ask again)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logFile.Event($"default audio device: reset prompt failed: {ex.GetType().Name}: {ex.Message}");
|
||||
}
|
||||
var page = new TaskDialogPage
|
||||
{
|
||||
Caption = "RemSound",
|
||||
Heading = "Default audio device prompt reset",
|
||||
Text = "RemSound will ask again next time you turn on a “Use Windows default audio device” option.",
|
||||
Icon = TaskDialogIcon.Information,
|
||||
Buttons = { TaskDialogButton.OK },
|
||||
};
|
||||
TaskDialog.ShowDialog(this, page);
|
||||
}
|
||||
|
||||
/// <summary>A receive-output card that was unplugged drops out of the WASAPI list (and its tick
|
||||
/// with it). When it returns, re-tick it from <see cref="rememberedReceiveOutputIds"/> so audio
|
||||
/// resumes automatically (issue #5). Only RE-ticks present-but-unticked remembered devices; it
|
||||
|
||||
@@ -27,6 +27,13 @@ internal sealed record CodecChoice(string Label, AudioTransportCodec Codec, int
|
||||
|
||||
internal sealed record AudioDeviceChoice(string Name, string? DeviceId, CaptureKind Kind = CaptureKind.Loopback)
|
||||
{
|
||||
/// <summary>True for the special "Use Windows default" pseudo-entry that sits at the top of the
|
||||
/// receive-output and send-input lists. When ticked, the current Windows default device is
|
||||
/// resolved live (Render for an output follower, Capture for an input follower — derived from
|
||||
/// <see cref="Kind"/>) and re-resolved whenever Windows' default changes. Its <see cref="DeviceId"/>
|
||||
/// is a sentinel, never a real endpoint id.</summary>
|
||||
public bool IsDefaultFollower { get; init; }
|
||||
|
||||
public override string ToString() => Name;
|
||||
}
|
||||
|
||||
|
||||
@@ -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>4.5</Version>
|
||||
<Version>4.6</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -181,6 +181,21 @@ public sealed class AppConfig
|
||||
/// readable in their profile files.</summary>
|
||||
public bool KeyboardShortcutsImportOffered { get; set; }
|
||||
|
||||
/// <summary>If true, the "Use Windows default output" follower at the top of the received-sound
|
||||
/// output list is ticked at startup, so received sound plays to whatever Windows currently calls
|
||||
/// the default output and re-routes when that changes (plug in headphones, it follows). Unlike a
|
||||
/// specific device tick — which can go stale and play to the wrong card — a follower can never be
|
||||
/// wrong, so it's safe to persist. Default false (opt-in). <see cref="UseDefaultInputDevice"/> is
|
||||
/// the same idea for the send-input list.</summary>
|
||||
public bool UseDefaultOutputDevice { get; set; }
|
||||
public bool UseDefaultInputDevice { get; set; }
|
||||
|
||||
/// <summary>Remembered answer to the "untick the other outputs/inputs when you turn on Use Windows
|
||||
/// default?" prompt. Null = ask each time; true = always untick; false = never untick. Set when the
|
||||
/// user ticks "Don't ask again" on that prompt. Machine-wide.</summary>
|
||||
public bool? UntickOthersWhenUsingDefaultOutput { get; set; }
|
||||
public bool? UntickOthersWhenUsingDefaultInput { get; set; }
|
||||
|
||||
/// <summary>If non-null and a profile with this title exists, RemSound skips the
|
||||
/// startup profile picker and loads this profile directly. Combine with
|
||||
/// <see cref="StartMinimised"/> + the Windows auto-start registry entry
|
||||
|
||||
Reference in New Issue
Block a user