Skip to content

fix: play update audio effects as the incoming track of new BGM - #374

Merged
han4wluc merged 5 commits into
mainfrom
fix/update-bgm-effect-on-start
Oct 2, 2026
Merged

han4wluc merged 5 commits into
mainfrom
fix/update-bgm-effect-on-start

Conversation

@han4wluc

@han4wluc han4wluc commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A line whose BGM action uses an update audio effect throws at line entry when the music starts or changes source:

[story.scenes["…"].sections["…"].lines["…"].actions.bgm.audioEffects]
[resources.audioEffects.…] Audio effect resource "…" has type "update", but the BGM action changes source identity. Use a transition resource.

In RouteVN Creator, fullscreen Preview fails to open with this error (seen on an iPad with a real project). The scene editor hides it because it strips the selected line's update effects. A game would also stop at that line.

Visual animations already allow this: an update animation used for add, remove, or replace animates one available target instead of throwing (docs/AnimationModel.md, "Update fallback").

Fix

An update effect now animates one track per sound, like the visual update fallback, preferring the sounds the line plays:

  • Retained sound: plays the update as an update. Unchanged.
  • Added or source-replaced sound: plays the update as its incoming (enter) track. The final keyframes must still match the sound's persistent mix, as before. Without an initialValue or startValue, the track starts at its persistent level, as Route Graphics does for enter.
  • Line stops every sound: each sound plays the update as its outgoing (exit) track, as a visual update animation plays on a removed element. An update that fades to 0 fades the music out.
  • Sound removed while others play or enter: stops without an effect; if the others don't change, the update has no effect at all. When a line switches tracks, the old one stops without fading out; a crossfade still needs a transition resource.
  • Relative pan in an incoming update continues from the channel pan before the line, or from the new channel pan when music starts.

Transition and update tracks now compile through one helper. docs/RouteEngine.md and the audio effect resource schema description document the rule.

Transition effects are unchanged. #355 separately skips a transition effect on music that keeps playing.

Validation

  • 20 new or rewritten unit and engine tests, including relative pan, playback rate, and a partial removal, covering start, source change (src, startAt, endAt, startDelayMs), switching sound ids, retained plus added plus removed sounds in one update, stop as an outgoing track, speed validation on stop lines, and the endpoint rule. 14 of them fail on main; the other two guard behavior main already has.
  • VT (browser): three new vt/specs/audioChannels/ specs, in the style of audio-effects-initial-entry.yaml, run the built engine in Chromium and assert the render state and emitted effect: audio-effects-update-initial-entry, audio-effects-update-source-change, and audio-effects-update-stop. All three pass and fail on main, where the line throws: the music never starts, the old track keeps playing, or the music never stops. The stop spec checks the outgoing fade-to-0 track. Running all 23 audioChannels specs locally (rtgl vt screenshot --folder audioChannels), 22 pass. audio-effects-initial-entry fails the same way on main ("expected 70, got 100"), so it predates this PR. These specs are not in CI, which only runs robustness/*.yaml VT specs that have screenshot references.
  • Route Engine has no AVT. AVT lives in Route Graphics, which already covers enter tracks.
  • Real audio check: I rendered the engine's render states with Route Graphics 1.46.0's AVT runner (Chromium, OfflineAudioContext) and measured the output level every 100 ms. Route Graphics accepted every effect.
    • No effect: 0.11 constant.
    • Update fade-in (initialValue: 0 → 100 over 1 s) on music start: 0.01 0.02 … 0.10 0.11, an audible fade-in.
    • Track switch at 0.5 s with the same effect: the old track holds 0.11 until 0.5 s, then the new track fades in from 0.02 to 0.11 over 1 s.
    • Line stopping the music at 0.5 s with an update to volume 0 over 1 s: 0.11 until 0.5 s, then 0.10 0.08 … 0.01 0.00, an audible fade-out.
  • bun run lint, check:test-markers, format:changed, and bun run test pass (2,178 tests).

Review

A code review found these; fixed in this PR:

  • An update on a line that stopped the music was dropped, unlike visual update animations on removed elements, and its selection was no longer validated. It now plays as the outgoing track.
  • The enter, update, and transition paths duplicated the compile-and-settle code. They now share one helper.
  • The transition-only graph comparison ran for every update target.
  • The schema description was out of date.
  • Test gaps: intermediate keyframes, and removed, retained, and added sounds in one update.

A second review found more; fixed in this PR:

  • Relative pan in an update played on a source change started from the tween's final pan, so a +0.2 step overshot (0.7 → 0.5 instead of -0.2 → 0.5).
  • The track helper chose its pan baseline through an implicit default. It now takes an explicit side and baseline.
  • The BGM update docs still said updates require retained sounds, and the schema said the final value always persists, which isn't true on a stop line.

Not changed here:

  • A transition effect on a retained sound whose mix changes still throws; fix: skip incompatible line BGM effects #355 handles that.
  • When a line switches tracks with an update effect, the old track stops without fading out, by design. Fading every removed sound with the same tween would layer both tracks on every switch.
  • An update that fades up used on a stop line plays as written (dip, swell, cut), as a visual update animation would.
  • If a host re-runs engine.init while the renderer still plays the same track, an entry-only effect can mismatch the renderer's lifecycle. Initial transition effects already behave this way, and Creator's snapshot mode avoids it.

Version bumped to 1.46.6.

A line whose BGM action uses an update audio effect threw at line entry
when the music started or changed source ("has type update, but the BGM
action changes source identity"). Creator's fullscreen preview could not
open, and a game would stop at that line.

An update effect now animates the music the line plays: a retained sound
updates, and an added or source-replaced sound plays the update as its
incoming (enter) track, as visual update animations play when their
element appears. Its final keyframes must still match the persistent mix.
A removed sound stops without an effect, since an update has no outgoing
track.
Address review of the update-effect change:

- When a line stops every BGM sound, an update effect now plays as each
  sound's outgoing (exit) track, matching the visual update fallback that
  animates a removed element. An update that fades to 0 fades the music
  out, and the selection is validated again on stop lines. A sound removed
  while others play or enter still stops without an effect.
- Compile transition and update tracks through one helper, and compute the
  transition-only graph comparison and id map only where they are used.
- Update the audio effect resource schema description.
- Tests: exact enter keyframes in the engine source-change test, removed
  plus retained plus added sounds in one update, stop as exit, and speed
  validation on stop lines.
…ates

Address the second review:

- An update played as the incoming track of a source change measured
  relative pan from the next BGM pan, which already holds the tween's final
  value, so a +0.2 step overshot. It now continues from the channel pan
  before the line, or from the new channel pan when music starts.
- compileLifecycleTrack takes one explicit side (sound, BGM, resources)
  and baseline instead of choosing them through an implicit default.
- Docs and schema: the BGM update section no longer says updates require
  retained sounds, the persistent-value rule covers stop lines, and a
  partial removal whose remaining sounds do not change has no effect.
- Tests: relative pan on a source change and on a start, playback rate as
  an incoming track, and a partial removal at the engine level.
@han4wluc
han4wluc merged commit b630e36 into main Oct 2, 2026
25 checks passed
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