v5 pre-release: multi-track drift fix, more self-tests, logging, CLI, version bump

Multi-track recording drift fix (Ed's question — can the separate tracks drift over an hour?):
 * Root: FlushPeerTracks skipped a peer that produced no samples in a render block, so a peer that
   went quiet long enough for its session to be pruned (>4 s idle) would have its track fall behind
   and desync. Now every peer track is padded to a full render block each cycle (silence when the
   peer produced nothing), so all peer tracks stay sample-locked to the single render clock — they
   can't drift apart however long the recording runs, and all end the same length. Same padding for
   the single-file bypass path. OnRecordBlockComplete now carries the block's float count.
   (The peer tracks are already resampled to the render clock per peer, so this makes peer-to-peer
   sync exact; your own "me" track is capture-clocked — same soundcard for capture+playback = same
   clock = no drift, different interfaces can drift slightly.)

 * Self-test: two new steps — "Per-peer shaping DSP" (PeerDspChain unity/master-off/volume/parametric
   + ParametricToPeaking) and "v5 settings and shaping round-trip" (AppConfig defaults, NamedPeers,
   MainTabOrder, parametric PeerShaping, recording default = Both).
 * Logging (gated by the logging checkbox): master shaping switch, EQ-mode change, parametric band
   add/delete, peer rename/clear/delete, and the applied Appearance settings after Preferences close.
 * CLI: --list-profiles and --list-named-peers (read-only), in --help.
 * Version bumped to 5.0; About-box changelog, RELEASE_NOTES.md and README updated for v5.

Build clean; --selftest passes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ednunp
2026-07-08 18:19:01 +01:00
co-authored by Claude Opus 4.8
parent fcc3cdc793
commit aeca24ae17
10 changed files with 193 additions and 31 deletions
+38
View File
@@ -80,6 +80,10 @@ internal static class CommandLine
return WithConsole(PrintVersion);
case "--devices": case "--list-devices":
return WithConsole(() => { WriteDevices(Console.Out); return 0; });
case "--profiles": case "--list-profiles":
return WithConsole(ListProfiles);
case "--named-peers": case "--list-named-peers":
return WithConsole(ListNamedPeers);
case "--selftest": case "--self-test": case "--smoke-test": case "--smoketest":
return WithConsole(() => SelfTest.Run(args));
case "--perftest": case "--perf-test":
@@ -139,6 +143,37 @@ internal static class CommandLine
return 0;
}
/// <summary>--list-profiles: print the saved profile titles (read-only).</summary>
private static int ListProfiles()
{
var cfg = AppConfig.Load();
var store = new ProfileStore(cfg.ProfilesDirectory ?? AppConfig.ProfilesBaseDirectory);
var titles = store.ListProfileTitles();
if (titles.Count == 0) { Console.WriteLine("No saved profiles."); return 0; }
Console.WriteLine($"Saved profiles ({titles.Count}):");
foreach (var t in titles) Console.WriteLine($" {t}");
return 0;
}
/// <summary>--list-named-peers: print the machine-wide named-peers book (read-only) — the friendly
/// names you've given peers, with where and when each was last seen.</summary>
private static int ListNamedPeers()
{
var named = AppConfig.Load().NamedPeers;
if (named.Count == 0) { Console.WriteLine("No named peers."); return 0; }
Console.WriteLine($"Named peers ({named.Count}):");
foreach (var kv in named.OrderBy(k => k.Value.FriendlyName, StringComparer.CurrentCultureIgnoreCase))
{
var np = kv.Value;
var seen = np.LastSeenUtc == default
? "not seen yet"
: $"last seen {np.LastSeenUtc.ToLocalTime():d MMM yyyy}"
+ (string.IsNullOrWhiteSpace(np.LastAddress) ? "" : $", {np.LastAddress}");
Console.WriteLine($" {np.FriendlyName} ({np.MachineName}) - {seen}");
}
return 0;
}
private static int PrintHelp()
{
Console.WriteLine($"RemSound {AppVersion} - command-line options");
@@ -150,6 +185,9 @@ internal static class CommandLine
Console.WriteLine(" --version Show the installed version.");
Console.WriteLine(" --devices List all microphones, outputs and ASIO drivers,");
Console.WriteLine(" with their formats and device ids.");
Console.WriteLine(" --list-profiles List your saved profile names.");
Console.WriteLine(" --list-named-peers List the friendly names you've given peers, with");
Console.WriteLine(" where and when each was last seen.");
Console.WriteLine(" --selftest [--seconds N] Run the built-in self-test - a localhost audio");
Console.WriteLine(" (or --smoke-test) round-trip plus checks of encryption, the wire format,");
Console.WriteLine(" settings, profiles, dialog accessibility and bundled files.");