Per-app send fixes + apps-mode UI rework (no send-all) + truly-global remembered lists

Per-app capture (the "foobar alone = no sound" report):
- ProcessLoopbackCapture: take ActivateAudioInterfaceAsync's out operation as a raw
  IntPtr (released after completion) instead of a typed interface. The eager RCW cast
  of the not-yet-realised operation object threw InvalidCastException / E_NOINTERFACE
  and killed EVERY specific-app capture at start.
- CompositeCaptureBackend: single IsPushEligible authority shared by Start,
  UpdateSources and the coalesced rebuild. The update paths were missing the
  ProcessLoopback exclusion, so switching whole-device -> one app kept the push
  backend and fed it "proc:<pid>" (GetDevice ArgumentException).
- PushModeWasapiBackend: loud backstop rejecting process-loopback specs.
- Self-test: lifecycle test now FAILS on an activation error (it previously passed
  green with the feature completely dead) + pure routing-rule checks.

Apps-mode UI (Ed 2026-07-16): the "Send all applications" checkbox is GONE from the
main window - applications mode always means picking specific apps; whole-system
audio is devices mode's job. Active list = running apps + any ticked app that is not
running ("(not running)" so it can be unticked); Remembered list = global address
book minus whatever is ticked. In-place list reconcile (no Clear+rebuild) kills the
NVDA double-read of the toggled row. Profile.SendAllApplications stays for the
SERVICE (deliberate divergence - a headless lock-screen sender wants system audio).

Remembered lists now genuinely machine-wide (AppConfig-backed): the settings store is
an intra-process cache, so remembered applications were forgotten on every exit and
remembered peers were per-profile in practice. Both books moved to AppConfig; legacy
per-profile peers are unioned in on profile load; profile save snapshots the global
book back for old-build compat. Cross-instance persistence pinned by self-test.

Service (issue #23): 15s capture pulse (callbacks/bytes/pre-encode peak/frames sent)
while sending - distinguishes "endpoint mix is genuinely silent at the lock screen"
from a pipeline fault, which callbacks alone cannot.

Gate: 38/38.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-07-16 22:50:38 +01:00
co-authored by Claude Opus 4.8
parent 92ad77ff52
commit 1003bbfdf2
10 changed files with 337 additions and 150 deletions
+13
View File
@@ -96,6 +96,19 @@ public sealed class AppConfig
/// and the Options → Manage named peers dialog. Empty by default.</summary>
public Dictionary<string, NamedPeer> NamedPeers { get; set; } = new();
/// <summary>Machine-wide remembered PEER entries — ONE shared address book across all profiles
/// (Ed, 2026-07: both remembered lists live in global, not the profile). Before this the list rode
/// in each profile's JSON, so it was per-profile in practice; each old profile's legacy list is
/// unioned in here the first time it's opened (RemSoundSettingsStore.ApplyProfile). Null = none yet.
/// Cleared from Preferences → General.</summary>
public List<string>? RememberedPeers { get; set; }
/// <summary>Machine-wide remembered APPLICATION process names (lower-case) — the shared "apps I
/// send" address book, companion to <see cref="RememberedPeers"/>. Before 2026-07-16 this only
/// lived in the in-memory settings cache, which silently forgot the list on every app exit. Null =
/// none yet. Cleared from Preferences → General.</summary>
public List<string>? RememberedApplications { get; set; }
/// <summary>Legacy flat name map (friendly name only). Kept so pre-registry configs still deserialise;
/// migrated into <see cref="NamedPeers"/> on load, then no longer written.</summary>
public Dictionary<string, string> PeerFriendlyNames { get; set; } = new();