sceMp3: stop decoding at endPos instead of running off the buffer

A game can notify more data than the file actually had - audio/mp3/stream
asks for 3360 bytes and notifies all of them even when the read came up
short - so the tail of the buffer holds stale bytes from the previous half.
We happily decoded those, six frames past the end of the stream, because the
end flag only suppressed the zero fill and never stopped the decoder.

Check it before decoding too. The post-decode check stays where it was: the
hardware rewinds in the same call that decodes the last frame, so the sum
reads back as zero right after it, which is what audio/mp3/getsumdecoded
records. Moving the whole thing up front breaks that test.

Fixes audio/mp3/stream, added to tests_good - it walks 27 refills end to end,
so it also covers the half-buffer handout.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5 committed 2026-09-07 09:18:02 -06:00
1 parent 24e8d931e0
commit b855cec6d4
2 files changed
+12 -2

No files matched your search

+11 -2
View File
@@ -503,8 +503,15 @@ u32 AuCtx::AuDecode(u32 pcmAddr) {
if (pcmAddr)
Memory::WriteOrException_U32(outptr, pcmAddr);
// The stream is over once the decoder has consumed up to endPos, whatever is still sitting in
// the buffer. A game can hand us more than the file actually had - audio/mp3/stream notifies
// the full size it asked for even when the read came up short - and the hardware won't decode
// that tail, it just reports the end. A stream that still has loops left was already rewound
// by the block below, so this only stops us for good.
bool end = (int64_t)readPos - AuBufAvailable >= (int64_t)endPos;
// Decode a single frame in sourcebuff and output into PCMBuf.
if (!sourcebuff.empty()) {
if (!end && !sourcebuff.empty()) {
// FFmpeg doesn't seem to search for a sync for us, so let's do that.
int nextSync = 0;
if (decoder->GetAudioType() == PSP_CODEC_MP3) {
@@ -533,7 +540,9 @@ u32 AuCtx::AuDecode(u32 pcmAddr) {
}
}
bool end = readPos - AuBufAvailable >= (int64_t)endPos;
// Check again now that the decode has consumed more. The hardware rewinds in the same call that
// decodes the last frame, so the sum reads back as zero right after it (audio/mp3/getsumdecoded).
end = (int64_t)readPos - AuBufAvailable >= (int64_t)endPos;
if (end && LoopNum != 0) {
// When looping, start the sum back off at zero and reset readPos to the start.
SumDecodedSamples = 0;