diff --git a/Core/HLE/sceAudiocodec.cpp b/Core/HLE/sceAudiocodec.cpp index 9fee68ce82..7e4777a6c8 100644 --- a/Core/HLE/sceAudiocodec.cpp +++ b/Core/HLE/sceAudiocodec.cpp @@ -36,7 +36,7 @@ // g_audioDecoderContexts is to store current playing audios. std::map 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 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::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::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::Create(ctxPtr)->edramAddr = 0; + auto ctx = PSPPointer::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::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; } diff --git a/Core/HLE/sceAudiocodec.h b/Core/HLE/sceAudiocodec.h index 1cd959b4a3..e6772f2174 100644 --- a/Core/HLE/sceAudiocodec.h +++ b/Core/HLE/sceAudiocodec.h @@ -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 };