Lock-to-clock always on (remove option); service audio hardcoded to Opus live config
Ed, 2026-07-17. The bigger half of the service crackle fix: raw PCM sounded hideous; Opus at the live-jamming frame with Small packets and lock-to-clock sounds right. Lock to audio clock (TightLatencyMode) is now ALWAYS ON and no longer a user option: - Removed the checkbox from the main window and the service dialog. Nobody ever runs with it off -- off just adds delay. - Main app forces the sender tight at startup; Profile.TightLatencyMode defaults true and is kept only for file compatibility. Load/SaveTightLatencyMode accessors removed. Service audio profile is now fixed and unconfigurable: - Removed the "Audio profile" tab from the service config dialog entirely (two tabs left: Connectivity, Audio send). - The service FORCES Opus + 2.5 ms frame (120 samples) + Small packets + lock-to-clock at runtime in ServiceSendHost.ApplyProfile, ignoring whatever the profile carries, so a stale/hand-edited profile can never put it back on a bad codec. SaveToProfile writes the same fixed values. Robustness: ApplyProfile now swallows a capture Start() failure (with lock-to-clock always on the WASAPI lane is push-mode, which opens the device synchronously and throws if it is invalid/gone) -- presence stays up and the device-watcher/self-heal re-open when a device is available, instead of the exception crashing the service loop. Self-test "Service sender parity" rewritten to prove the service overrides a PCM profile to Opus/120. Gate: 39/39. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
ec149ed09e
commit
a450cae66a
@@ -806,12 +806,15 @@ internal static class SelfTest
|
||||
/// divergences found auditing the service against the main app.</summary>
|
||||
private static string? ServiceSenderParity()
|
||||
{
|
||||
// The profile deliberately carries the WRONG audio transport (raw PCM, broadcast frame, Standard
|
||||
// rate) to prove the service IGNORES it and forces its known-good live config (Opus, 2.5 ms frame,
|
||||
// Small packets, lock-to-clock) — the fix for the crackly-service report (Ed, 2026-07-17).
|
||||
var profile = new Profile
|
||||
{
|
||||
Title = "parity",
|
||||
Codec = AudioTransportCodec.Opus,
|
||||
OpusFrameSamplesPerChannel = 960, // broadcast
|
||||
SendRate = SendRate.Tight, // "Small" — should halve the Opus frame to 480
|
||||
Codec = AudioTransportCodec.Pcm,
|
||||
OpusFrameSamplesPerChannel = 960,
|
||||
SendRate = SendRate.Standard,
|
||||
WasapiSendMode = "devices",
|
||||
};
|
||||
profile.SelectedWasapiSendOutputs.Add("fake-device-id"); // a source so ApplyProfile proceeds
|
||||
@@ -820,17 +823,20 @@ internal static class SelfTest
|
||||
profile.Password = RemSoundCrypto.Obfuscate(pw);
|
||||
|
||||
using var host = new ServiceSendHost(() => profile);
|
||||
Check(host.ApplyProfile(profile), "ApplyProfile should proceed with a source + peer + password");
|
||||
// ApplyProfile sets the sender's crypto + codec BEFORE it opens the (fake) device; a device-open
|
||||
// failure is now swallowed inside ApplyProfile, so this returns and the config is readable.
|
||||
host.ApplyProfile(profile);
|
||||
var cfg = host.SenderConfigForTest;
|
||||
|
||||
Check(cfg.Key is { Length: > 0 } && cfg.Key.SequenceEqual(RemSoundCrypto.DeriveKey(pw)),
|
||||
"the service must set the audio key = DeriveKey(password)");
|
||||
Check(cfg.Fingerprint is { Length: > 0 } && cfg.Fingerprint.SequenceEqual(RemSoundCrypto.Fingerprint(pw)),
|
||||
"the service must set the audio FINGERPRINT = Fingerprint(password), or the peer rejects the stream");
|
||||
Check(cfg.Codec == AudioTransportCodec.Opus, "codec must round-trip to the sender");
|
||||
Check(cfg.Frame == MainForm.EffectiveOpusFrameSamples(AudioTransportCodec.Opus, 960, SendRate.Tight) && cfg.Frame == 480,
|
||||
$"the Small send rate must halve the Opus frame like the main app (got {cfg.Frame})");
|
||||
return "key + fingerprint + effective Opus frame match the main app";
|
||||
Check(cfg.Codec == AudioTransportCodec.Opus,
|
||||
$"the service must FORCE Opus regardless of the profile codec (got {cfg.Codec})");
|
||||
Check(cfg.Frame == 120,
|
||||
$"the service must force the 2.5 ms live Opus frame (120 samples), regardless of the profile (got {cfg.Frame})");
|
||||
return "service forces Opus + 2.5ms frame + lock-to-clock regardless of profile; crypto matches";
|
||||
}
|
||||
|
||||
/// <summary>The lock-screen service's app-yield token: while a hold is active the service must see an
|
||||
|
||||
Reference in New Issue
Block a user