Skip to content

Audio clicks at every loop boundary: redundant wrap re-seek + unflushed swr + ring.clear() race #78

Description

@duremovich

From the 2026-07-03 comprehensive review. Three compounding bugs produce audible clicks at every loop boundary; fix as one unit.

  1. Redundant wrap re-seek (AudioSystem.cpp:382-401): expectedSample is computed from the wrapped mediaLocalFrame (:349-371), so at each loop boundary it jumps ~sourceLength→0 — far past the oneTickSamples*3 threshold — firing seekPending on every wrap. The decode worker already loops seamlessly on its own (AudioDecodeWorker.cpp:74-81) and has ~1 s of looped audio buffered; the controller's redundant seek clears that ring and re-seeks. Any Loop/PingPong clip whose source is shorter than its authored window clicks at every wrap.
  2. swresample not flushed on seek (AudioDecoder.cpp:146-147): av_seek_frame + avcodec_flush_buffers leave m_swr internal delay/buffered samples intact → stale resampled audio emitted first after every seek, and swr_get_delay accounting is off. Drain (swr_convert null input) or reinit swr on seek.
  3. ring.clear() races the RT mixer (AudioRingBuffer.hpp:67-70): clear()'s own doc says "only call when the consumer is known idle," but the decode worker calls w->ring.clear() on seek (AudioDecodeWorker.cpp:47) while mixSource.active is still true at a loop wrap (AudioSystem.cpp:300-301) → relaxed-store race with read(). Stays in-bounds (indices are % capacity) so glitch-not-crash, but it is the audible artifact path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtier:coreLives in core (GPLv3, in-repo); always free, fully featured

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions