sceAudiocodec: Fill in the context the way the PSP does

Worked out by dumping the context after every call on hardware.
SceAudiocodecCodec now names all of them.

Atrac3+: CheckNeedMem and Init parse the format bytes (sample rate,
channels, frame size) into the context, and neededMem goes by the channel
count. Init also writes the bitrate and the output channels, checks the
rate (44100 or 48000), channels and frame size, and fails unless inited is
exactly 1. InitMono refuses stereo, and decode honours the output channels.

MP3: GetInfo, and every decode, read the whole frame header into the
context. Without a frame GetInfo fails and fills in -1, where we guessed.

AAC: Init takes the ME's list of sample rates (not 12000) and fails with
either flag byte set. GetOutputBytes works for AAC, and goes by the
context for Atrac3+ and MP3.

GetEDRAM gives every codec the same address, and ReleaseEDRAM clears the
allocation at 0x68 (pspautotests audio/audiocodec/context).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-09-30 11:11:22 -06:00
1 parent 5eb7d45b58
commit 551fcf2c10
2 files changed
+263 -133

No files matched your search

+198 -83
View File
@@ -36,7 +36,7 @@
// g_audioDecoderContexts is to store current playing audios.
std::map<u32, AudioDecoder *> g_audioDecoderContexts;
// The Atrac3+ frame size each decoder in the map above was created for. mpeg.prx doesn't put the
// The Atrac3+ frame size each decoder in the map above was created for. sceMpeg doesn't put the
// frame size in the context, and at init time the input buffer is still empty, so for that path we
// only learn it from the first frame - at which point the decoder has to be rebuilt to match.
static std::map<u32, int> g_at3PlusFrameBytes;
@@ -58,8 +58,7 @@ static_assert(offsetof(SceAudiocodecCodec, fmt.mp3.sampleRateIndex) == 0x48);
static_assert(offsetof(SceAudiocodecCodec, fmt.mp3.channelConfig) == 0x54);
static_assert(offsetof(SceAudiocodecCodec, allocMem) == 0x68);
// Notes on the codec-specific fields, from watching games and from reading the firmware modules
// that drive this interface (avcodec.prx, libatrac3plus.prx, libmp3.prx). See the union in
// Notes on the codec-specific fields, from watching games, pspautotests, etc. See the union in
// sceAudiocodec.h for the layout.
//
// A general point worth knowing: the hardware never needs an exact input frame size here. The
@@ -132,6 +131,25 @@ static void At3PlusFormat(u8 formatByte1, u8 formatByte2, int *inputBytes, int *
*inputBytes = (((formatByte1 & 3) << 8) | formatByte2) * 8 + 8;
}
// ME command 0x63, which CheckNeedMem and Init send: the format bytes as one big-endian word, sample
// rate in bits 13-15, channels in 10-12, frame bytes / 8 - 1 in 0-9. Writes what it finds to the
// context (the channels always, the rest only for a known rate), and returns false for an unknown
// rate.
static bool At3PlusParseFormat(SceAudiocodecCodec *ctx, int *sampleRate, int *channels, int *frameBytes) {
const u32 word = (ctx->fmt.at3.formatByte1 << 8) | ctx->fmt.at3.formatByte2;
*channels = (word >> 10) & 7;
ctx->fmt.at3.channels = *channels;
static const int rates[3] = { 32000, 44100, 48000 };
if ((word >> 13) >= ARRAY_SIZE(rates)) {
return false;
}
*sampleRate = rates[word >> 13];
*frameBytes = ((word & 0x3FF) + 1) * 8;
ctx->fmt.at3.sampleRate = *sampleRate;
ctx->fmt.at3.frameBytes = *frameBytes;
return true;
}
// For a decode (headerBytes non-null), also handles the frame header, and returns the ME error
// code for a frame that doesn't match the context, or 0.
static int CalculateInputBytesAndChannelsAt3Plus(const SceAudiocodecCodec *ctx, int *inputBytes, int *channels, int *headerBytes = nullptr) {
@@ -183,6 +201,38 @@ static int Mp3ChannelsFromContext(const SceAudiocodecCodec *ctx) {
return ctx->fmt.mp3.channelConfig == 3 ? 1 : 2;
}
// What the ME reads off an MP3 frame header into the context, in GetInfo and in every decode
// (pspautotests audio/audiocodec/context). Apart from the version index and the layer number,
// these are the header's own fields. Returns false if there's no header at inBuf.
static bool Mp3ReadHeader(SceAudiocodecCodec *ctx, bool decoding) {
// Header version bits are 0 = MPEG2.5, 2 = MPEG2, 3 = MPEG1 (1 is reserved); the field
// wants 0 = MPEG2, 1 = MPEG1, 2 = MPEG2.5.
static const int versionFromHeader[4] = { 2, -1, 0, 1 };
const u8 *h = Memory::IsValidRange(ctx->inBuf, 4) ? Memory::GetPointerUnchecked(ctx->inBuf) : nullptr;
if (!h || h[0] != 0xFF || (h[1] & 0xE0) != 0xE0 || versionFromHeader[(h[1] >> 3) & 3] < 0 || ((h[1] >> 1) & 3) == 0) {
return false;
}
auto &mp3 = ctx->fmt.mp3;
mp3.version = versionFromHeader[(h[1] >> 3) & 3];
mp3.layer = 4 - ((h[1] >> 1) & 3);
mp3.crc = (h[1] & 1) ^ 1;
mp3.bitrateIndex = (h[2] >> 4) & 0x0F;
mp3.sampleRateIndex = (h[2] >> 2) & 3;
mp3.padding = (h[2] >> 1) & 1;
mp3.privateBit = h[2] & 1;
mp3.channelConfig = (h[3] >> 6) & 3;
mp3.modeExtension = (h[3] >> 4) & 3;
mp3.copyright = (h[3] >> 3) & 1;
mp3.original = (h[3] >> 2) & 1;
mp3.emphasis = h[3] & 3;
if (decoding) {
mp3.unk2c = 0;
mp3.channels = Mp3ChannelsFromContext(ctx);
mp3.granules = mp3.version == 1 ? 2 : 1;
}
return true;
}
// find the audio decoder for corresponding ctxPtr in audioList
static AudioDecoder *findDecoder(u32 ctxPtr) {
auto it = g_audioDecoderContexts.find(ctxPtr);
@@ -256,7 +306,8 @@ static int InitUs(int codec, const SceAudiocodecCodec *ctx) {
// libmp4.prx puts the sample rate here. On hardware 22050 and 44100 are accepted, 0 and 12345
// aren't; that the rest of the standard AAC rates are accepted is an assumption. 0 if not valid.
static int AacSampleRateFromContext(const SceAudiocodecCodec *ctx) {
static const int aacRates[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 };
// me_wrapper.prx's list, which leaves out 12000 and 7350.
static const int aacRates[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000 };
for (int rate : aacRates) {
if ((int)ctx->fmt.aac.sampleRate == rate) {
return rate;
@@ -311,8 +362,8 @@ static AudioDecoder *CreateDecoderForContext(u32 ctxPtr, PSPAudioType audioType)
return decoder;
}
// For Atrac3, InitMono only differs in what it writes to the context. For Atrac3+ it only changes
// the setup time so far.
// InitMono only takes mono streams, and only differs in the output channels it writes to the
// context. It isn't available for MP3 or AAC.
static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
const PSPAudioType audioType = (PSPAudioType)codec;
if (!IsValidCodec(audioType)) {
@@ -334,6 +385,14 @@ static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
int primingFrames = 0;
if (mono && audioType != PSP_CODEC_AT3 && audioType != PSP_CODEC_AT3PLUS) {
// Hardware still marks an MP3 version unknown before the ME refuses.
if (audioType == PSP_CODEC_MP3) {
ctx->fmt.mp3.version = 9999;
}
return hleLogError(Log::ME, SCE_AVCODEC_ERROR_UNSUPPORTED, "InitMono for %s", GetCodecName(codec));
}
// Special actions for some codecs, and what the hardware rejects.
switch (audioType) {
case PSP_CODEC_MP3:
@@ -343,18 +402,47 @@ static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
break;
case PSP_CODEC_AAC:
if (!AacSampleRateFromContext(ctx)) {
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_UNSUPPORTED, "bad AAC sample rate %d", ctx->fmt.aac.sampleRate), InitUs(codec, ctx));
// Refused before it gets to the ME.
return hleLogError(Log::ME, SCE_AVCODEC_ERROR_UNSUPPORTED, "bad AAC sample rate %d", ctx->fmt.aac.sampleRate);
}
// Either flag makes Init send another ME command (0x95, 0x97), and both fail.
if (ctx->fmt.aac.flag2c || ctx->fmt.aac.flag2d) {
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_UNSUPPORTED, "AAC flags %02x %02x", ctx->fmt.aac.flag2c, ctx->fmt.aac.flag2d), InitUs(codec, ctx));
}
primingFrames = 2;
break;
case PSP_CODEC_AT3PLUS:
{
int bytesPerFrame, channels;
CalculateInputBytesAndChannelsAt3Plus(ctx, &bytesPerFrame, &channels);
// As me_wrapper.prx does it, in this order (pspautotests audio/audiocodec/context).
int sampleRate = 0, channels, frameBytes = 0;
if (!At3PlusParseFormat(ctx, &sampleRate, &channels, &frameBytes)) {
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "unknown Atrac3+ sample rate"), InitUs(codec, ctx));
}
if (channels == 0) {
ctx->err = 0x202;
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "bad Atrac3+ format byte %02x", ctx->fmt.at3.formatByte1), InitUs(codec, ctx));
}
if (mono && channels != 1) {
// After the ME has parsed the format.
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_UNSUPPORTED, "InitMono with %d Atrac3+ channels", channels), 56);
}
static const u8 outputChannels[8] = { 0, 2, 2, 3, 4, 6, 7, 8 };
ctx->fmt.at3.outputChannels = mono ? 1 : outputChannels[channels];
// ME command 0x67 sets up the decoder, and checks these.
if (sampleRate != 44100 && sampleRate != 48000) {
ctx->err = 0x201;
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "unsupported Atrac3+ sample rate %d", sampleRate), InitUs(codec, ctx));
}
if (frameBytes > 0x1000) {
ctx->err = 0x203;
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "Atrac3+ frames too large (%d bytes)", frameBytes), InitUs(codec, ctx));
}
ctx->fmt.at3.bitrateKbps = (u32)((u64)frameBytes * 8 * sampleRate / 2048 / 1000);
// ME command 0x68.
if (ctx->inited != 1) {
ctx->err = 0x8001;
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "Atrac3+ context not marked inited (%d)", ctx->inited), InitUs(codec, ctx));
}
primingFrames = 1;
break;
}
@@ -371,8 +459,10 @@ static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
return hleLogError(Log::ME, SCE_AVCODEC_ERROR_UNSUPPORTED, "InitMono with stereo Atrac3 parameter %08x", *(const u32_le *)ctx->fmt.raw);
}
ctx->fmt.atrac3.sampleRate = 44100;
ctx->fmt.atrac3.frameBytes = bytesPerFrame;
ctx->fmt.atrac3.outputChannels = mono ? 1 : 2;
ctx->fmt.atrac3.channelBytes = bytesPerFrame / channels;
if (channels == 1) {
ctx->fmt.atrac3.outputChannels = mono ? 1 : 2;
}
break;
}
default:
@@ -470,7 +560,7 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
// 0x600 when it is zero (decodeUtility, case 0x1003). srcBytesRead, which this used to read,
// is an output field and is 0 on the first call, so the decoder got nothing to read and
// audio never started (seen in Meururu no Atelier Plus running the real mpeg.prx).
bytesPerFrame = ctx->fmt.aac.unk2c ? 0x609 : 0x600;
bytesPerFrame = ctx->fmt.aac.flag2c ? 0x609 : 0x600;
sampleRate = ctx->fmt.aac.sampleRate;
break;
case PSP_CODEC_AT3:
@@ -530,7 +620,17 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
return hleLogError(Log::ME, 0, "%d bytes at %08x isn't readable", bytesPerFrame, inAddr);
}
const int outputChannels = (codec == PSP_CODEC_AT3 && channels == 1 && ctx->fmt.atrac3.outputChannels != 2) ? 1 : 2;
// The output channels are read from the context at every decode. Setting an Atrac3+
// context to 1 on a stereo stream makes the hardware write one channel of samples that
// matches neither input channel, some mixdown we haven't worked out. We write the left
// channel instead. Nothing should hit this: InitMono refuses a stereo stream, so only a
// caller that writes 0x48 itself after Init gets there.
int outputChannels = 2;
if (codec == PSP_CODEC_AT3 && channels == 1 && ctx->fmt.atrac3.outputChannels != 2) {
outputChannels = 1;
} else if (codec == PSP_CODEC_AT3PLUS && ctx->fmt.at3.outputChannels == 1) {
outputChannels = 1;
}
bool result = decoder->Decode(inBuf, bytesPerFrame, &inDataConsumed, outputChannels, outBuf, &outSamples);
if (!result && (codec == PSP_CODEC_AT3PLUS || codec == PSP_CODEC_AT3)) {
// What the hardware reports for a frame that doesn't decode (0x208 is also possible for
@@ -551,6 +651,9 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
ERROR_LOG(Log::ME, "AudioCodec decode failed. Setting error to %08x", ctx->err);
} else {
ctx->err = 0;
if (codec == PSP_CODEC_MP3) {
Mp3ReadHeader(ctx, true);
}
auto priming = g_primingFrames.find(ctxPtr);
if (priming != g_primingFrames.end() && priming->second > 0) {
priming->second--;
@@ -562,8 +665,8 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
// In bytes, not samples. sceAudiocodecGetOutputBytes describes the same quantity in bytes
// (0x1200 for MPEG1 MP3), and libmp3.prx takes this as the length of the PCM to hand on -
// reporting the sample count instead gave it a quarter of every frame, which played back
// fast and metallic. The decoder writes stereo 16-bit, whatever the source is, except for
// mono Atrac3 whose context asks for mono output.
// fast and metallic. The decoder writes stereo 16-bit, whatever the source is, unless an
// Atrac3 or Atrac3+ context asks for mono.
ctx->dstBytesWritten = outSamples * outputChannels * (int)sizeof(int16_t);
decodeUs = EstimateDecodeUs(codec, channels, inDataConsumed, ctx);
@@ -593,38 +696,21 @@ static int sceAudiocodecGetInfo(u32 ctxPtr, int codec) {
// this call - libmp3.prx reads it straight back out, and leaving it at 9999 is what made
// Meruru no Atelier Plus go silent: it got this far and then stopped without ever asking
// for a decode.
//
// The hardware reads these off the frame, so read them off the frame. Apart from the
// version index, which has its own numbering, they are the raw MPEG header fields.
ctx->fmt.mp3.unk3c = 3;
ctx->fmt.mp3.unk60 = 1;
// Header version bits are 0 = MPEG2.5, 2 = MPEG2, 3 = MPEG1 (1 is reserved); the field
// wants 0 = MPEG2, 1 = MPEG1, 2 = MPEG2.5.
static const int versionFromHeader[4] = { 2, -1, 0, 1 };
const u8 *header = Memory::IsValidRange(ctx->inBuf, 4) ? Memory::GetPointerUnchecked(ctx->inBuf) : nullptr;
const bool haveFrame = header && header[0] == 0xFF && (header[1] & 0xE0) == 0xE0 &&
versionFromHeader[(header[1] >> 3) & 3] >= 0;
if (haveFrame) {
ctx->fmt.mp3.version = versionFromHeader[(header[1] >> 3) & 3];
ctx->fmt.mp3.bitrateIndex = (header[2] >> 4) & 0x0F;
ctx->fmt.mp3.sampleRateIndex = (header[2] >> 2) & 0x03;
ctx->fmt.mp3.channelConfig = (header[3] >> 6) & 0x03;
INFO_LOG(Log::ME, "GetInfo MP3: sdk=%08x version=%d bitrateIdx=%d sampleRateIdx=%d channelConfig=%d (hdr %02x %02x %02x %02x)",
sceKernelGetCompiledSdkVersion(), (int)ctx->fmt.mp3.version, (int)ctx->fmt.mp3.bitrateIndex, (int)ctx->fmt.mp3.sampleRateIndex,
(int)ctx->fmt.mp3.channelConfig, header[0], header[1], header[2], header[3]);
} else {
// Nothing readable to look at. Claim 128kbps 44.1kHz stereo, as this used to
// unconditionally - but do set the version, since 9999 stops the caller dead.
// Worth hearing about: everything the caller does with the stream follows from these,
// so if a game ever lands here its audio will be wrong in a way that starts right here.
WARN_LOG(Log::ME, "sceAudiocodecGetInfo: no MP3 frame at inBuf %08x, guessing 128kbps 44.1kHz stereo",
ctx->inBuf);
ctx->fmt.mp3.version = 1;
ctx->fmt.mp3.bitrateIndex = 9;
ctx->fmt.mp3.sampleRateIndex = 0;
ctx->fmt.mp3.channelConfig = 1;
if (!Mp3ReadHeader(ctx, false)) {
// What the hardware does without a frame to read: every field it would have filled
// in, and 0x2c, set to -1 (pspautotests audio/audiocodec/context). If a game ever lands
// here, its audio will be wrong in a way that starts right here.
WARN_LOG(Log::ME, "sceAudiocodecGetInfo: no MP3 frame at inBuf %08x", ctx->inBuf);
ctx->fmt.mp3.unk2c = 0xFFFFFFFF;
for (s32 *field = &ctx->fmt.mp3.version; field <= &ctx->fmt.mp3.emphasis; ++field) {
*field = -1;
}
ctx->err = -2;
return MECall(hleLogWarning(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "no MP3 frame"), 82);
}
INFO_LOG(Log::ME, "GetInfo MP3: sdk=%08x version=%d bitrateIdx=%d sampleRateIdx=%d channelConfig=%d",
sceKernelGetCompiledSdkVersion(), (int)ctx->fmt.mp3.version, (int)ctx->fmt.mp3.bitrateIndex, (int)ctx->fmt.mp3.sampleRateIndex,
(int)ctx->fmt.mp3.channelConfig);
break;
}
}
@@ -646,18 +732,28 @@ static int sceAudiocodecCheckNeedMem(u32 ctxPtr, int codec) {
// Check for expected values.
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr); // On game-owned heap, no need to allocate.
// Set before anything can fail.
ctx->magic = 0x5100601;
// The sizes are fixed per codec: on hardware they don't depend on the Atrac3+ format bytes,
// inited, or the AAC sample rate.
// The sizes are fixed per codec, except for Atrac3+, where they go by the channel count.
switch (codec) {
case 0x1000:
// The ME does look at the format bytes, though: both zero fails.
if (ctx->fmt.at3.formatByte1 == 0 && ctx->fmt.at3.formatByte2 == 0) {
ctx->err = 0x20f;
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "no Atrac3+ format"), 147);
{
// ME commands 0x63, which also fills in the format fields, and 0x66, which sizes by its
// two per-channel-count tables (pspautotests audio/audiocodec/context).
int sampleRate, channels, frameBytes;
if (!At3PlusParseFormat(ctx, &sampleRate, &channels, &frameBytes)) {
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "unknown Atrac3+ sample rate"), 147);
}
ctx->neededMem = 0x7bc0;
if (channels == 0) {
ctx->err = 0x20f;
return MECall(hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "no Atrac3+ channels"), 147);
}
static const u8 blocks[8] = { 0, 1, 2, 3, 4, 6, 7, 8 };
static const u8 extras[8] = { 0, 1, 1, 2, 3, 4, 5, 5 };
ctx->neededMem = 0xC0 + 0xA00 * extras[channels] + 0x3880 * blocks[channels];
break;
}
case 0x1001:
ctx->neededMem = 0x3de0;
break;
@@ -678,7 +774,6 @@ static int sceAudiocodecCheckNeedMem(u32 ctxPtr, int codec) {
}
ctx->err = 0;
ctx->magic = 0x5100601;
static const int needMemUs[4] = { 147, 80, 79, 57 };
return MECall(hleLogInfo(Log::ME, 0, "%s: %x", GetCodecName(codec), ctx->neededMem), needMemUs[codec - 0x1000]);
@@ -686,16 +781,9 @@ static int sceAudiocodecCheckNeedMem(u32 ctxPtr, int codec) {
static int sceAudiocodecGetEDRAM(u32 ctxPtr, int codec) {
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr); // On game-owned heap, no need to allocate.
// TODO: Set this a bit more dynamically. No idea what the allocation algorithm is...
switch (codec) {
case PSP_CODEC_MP3:
ctx->allocMem = 0x001B3124;
break;
case PSP_CODEC_AT3:
default:
ctx->allocMem = 0x0018ea90;
break;
}
// What the ME's allocator gave every codec on hardware, with nothing else allocated
// (pspautotests audio/audiocodec/context).
ctx->allocMem = 0x0018ea90;
ctx->edramAddr = (ctx->allocMem + 0x3f) & ~0x3f; // round up to 64 bytes.
return MECall(hleLogInfo(Log::ME, 0, "edram address set to %08x", ctx->edramAddr), 56);
}
@@ -708,7 +796,9 @@ static int sceAudiocodecGetEDRAM(u32 ctxPtr, int codec) {
// to need one, so there is often nothing here to drop.
static int sceAudiocodecReleaseEDRAM(u32 ctxPtr) {
if (Memory::IsValidRange(ctxPtr, sizeof(SceAudiocodecCodec))) {
PSPPointer<SceAudiocodecCodec>::Create(ctxPtr)->edramAddr = 0;
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr);
ctx->edramAddr = 0;
ctx->allocMem = 0;
}
if (removeDecoder(ctxPtr)) {
return MECall(hleLogInfo(Log::ME, 0), 56);
@@ -722,11 +812,32 @@ static int sceAudiocodecGetOutputBytes(u32 ctxPtr, int codec, u32 outBytesAddr)
return hleLogError(Log::ME, SCE_MP3_ERROR_BAD_ADDR);
}
if (!Memory::IsValidRange(ctxPtr, sizeof(SceAudiocodecCodec))) {
return hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "bad context");
}
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr);
// What avcodec.prx's GetOutputSizeInBytes works out from the context (pspautotests
// audio/audiocodec/context).
int bytes = 0;
switch (codec) {
case PSP_CODEC_AT3PLUS: bytes = 0x2000; break;
case PSP_CODEC_AT3: bytes = 0x1000; break; // Atrac3
case PSP_CODEC_MP3: bytes = 0x1200; break;
case PSP_CODEC_AT3PLUS:
bytes = ctx->fmt.at3.outputChannels << 12;
break;
case PSP_CODEC_AT3:
bytes = 0x1000;
break;
case PSP_CODEC_MP3:
// Before GetInfo has filled the version in, this does it first, and fails without a frame.
if (ctx->fmt.mp3.version == 9999 && !Mp3ReadHeader(ctx, false)) {
ctx->err = -0x15;
return MECall(hleLogWarning(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "no MP3 frame to size"), 82);
}
bytes = ctx->fmt.mp3.version == 1 ? 0x1200 : 0x900;
break;
case PSP_CODEC_AAC:
bytes = ctx->fmt.aac.flag2d ? 0x2000 : 0x1000;
break;
default:
return hleLogWarning(Log::ME, 0, "Block size query not implemented for codec %04x", codec);
}
@@ -752,25 +863,29 @@ static const At3Param at3Params[] = {
{ 0x0F, 0x0098, 1, 0 },
};
// What the ME does with each Atrac3 parameter, from me_wrapper.prx's table at 08002ac4: coding mode
// (0 stereo, 1 joint stereo, 2 mono) and the frame size of one channel. libatrac3plus.prx only
// ever picks five of them (at3Params above).
static const struct {
u8 mode;
u16 channelBytes;
} meAtrac3Layouts[16] = {
{ 0, 0x100 }, { 1, 0x100 }, { 0, 0xD4 }, { 1, 0xD4 }, { 0, 0xC0 }, { 1, 0xC0 }, { 0, 0x98 }, { 1, 0x98 },
{ 0, 0x88 }, { 1, 0x88 }, { 0, 0x60 }, { 1, 0x60 }, { 1, 0x44 }, { 1, 0x30 }, { 2, 0xC0 }, { 2, 0x98 },
};
// Returns false for a parameter the hardware rejects.
static bool Atrac3LayoutFromContext(const SceAudiocodecCodec *ctx, int *bytesPerFrame, int *channels, bool *jointStereo) {
const u32 param = *(const u32_le *)ctx->fmt.raw;
for (const At3Param &p : at3Params) {
if (p.param == param) {
*bytesPerFrame = p.bytes;
*channels = p.channels;
*jointStereo = p.jointStereo != 0;
return true;
}
}
// Not in libatrac3plus.prx's table. Fall back to 132kbps stereo, the most common.
*bytesPerFrame = 0x180;
*channels = 2;
*jointStereo = false;
if (param > 0x0F) {
const u32 param = ctx->fmt.atrac3.param;
if (param >= ARRAY_SIZE(meAtrac3Layouts)) {
*bytesPerFrame = 0x180;
*channels = 2;
*jointStereo = false;
return false;
}
WARN_LOG(Log::ME, "Unknown Atrac3 parameter %02x", param);
*channels = meAtrac3Layouts[param].mode == 2 ? 1 : 2;
*jointStereo = meAtrac3Layouts[param].mode == 1;
*bytesPerFrame = meAtrac3Layouts[param].channelBytes * *channels;
return true;
}
+65 -50
View File
@@ -38,15 +38,19 @@ enum PSPAudioType {
//
// Offsets 0x00..0x27 are identical for every codec, and sceVideocodec's context shares them too
// (mpeg.prx and videocodec_260.prx annotate the same fields), so this really is one ME "codec
// context" ABI. Everything from 0x28 on is per-codec, which is what the union models: each
// library writes a different set of fields there.
// context" ABI. Everything from 0x28 on is per-codec, which is what the union models.
//
// What each call writes was dumped from hardware after every call (pspautotests
// audio/audiocodec/context), and read out of me_wrapper.prx, the main-CPU side of the ME calls,
// which fills in most of these fields itself around the ME commands it sends. The ME only ever
// sees the first 0x68 bytes: me_wrapper flushes exactly that much before each command.
struct SceAudiocodecCodec {
u32 magic; // 0x00 set to 0x05100601 before every ME call
s32 unk4; // 0x04
s32 unk4; // 0x04 set by GetInfo type 3 (ME command 0x61 and friends)
s32 err; // 0x08
u32 edramAddr; // 0x0c in ME memory
s32 neededMem; // 0x10 0x102400 for Atrac3+
s32 inited; // 0x14
u32 edramAddr; // 0x0c in ME memory, 64-byte aligned. ReleaseEDRAM clears it.
s32 neededMem; // 0x10 from CheckNeedMem
s32 inited; // 0x14 Atrac3+ Init fails with err 0x8001 unless this is exactly 1
u32 inBuf; // 0x18 the raw frame to decode
s32 srcBytesRead; // 0x1c written by the decoder
u32 outBuf; // 0x20 where decoded PCM goes
@@ -54,79 +58,90 @@ struct SceAudiocodecCodec {
// Codec-specific, 0x28..0x67.
union {
// Atrac3plus. libatrac3plus.prx writes formatByte1/formatByte2 at init
// (0x28/0x5c for the worst case it sizes EDRAM against) and zeroes at3Related before
// every Atrac3plus decode.
// Atrac3plus (0x1000). The caller writes the format bytes; libatrac3plus.prx uses 0x28/0x5c
// (the worst case) to size EDRAM, and zeroes at3Related before every decode.
struct {
u8 formatByte1; // 0x28 bit 3 appears to mean stereo
u8 formatByte2; // 0x29 frame size in bytes = formatByte2 * 8 + 8
u8 unk2a; // 0x2a
u8 unk2b; // 0x2b
u32 unk2c; // 0x2c
u32 at3Related; // 0x30 zeroed for Atrac3plus, left alone for Atrac3
s32 unk34; // 0x34
s32 unk38[12]; // 0x38..0x67
// 0x28/0x29, as a big-endian word: bits 13-15 sample rate (0 = 32000, 1 = 44100,
// 2 = 48000), 10-12 channels, 0-9 frame bytes / 8 - 1. ME command 0x63 parses them.
u8 formatByte1;
u8 formatByte2;
u8 unk2a; // 0x2a
u8 unk2b; // 0x2b
u32 unk2c; // 0x2c Init's ME command 0x69 writes here (0 in every test)
// 0x30 Frames start with an 8-byte header (0F D0 + the format bytes) when set.
u32 at3Related;
u32 sampleRate; // 0x34 CheckNeedMem and Init write these three from the format
u32 unk38; // 0x38
u32 channels; // 0x3c
u32 frameBytes; // 0x40
// 0x44 Init: the bitrate in kbps, frameBytes * 8 * sampleRate / 2048 / 1000.
u32 bitrateKbps;
// 0x48 Init: the output channels, 2 for 1-2 channels, one more than the input for
// 5-7; InitMono: 1. Read at every decode, and GetOutputBytes is this * 0x1000. Written
// as 1 over a stereo stream, the hardware mixes it down somehow; see the decode.
u32 outputChannels;
s32 unk4c[7]; // 0x4c..0x67
} at3;
// Atrac3. Init fills in 0x2c-0x34 (pspautotests audio/audiocodec/at3errors).
// Atrac3 (0x1001). The caller writes the parameter, an index into me_wrapper.prx's table
// (see at3Params); Init fills in the rest.
struct {
u32 param; // 0x28 the layout, from libatrac3plus.prx's table (see at3Params)
u32 param; // 0x28
u32 sampleRate; // 0x2c 44100
u32 frameBytes; // 0x30
// 0x34 Init writes 2, InitMono 1. Read at every decode: a mono layout is written as
// stereo only when this is 2. Stereo layouts are always written as stereo.
u32 channelBytes; // 0x30 the frame size of one channel
// 0x34 Init writes 2 and InitMono 1, for the mono layouts only. Read at every decode:
// a mono layout is written as stereo only when this is 2.
u32 outputChannels;
s32 unk38[12]; // 0x38..0x67
} atrac3;
// MP3. Field meanings are from avcodec.prx's frame-size calculator, which indexes the
// standard MPEG bitrate and sample-rate tables with them, and from libmp3.prx reading
// version and channelConfig back out.
// MP3 (0x1002). GetInfo, and every decode, fill 0x38-0x64 in from the frame header; these
// are the header's own fields except for the version and layer numbering.
struct {
// 0x28 libmp3.prx writes 0x5A1 here once at setup, and never per frame: it is the
// largest an MP3 frame can be (144 * 320000 / 32000 + 1 padding byte). The hardware
// only uses it as the length for a cache writeback over inBuf before handing the
// frame to the ME, so it is an upper bound rather than this frame's size.
u32 maxFrameBytes;
u32 unk2c; // 0x2c
s32 unk30; // 0x30
s32 unk34; // 0x34
u32 unk2c; // 0x2c 0 after a decode
s32 channels; // 0x30 decode: 1 for mono, else 2
s32 granules; // 0x34 decode: 2 for MPEG1, 1 for MPEG2 and 2.5
// 0x38 MPEG version index: 0 = MPEG2, 1 = MPEG1, 2 = MPEG2.5. sceAudiocodecInit
// presets it to 9999 to mean "not known yet"; GetInfo fills it in.
s32 version;
// 0x3c Observed to be 3. avcodec's (unreachable) frame-size path treats this as the
// raw MPEG layer bits and only computes a size for 1 or 2, where 1 is Layer III -
// so 3 would fall through to its maximum-size fallback. Unexplained.
s32 unk3c;
s32 unk40; // 0x40
s32 layer; // 0x3c 3 for Layer III
s32 crc; // 0x40 the protection bit, inverted: 1 if the frame has a CRC
s32 bitrateIndex; // 0x44 index into the MPEG Layer III bitrate table (9 = 128kbps)
s32 sampleRateIndex; // 0x48 0..2 within the version's rates (0 = 44100 for MPEG1)
s32 unk4c; // 0x4c
s32 unk50; // 0x50
// 0x54 libmp3.prx reads this as: channels = (channelConfig == 3) ? 1 : 2.
s32 padding; // 0x4c
s32 privateBit; // 0x50
// 0x54 The channel mode. libmp3.prx reads it as: channels = (channelConfig == 3) ? 1 : 2.
s32 channelConfig;
s32 unk58; // 0x58
s32 unk5c; // 0x5c
s32 unk60; // 0x60
s32 unk64; // 0x64
s32 modeExtension; // 0x58
s32 copyright; // 0x5c
s32 original; // 0x60
s32 emphasis; // 0x64
} mp3;
// AAC. The input frame size is 0x609 when unk2c is nonzero and 0x600 when it is zero, and
// the output size comes from unk2d (avcodec.prx decodeUtility); the sample rate at 0x28 is
// our own observation from games.
// AAC (0x1003). The input frame size is 0x609 when flag2c is nonzero and 0x600 when it is
// zero, and the output size 0x2000 when flag2d is set (avcodec.prx decodeUtility). Init
// sends extra ME commands for either flag (0x95, 0x97), which fail on the firmware tested.
struct {
u32 sampleRate; // 0x28
u8 unk2c; // 0x2c selects the input frame size (see above)
u8 unk2d; // 0x2d selects the output size
u8 unk2e; // 0x2e
u8 unk2f; // 0x2f
s32 unk30[14]; // 0x30..0x67
// 0x28 One of the AAC rates from 8000 to 96000 (not 12000 or 7350), or Init fails.
u32 sampleRate;
u8 flag2c; // 0x2c
u8 flag2d; // 0x2d
u8 unk2e; // 0x2e
u8 unk2f; // 0x2f
s32 unk30[14]; // 0x30..0x67
} aac;
u8 raw[0x40];
} fmt;
u32 allocMem; // 0x68 not hardware - where our GetEDRAM stores what it handed out
// 0x68 What GetEDRAM got from the ME's allocator, before rounding up to edramAddr.
// ReleaseEDRAM clears it. Past what the ME sees.
u32 allocMem;
u8 unk[0x14]; // 0x6c
};