sceAudiocodec: Recreate decoders from the context on load

They were recreated without block size or extradata, which Atrac3 needs,
so it stayed silent after a load. Also drop the old decoders when the
state has none, and don't overflow on v1 states.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-09-28 09:34:06 -06:00
1 parent 490f08d220
commit a4e169893f
1 file changed
+57 -41
+57 -41
View File
@@ -224,44 +224,17 @@ void __AudioCodecShutdown() {
clearDecoders();
}
// TODO: Actually support mono output.
static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
const PSPAudioType audioType = (PSPAudioType)codec;
if (!IsValidCodec(audioType)) {
return hleLogError(Log::ME, SCE_KERNEL_ERROR_OUT_OF_RANGE, "Invalid codec");
}
// Re-initialising a context that still has a decoder is normal, not a report-worthy
// surprise: mpeg.prx sizes the allocation through a scratch context of its own and only ever
// releases that one, so the context it actually decodes through still holds a decoder when the
// next movie starts. Once per video, on every game running the real module.
if (removeDecoder(ctxPtr)) {
INFO_LOG(Log::HLE, "sceAudiocodecInit(%08x, %d): replacing existing context", ctxPtr, codec);
}
// Initialize the codec memory.
// Creates a context's decoder from what the context holds. Init does this, and so does a state
// load, since the decoders themselves aren't saved.
static AudioDecoder *CreateDecoderForContext(u32 ctxPtr, PSPAudioType audioType) {
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr);
ctx->magic = 0x5100601;
ctx->err = 0;
int bytesPerFrame = 0;
int channels = 2;
uint8_t extraData[14]{};
SceAudiocodecCodec *ptr = ctx;
// Special actions for some codecs.
switch (audioType) {
case PSP_CODEC_MP3:
// Not seeing inited in Kurok (homebrew)
// _dbg_assert_(ctx->inited == 1);
ctx->fmt.mp3.version = 9999;
break;
case PSP_CODEC_AAC:
// AAC / mp4
// offsets 40-42 are a 24-bit LE number specifying the sample rate. It's 32000, 44100 or 48000.
// neededMem has been set to 0x18f20.
break;
case PSP_CODEC_AT3PLUS:
CalculateInputBytesAndChannelsAt3Plus(ctx, &bytesPerFrame, &channels);
break;
@@ -284,12 +257,52 @@ static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
}
// Create audio decoder for given audio codec and push it into AudioList
INFO_LOG(Log::ME, "sceAudioDecoder: Creating codec with %04x frame size and %d channels, codec %04x", bytesPerFrame, channels, codec);
INFO_LOG(Log::ME, "sceAudioDecoder: Creating codec with %04x frame size and %d channels, codec %04x", bytesPerFrame, channels, (int)audioType);
// We send in extra data with all codec, most ignore it.
AudioDecoder *decoder = CreateAudioDecoder(audioType, 44100, channels, bytesPerFrame, extraData, sizeof(extraData));
decoder->SetCtxPtr(ctxPtr);
g_audioDecoderContexts[ctxPtr] = decoder;
g_at3PlusFrameBytes[ctxPtr] = bytesPerFrame;
return decoder;
}
// TODO: Actually support mono output.
static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
const PSPAudioType audioType = (PSPAudioType)codec;
if (!IsValidCodec(audioType)) {
return hleLogError(Log::ME, SCE_KERNEL_ERROR_OUT_OF_RANGE, "Invalid codec");
}
// Re-initialising a context that still has a decoder is normal, not a report-worthy
// surprise: mpeg.prx sizes the allocation through a scratch context of its own and only ever
// releases that one, so the context it actually decodes through still holds a decoder when the
// next movie starts. Once per video, on every game running the real module.
if (removeDecoder(ctxPtr)) {
INFO_LOG(Log::HLE, "sceAudiocodecInit(%08x, %d): replacing existing context", ctxPtr, codec);
}
// Initialize the codec memory.
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr);
ctx->magic = 0x5100601;
ctx->err = 0;
// Special actions for some codecs.
switch (audioType) {
case PSP_CODEC_MP3:
// Not seeing inited in Kurok (homebrew)
// _dbg_assert_(ctx->inited == 1);
ctx->fmt.mp3.version = 9999;
break;
case PSP_CODEC_AAC:
// AAC / mp4
// offsets 40-42 are a 24-bit LE number specifying the sample rate. It's 32000, 44100 or 48000.
// neededMem has been set to 0x18f20.
break;
default:
break;
}
CreateDecoderForContext(ctxPtr, audioType);
return hleLogDebug(Log::ME, 0);
}
@@ -356,9 +369,7 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
if (!decoder && oldStateLoaded) {
// We must have loaded an old state that did not have sceAudiocodec information.
// Fake it by creating the desired context.
decoder = CreateAudioDecoder(audioType, 44100, channels, bytesPerFrame);
decoder->SetCtxPtr(ctxPtr);
g_audioDecoderContexts[ctxPtr] = decoder;
decoder = CreateDecoderForContext(ctxPtr, audioType);
}
if (decoder && codec == PSP_CODEC_AT3PLUS && bytesPerFrame > 0) {
@@ -654,13 +665,16 @@ void __sceAudiocodecDoState(PointerWrap &p){
int count = (int)g_audioDecoderContexts.size();
Do(p, count);
if (p.mode == PointerWrap::MODE_READ) {
clearDecoders();
}
if (count > 0) {
if (p.mode == PointerWrap::MODE_READ) {
clearDecoders();
// loadstate if audioList is nonempty
auto codec_ = new int[count];
auto ctxPtr_ = new u32[count];
// v1 read a fixed two, whatever the count.
auto codec_ = new int[std::max(count, 2)];
auto ctxPtr_ = new u32[std::max(count, 2)];
// These sizeof(pointers) are wrong, but kept to avoid breaking on old saves.
// They're not used in new savestates.
#ifdef __clang__
@@ -674,9 +688,11 @@ void __sceAudiocodecDoState(PointerWrap &p){
DoArray(p, codec_, s >= 2 ? count : (int)ARRAY_SIZE(codec_));
DoArray(p, ctxPtr_, s >= 2 ? count : (int)ARRAY_SIZE(ctxPtr_));
for (int i = 0; i < count; i++) {
auto decoder = CreateAudioDecoder((PSPAudioType)codec_[i]);
decoder->SetCtxPtr(ctxPtr_[i]);
g_audioDecoderContexts[ctxPtr_[i]] = decoder;
if (!Memory::IsValidRange(ctxPtr_[i], sizeof(SceAudiocodecCodec))) {
ERROR_LOG(Log::ME, "Savestate has an audiocodec context at an invalid address %08x", ctxPtr_[i]);
continue;
}
CreateDecoderForContext(ctxPtr_[i], (PSPAudioType)codec_[i]);
}
#ifdef __clang__
#pragma clang diagnostic pop