Skip to content

t2bce_audio: playback crackle builds up over minutes and resets on stream restart (timer-driven copy drifts against the T2 read position) — MacBookPro16,2, linux-t2 7.2.4 #28

Description

@Real-Bimox

Hardware: MacBookPro16,2 (13" 2020, T2). Kernel: linux-t2 7.2.4.arch1-2 (Watanare), t2bce_audio v0.02 (staging).
Userspace: PipeWire 1.6.8, WirePlumber 0.5.17, t2-apple-audio-dsp 16_2 graph (also reproduces on the raw PCM without DSP).

Symptom

Playback starts clean. After some minutes a crackle appears, grows steadily, becomes louder than the programme material, and vanishes instantly whenever the stream stops and restarts (e.g. a YouTube ad, loading a new video, pause/resume > 5 s). Then it builds up again.

Evidence

  • PipeWire graph: 0 xruns, steady ~70 ms ALSA queue (/proc/asound/card0/pcm0p/sub0/status sampled every 5 s for a minute), CPU idle.
  • The digital signal captured at the ALSA node monitor is clean while the crackle is audible (no clipping, no discontinuities, kurtosis ~2.2, no impulsive outliers) -> the artefact is created below the ALSA node.
  • hw_params: period_size: 1, buffer_size: 16640, SNDRV_PCM_INFO_NO_PERIOD_WAKEUP.
  • Reported pointer advance rate inside one session: 47999.61 frames/s (-8 ppm) measured against the driver's own tstamp.

Root cause (from t2bce_audio/pcm.c at AdityaGarg8/t2bce 525498b5, "Update 1", 2026-09-13)

t2audio_playback_timer() runs every 1 ms (T2AUDIO_PLAYBACK_TICK_NS) and does
frames = elapsed_ns * runtime->rate / NSEC_PER_SEC, then t2audio_playback_copy() copies that many frames from the ALSA ring into the BCE shared buffer (buf->ptr) at bridge_pos, and t2audio_pcm_pointer() returns playback_frames % buffer_size.

The host-side write position is therefore driven purely by CLOCK_MONOTONIC, while the T2 consumes the shared buffer on its own audio clock. There is no feedback from the T2 on the playback path. Commit ab4b0ac2 ("correlate host and remote clock") only feeds the remote timestamps into the capture pointer (time_from_start = ktime_get_boottime() - stream->remote_timestamp); the playback branch of t2audio_pcm_pointer() still returns the timer-driven playback_frames. Any ppm difference between the two clocks accumulates: the copy position slowly walks into the T2's read position, first producing partial-buffer reads (crackle that grows with signal level), then full overlap. A stop/start (started cleared, bridge_pos/playback_frames reset) realigns the two positions, which is why the symptom resets on every stream restart. With a ~70 ms host lead and tens of ppm of drift, onset after 10-30 minutes matches what is observed.

Suggested fix

Rate-correct the host copy against T2 feedback (consumed position or the same remote timestamps the capture path already uses), or expose a real DMA position instead of a timer estimate; alternatively track the T2's read position and clamp bridge_pos so the host never overtakes it. Also consider a larger period_size (the current 1-frame period makes the ALSA core register a ~21 us CPU latency PM-QoS while the PCM is open, which blocks C2/C3 and heats the machine).

Workaround

Pausing playback for >5 s (so PipeWire stops/restarts the PCM) clears the crackle.

Filed here because issues are disabled on AdityaGarg8/t2bce and deqrocks/t2bce. Happy to test patches on this machine (Arch, linux-t2 from the arch-mact2 repo).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions