Add MP3 audio stream support with hardware/software decoder backends
New ASSET_LOADER_TYPE_MP3 (hand-rolled MPEG-1/2/2.5 Layer III header parser - no third-party dependency needed just for metadata, since PSP's hardware path doesn't need one at all) plus a shared audiostreammp3.c stream layer mirroring audiostreampcm.c's shape. Generalized the stream dispatch (hoisted sampleRate/channels onto audiostream_t, added audioStreamGetTotalFrames()/Seek()/Read()) so all three platform audio backends keep working unchanged, just calling the generic names instead of PCM-specific ones. Two decoder backends behind one interface: PSP uses the real sceMp3 hardware decoder (firmware-offloaded, lazily initialized on first use); Linux and Dolphin share one minimp3-based software decoder (public domain, vendored via CMake FetchContent) - libogc's own MP3Player wraps libmad (GPL) and drives its own output pipeline, not a fit for the ansnd-based architecture already in place, so skipped in favor of the shared minimp3 path. Fixed three real bugs found via hardware/runtime testing along the way: - LAME's Xing header counts its own placeholder frame in the declared total, which made playback stall permanently one frame short of the declared end (looked like "never loops") - fixed by subtracting it. - sceMp3Decode() can return more PCM than one MPEG frame's worth in a single call (PSP's pcmBuf is provisioned for 2x), overflowing the shared per-frame decode buffer with no bound check - very intermittent corruption/clicking on real hardware. Widened the buffer to the real worst case and added an assertion. - sceMp3ResetPlayPosition()'s exact internal reset semantics aren't documented precisely enough to trust for looping - occasionally disagreed with the fresh stream position fed right after, clicking at the loop boundary about 1 in 3-4 loops. Rewind now fully tears down and recreates the decoder instead, the same path already proven correct at first Init. Also widened the PSP ring buffer to absorb that now-heavier operation, capping each top-up call's own work so the bigger buffer doesn't turn into one long blocking decode burst instead. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -16,13 +16,32 @@ typedef struct audiostream_s audiostream_t;
|
||||
// Physical capacity of the ring; must comfortably exceed
|
||||
// AUDIO_PSP_LEAD_FRAMES plus one top-up step so a single top-up call can
|
||||
// never overwrite data the output thread hasn't consumed yet.
|
||||
#define AUDIO_PSP_RING_FRAMES 16384
|
||||
//
|
||||
// Sized well above what a PCM stream would ever need on its own, because
|
||||
// an MP3 stream's loop-wrap (audioStreamMp3DecoderRewind() on PSP) fully
|
||||
// tears down and recreates the hardware decoder - a real handle release/
|
||||
// reserve round-trip plus a fresh ~16KB Memory Stick read to re-prime
|
||||
// sceMp3's stream buffer - all synchronously, inside the same TopUp() call
|
||||
// that's supposed to be keeping this ring fed. That's slow enough,
|
||||
// occasionally, to eat into a tighter lead margin and click right at the
|
||||
// loop point (confirmed on real hardware) - this gives it generous room to
|
||||
// do so without the output thread ever catching up to empty.
|
||||
#define AUDIO_PSP_RING_FRAMES 49152
|
||||
|
||||
// Top-up threshold - audioStreamPSPTopUp() (called once per engine
|
||||
// Update(), see audioStreamPSPIsFinished()) only reads more data once the
|
||||
// ring drops below this, same trigger dusklinux uses for its own lead
|
||||
// margin.
|
||||
#define AUDIO_PSP_LEAD_FRAMES 4096
|
||||
// margin. See AUDIO_PSP_RING_FRAMES's own comment for why this is sized
|
||||
// well beyond dusklinux's equivalent.
|
||||
#define AUDIO_PSP_LEAD_FRAMES 12288
|
||||
|
||||
// Max frames audioStreamPSPTopUp() will read in one call, regardless of
|
||||
// how much ring room is actually free - keeps each call's worst-case
|
||||
// blocking time on the main thread bounded and roughly constant even
|
||||
// though AUDIO_PSP_RING_FRAMES/AUDIO_PSP_LEAD_FRAMES are large; reaching
|
||||
// LEAD from an empty ring just takes a few calls (a few engine frames)
|
||||
// instead of one long one. See AUDIO_PSP_RING_FRAMES's own comment.
|
||||
#define AUDIO_PSP_TOPUP_STEP_FRAMES 4096
|
||||
|
||||
// Max number of pending loop-wrap events (see audiostreampsploopmarker_t
|
||||
// below) the ring can remember at once. Sized generously relative to how
|
||||
|
||||
Reference in New Issue
Block a user