fix(core): preserve native audio fallback - #3048
Draft
cwhy wants to merge 1 commit into
Draft
Conversation
cwhy
marked this pull request as draft
August 5, 2026 09:17
cwhy
marked this pull request as ready for review
August 5, 2026 09:36
cwhy
marked this pull request as draft
August 5, 2026 09:53
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Root cause
The runtime supports mixed ownership on a per-element basis:
HTMLMediaElementBefore this change, one active WebAudio source made
webAudio.isActive()true and passedoutputMuted: truetosyncRuntimeMedia. BecauseoutputMutedmeans “force-mute every media element,” one WebAudio-owned track globally muted unrelated native fallback tracks. In the reported mixed-CORS case, background music entered WebAudio while narration could not; the narration continued advancing natively but was silent.Ownership invariant
The intended rule is:
syncRuntimeMediaalready implements that split:outputMuted || userMutedforce-mutes all elementsisWebAudioOwned(el)mutes only the element duplicated through WebAudioThis PR therefore passes only
state.mediaOutputMutedas the global output mute and retainsisWebAudioOwned: (el) => webAudio.ownsElement(el)for per-element ownership.Why the removed clauses are not lost safeguards
nativeMediaSyncDisabledThe call to
syncRuntimeMediais already insideif (!state.nativeMediaSyncDisabled). Rechecking the same flag inside theoutputMutedexpression was redundant. When native media sync is disabled, this call is skipped entirely.webAudioMediaDisabledDisabling WebAudio prevents new WebAudio scheduling and stops active sources. With no active source,
webAudio.ownsElement(el)is false and native media is the intended output. This flag should choose the transport, not globally silence native media.webAudio.isActive()This reports whether any WebAudio source is active; it does not report whether WebAudio owns a particular element. Using it as a global mute conflated composition-level activity with per-element ownership and defeated the existing
ownsElement(el)contract.Safety behavior that remains unchanged
state.mediaOutputMuted, immediately mutes the WebAudio master and every DOM media element, and is reasserted on subsequent ticks.state.bridgeMuted/userMuted.ownsElement(el)continues to prevent double audio.media-autoplay-blocked, allowing the player to promote audio ownership to parent proxies.Regression coverage
The new runtime test creates two simultaneous tracks while WebAudio is globally active:
set-media-output-muted: true, both tracks are mutedExisting media and transport tests additionally cover owned/unowned separation, global parent/user mute, exact-element ownership, and ownership cleanup.
Verification
Draft status and remaining validation
This PR intentionally remains a draft while stronger end-to-end coverage is added or reviewed. Useful follow-ups before marking it ready:
NotAllowedErrorpromotion to parent-proxy ownershipThose gaps do not change the ownership reasoning above, but documenting and exercising them reduces regression risk before merge.