v3.4: handle-leak fix, Realtek ASIO block, device notifications, quick profile switch, hotkey announcements
Freezes the v3.4 feature set (everything since the v3.3 public release): - Fix the receiver handle leak: the 3-second device-refresh timer reopened the configured ASIO driver every tick, and Realtek's ASIO driver leaks Event+Mutant handles on every open. Cache the ASIO probe per driver so it is opened once. - Realtek ASIO block: detect a Realtek ASIO driver, offer once to disable it, and never touch it again if disabled; Options-menu toggle to reverse. Global config. - Device hot-plug is event-driven (AudioDeviceChangeNotifier) instead of a 3s poll; debounced refresh, falls back to polling if registration fails. - Quick profile switch: new global hotkey opens an NVDA-friendly popup of all profiles (current marked); Enter/click switches; plays a new "profile menu open" cue with Preferences mute + custom-sound. - Announce assigned global hotkeys on the controls/menu items they drive (NVDA reads "press X anywhere"). File > Open already had Ctrl+O. - Held-back changes folded in: config-folder migration, codec-column fix, Tailscale endpoint network-prune, empty-password guard, and the handle-leak diagnostics (ProcessSelfMeter, HandleTypeProbe). Version bumped to 3.4.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
fc451ba3e3
commit
dd70613017
@@ -832,6 +832,36 @@ public sealed class AudioReceiver : IDisposable
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wire codec of the freshest currently-active receive session across all peers, or
|
||||
/// null when nothing is being received. Surfaced in the SNAP log's Codec column so a
|
||||
/// receive-only node reports what it is actually decoding rather than its dormant send-codec
|
||||
/// setting (the old behaviour logged "Pcm" for a node receiving "PCM over Opus"). Lockless
|
||||
/// scan for the freshest session, then a short lock to read its codec.
|
||||
/// </summary>
|
||||
public AudioTransportCodec? ActiveReceiveCodec
|
||||
{
|
||||
get
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
SessionPlayout? freshest = null;
|
||||
foreach (var sp in playoutEngine.ActiveSessions)
|
||||
{
|
||||
if (now - sp.LastWriteUtc > SessionIdleTimeout) continue;
|
||||
if (freshest is null || sp.LastWriteUtc > freshest.LastWriteUtc) freshest = sp;
|
||||
}
|
||||
if (freshest is null) return null;
|
||||
lock (sessionsLock)
|
||||
{
|
||||
if (sessions.TryGetValue((freshest.Endpoint, freshest.StreamId), out var session))
|
||||
{
|
||||
return session.Codec;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// === Packet routing (called on network thread) ===
|
||||
|
||||
/// <summary>Hook for Heartbeat packets that arrive on the audio receiver's socket. The
|
||||
|
||||
Reference in New Issue
Block a user