- Fixed PSP pause taking up to ~1.1s to actually go silent: its output
thread ran independently of stream->state, draining its whole software
ring regardless. audiostream_t.state is now volatile and the output
thread checks AUDIO_STREAM_STATE_PLAYING before each hardware chunk,
skipping output (without consuming the ring) while paused - pause now
goes silent within about one chunk (~23ms) and resume has no gap.
- Added audioMixerPause/Resume/SetPan/SetLoop/FadeTo/IsFading to the
mixer - immediate, channel-indexed primitives for cutscenes to drive.
Fade transitions (fadeFrom/To/Duration/Time/Easing) live on
audiomixerchannelstate_t and advance every frame in
audioMixerChannelApplyVolume(), reusing the same easingApply()
interpolation uifullbox_t already uses for screen fades.
- New src/dusk/rpg/cutscene/item/audio/ with 9 cutscene item types:
AUDIO_PLAY (+ AUDIO_PLAY_SIMPLE/AUDIO_PLAY_LOOPED shorthands),
AUDIO_STOP, AUDIO_PAUSE, AUDIO_RESUME, AUDIO_FADE (+ FADE_OUT/FADE_IN
shorthands), AUDIO_FADE_WAIT, AUDIO_SET_PAN, AUDIO_SET_LOOP, and the
combined AUDIO_SET - registered through the same enum/union/callback
table/macro mechanism every other item type uses.
- Wired the same 9 types into the JSON-based (offline JSONC -> binary
.cts) cutscene asset pipeline: tools/asset/cutscene/__main__.py's
encoder and assetcutsceneloader.c's decoder. Verified round-trip by
hand-encoding/decoding a test file covering all 9 types, and confirmed
the two existing real cutscene files re-encode byte-identical.
Built and verified on Linux, PSP (Docker), GameCube (Docker) and Wii
(Docker).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
New ASSET_LOADER_TYPE_CUTSCENE (assetcutsceneloader.c) reads a versioned
binary .cts format decoded into a heap-allocated cutsceneitem_t array + a
string/data pool, both sized to the file's actual content rather than a
fixed capacity, so the shared assetloaderoutput_t union doesn't bloat for
every asset slot regardless of type. Authoring pipeline mirrors the chunk
asset pattern: assetsraw/cutscenes/*.jsonc (JSON plus // and /* */
comments) -> tools/asset/cutscene -> assets/cutscenes/*.cts.
cutsceneSystemSetOnComplete() lets the caller arm a native callback that
fires when a cutscene finishes normally, so a file (which can't store a
function pointer) can end plainly and still hand off to native code -
cutsceneRestart() preserves it across a retry loop rather than clearing it,
since a restart is the same logical run trying again.
The initial and main-menu start-game cutscenes are now loaded from files
instead of compiled in via the CUTSCENE(...) macro.
Fixed a real bug found while converting these: the sync loader read the
file's total size from assetfile_t.size to locate the trailing pool
region, but assetFileDispose() (called at the end of the async phase)
zeroes that whole struct first, so the size was always 0 and the pool
offset computation underflowed into an out-of-bounds read - intermittent
depending on heap layout. Fixed by saving the size before disposal; also
fixed the read-completeness assert being checked after that same zeroing
(a no-op 0 == 0 check).
Co-Authored-By: Claude Sonnet 5 <[email protected]>