Pure whitespace/line-break reformatting (braces, newlines, and line
continuations matching this codebase's existing wrap conventions) - no
logic, string content, or identifiers changed anywhere. Confirmed via
diff against the pre-change tree and by rebuilding + re-running the
affected test suites, which produce identical pass/fail results.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Fixes the main-thread hitch reported on Dolphin (and, less severely,
elsewhere) whenever the MP3 decode buffer needed refilling:
audioStreamMp3Read() -> audioStreamMp3DecoderDecodeFrame() does real work
synchronously (asset I/O + libmad decode), previously called directly from
each platform's once-per-frame Feed(), blocking rendering for a frame on
every refill.
Adds a background decode-ahead ring (src/duskmad/audiostreammp3ring.c):
a one-shot-per-pass thread reads via the existing audioStreamRead() into a
ring buffer ahead of need; Feed() becomes a cheap, lock-protected drain
instead of a decode call. Reuses this project's own thread_t/threadmutex_t
(mutex+condvar) primitives, mirroring PSP's own background-thread-plus-ring
precedent. Fixes a real (if narrow) race in thread.c along the way:
threadHandler() reset thread->threadId outside the mutex it also used to
signal STOPPED, which a stop-then-immediately-restart pattern (needed once
per pass: play/seek/loop-restart) could hit as a stale-threadId assertion.
Initially built Dolphin-only, then generalized: the ring doesn't touch
ASND at all, so it was straightforward to share with Linux too, moving
both it and the libmad decoder into a new top-level src/duskmad/ - a
shared-capability directory in the same vein as src/duskgl or
src/dusknetwork, pulled in by whichever DUSK_TARGET_SYSTEM values need it
(linux/knulli, wii/gamecube) rather than PSP, which keeps its own
hardware sceMp3 decoder and ring untouched.
With both platforms now sharing real code, deduped what was left:
audioStreamComputeEndFrame() (loop-segment math, byte-identical between
Linux/Dolphin) moved to the generic audio/stream/audiostream.c, and
audioStreamMp3Ring*IfNeeded()/audioStreamReadForPlayback() wrappers (in
duskmad) collapse each platform's own `if(stream->type ==
AUDIO_STREAM_TYPE_MP3)` branches at every Init/Dispose/Buffer/Feed call
site into one check each, living in the ring module itself.
Co-Authored-By: Claude Sonnet 5 <[email protected]>