refactor: simplify clock drift compensation with hardcoded parameters and improved frame drop logic

This commit is contained in:
pstruebi
2025-10-23 18:32:59 +02:00
parent 60aa653aeb
commit da957141b4
3 changed files with 11 additions and 16 deletions

View File

@@ -42,7 +42,6 @@ class AuracastGlobalConfig(BaseModel):
assisted_listening_stream: bool = False
# Clock drift compensation: discard excess samples when buffer exceeds threshold
enable_drift_compensation: bool = False
drift_threshold_ms: float = 2.0 # Discard threshold in milliseconds
# "Audio input. "
# "'device' -> use the host's default sound input device, "

View File

@@ -651,19 +651,22 @@ class Streamer():
bigs = self.bigs
self.is_streaming = True
# Sample discard stats (clock drift compensation)
# frame drop algo parameters
sample_rate = big['audio_input']._pcm_format.sample_rate
samples_discarded_total = 0 # Total samples discarded
discard_events = 0 # Number of times we discarded samples
frames_since_last_discard = 999 # Guard: frames since last discard (start high to allow first drop)
enable_drift_compensation = global_config.enable_drift_compensation
discard_guard_frames = sample_rate // 100 # Don't allow discard within this many frames of previous discard
# Calculate threshold based on config (default 2ms)
drift_threshold_ms = global_config.drift_threshold_ms if global_config.enable_drift_compensation else 0
# Hardcoded parameters (unit: milliseconds)
drift_threshold_ms = 2.0 if enable_drift_compensation else 0.0
static_drop_ms = 0.5 if enable_drift_compensation else 0.0
# Guard interval measured in LC3 frames (10 ms each); 50 => 500 ms cooldown
discard_guard_frames = int(2*sample_rate / 1000) if enable_drift_compensation else 0
# Derived sample counts
drop_threshold_samples = int(sample_rate * drift_threshold_ms / 1000.0)
static_drop_samples = int(sample_rate * 0.0005) # Always drop a static amount of samples
if global_config.enable_drift_compensation:
static_drop_samples = int(sample_rate * static_drop_ms / 1000.0)
if enable_drift_compensation:
logging.info(f"Clock drift compensation ENABLED: threshold={drift_threshold_ms}ms, guard={discard_guard_frames} frames")
else:
logging.info("Clock drift compensation DISABLED")
@@ -699,12 +702,6 @@ class Streamer():
stream_finished[i] = True
continue
# Calculate threshold samples based on sample rate (only once per BIG)
if enable_drift_compensation and drop_threshold_samples == 0:
drop_threshold_samples = int(sample_rate * drift_threshold_ms / 1000.0)
logging.info(f"Drift compensation threshold: {drop_threshold_samples} samples ({drift_threshold_ms}ms @ {sample_rate}Hz)")
logging.info(f"Static drop amount: {static_drop_samples} samples (3.0ms @ {sample_rate}Hz)")
# Discard excess samples in buffer if above threshold (clock drift compensation)
if enable_drift_compensation and hasattr(big['audio_input'], '_stream') and big['audio_input']._stream:
sd_buffer_samples = big['audio_input']._stream.read_available
@@ -713,7 +710,7 @@ class Streamer():
if sd_buffer_samples > drop_threshold_samples and frames_since_last_discard >= discard_guard_frames:
# Always drop a static amount (3ms) for predictable behavior
# This matches the crossfade duration better for smoother transitions
samples_to_drop = static_drop_samples
samples_to_drop = min(static_drop_samples, max(1, big['lc3_frame_samples'] - 1))
try:
discarded_data = await anext(big['audio_input'].frames(samples_to_drop))
samples_discarded_total += samples_to_drop

View File

@@ -111,7 +111,6 @@ if __name__ == "__main__":
octets_per_frame = OCTETS_PER_FRAME,
transport=TRANSPORT1,
enable_drift_compensation=True,
drift_threshold_ms=2.0
)
config.debug = False