v5.2: stability and polish — deep-audit bug fixes + install-flow fixes
Verified findings from a multi-dimension code audit, plus the two install-flow bugs: - Fix Opus encoder use-after-free on a codec/rate change while streaming (guard swap vs encode). - Fix "both" single-file recording dropping audio + drifting (drain both directions in lockstep). - Fix broken clip counter, UPnP teardown on exit, auto-update-restart foreground grant, and a malformed-Opus-format packet orphaning a playout session forever. - Post-install relaunch now respects start-minimised; uninstall is path-aware so it won't clear a different copy's run-at-startup. - Perf/hygiene: cache AppConfig off UI hot paths, fold per-peer EQ+gain into one pass, deterministic disposal (tray menu, timers, COM shortcut, Process handles, process meter), ring-buffer overflow guard, receiver session-lock fix, remote-control allow-list moved onto the UI thread. - Remove dead code (two IsAsioBackend, SessionPlayout.Reset, IsSameEndpoint, RemSoundUpdater IDisposable); several stale-doc fixes. Deferred (not in this release): drift-estimator tweak, peer-discovery pruning, uninstall retry-loop, encryption nonce. Wire format unchanged (interops v3.3-v5.1). Version -> 5.2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7038fef67c
commit
2fb9274a95
@@ -49,6 +49,17 @@ public sealed class AudioRingBuffer
|
||||
/// <summary>Producer side. Writes the entire span; if the buffer is full, drops the oldest bytes to make room.</summary>
|
||||
public void Write(ReadOnlySpan<byte> source)
|
||||
{
|
||||
if (source.Length > storage.Length)
|
||||
{
|
||||
// A single write bigger than the whole ring can't fit. Keep only the NEWEST storage.Length
|
||||
// bytes and count the older excess as dropped, so an oversized write degrades to dropped
|
||||
// audio instead of throwing (a CopyTo overflow) on this lock-free real-time producer path.
|
||||
// Unreachable in normal use — writes are MTU-bounded and the ring is far larger — but this
|
||||
// turns an undocumented precondition into a safe, real invariant.
|
||||
Interlocked.Add(ref drops, source.Length - storage.Length);
|
||||
source = source[^storage.Length..];
|
||||
}
|
||||
|
||||
var currentTail = tail;
|
||||
var currentHead = Volatile.Read(ref head);
|
||||
var available = storage.Length - ((currentTail - currentHead) & 0x7FFFFFFF);
|
||||
|
||||
@@ -102,7 +102,7 @@ public sealed class RemSoundSettingsStore
|
||||
public void SaveMaxLatencyMs(int value)
|
||||
{
|
||||
var s = Load() ?? new Settings();
|
||||
s.MaxLatencyMs = Math.Clamp(value, 1, 500);
|
||||
s.MaxLatencyMs = Math.Clamp(value, 5, 500); // match LoadMaxLatencyMs's [5,500] so stored == effective
|
||||
Save(s);
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ public sealed class RemSoundSettingsStore
|
||||
public void SaveMaxLatencyMsAsio(int value)
|
||||
{
|
||||
var s = Load() ?? new Settings();
|
||||
s.MaxLatencyMsAsio = Math.Clamp(value, 1, 500);
|
||||
s.MaxLatencyMsAsio = Math.Clamp(value, 5, 500); // match LoadMaxLatencyMsAsio's [5,500]
|
||||
Save(s);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user