mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
More sceAudioCodec implementation work. Atrac3+ partially works now.
This commit is contained in:
+89
-16
@@ -36,6 +36,50 @@ static bool oldStateLoaded = false;
|
||||
|
||||
static_assert(sizeof(SceAudiocodecCodec) == 128);
|
||||
|
||||
// Atrac3+ (0x1000) frame sizes, and control bytes
|
||||
//
|
||||
// Bitrate Frame Size Byte 1 Byte 2 Channels
|
||||
// -----------------------------------------------------
|
||||
// 48kbps 0x118 0x24 0x22 1? // This hits "Frame data doesn't match channel configuration".
|
||||
// 64kbps 0x178
|
||||
// 96kbps? 0x230 0x28 0x45 2
|
||||
// 128kbps 0x2E8 0x28 0x5c 2
|
||||
//
|
||||
// Seems like maybe the frame size is equal to "Byte 2" * 8 + 8
|
||||
//
|
||||
// Known byte values.
|
||||
|
||||
void CalculateInputBytesAndChannels(const SceAudiocodecCodec *ctx, int codec, int *inputBytes, int *channels) {
|
||||
*inputBytes = 0;
|
||||
*channels = 2;
|
||||
switch (codec) {
|
||||
case PSP_CODEC_AT3PLUS:
|
||||
{
|
||||
int size = ctx->unk41 * 8 + 8;
|
||||
// No idea if this is accurate, this is just a guess...
|
||||
if (ctx->unk40 & 8) {
|
||||
*channels = 2;
|
||||
} else {
|
||||
*channels = 1;
|
||||
}
|
||||
switch (size) {
|
||||
case 0x118:
|
||||
case 0x178:
|
||||
case 0x230:
|
||||
case 0x2E8:
|
||||
// These have been seen before, let's return it.
|
||||
*inputBytes = size;
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
// Unsupported codec, ignore.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// find the audio decoder for corresponding ctxPtr in audioList
|
||||
static AudioDecoder *findDecoder(u32 ctxPtr) {
|
||||
auto it = g_audioDecoderContexts.find(ctxPtr);
|
||||
@@ -93,10 +137,19 @@ static int __AudioCodecInitCommon(u32 ctxPtr, int codec, bool mono) {
|
||||
WARN_LOG_REPORT(Log::HLE, "sceAudiocodecInit(%08x, %d): replacing existing context", ctxPtr, codec);
|
||||
}
|
||||
|
||||
AudioDecoder *decoder = CreateAudioDecoder(audioType);
|
||||
decoder->SetCtxPtr(ctxPtr);
|
||||
g_audioDecoderContexts[ctxPtr] = decoder;
|
||||
int inFrameBytes;
|
||||
int channels;
|
||||
CalculateInputBytesAndChannels(ctx, codec, &inFrameBytes, &channels);
|
||||
|
||||
if (inFrameBytes) {
|
||||
INFO_LOG(Log::ME, "sceAudioDecoder: Creating codec with %04x frame size and %d channels, codec %04x", inFrameBytes, channels, codec);
|
||||
AudioDecoder *decoder = CreateAudioDecoder(audioType, 44100, channels, inFrameBytes);
|
||||
decoder->SetCtxPtr(ctxPtr);
|
||||
g_audioDecoderContexts[ctxPtr] = decoder;
|
||||
} else {
|
||||
ERROR_LOG(Log::ME, "sceAudioDecoder: Unsupported codec %08x", codec);
|
||||
g_audioDecoderContexts[ctxPtr] = nullptr;
|
||||
}
|
||||
return hleLogDebug(Log::ME, 0);
|
||||
}
|
||||
|
||||
@@ -119,33 +172,42 @@ static int sceAudiocodecDecode(u32 ctxPtr, int codec) {
|
||||
return hleLogError(Log::ME, 0, "UNIMPL sceAudiocodecDecode(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec));
|
||||
}
|
||||
|
||||
// TODO: Should check that codec corresponds to the currently used codec in the context, I guess..
|
||||
|
||||
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr); // On game-owned heap, no need to allocate.
|
||||
int inFrameBytes;
|
||||
int channels;
|
||||
CalculateInputBytesAndChannels(ctx, codec, &inFrameBytes, &channels);
|
||||
|
||||
// find a decoder in audioList
|
||||
auto decoder = findDecoder(ctxPtr);
|
||||
|
||||
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);
|
||||
decoder = CreateAudioDecoder(audioType, 44100, channels, inFrameBytes);
|
||||
decoder->SetCtxPtr(ctxPtr);
|
||||
g_audioDecoderContexts[ctxPtr] = decoder;
|
||||
}
|
||||
|
||||
if (decoder) {
|
||||
// Use SimpleAudioDec to decode audio
|
||||
auto ctx = PSPPointer<SceAudiocodecCodec>::Create(ctxPtr); // On game-owned heap, no need to allocate.
|
||||
// Decode audio
|
||||
int inDataConsumed = 0;
|
||||
int outSamples = 0;
|
||||
|
||||
INFO_LOG(Log::ME, "decoder. in: %08x out: %08x format1: %d format2: %d", ctx->inBuf, ctx->outBuf, ctx->format1, ctx->format2);
|
||||
INFO_LOG(Log::ME, "decoder. in: %08x out: %08x unk40: %d unk41: %d", ctx->inBuf, ctx->outBuf, ctx->unk40, ctx->unk41);
|
||||
|
||||
int16_t *outBuf = (int16_t *)Memory::GetPointerWrite(ctx->outBuf);
|
||||
|
||||
bool result = decoder->Decode(Memory::GetPointer(ctx->inBuf), ctx->srcFrameSize, &inDataConsumed, 2, outBuf, &outSamples);
|
||||
bool result = decoder->Decode(Memory::GetPointer(ctx->inBuf), inFrameBytes, &inDataConsumed, 2, outBuf, &outSamples);
|
||||
if (!result) {
|
||||
ctx->err = 0x20b;
|
||||
ERROR_LOG(Log::ME, "AudioCodec decode failed. Setting error to %08x", ctx->err);
|
||||
}
|
||||
|
||||
ctx->srcBytesRead = inDataConsumed;
|
||||
ctx->dstSamplesWritten = outSamples;
|
||||
}
|
||||
return hleLogInfo(Log::ME, 0, "codec %s", GetCodecName(codec));
|
||||
}
|
||||
@@ -166,23 +228,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.
|
||||
if (ctx->format1 != 0x28 || ctx->format2 != 0x5c) {
|
||||
ctx->err = 0x20f;
|
||||
return hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "Bad format values");
|
||||
}
|
||||
ctx->unk_init = 0x5100601;
|
||||
ctx->err = 0;
|
||||
|
||||
switch (codec) {
|
||||
case 0x1000: // at3+
|
||||
case 0x1000:
|
||||
ctx->neededMem = 0x7bc0;
|
||||
if (ctx->unk40 != 0x28 || ctx->unk41 != 0x5c) {
|
||||
ctx->err = 0x20f;
|
||||
return hleLogError(Log::ME, SCE_AVCODEC_ERROR_INVALID_DATA, "Bad format values: %02x %02x", ctx->unk40, ctx->unk41);
|
||||
}
|
||||
break;
|
||||
case 0x1001:
|
||||
ctx->neededMem = 0x3de0;
|
||||
break;
|
||||
case 0x1003:
|
||||
// Kosmodrones uses sceAudiocodec directly (no intermediate library).
|
||||
INFO_LOG(Log::ME, "CheckNeedMem for codec %04x: format %02x %02x", ctx->unk40, ctx->unk41);
|
||||
break;
|
||||
}
|
||||
|
||||
return hleLogWarning(Log::ME, 0, "UNIMPL sceAudiocodecCheckNeedMem(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec));
|
||||
ctx->err = 0;
|
||||
ctx->unk_init = 0x5100601;
|
||||
|
||||
return hleLogWarning(Log::ME, 0, "%s", GetCodecName(codec));
|
||||
}
|
||||
|
||||
static int sceAudiocodecGetEDRAM(u32 ctxPtr, int codec) {
|
||||
@@ -200,7 +267,13 @@ static int sceAudiocodecReleaseEDRAM(u32 ctxPtr, int id) {
|
||||
return hleLogWarning(Log::ME, 0, "failed to remove decoder");
|
||||
}
|
||||
|
||||
static int sceAudiocodecGetOutputBytes(u32 ctxPtr, int id) {
|
||||
static int sceAudiocodecGetOutputBytes(u32 ctxPtr, int codec) {
|
||||
switch (codec) {
|
||||
case 0x1000: return hleLogInfo(Log::ME, 0x2000); // Atrac3+
|
||||
case 0x1001: return hleLogInfo(Log::ME, 0x1000); // Atrac3
|
||||
default:
|
||||
return hleLogWarning(Log::ME, 0, "Block size query not implemented for codec %04x", codec);
|
||||
}
|
||||
return hleLogInfo(Log::ME, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,15 @@
|
||||
|
||||
class PointerWrap;
|
||||
|
||||
// audioType
|
||||
enum PSPAudioType {
|
||||
PSP_CODEC_AT3PLUS = 0x00001000,
|
||||
PSP_CODEC_AT3 = 0x00001001,
|
||||
PSP_CODEC_MP3 = 0x00001002,
|
||||
PSP_CODEC_AAC = 0x00001003,
|
||||
PSP_CODEC_WMA = 0x00001005,
|
||||
};
|
||||
|
||||
struct SceAudiocodecCodec {
|
||||
s32 unk_init;
|
||||
s32 unk4;
|
||||
@@ -29,18 +38,37 @@ struct SceAudiocodecCodec {
|
||||
s32 neededMem; // 10 // 0x102400 for Atrac3+
|
||||
s32 inited; // 14
|
||||
u32 inBuf; // 18 // Before decoding, set this to the start of the raw frame.
|
||||
s32 srcFrameSize; // 1c
|
||||
s32 srcBytesRead; // 1c
|
||||
u32 outBuf; // 20 // This is where the decoded data is written.
|
||||
s32 dstBytesWritten; // 24
|
||||
s8 format1; // 28 format or looping related // Probably, from here on out is a union with different fields for different codecs.
|
||||
s8 format2; // 29 format or looping related
|
||||
s16 unk42; // 2a
|
||||
u32 unk44; // 2c
|
||||
s32 dstSamplesWritten; // 24
|
||||
// Probably, from here on out is a union with different fields for different codecs.
|
||||
union { // offset 40 / 0x28
|
||||
struct {
|
||||
s8 unk40; // 28 format or looping related
|
||||
s8 unk41; // 29 format or looping related
|
||||
s8 unk42;
|
||||
s8 unk43;
|
||||
};
|
||||
u32 formatOutSamples;
|
||||
};
|
||||
union { // offset 44 / 0x2C
|
||||
struct {
|
||||
u8 unk44;
|
||||
s8 unk45;
|
||||
s8 unk46;
|
||||
s8 unk47;
|
||||
};
|
||||
struct {
|
||||
s16 unk44_16;
|
||||
s16 unk46_16;
|
||||
};
|
||||
u32 unk44_32;
|
||||
};
|
||||
s32 unk48; // 30 Atrac3 (non-+) related. Zero with Atrac3+.
|
||||
s32 unk52; // 34
|
||||
s32 mp3_9999; // 38
|
||||
s32 mp3_9999; // 38 // unk56
|
||||
s32 unk60;
|
||||
s32 unk64;
|
||||
s32 unk64; // Atrac3+ size related
|
||||
s32 unk68;
|
||||
s32 unk72;
|
||||
s32 unk76;
|
||||
|
||||
@@ -83,6 +83,13 @@ static void NotifyLoadStatusAvcodec(int state, u32 loadAddr, u32 totalSize) {
|
||||
|
||||
static void NotifyLoadStatusAtrac(int state, u32 loadAddr, u32 totalSize) {
|
||||
if (state == 1) {
|
||||
// If HLE of sceAtrac is disabled, things will break!
|
||||
// For now we do angry logging and a debug assert.
|
||||
if ((DisableHLEFlags)g_Config.iDisableHLE & DisableHLEFlags::sceAtrac) {
|
||||
ERROR_LOG(Log::ME, "sceAtrac HLE is disabled, and the game tries to load sceAtrac from firmware - this won't work!");
|
||||
_dbg_assert_(false);
|
||||
}
|
||||
|
||||
// We try to imitate a recent version of the prx.
|
||||
// Let's just give it a piece of the space.
|
||||
constexpr int version = 0x105; // latest.
|
||||
|
||||
@@ -18,18 +18,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/HW/MediaEngine.h"
|
||||
#include "Core/HLE/sceAudio.h"
|
||||
#include "Core/HLE/sceAudiocodec.h"
|
||||
|
||||
// Decodes packet by packet - does NOT demux.
|
||||
|
||||
// audioType
|
||||
enum PSPAudioType {
|
||||
PSP_CODEC_AT3PLUS = 0x00001000,
|
||||
PSP_CODEC_AT3 = 0x00001001,
|
||||
PSP_CODEC_MP3 = 0x00001002,
|
||||
PSP_CODEC_AAC = 0x00001003,
|
||||
};
|
||||
|
||||
// This basically corresponds to the core of sceAudiocodec.
|
||||
class AudioDecoder {
|
||||
public:
|
||||
virtual ~AudioDecoder() {}
|
||||
|
||||
@@ -329,14 +329,9 @@ int atrac3p_decode_frame(ATRAC3PContext *ctx, float *out_data[2], int *nb_sample
|
||||
if (ch_block >= ctx->num_channel_blocks ||
|
||||
ctx->channel_blocks[ch_block] != ch_unit_id) {
|
||||
av_log(AV_LOG_WARNING, "Frame data doesn't match channel configuration! ch_block %d >= num_channel_blocks %d", ch_block, ctx->num_channel_blocks);
|
||||
// This happens in Code Lyoko, see issue #19994.
|
||||
// The atrac3+ wav header clearly specifies stereo, so we only have one channel block, but here we try to decode a third (and/or fourth) one.
|
||||
// Maybe the data chunk is just improperly terminated, but it's reported that when ignoring this error previously,
|
||||
// it didn't sound right. So I'm really not sure what's going on here. Most likely some unknown feature of the Atrac3+ format that we
|
||||
// failed to parse previously?
|
||||
//
|
||||
// return AVERROR_INVALIDDATA; // This soft-locks the game.
|
||||
return FFMIN(ctx->block_align, indata_size); // We try to stumble along.
|
||||
// We used to have trouble in Code Lyoko and other games here. It's usually because data is corrupted
|
||||
// or the wrong channel configuration is used..
|
||||
return AVERROR_INVALIDDATA;
|
||||
}
|
||||
|
||||
ctx->ch_units[ch_block].unit_type = ch_unit_id;
|
||||
|
||||
Reference in New Issue
Block a user