Skip to content

refactor(presenter): decouple audio delivery from rendering - #132

Draft
Shinokawa wants to merge 3 commits into
mainfrom
codex/audio-delivery-owner
Draft

Shinokawa wants to merge 3 commits into
mainfrom
codex/audio-delivery-owner

Conversation

@Shinokawa

@Shinokawa Shinokawa commented Sep 6, 2026

Copy link
Copy Markdown
Member

Problem and behavior

Presenter currently supplies PCM and renders on the same host tick. A blocked
render call therefore stops PCM delivery even though decoding and the audio device
can continue. This PR gives audio delivery its own owner so rendering stalls do
not interrupt an otherwise running audio stream. Video remains scheduled against
the shared playback clock when rendering becomes available again.

Dependency and review scope

Validated clock observations and foreground audio continuity are provided by
#131, which is now merged into main. This PR targets main and contains only
the audio ownership refactor and its tests/documentation. There are no changes to
core.rs, playback.rs, Metal, or clock-validity rules in this diff.

This remains a draft for independent architecture review.

Implementation

  • A dedicated erika-audio thread creates, calls and destroys the backend. A
    factory creates the platform object inside that thread, so no unsafe backend
    Send implementation is required. It owns the bounded PCM receiver, SoundTouch,
    clock/device reports, and pending playback-rate commits.
  • Acknowledged commands fence lifecycle transitions: quiesce the consumer before
    the producer, reset/drain after both ACKs, then resume producer before consumer.
    Expired queued commands do not execute later; failed resets return errors.
    No shared snapshot lock spans a backend operation or an ACK wait.
  • Existing pause, seek, track changes, open/close, stop/replay, device recovery,
    background opt-in and foreground video recovery contracts remain in force.
    A successful video present for the current generation releases the audio-start
    gate. Natural EOF hands remaining PCM to the device and parks the feeder without
    cutting off the hardware tail; explicit stop/close still release output.
  • Queue admission retains the 250 ms high-water target and fix(audio): smooth playback-rate transitions across native outputs #82's old-rate bridge.
    Pending rates commit even while rendering is blocked, and mixed-rate feedback
    remains suppressed.
  • The C ABI and host bindings are unchanged. Documentation in English, Chinese and
    Japanese explains that hosts use pause/stop/close to stop audio; stopping a
    display timer alone no longer starves it. audio_only_tick retains its video
    suspension/foreground recovery role.

Validation

The integration harness uses real FFmpeg fixture decoding, Player worker,
Presenter, the production audio owner and BufferedAudioOutput ring. Only render
stalls and the wall-time device callback are simulated; there is no extra feeder.

Render stall Queue before stall Underflow Core/ring clock gap after return
733 ms 158.292 ms 0 ms 2.850 ms absolute
1600 ms 154.563 ms 0 ms 1.206 ms absolute

These measurements concern core/ring clocks, not physical DAC/display latency or
macOS native fullscreen animation. See this PR's checks for platform build results.

@Shinokawa
Shinokawa changed the base branch from codex/pr131-clock-base to main September 6, 2026 17:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant