1
0
mirror of https://github.com/RPCS3/rpcs3 synced 2024-07-01 07:04:22 +00:00

Compare commits

...

8 Commits

Author SHA1 Message Date
capriots
4d014f6c40
Merge 6ee62f9466 into 71524271e9 2024-06-28 06:47:27 -04:00
kd-11
71524271e9 rsx: Fix codegen when depth-conversion is enabled 2024-06-28 12:13:33 +02:00
capriots
6ee62f9466
cellAtracXdec: review fixes 2024-06-12 21:34:50 +02:00
capriots
5829b4e2ad
cellAtracXdec: review fixes 2024-06-11 19:23:48 +02:00
capriots
864640d3aa
cellAtracXdec: review fixes 2024-05-08 12:15:32 +02:00
capriots
b94ec1c35c
cellAtracXdec: set to HLE by default 2024-05-01 00:15:50 +02:00
capriots
4305fe9d93
cellAtracXdec implementation 2024-05-01 00:15:50 +02:00
capriots
03d7559946
cellAdec: add internal datatypes 2024-05-01 00:15:43 +02:00
12 changed files with 1381 additions and 202 deletions

View File

@ -239,6 +239,7 @@ target_sources(rpcs3_emu PRIVATE
Cell/Modules/cellAdec.cpp
Cell/Modules/cellAtrac.cpp
Cell/Modules/cellAtracMulti.cpp
Cell/Modules/cellAtracXdec.cpp
Cell/Modules/cellAudio.cpp
Cell/Modules/cellAudioOut.cpp
Cell/Modules/cellAuthDialog.cpp

View File

@ -28,6 +28,7 @@ extern "C"
#endif
#include "cellPamf.h"
#include "cellAtracXdec.h"
#include "cellAdec.h"
#include <mutex>
@ -136,63 +137,6 @@ void fmt_class_string<CellAdecError>::format(std::string& out, u64 arg)
STR_CASE(CELL_ADEC_ERROR_AT3_BUSY);
STR_CASE(CELL_ADEC_ERROR_AT3_EMPTY);
STR_CASE(CELL_ADEC_ERROR_AT3_ERROR);
STR_CASE(CELL_ADEC_ERROR_ATX_OK); // CELL_ADEC_ERROR_ATX_OFFSET, CELL_ADEC_ERROR_ATX_NONE
STR_CASE(CELL_ADEC_ERROR_ATX_BUSY);
STR_CASE(CELL_ADEC_ERROR_ATX_EMPTY);
STR_CASE(CELL_ADEC_ERROR_ATX_ATSHDR);
STR_CASE(CELL_ADEC_ERROR_ATX_NON_FATAL);
STR_CASE(CELL_ADEC_ERROR_ATX_NOT_IMPLE);
STR_CASE(CELL_ADEC_ERROR_ATX_PACK_CE_OVERFLOW);
STR_CASE(CELL_ADEC_ERROR_ATX_ILLEGAL_NPROCQUS);
STR_CASE(CELL_ADEC_ERROR_ATX_FATAL);
STR_CASE(CELL_ADEC_ERROR_ATX_ENC_OVERFLOW);
STR_CASE(CELL_ADEC_ERROR_ATX_PACK_CE_UNDERFLOW);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDCT);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GAINADJ);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_SPECTRA);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GHWAVE);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_SHEADER);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_B);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_C);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_D);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_E);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_B);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_C);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_D);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDCT_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_NGC);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_B);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_B);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_SN_NWVS);
STR_CASE(CELL_ADEC_ERROR_ATX_FATAL_HANDLE);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_SAMPLING_FREQ);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_CH_CONFIG_INDEX);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_NBYTES);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_NUM);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_ID);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_CHANNELS);
STR_CASE(CELL_ADEC_ERROR_ATX_UNINIT_BLOCK_SPECIFIED);
STR_CASE(CELL_ADEC_ERROR_ATX_POSCFG_PRESENT);
STR_CASE(CELL_ADEC_ERROR_ATX_BUFFER_OVERFLOW);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_BLK_TYPE_ID);
STR_CASE(CELL_ADEC_ERROR_ATX_UNPACK_CHANNEL_BLK_FAILED);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_1);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_2);
STR_CASE(CELL_ADEC_ERROR_ATX_ILLEGAL_ENC_SETTING);
STR_CASE(CELL_ADEC_ERROR_ATX_ILLEGAL_DEC_SETTING);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_NSAMPLES);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_SYNCWORD);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_SAMPLING_FREQ);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_CH_CONFIG_INDEX);
STR_CASE(CELL_ADEC_ERROR_ATX_RAW_DATA_FRAME_SIZE_OVER);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_ENHANCE_LENGTH_OVER);
STR_CASE(CELL_ADEC_ERROR_ATX_SPU_INTERNAL_FAIL);
STR_CASE(CELL_ADEC_ERROR_LPCM_FATAL);
STR_CASE(CELL_ADEC_ERROR_LPCM_SEQ);
STR_CASE(CELL_ADEC_ERROR_LPCM_ARG);
@ -910,7 +854,7 @@ error_code cellAdecDecodeAu(u32 handle, vm::ptr<CellAdecAuInfo> auInfo)
AdecTask task(adecDecodeAu);
task.au.auInfo_addr = auInfo.addr();
task.au.addr = auInfo->startAddr;
task.au.addr = auInfo->startAddr.addr();
task.au.size = auInfo->size;
task.au.pts = (u64{auInfo->pts.upper} << 32) | u64{auInfo->pts.lower};
task.au.userdata = auInfo->userData;
@ -1072,7 +1016,7 @@ error_code cellAdecGetPcmItem(u32 handle, vm::pptr<CellAdecPcmItem> pcmItem)
pcm->auInfo.pts.lower = static_cast<u32>(af.pts);
pcm->auInfo.pts.upper = static_cast<u32>(af.pts >> 32);
pcm->auInfo.size = af.auSize;
pcm->auInfo.startAddr = af.auAddr;
pcm->auInfo.startAddr.set(af.auAddr);
pcm->auInfo.userData = af.userdata;
if (adecIsAtracX(adec->type))
@ -1119,7 +1063,6 @@ DECLARE(ppu_module_manager::cellAdec)("cellAdec", []()
{
static ppu_static_module cell_libac3dec("cell_libac3dec");
static ppu_static_module cellAtrac3dec("cellAtrac3dec");
static ppu_static_module cellAtracXdec("cellAtracXdec");
static ppu_static_module cellCelpDec("cellCelpDec");
static ppu_static_module cellDTSdec("cellDTSdec");
static ppu_static_module cellM2AACdec("cellM2AACdec");

View File

@ -111,68 +111,6 @@ enum CellAdecError : u32
CELL_ADEC_ERROR_AT3_ERROR = 0x80612180,
CELL_ADEC_ERROR_ATX_OFFSET = 0x80612200,
CELL_ADEC_ERROR_ATX_NONE = 0x80612200,
CELL_ADEC_ERROR_ATX_OK = 0x80612200,
CELL_ADEC_ERROR_ATX_BUSY = 0x80612264,
CELL_ADEC_ERROR_ATX_EMPTY = 0x80612265,
CELL_ADEC_ERROR_ATX_ATSHDR = 0x80612266,
CELL_ADEC_ERROR_ATX_NON_FATAL = 0x80612281,
CELL_ADEC_ERROR_ATX_NOT_IMPLE = 0x80612282,
CELL_ADEC_ERROR_ATX_PACK_CE_OVERFLOW = 0x80612283,
CELL_ADEC_ERROR_ATX_ILLEGAL_NPROCQUS = 0x80612284,
CELL_ADEC_ERROR_ATX_FATAL = 0x8061228c,
CELL_ADEC_ERROR_ATX_ENC_OVERFLOW = 0x8061228d,
CELL_ADEC_ERROR_ATX_PACK_CE_UNDERFLOW = 0x8061228e,
CELL_ADEC_ERROR_ATX_SYNTAX_IDCT = 0x8061228f,
CELL_ADEC_ERROR_ATX_SYNTAX_GAINADJ = 0x80612290,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF = 0x80612291,
CELL_ADEC_ERROR_ATX_SYNTAX_SPECTRA = 0x80612292,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL = 0x80612293,
CELL_ADEC_ERROR_ATX_SYNTAX_GHWAVE = 0x80612294,
CELL_ADEC_ERROR_ATX_SYNTAX_SHEADER = 0x80612295,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_A = 0x80612296,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_B = 0x80612297,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_C = 0x80612298,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_D = 0x80612299,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_E = 0x8061229a,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_A = 0x8061229b,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_B = 0x8061229c,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_C = 0x8061229d,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_D = 0x8061229e,
CELL_ADEC_ERROR_ATX_SYNTAX_IDCT_A = 0x8061229f,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_NGC = 0x806122a0,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_A = 0x806122a1,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_A = 0x806122a2,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_B = 0x806122a3,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_B = 0x806122a4,
CELL_ADEC_ERROR_ATX_SYNTAX_SN_NWVS = 0x806122a5,
CELL_ADEC_ERROR_ATX_FATAL_HANDLE = 0x806122aa,
CELL_ADEC_ERROR_ATX_ASSERT_SAMPLING_FREQ = 0x806122ab,
CELL_ADEC_ERROR_ATX_ASSERT_CH_CONFIG_INDEX = 0x806122ac,
CELL_ADEC_ERROR_ATX_ASSERT_NBYTES = 0x806122ad,
CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_NUM = 0x806122ae,
CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_ID = 0x806122af,
CELL_ADEC_ERROR_ATX_ASSERT_CHANNELS = 0x806122b0,
CELL_ADEC_ERROR_ATX_UNINIT_BLOCK_SPECIFIED = 0x806122b1,
CELL_ADEC_ERROR_ATX_POSCFG_PRESENT = 0x806122b2,
CELL_ADEC_ERROR_ATX_BUFFER_OVERFLOW = 0x806122b3,
CELL_ADEC_ERROR_ATX_ILL_BLK_TYPE_ID = 0x806122b4,
CELL_ADEC_ERROR_ATX_UNPACK_CHANNEL_BLK_FAILED = 0x806122b5,
CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_1 = 0x806122b6,
CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_2 = 0x806122b7,
CELL_ADEC_ERROR_ATX_ILLEGAL_ENC_SETTING = 0x806122b8,
CELL_ADEC_ERROR_ATX_ILLEGAL_DEC_SETTING = 0x806122b9,
CELL_ADEC_ERROR_ATX_ASSERT_NSAMPLES = 0x806122ba,
CELL_ADEC_ERROR_ATX_ILL_SYNCWORD = 0x806122bb,
CELL_ADEC_ERROR_ATX_ILL_SAMPLING_FREQ = 0x806122bc,
CELL_ADEC_ERROR_ATX_ILL_CH_CONFIG_INDEX = 0x806122bd,
CELL_ADEC_ERROR_ATX_RAW_DATA_FRAME_SIZE_OVER = 0x806122be,
CELL_ADEC_ERROR_ATX_SYNTAX_ENHANCE_LENGTH_OVER = 0x806122bf,
CELL_ADEC_ERROR_ATX_SPU_INTERNAL_FAIL = 0x806122c8,
CELL_ADEC_ERROR_LPCM_FATAL = 0x80612001,
CELL_ADEC_ERROR_LPCM_SEQ = 0x80612002,
CELL_ADEC_ERROR_LPCM_ARG = 0x80612003,
@ -250,38 +188,38 @@ enum CellAdecError : u32
// Audio Codec Type
enum AudioCodecType : s32
{
CELL_ADEC_TYPE_RESERVED1,
CELL_ADEC_TYPE_INVALID1,
CELL_ADEC_TYPE_LPCM_PAMF,
CELL_ADEC_TYPE_AC3,
CELL_ADEC_TYPE_ATRACX,
CELL_ADEC_TYPE_MP3,
CELL_ADEC_TYPE_ATRAC3,
CELL_ADEC_TYPE_MPEG_L2,
CELL_ADEC_TYPE_RESERVED5,
CELL_ADEC_TYPE_RESERVED6,
CELL_ADEC_TYPE_RESERVED7,
CELL_ADEC_TYPE_RESERVED8,
CELL_ADEC_TYPE_M2AAC,
CELL_ADEC_TYPE_EAC3,
CELL_ADEC_TYPE_TRUEHD,
CELL_ADEC_TYPE_DTS, // Removed in firmware 4.00, integrated into DTSHD
CELL_ADEC_TYPE_CELP,
CELL_ADEC_TYPE_RESERVED10,
CELL_ADEC_TYPE_LPCM_BLURAY,
CELL_ADEC_TYPE_ATRACX_2CH,
CELL_ADEC_TYPE_ATRACX_6CH,
CELL_ADEC_TYPE_ATRACX_8CH,
CELL_ADEC_TYPE_M4AAC,
CELL_ADEC_TYPE_RESERVED12,
CELL_ADEC_TYPE_RESERVED13,
CELL_ADEC_TYPE_RESERVED14,
CELL_ADEC_TYPE_RESERVED15,
CELL_ADEC_TYPE_RESERVED16,
CELL_ADEC_TYPE_RESERVED17,
CELL_ADEC_TYPE_RESERVED18,
CELL_ADEC_TYPE_RESERVED19,
CELL_ADEC_TYPE_LPCM_DVD,
CELL_ADEC_TYPE_WMA,
CELL_ADEC_TYPE_DTSLBR,
CELL_ADEC_TYPE_M4AAC_2CH,
CELL_ADEC_TYPE_DTSHD,
CELL_ADEC_TYPE_MPEG_L1,
CELL_ADEC_TYPE_MP3S,
CELL_ADEC_TYPE_M4AAC_2CH_MOD,
CELL_ADEC_TYPE_CELP8,
CELL_ADEC_TYPE_RESERVED20,
CELL_ADEC_TYPE_RESERVED21,
CELL_ADEC_TYPE_RESERVED22,
CELL_ADEC_TYPE_RESERVED23,
CELL_ADEC_TYPE_RESERVED24,
CELL_ADEC_TYPE_RESERVED25,
CELL_ADEC_TYPE_INVALID2,
CELL_ADEC_TYPE_INVALID3,
CELL_ADEC_TYPE_RESERVED22, // Either WMA Pro or WMA Lossless, was never released
CELL_ADEC_TYPE_RESERVED23, // Either WMA Pro or WMA Lossless, was never released
CELL_ADEC_TYPE_DTSHDCORE, // Removed in firmware 4.00, integrated into DTSHD
CELL_ADEC_TYPE_ATRAC3MULTI,
};
inline bool adecIsAtracX(s32 type)
@ -313,10 +251,12 @@ enum CellAdecChannel : s32
// Sampling Rate
enum CellAdecSampleRate : s32
{
CELL_ADEC_FS_RESERVED1 = 0,
CELL_ADEC_FS_48kHz = 1,
CELL_ADEC_FS_16kHz = 2,
CELL_ADEC_FS_8kHz = 5,
CELL_ADEC_FS_RESERVED1,
CELL_ADEC_FS_48kHz,
CELL_ADEC_FS_16kHz,
CELL_ADEC_FS_96kHz,
CELL_ADEC_FS_192kHz,
CELL_ADEC_FS_8kHz,
};
enum CellAdecBitLength : s32
@ -359,6 +299,13 @@ struct CellAdecResourceEx
be_t<u32> maxContention;
};
struct CellAdecResourceSpurs
{
be_t<u32> spurs_addr; // CellSpurs*
u8 priority[8];
be_t<u32> maxContention;
};
// Callback Messages
enum CellAdecMsgType : s32
{
@ -370,16 +317,20 @@ enum CellAdecMsgType : s32
using CellAdecCbMsg = s32(u32 handle, CellAdecMsgType msgType, s32 msgData, u32 cbArg);
struct CellAdecCb
// Used for internal callbacks as well
template <typename F>
struct AdecCb
{
vm::bptr<CellAdecCbMsg> cbFunc;
be_t<u32> cbArg;
vm::bptr<F> cbFunc;
vm::bptr<void> cbArg;
};
typedef AdecCb<CellAdecCbMsg> CellAdecCb;
// AU Info
struct CellAdecAuInfo
{
be_t<u32> startAddr;
vm::bcptr<u8> startAddr;
be_t<u32> size;
CellCodecTimeStamp pts;
be_t<u64> userData;
@ -401,6 +352,125 @@ struct CellAdecPcmItem
CellAdecAuInfo auInfo;
};
// Controls how much is added to the presentation time stamp of the previous frame if the game didn't set a pts itself in CellAdecAuInfo when calling cellAdecDecodeAu()
enum AdecCorrectPtsValueType : s8
{
ADEC_CORRECT_PTS_VALUE_TYPE_UNSPECIFIED = -1,
// Adds a fixed amount
ADEC_CORRECT_PTS_VALUE_TYPE_LPCM = 0,
// 1
ADEC_CORRECT_PTS_VALUE_TYPE_ATRACX_48000Hz = 2,
ADEC_CORRECT_PTS_VALUE_TYPE_ATRACX_44100Hz = 3,
ADEC_CORRECT_PTS_VALUE_TYPE_ATRACX_32000Hz = 4,
// 5: Dolby Digital
// 6: ATRAC3
// 7: MP3
// 8: MP3
// 9: MP3
// 39: ATRAC3 multi-track
// Calls a decoder function (_SceAdecCorrectPtsValue_codec())
// 17: Dolby Digital Plus
// 18
// 19
// 20
// 21: DTS HD
// 22
// 23
// 24: CELP
// 25: MPEG-2 AAC
// 26: MPEG-2 BC
// 27: Dolby TrueHD
// 28: DTS
// 29: MPEG-4 AAC
// 30: Windows Media Audio
// 31: DTS Express
// 32: MP1
// 33: MP3 Surround
// 34: CELP8
// 35: Windows Media Audio Professional
// 36: Windows Media Audio Lossless
// 37: DTS HD Core
// 38: DTS HD Core
};
// Internal callbacks
using AdecNotifyAuDone = error_code(s32 pcmHandle, vm::ptr<void> cbArg);
using AdecNotifyPcmOut = error_code(s32 pcmHandle, vm::ptr<void> pcmAddr, u32 pcmSize, vm::ptr<void> cbArg, vm::cpptr<void> bsiInfo, AdecCorrectPtsValueType correctPtsValueType, s32 errorCode);
using AdecNotifyError = error_code(s32 errorCode, vm::ptr<void> cbArg);
using AdecNotifySeqDone = error_code(vm::ptr<void> cbArg);
// Decoder functions
using CellAdecCoreOpGetMemSize = error_code(vm::ptr<CellAdecAttr> attr);
using CellAdecCoreOpOpen = error_code(vm::ptr<void> coreHandle, vm::ptr<AdecNotifyAuDone> cbFuncAuDone, vm::ptr<void> cbArgAuDone, vm::ptr<AdecNotifyPcmOut> cbFuncPcmOut, vm::ptr<void> cbArgPcmOut,
vm::ptr<AdecNotifyError> cbFuncError, vm::ptr<void> cbArgError, vm::ptr<AdecNotifySeqDone> cbFuncSeqDone, vm::ptr<void> cbArgSeqDone, vm::cptr<CellAdecResource> res);
using CellAdecCoreOpClose = error_code(vm::ptr<void> coreHandle);
using CellAdecCoreOpStartSeq = error_code(vm::ptr<void> coreHandle, vm::cptr<void> param);
using CellAdecCoreOpEndSeq = error_code(vm::ptr<void> coreHandle);
using CellAdecCoreOpDecodeAu = error_code(vm::ptr<void> coreHandle, s32 pcmHandle, vm::cptr<CellAdecAuInfo> auInfo);
using CellAdecCoreOpGetVersion = void(vm::ptr<std::array<u8, 4>> version);
using CellAdecCoreOpRealign = error_code(vm::ptr<void> coreHandle, vm::ptr<void> outBuffer, vm::cptr<void> pcmStartAddr);
using CellAdecCoreOpReleasePcm = error_code(vm::ptr<void> coreHandle, s32 pcmHandle, vm::cptr<void> outBuffer);
using CellAdecCoreOpGetPcmHandleNum = s32();
using CellAdecCoreOpGetBsiInfoSize = u32();
using CellAdecCoreOpOpenExt = error_code(vm::ptr<void> coreHandle, vm::ptr<AdecNotifyAuDone> cbFuncAuDone, vm::ptr<void> cbArgAuDone, vm::ptr<AdecNotifyPcmOut> cbFuncPcmOut, vm::ptr<void> cbArgPcmOut,
vm::ptr<AdecNotifyError> cbFuncError, vm::ptr<void> cbArgError, vm::ptr<AdecNotifySeqDone> cbFuncSeqDone, vm::ptr<void> cbArgSeqDone, vm::cptr<CellAdecResource> res, vm::cptr<CellAdecResourceSpurs> spursRes);
// Decoders export a pointer to this struct
struct CellAdecCoreOps
{
vm::bptr<CellAdecCoreOpGetMemSize> getMemSize;
vm::bptr<CellAdecCoreOpOpen> open;
vm::bptr<CellAdecCoreOpClose> close;
vm::bptr<CellAdecCoreOpStartSeq> startSeq;
vm::bptr<CellAdecCoreOpEndSeq> endSeq;
vm::bptr<CellAdecCoreOpDecodeAu> decodeAu;
vm::bptr<CellAdecCoreOpGetVersion> getVersion;
vm::bptr<CellAdecCoreOpRealign> realign;
vm::bptr<CellAdecCoreOpReleasePcm> releasePcm;
vm::bptr<CellAdecCoreOpGetPcmHandleNum> getPcmHandleNum;
vm::bptr<CellAdecCoreOpGetBsiInfoSize> getBsiInfoSize;
vm::bptr<CellAdecCoreOpOpenExt> openExt;
};
// Used by several decoders as command queue
template <typename T>
struct AdecCmdQueue
{
T elements[4];
be_t<s32> front = 0;
be_t<s32> back = 0;
be_t<s32> size = 0;
template <bool is_peek = false>
void pop(T& cmd)
{
// LLE returns uninitialized stack memory if the queue is empty
cmd = elements[front];
if constexpr (!is_peek)
{
elements[front].pcm_handle = 0xff;
front = (front + 1) & 3;
size--;
}
}
void emplace(auto&&... args)
{
new (&elements[back]) T(std::forward<decltype(args)>(args)...);
back = (back + 1) & 3;
size++;
}
void peek(T& cmd) const { return pop<true>(cmd); }
bool empty() const { return size == 0; }
bool full() const { return size >= 4; }
};
struct CellAdecParamLpcm
{
be_t<u32> channelNumber;
@ -771,45 +841,6 @@ struct CellAdecAtrac3Info
be_t<s32> nbytes;
};
enum ATRACX_WordSize : s32
{
CELL_ADEC_ATRACX_WORD_SZ_16BIT = 0x02,
CELL_ADEC_ATRACX_WORD_SZ_24BIT = 0x03,
CELL_ADEC_ATRACX_WORD_SZ_32BIT = 0x04,
CELL_ADEC_ATRACX_WORD_SZ_FLOAT = 0x84,
};
enum ATRACX_ATSHeaderInclude : u8
{
CELL_ADEC_ATRACX_ATS_HDR_NOTINC = 0,
CELL_ADEC_ATRACX_ATS_HDR_INC = 1,
};
enum ATRACX_DownmixFlag : u8
{
ATRACX_DOWNMIX_OFF = 0,
ATRACX_DOWNMIX_ON = 1,
};
struct CellAdecParamAtracX
{
be_t<s32> sampling_freq;
be_t<s32> ch_config_idx;
be_t<s32> nch_out;
be_t<s32> nbytes;
std::array<u8, 4> extra_config_data; // downmix coefficients
be_t<s32> bw_pcm; // ATRACX_WordSize
ATRACX_DownmixFlag downmix_flag;
ATRACX_ATSHeaderInclude au_includes_ats_hdr_flg;
};
struct CellAdecAtracXInfo
{
be_t<u32> samplingFreq; // [Hz]
be_t<u32> channelConfigIndex;
be_t<u32> nbytes;
};
enum MP3_WordSize : s32
{
CELL_ADEC_MP3_WORD_SZ_16BIT = 3,

View File

@ -0,0 +1,894 @@
#include "stdafx.h"
#include "Emu/perf_meter.hpp"
#include "Emu/Cell/PPUModule.h"
#include "Emu/Cell/lv2/sys_sync.h"
#include "Emu/Cell/lv2/sys_ppu_thread.h"
#include "Emu/savestate_utils.hpp"
#include "sysPrxForUser.h"
#include "util/asm.hpp"
#include "util/media_utils.h"
#include "cellAtracXdec.h"
vm::gvar<CellAdecCoreOps> g_cell_adec_core_ops_atracx2ch;
vm::gvar<CellAdecCoreOps> g_cell_adec_core_ops_atracx6ch;
vm::gvar<CellAdecCoreOps> g_cell_adec_core_ops_atracx8ch;
vm::gvar<CellAdecCoreOps> g_cell_adec_core_ops_atracx;
LOG_CHANNEL(cellAtracXdec);
template <>
void fmt_class_string<CellAtracXdecError>::format(std::string& out, u64 arg)
{
format_enum(out, arg, [](CellAtracXdecError value)
{
switch (value)
{
STR_CASE(CELL_ADEC_ERROR_ATX_OK); // CELL_ADEC_ERROR_ATX_OFFSET, CELL_ADEC_ERROR_ATX_NONE
STR_CASE(CELL_ADEC_ERROR_ATX_BUSY);
STR_CASE(CELL_ADEC_ERROR_ATX_EMPTY);
STR_CASE(CELL_ADEC_ERROR_ATX_ATSHDR);
STR_CASE(CELL_ADEC_ERROR_ATX_NON_FATAL);
STR_CASE(CELL_ADEC_ERROR_ATX_NOT_IMPLE);
STR_CASE(CELL_ADEC_ERROR_ATX_PACK_CE_OVERFLOW);
STR_CASE(CELL_ADEC_ERROR_ATX_ILLEGAL_NPROCQUS);
STR_CASE(CELL_ADEC_ERROR_ATX_FATAL);
STR_CASE(CELL_ADEC_ERROR_ATX_ENC_OVERFLOW);
STR_CASE(CELL_ADEC_ERROR_ATX_PACK_CE_UNDERFLOW);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDCT);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GAINADJ);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_SPECTRA);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GHWAVE);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_SHEADER);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_B);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_C);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_D);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_E);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_B);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_C);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_D);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_IDCT_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_NGC);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_A);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_B);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_B);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_SN_NWVS);
STR_CASE(CELL_ADEC_ERROR_ATX_FATAL_HANDLE);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_SAMPLING_FREQ);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_CH_CONFIG_INDEX);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_NBYTES);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_NUM);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_ID);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_CHANNELS);
STR_CASE(CELL_ADEC_ERROR_ATX_UNINIT_BLOCK_SPECIFIED);
STR_CASE(CELL_ADEC_ERROR_ATX_POSCFG_PRESENT);
STR_CASE(CELL_ADEC_ERROR_ATX_BUFFER_OVERFLOW);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_BLK_TYPE_ID);
STR_CASE(CELL_ADEC_ERROR_ATX_UNPACK_CHANNEL_BLK_FAILED);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_1);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_2);
STR_CASE(CELL_ADEC_ERROR_ATX_ILLEGAL_ENC_SETTING);
STR_CASE(CELL_ADEC_ERROR_ATX_ILLEGAL_DEC_SETTING);
STR_CASE(CELL_ADEC_ERROR_ATX_ASSERT_NSAMPLES);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_SYNCWORD);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_SAMPLING_FREQ);
STR_CASE(CELL_ADEC_ERROR_ATX_ILL_CH_CONFIG_INDEX);
STR_CASE(CELL_ADEC_ERROR_ATX_RAW_DATA_FRAME_SIZE_OVER);
STR_CASE(CELL_ADEC_ERROR_ATX_SYNTAX_ENHANCE_LENGTH_OVER);
STR_CASE(CELL_ADEC_ERROR_ATX_SPU_INTERNAL_FAIL);
}
return unknown;
});
}
constexpr u32 atracXdecGetSpursMemSize(u32 nch_in)
{
switch (nch_in)
{
case 1: return 0x6000;
case 2: return 0x6000;
case 3: return 0x12880;
case 4: return 0x19c80;
case 5: return -1;
case 6: return 0x23080;
case 7: return 0x2a480;
case 8: return 0x2c480;
default: return -1;
}
}
void AtracXdecDecoder::alloc_avcodec()
{
codec = avcodec_find_decoder(AV_CODEC_ID_ATRAC3P);
if (!codec)
{
fmt::throw_exception("avcodec_find_decoder() failed");
}
ensure(!(codec->capabilities & AV_CODEC_CAP_SUBFRAMES));
ctx = avcodec_alloc_context3(codec);
if (!ctx)
{
fmt::throw_exception("avcodec_alloc_context3() failed");
}
// Allows FFmpeg to output directly into guest memory
ctx->opaque = this;
ctx->get_buffer2 = [](AVCodecContext* s, AVFrame* frame, int /*flags*/) -> int
{
for (s32 i = 0; i < frame->ch_layout.nb_channels; i++)
{
frame->data[i] = static_cast<AtracXdecDecoder*>(s->opaque)->work_mem.get_ptr() + ATXDEC_MAX_FRAME_LENGTH + ATXDEC_SAMPLES_PER_FRAME * sizeof(f32) * i;
frame->linesize[i] = ATXDEC_SAMPLES_PER_FRAME * sizeof(f32);
}
frame->buf[0] = av_buffer_create(frame->data[0], ATXDEC_SAMPLES_PER_FRAME * sizeof(f32) * frame->ch_layout.nb_channels, [](void*, uint8_t*){}, nullptr, 0);
return 0;
};
packet = av_packet_alloc();
if (!packet)
{
fmt::throw_exception("av_packet_alloc() failed");
}
frame = av_frame_alloc();
if (!frame)
{
fmt::throw_exception("av_frame_alloc() failed");
}
}
void AtracXdecDecoder::free_avcodec()
{
av_packet_free(&packet);
av_frame_free(&frame);
avcodec_free_context(&ctx);
}
void AtracXdecDecoder::init_avcodec()
{
if (int err = avcodec_close(ctx); err)
{
fmt::throw_exception("avcodec_close() failed (err=0x%x='%s')", err, utils::av_error_to_string(err));
}
ctx->block_align = nbytes;
ctx->ch_layout.nb_channels = nch_in;
ctx->sample_rate = sampling_freq;
if (int err = avcodec_open2(ctx, codec, nullptr); err)
{
fmt::throw_exception("avcodec_open2() failed (err=0x%x='%s')", err, utils::av_error_to_string(err));
}
packet->data = work_mem.get_ptr();
packet->size = nbytes;
packet->buf = av_buffer_create(work_mem.get_ptr(), nbytes, [](void*, uint8_t*){}, nullptr, 0);
}
error_code AtracXdecDecoder::set_config_info(u32 sampling_freq, u32 ch_config_idx, u32 nbytes)
{
cellAtracXdec.notice("AtracXdecDecoder::set_config_info(sampling_freq=%d, ch_config_idx=%d, nbytes=0x%x)", sampling_freq, ch_config_idx, nbytes);
this->sampling_freq = sampling_freq;
this->ch_config_idx = ch_config_idx;
this->nbytes = nbytes;
this->nbytes_128_aligned = utils::align(nbytes, 0x80);
this->nch_in = ch_config_idx <= 4 ? ch_config_idx : ch_config_idx + 1;
if (ch_config_idx > 7u)
{
this->config_is_set = false;
return { 0x80004005, "AtracXdecDecoder::set_config_info() failed: Invalid channel configuration: %d", ch_config_idx };
}
this->nch_blocks = ATXDEC_NCH_BLOCKS_MAP[ch_config_idx];
// These checks are performed on the LLE SPU thread
if (ch_config_idx == 0u)
{
this->config_is_set = false;
return { 0x80004005, "AtracXdecDecoder::set_config_info() failed: Invalid channel configuration: %d", ch_config_idx };
}
if (sampling_freq != 48000u && sampling_freq != 44100u) // 32kHz is not supported, even though official docs claim it is
{
this->config_is_set = false;
return { 0x80004005, "AtracXdecDecoder::set_config_info() failed: Invalid sample rate: %d", sampling_freq };
}
if (nbytes == 0u || nbytes > ATXDEC_MAX_FRAME_LENGTH)
{
this->config_is_set = false;
return { 0x80004005, "AtracXdecDecoder::set_config_info() failed: Invalid frame length: 0x%x", nbytes };
}
this->config_is_set = true;
return CELL_OK;
}
error_code AtracXdecDecoder::init_decode(u32 bw_pcm, u32 nch_out)
{
if (bw_pcm < CELL_ADEC_ATRACX_WORD_SZ_16BIT || (bw_pcm > CELL_ADEC_ATRACX_WORD_SZ_32BIT && bw_pcm != CELL_ADEC_ATRACX_WORD_SZ_FLOAT))
{
return { 0x80004005, "AtracXdecDecoder::init_decode() failed: Invalid PCM output format" };
}
this->bw_pcm = bw_pcm;
this->nch_out = nch_out; // Not checked for invalid values on LLE
this->pcm_output_size = (bw_pcm == CELL_ADEC_ATRACX_WORD_SZ_16BIT ? sizeof(s16) : sizeof(f32)) * nch_in * ATXDEC_SAMPLES_PER_FRAME;
init_avcodec();
return CELL_OK;
}
error_code AtracXdecDecoder::parse_ats_header(vm::cptr<u8> au_start_addr)
{
const auto ats = std::bit_cast<AtracXdecAtsHeader>(vm::read64(au_start_addr.addr()));
if (ats.sync_word != 0x0fd0)
{
return { CELL_ADEC_ERROR_ATX_ATSHDR, "AtracXdecDecoder::parse_ats_header() failed: Invalid sync word: 0x%x", ats.sync_word };
}
const u8 sample_rate_idx = ats.params >> 13;
const u8 ch_config_idx = ats.params >> 10 & 7;
const u16 nbytes = ((ats.params & 0x3ff) + 1) * 8;
if (ch_config_idx == 0u)
{
return { CELL_ADEC_ERROR_ATX_ATSHDR, "AtracXdecDecoder::parse_ats_header() failed: Invalid channel configuration: %d", ch_config_idx };
}
u32 sampling_freq;
switch (sample_rate_idx)
{
case 1: sampling_freq = 44100; break;
case 2: sampling_freq = 48000; break;
default: return { CELL_ADEC_ERROR_ATX_ATSHDR, "AtracXdecDecoder::parse_ats_header() failed: Invalid sample rate index: %d", sample_rate_idx };
}
return set_config_info(sampling_freq, ch_config_idx, nbytes); // Cannot return error here, values were already checked
}
void AtracXdecContext::exec(ppu_thread& ppu)
{
perf_meter<"ATXDEC"_u64> perf0;
// Savestates
if (decoder.config_is_set)
{
decoder.init_avcodec();
}
for (;;cmd_counter++)
{
cellAtracXdec.trace("Command counter: %llu, waiting for next command...", cmd_counter);
if (!skip_getting_command)
{
lv2_obj::sleep(ppu);
std::lock_guard lock{queue_mutex};
while (cmd_queue.empty() && !ppu.is_stopped())
{
lv2_obj::sleep(ppu);
queue_not_empty.wait(queue_mutex, 20000);
}
if (ppu.is_stopped())
{
ppu.state += cpu_flag::again;
return;
}
cmd_queue.pop(cmd);
if (!run_thread)
{
return;
}
}
cellAtracXdec.trace("Command type: %d", static_cast<u32>(cmd.type.get()));
switch (cmd.type)
{
case AtracXdecCmdType::start_seq:
{
first_decode = true;
skip_next_frame = true;
// Skip if access units contain an ATS header, the parameters are included in the header and we need to wait for the first decode command to parse them
if (cmd.atracx_param.au_includes_ats_hdr_flg == CELL_ADEC_ATRACX_ATS_HDR_NOTINC)
{
if (decoder.set_config_info(cmd.atracx_param.sampling_freq, cmd.atracx_param.ch_config_idx, cmd.atracx_param.nbytes) == static_cast<s32>(0x80004005))
{
break;
}
if (decoder.init_decode(cmd.atracx_param.bw_pcm, cmd.atracx_param.nch_out) == static_cast<s32>(0x80004005))
{
break;
}
}
atracx_param = cmd.atracx_param;
break;
}
case AtracXdecCmdType::end_seq:
{
skip_getting_command = true;
// Block savestate creation during callbacks
std::unique_lock savestate_lock{g_fxo->get<hle_locks_t>(), std::try_to_lock};
if (!savestate_lock.owns_lock())
{
ppu.state += cpu_flag::again;
return;
}
skip_getting_command = false;
// Doesn't do anything else
notify_seq_done.cbFunc(ppu, notify_seq_done.cbArg);
break;
}
case AtracXdecCmdType::decode_au:
{
skip_getting_command = true;
ensure(!!cmd.au_start_addr); // Not checked on LLE
cellAtracXdec.trace("Waiting for output to be consumed...");
lv2_obj::sleep(ppu);
std::unique_lock output_mutex_lock{output_mutex};
while (output_locked && !ppu.is_stopped())
{
lv2_obj::sleep(ppu);
output_consumed.wait(output_mutex, 20000);
}
if (ppu.is_stopped())
{
ppu.state += cpu_flag::again;
return;
}
if (!run_thread)
{
return;
}
cellAtracXdec.trace("Output consumed");
u32 error = CELL_OK;
// Only the first valid ATS header after starting a sequence is parsed. It is ignored on all subsequent access units
if (first_decode && atracx_param.au_includes_ats_hdr_flg == CELL_ADEC_ATRACX_ATS_HDR_INC)
{
// Block savestate creation during callbacks
std::unique_lock savestate_lock{g_fxo->get<hle_locks_t>(), std::try_to_lock};
if (!savestate_lock.owns_lock())
{
ppu.state += cpu_flag::again;
return;
}
if (error = decoder.parse_ats_header(cmd.au_start_addr); error != CELL_OK)
{
notify_error.cbFunc(ppu, error, notify_error.cbArg);
}
else if (decoder.init_decode(atracx_param.bw_pcm, atracx_param.nch_out) != CELL_OK)
{
notify_error.cbFunc(ppu, CELL_ADEC_ERROR_ATX_FATAL, notify_error.cbArg);
}
}
// LLE does not initialize the output address if parsing the ATS header fails
vm::ptr<void> output = vm::null;
u32 decoded_samples_num = 0;
if (error != CELL_ADEC_ERROR_ATX_ATSHDR)
{
// The LLE SPU thread would crash if you attempt to decode without a valid configuration
ensure(decoder.config_is_set, "Attempted to decode with invalid configuration");
output.set(work_mem.addr() + atracXdecGetSpursMemSize(decoder.nch_in));
const auto au_start_addr = atracx_param.au_includes_ats_hdr_flg == CELL_ADEC_ATRACX_ATS_HDR_INC ? cmd.au_start_addr.get_ptr() + sizeof(AtracXdecAtsHeader) : cmd.au_start_addr.get_ptr();
std::memcpy(work_mem.get_ptr(), au_start_addr, decoder.nbytes);
if (int err = avcodec_send_packet(decoder.ctx, decoder.packet); err)
{
// These errors should never occur
if (err == AVERROR(EAGAIN) || err == AVERROR_EOF || err == AVERROR(EINVAL) || err == AVERROR(ENOMEM))
{
fmt::throw_exception("avcodec_send_packet() failed (err=0x%x='%s')", err, utils::av_error_to_string(err));
}
// Game sent invalid data
cellAtracXdec.error("avcodec_send_packet() failed (err=0x%x='%s')", err, utils::av_error_to_string(err));
error = CELL_ADEC_ERROR_ATX_NON_FATAL; // Not accurate, FFmpeg doesn't provide detailed errors like LLE
av_frame_unref(decoder.frame);
}
else if (err = avcodec_receive_frame(decoder.ctx, decoder.frame))
{
fmt::throw_exception("avcodec_receive_frame() failed (err=0x%x='%s')", err, utils::av_error_to_string(err));
}
decoded_samples_num = decoder.frame->nb_samples;
ensure(decoded_samples_num == 0u || decoded_samples_num == ATXDEC_SAMPLES_PER_FRAME);
// The first frame after a starting a new sequence or after an error is replaced with silence
if (skip_next_frame && error == CELL_OK)
{
skip_next_frame = false;
decoded_samples_num = 0;
std::memset(output.get_ptr(), 0, ATXDEC_SAMPLES_PER_FRAME * (decoder.bw_pcm & 0x7full) * decoder.nch_out);
}
// Convert FFmpeg output to LLE output
const auto output_f32 = vm::static_ptr_cast<f32>(output).get_ptr();
const auto output_s16 = vm::static_ptr_cast<s16>(output).get_ptr();
const auto output_s32 = vm::static_ptr_cast<s32>(output).get_ptr();
const u8* const ch_map = ATXDEC_AVCODEC_CH_MAP[decoder.ch_config_idx - 1];
const u32 nch_in = decoder.nch_in;
switch (decoder.bw_pcm)
{
case CELL_ADEC_ATRACX_WORD_SZ_FLOAT:
for (u32 channel_idx = 0; channel_idx < nch_in; channel_idx++)
{
const f32* samples = reinterpret_cast<f32*>(decoder.frame->data[channel_idx]);
for (u32 in_sample_idx = 0, out_sample_idx = ch_map[channel_idx]; in_sample_idx < decoded_samples_num; in_sample_idx++, out_sample_idx += nch_in)
{
const f32 sample = samples[in_sample_idx];
if (sample >= std::bit_cast<f32>(std::bit_cast<u32>(1.f) - 1))
{
output_f32[out_sample_idx] = std::bit_cast<be_t<f32>>("\x3f\x7f\xff\xff"_u32); // Prevents an unnecessary endian swap
}
else if (sample <= -1.f)
{
output_f32[out_sample_idx] = -1.f;
}
else
{
output_f32[out_sample_idx] = sample;
}
}
}
break;
case CELL_ADEC_ATRACX_WORD_SZ_16BIT:
for (u32 channel_idx = 0; channel_idx < nch_in; channel_idx++)
{
const f32* samples = reinterpret_cast<f32*>(decoder.frame->data[channel_idx]);
for (u32 in_sample_idx = 0, out_sample_idx = ch_map[channel_idx]; in_sample_idx < decoded_samples_num; in_sample_idx++, out_sample_idx += nch_in)
{
const f32 sample = samples[in_sample_idx];
if (sample >= 1.f)
{
output_s16[out_sample_idx] = INT16_MAX;
}
else if (sample <= -1.f)
{
output_s16[out_sample_idx] = INT16_MIN;
}
else
{
output_s16[out_sample_idx] = static_cast<s16>(std::floor(sample * 0x8000u));
}
}
}
break;
case CELL_ADEC_ATRACX_WORD_SZ_24BIT:
for (u32 channel_idx = 0; channel_idx < nch_in; channel_idx++)
{
const f32* samples = reinterpret_cast<f32*>(decoder.frame->data[channel_idx]);
for (u32 in_sample_idx = 0, out_sample_idx = ch_map[channel_idx]; in_sample_idx < decoded_samples_num; in_sample_idx++, out_sample_idx += nch_in)
{
const f32 sample = samples[in_sample_idx];
if (sample >= 1.f)
{
output_s32[out_sample_idx] = 0x007fffff;
}
else if (sample <= -1.f)
{
output_s32[out_sample_idx] = 0x00800000;
}
else
{
output_s32[out_sample_idx] = static_cast<s32>(std::floor(sample * 0x00800000u)) & 0x00ffffff;
}
}
}
break;
case CELL_ADEC_ATRACX_WORD_SZ_32BIT:
for (u32 channel_idx = 0; channel_idx < nch_in; channel_idx++)
{
const f32* samples = reinterpret_cast<f32*>(decoder.frame->data[channel_idx]);
for (u32 in_sample_idx = 0, out_sample_idx = ch_map[channel_idx]; in_sample_idx < decoded_samples_num; in_sample_idx++, out_sample_idx += nch_in)
{
const f32 sample = samples[in_sample_idx];
if (sample >= 1.f)
{
output_s32[out_sample_idx] = INT32_MAX;
}
else if (sample <= -1.f)
{
output_s32[out_sample_idx] = INT32_MIN;
}
else
{
output_s32[out_sample_idx] = static_cast<s32>(std::floor(sample * 0x80000000u));
}
}
}
}
first_decode = false;
if (error != CELL_OK)
{
// Block savestate creation during callbacks
std::unique_lock savestate_lock{g_fxo->get<hle_locks_t>(), std::try_to_lock};
if (!savestate_lock.owns_lock())
{
ppu.state += cpu_flag::again;
return;
}
skip_next_frame = true;
notify_error.cbFunc(ppu, error, notify_error.cbArg);
}
}
// Block savestate creation during callbacks
std::unique_lock savestate_lock{g_fxo->get<hle_locks_t>(), std::try_to_lock};
if (!savestate_lock.owns_lock())
{
ppu.state += cpu_flag::again;
return;
}
skip_getting_command = false;
// au_done and pcm_out callbacks are always called after a decode command, even if an error occurred
// The output always has to be consumed as well
notify_au_done.cbFunc(ppu, cmd.pcm_handle, notify_au_done.cbArg);
output_locked = true;
output_mutex_lock.unlock();
const u32 output_size = decoded_samples_num * (decoder.bw_pcm & 0x7fu) * decoder.nch_out;
const vm::var<CellAdecAtracXInfo> bsi_info{{ decoder.sampling_freq, decoder.ch_config_idx, decoder.nbytes }};
const AdecCorrectPtsValueType correct_pts_type = [&]
{
switch (decoder.sampling_freq)
{
case 32000u: return ADEC_CORRECT_PTS_VALUE_TYPE_ATRACX_32000Hz;
case 44100u: return ADEC_CORRECT_PTS_VALUE_TYPE_ATRACX_44100Hz;
case 48000u: return ADEC_CORRECT_PTS_VALUE_TYPE_ATRACX_48000Hz;
default: return ADEC_CORRECT_PTS_VALUE_TYPE_UNSPECIFIED;
}
}();
notify_pcm_out.cbFunc(ppu, cmd.pcm_handle, output, output_size, notify_pcm_out.cbArg, vm::make_var<vm::bcptr<void>>(bsi_info), correct_pts_type, error);
break;
}
default:
fmt::throw_exception("Invalid command");
}
}
}
template <AtracXdecCmdType type>
error_code AtracXdecContext::send_command(ppu_thread& ppu, auto&&... args)
{
ppu.state += cpu_flag::wait;
{
std::lock_guard lock{queue_mutex};
if constexpr (type == AtracXdecCmdType::close)
{
// Close command is only sent if the queue is empty on LLE
if (!cmd_queue.empty())
{
return {};
}
}
if (cmd_queue.full())
{
return CELL_ADEC_ERROR_ATX_BUSY;
}
cmd_queue.emplace(std::forward<AtracXdecCmdType>(type), std::forward<decltype(args)>(args)...);
}
queue_not_empty.notify_one();
return CELL_OK;
}
void atracXdecEntry(ppu_thread& ppu, vm::ptr<AtracXdecContext> atxdec)
{
atxdec->decoder.alloc_avcodec();
atxdec->exec(ppu);
atxdec->decoder.free_avcodec();
if (ppu.state & cpu_flag::again)
{
// For savestates, save argument
ppu.syscall_args[0] = atxdec.addr();
return;
}
ppu_execute<&sys_ppu_thread_exit>(ppu, CELL_OK);
}
template <u32 nch_in>
error_code _CellAdecCoreOpGetMemSize_atracx(vm::ptr<CellAdecAttr> attr)
{
cellAtracXdec.notice("_CellAdecCoreOpGetMemSize_atracx<nch_in=%d>(attr=*0x%x)", nch_in, attr);
ensure(!!attr); // Not checked on LLE
constexpr u32 mem_size =
sizeof(AtracXdecContext) + 0x7f
+ ATXDEC_SPURS_STRUCTS_SIZE + 0x1d8
+ atracXdecGetSpursMemSize(nch_in)
+ ATXDEC_SAMPLES_PER_FRAME * sizeof(f32) * nch_in;
attr->workMemSize = utils::align(mem_size, 0x80);
return CELL_OK;
}
error_code _CellAdecCoreOpOpenExt_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle, vm::ptr<AdecNotifyAuDone> notifyAuDone, vm::ptr<void> notifyAuDoneArg, vm::ptr<AdecNotifyPcmOut> notifyPcmOut, vm::ptr<void> notifyPcmOutArg,
vm::ptr<AdecNotifyError> notifyError, vm::ptr<void> notifyErrorArg, vm::ptr<AdecNotifySeqDone> notifySeqDone, vm::ptr<void> notifySeqDoneArg, vm::cptr<CellAdecResource> res, vm::cptr<CellAdecResourceSpurs> spursRes)
{
std::unique_lock savestate_lock{g_fxo->get<hle_locks_t>(), std::try_to_lock};
if (!savestate_lock.owns_lock())
{
ppu.state += cpu_flag::again;
return {};
}
cellAtracXdec.notice("_CellAdecCoreOpOpenExt_atracx(handle=*0x%x, notifyAuDone=*0x%x, notifyAuDoneArg=*0x%x, notifyPcmOut=*0x%x, notifyPcmOutArg=*0x%x, notifyError=*0x%x, notifyErrorArg=*0x%x, notifySeqDone=*0x%x, notifySeqDoneArg=*0x%x, res=*0x%x, spursRes=*0x%x)",
handle, notifyAuDone, notifyAuDoneArg, notifyPcmOut, notifyPcmOutArg, notifyError, notifyErrorArg, notifySeqDone, notifySeqDoneArg, res, spursRes);
ensure(!!handle && !!res); // Not checked on LLE
ensure(handle.aligned(0x80)); // On LLE, this functions doesn't check the alignment or aligns the address itself. The address should already be aligned to 128 bytes by cellAdec
ensure(!!notifyAuDone && !!notifyAuDoneArg && !!notifyPcmOut && !!notifyPcmOutArg && !!notifyError && !!notifyErrorArg && !!notifySeqDone && !!notifySeqDoneArg); // These should always be set by cellAdec
write_to_ptr(handle.get_ptr(), AtracXdecContext(notifyAuDone, notifyAuDoneArg, notifyPcmOut, notifyPcmOutArg, notifyError, notifyErrorArg, notifySeqDone, notifySeqDoneArg,
vm::bptr<u8>::make(handle.addr() + utils::align(static_cast<u32>(sizeof(AtracXdecContext)), 0x80) + ATXDEC_SPURS_STRUCTS_SIZE)));
const vm::var<char[]> _name = vm::make_str("HLE ATRAC3plus decoder");
const auto entry = g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(atracXdecEntry));
ppu_execute<&sys_ppu_thread_create>(ppu, handle.ptr(&AtracXdecContext::thread_id), entry, handle.addr(), +res->ppuThreadPriority, +res->ppuThreadStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, +_name);
return CELL_OK;
}
error_code _CellAdecCoreOpOpen_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle, vm::ptr<AdecNotifyAuDone> notifyAuDone, vm::ptr<void> notifyAuDoneArg, vm::ptr<AdecNotifyPcmOut> notifyPcmOut, vm::ptr<void> notifyPcmOutArg,
vm::ptr<AdecNotifyError> notifyError, vm::ptr<void> notifyErrorArg, vm::ptr<AdecNotifySeqDone> notifySeqDone, vm::ptr<void> notifySeqDoneArg, vm::cptr<CellAdecResource> res)
{
cellAtracXdec.notice("_CellAdecCoreOpOpen_atracx(handle=*0x%x, notifyAuDone=*0x%x, notifyAuDoneArg=*0x%x, notifyPcmOut=*0x%x, notifyPcmOutArg=*0x%x, notifyError=*0x%x, notifyErrorArg=*0x%x, notifySeqDone=*0x%x, notifySeqDoneArg=*0x%x, res=*0x%x)",
handle, notifyAuDone, notifyAuDoneArg, notifyPcmOut, notifyPcmOutArg, notifyError, notifyErrorArg, notifySeqDone, notifySeqDoneArg, res);
return _CellAdecCoreOpOpenExt_atracx(ppu, handle, notifyAuDone, notifyAuDoneArg, notifyPcmOut, notifyPcmOutArg, notifyError, notifyErrorArg, notifySeqDone, notifySeqDoneArg, res, vm::null);
}
error_code _CellAdecCoreOpClose_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle)
{
std::unique_lock savestate_lock{g_fxo->get<hle_locks_t>(), std::try_to_lock};
if (!savestate_lock.owns_lock())
{
ppu.state += cpu_flag::again;
return {};
}
ppu.state += cpu_flag::wait;
cellAtracXdec.notice("_CellAdecCoreOpClose_atracx(handle=*0x%x)", handle);
ensure(!!handle); // Not checked on LLE
handle->run_thread = false;
handle->send_command<AtracXdecCmdType::close>(ppu);
{
std::lock_guard lock{handle->output_mutex};
handle->output_locked = false;
}
handle->output_consumed.notify_one();
if (vm::var<u64> ret; sys_ppu_thread_join(ppu, static_cast<u32>(handle->thread_id), +ret) != CELL_OK)
{
// Other thread already closed the decoder
return CELL_ADEC_ERROR_FATAL;
}
return CELL_OK;
}
error_code _CellAdecCoreOpStartSeq_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle, vm::cptr<CellAdecParamAtracX> atracxParam)
{
cellAtracXdec.notice("_CellAdecCoreOpStartSeq_atracx(handle=*0x%x, atracxParam=*0x%x)", handle, atracxParam);
ensure(!!handle && !!atracxParam); // Not checked on LLE
cellAtracXdec.notice("_CellAdecCoreOpStartSeq_atracx(): sampling_freq=%d, ch_config_idx=%d, nch_out=%d, nbytes=0x%x, extra_config_data=0x%08x, bw_pcm=0x%x, downmix_flag=%d, au_includes_ats_hdr_flg=%d",
atracxParam->sampling_freq, atracxParam->ch_config_idx, atracxParam->nch_out, atracxParam->nbytes, std::bit_cast<u32>(atracxParam->extra_config_data), atracxParam->bw_pcm, atracxParam->downmix_flag, atracxParam->au_includes_ats_hdr_flg);
return handle->send_command<AtracXdecCmdType::start_seq>(ppu, *atracxParam);
}
error_code _CellAdecCoreOpEndSeq_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle)
{
cellAtracXdec.notice("_CellAdecCoreOpEndSeq_atracx(handle=*0x%x)", handle);
ensure(!!handle); // Not checked on LLE
return handle->send_command<AtracXdecCmdType::end_seq>(ppu);
}
error_code _CellAdecCoreOpDecodeAu_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle, s32 pcmHandle, vm::cptr<CellAdecAuInfo> auInfo)
{
cellAtracXdec.trace("_CellAdecCoreOpDecodeAu_atracx(handle=*0x%x, pcmHandle=%d, auInfo=*0x%x)", handle, pcmHandle, auInfo);
ensure(!!handle && !!auInfo); // Not checked on LLE
cellAtracXdec.trace("_CellAdecCoreOpDecodeAu_atracx(): startAddr=*0x%x, size=0x%x, pts=%lld, userData=0x%llx", auInfo->startAddr, auInfo->size, std::bit_cast<be_t<u64>>(auInfo->pts), auInfo->userData);
return handle->send_command<AtracXdecCmdType::decode_au>(ppu, pcmHandle, *auInfo);
}
void _CellAdecCoreOpGetVersion_atracx(vm::ptr<u8> version)
{
cellAtracXdec.notice("_CellAdecCoreOpGetVersion_atracx(version=*0x%x)", version);
ensure(!!version); // Not checked on LLE
version[0] = 0x01;
version[1] = 0x02;
version[2] = 0x00;
version[3] = 0x00;
}
error_code _CellAdecCoreOpRealign_atracx(vm::ptr<AtracXdecContext> handle, vm::ptr<void> outBuffer, vm::cptr<void> pcmStartAddr)
{
cellAtracXdec.trace("_CellAdecCoreOpRealign_atracx(handle=*0x%x, outBuffer=*0x%x, pcmStartAddr=*0x%x)", handle, outBuffer, pcmStartAddr);
if (outBuffer)
{
ensure(!!handle && !!pcmStartAddr); // Not checked on LLE
ensure(vm::check_addr(outBuffer.addr(), vm::page_info_t::page_writable, handle->decoder.pcm_output_size));
std::memcpy(outBuffer.get_ptr(), pcmStartAddr.get_ptr(), handle->decoder.pcm_output_size);
}
return CELL_OK;
}
error_code _CellAdecCoreOpReleasePcm_atracx(ppu_thread& ppu, vm::ptr<AtracXdecContext> handle, s32 pcmHandle, vm::cptr<void> outBuffer)
{
ppu.state += cpu_flag::wait;
cellAtracXdec.trace("_CellAdecCoreOpReleasePcm_atracx(handle=*0x%x, pcmHandle=%d, outBuffer=*0x%x)", handle, pcmHandle, outBuffer);
ensure(!!handle); // Not checked on LLE
std::lock_guard lock{handle->output_mutex};
handle->output_locked = false;
handle->output_consumed.notify_one();
return CELL_OK;
}
s32 _CellAdecCoreOpGetPcmHandleNum_atracx()
{
cellAtracXdec.notice("_CellAdecCoreOpGetPcmHandleNum_atracx()");
return 3;
}
u32 _CellAdecCoreOpGetBsiInfoSize_atracx()
{
cellAtracXdec.notice("_CellAdecCoreOpGetBsiInfoSize_atracx()");
return sizeof(CellAdecAtracXInfo);
}
static void init_gvar(vm::gvar<CellAdecCoreOps>& var)
{
var->open.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpOpen_atracx)));
var->close.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpClose_atracx)));
var->startSeq.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpStartSeq_atracx)));
var->endSeq.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpEndSeq_atracx)));
var->decodeAu.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpDecodeAu_atracx)));
var->getVersion.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpGetVersion_atracx)));
var->realign.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpRealign_atracx)));
var->releasePcm.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpReleasePcm_atracx)));
var->getPcmHandleNum.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpGetPcmHandleNum_atracx)));
var->getBsiInfoSize.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpGetBsiInfoSize_atracx)));
var->openExt.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpOpenExt_atracx)));
}
DECLARE(ppu_module_manager::cellAtracXdec)("cellAtracXdec", []()
{
REG_VNID(cellAtracXdec, 0x076b33ab, g_cell_adec_core_ops_atracx2ch).init = []()
{
g_cell_adec_core_ops_atracx2ch->getMemSize.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpGetMemSize_atracx<2>)));
init_gvar(g_cell_adec_core_ops_atracx2ch);
};
REG_VNID(cellAtracXdec, 0x1d210eaa, g_cell_adec_core_ops_atracx6ch).init = []()
{
g_cell_adec_core_ops_atracx6ch->getMemSize.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpGetMemSize_atracx<6>)));
init_gvar(g_cell_adec_core_ops_atracx6ch);
};
REG_VNID(cellAtracXdec, 0xe9a86e54, g_cell_adec_core_ops_atracx8ch).init = []()
{
g_cell_adec_core_ops_atracx8ch->getMemSize.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpGetMemSize_atracx<8>)));
init_gvar(g_cell_adec_core_ops_atracx8ch);
};
REG_VNID(cellAtracXdec, 0x4944af9a, g_cell_adec_core_ops_atracx).init = []()
{
g_cell_adec_core_ops_atracx->getMemSize.set(g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(_CellAdecCoreOpGetMemSize_atracx<8>)));
init_gvar(g_cell_adec_core_ops_atracx);
};
REG_HIDDEN_FUNC(_CellAdecCoreOpGetMemSize_atracx<2>);
REG_HIDDEN_FUNC(_CellAdecCoreOpGetMemSize_atracx<6>);
REG_HIDDEN_FUNC(_CellAdecCoreOpGetMemSize_atracx<8>);
REG_HIDDEN_FUNC(_CellAdecCoreOpOpen_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpClose_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpStartSeq_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpEndSeq_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpDecodeAu_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpGetVersion_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpRealign_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpReleasePcm_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpGetPcmHandleNum_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpGetBsiInfoSize_atracx);
REG_HIDDEN_FUNC(_CellAdecCoreOpOpenExt_atracx);
REG_HIDDEN_FUNC(atracXdecEntry);
});

View File

@ -0,0 +1,300 @@
#pragma once
#ifdef _MSC_VER
#pragma warning(push, 0)
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wall"
#pragma GCC diagnostic ignored "-Wextra"
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
extern "C"
{
#include "libavcodec/avcodec.h"
}
#ifdef _MSC_VER
#pragma warning(pop)
#else
#pragma GCC diagnostic pop
#endif
#include "Utilities/cond.h"
#include "cellPamf.h"
#include "cellAdec.h"
enum CellAtracXdecError : u32
{
CELL_ADEC_ERROR_ATX_OFFSET = 0x80612200,
CELL_ADEC_ERROR_ATX_NONE = 0x80612200,
CELL_ADEC_ERROR_ATX_OK = 0x80612200,
CELL_ADEC_ERROR_ATX_BUSY = 0x80612264,
CELL_ADEC_ERROR_ATX_EMPTY = 0x80612265,
CELL_ADEC_ERROR_ATX_ATSHDR = 0x80612266,
CELL_ADEC_ERROR_ATX_NON_FATAL = 0x80612281,
CELL_ADEC_ERROR_ATX_NOT_IMPLE = 0x80612282,
CELL_ADEC_ERROR_ATX_PACK_CE_OVERFLOW = 0x80612283,
CELL_ADEC_ERROR_ATX_ILLEGAL_NPROCQUS = 0x80612284,
CELL_ADEC_ERROR_ATX_FATAL = 0x8061228c,
CELL_ADEC_ERROR_ATX_ENC_OVERFLOW = 0x8061228d,
CELL_ADEC_ERROR_ATX_PACK_CE_UNDERFLOW = 0x8061228e,
CELL_ADEC_ERROR_ATX_SYNTAX_IDCT = 0x8061228f,
CELL_ADEC_ERROR_ATX_SYNTAX_GAINADJ = 0x80612290,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF = 0x80612291,
CELL_ADEC_ERROR_ATX_SYNTAX_SPECTRA = 0x80612292,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL = 0x80612293,
CELL_ADEC_ERROR_ATX_SYNTAX_GHWAVE = 0x80612294,
CELL_ADEC_ERROR_ATX_SYNTAX_SHEADER = 0x80612295,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_A = 0x80612296,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_B = 0x80612297,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_C = 0x80612298,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_D = 0x80612299,
CELL_ADEC_ERROR_ATX_SYNTAX_IDWL_E = 0x8061229a,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_A = 0x8061229b,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_B = 0x8061229c,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_C = 0x8061229d,
CELL_ADEC_ERROR_ATX_SYNTAX_IDSF_D = 0x8061229e,
CELL_ADEC_ERROR_ATX_SYNTAX_IDCT_A = 0x8061229f,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_NGC = 0x806122a0,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_A = 0x806122a1,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_A = 0x806122a2,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLEV_B = 0x806122a3,
CELL_ADEC_ERROR_ATX_SYNTAX_GC_IDLOC_B = 0x806122a4,
CELL_ADEC_ERROR_ATX_SYNTAX_SN_NWVS = 0x806122a5,
CELL_ADEC_ERROR_ATX_FATAL_HANDLE = 0x806122aa,
CELL_ADEC_ERROR_ATX_ASSERT_SAMPLING_FREQ = 0x806122ab,
CELL_ADEC_ERROR_ATX_ASSERT_CH_CONFIG_INDEX = 0x806122ac,
CELL_ADEC_ERROR_ATX_ASSERT_NBYTES = 0x806122ad,
CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_NUM = 0x806122ae,
CELL_ADEC_ERROR_ATX_ASSERT_BLOCK_ID = 0x806122af,
CELL_ADEC_ERROR_ATX_ASSERT_CHANNELS = 0x806122b0,
CELL_ADEC_ERROR_ATX_UNINIT_BLOCK_SPECIFIED = 0x806122b1,
CELL_ADEC_ERROR_ATX_POSCFG_PRESENT = 0x806122b2,
CELL_ADEC_ERROR_ATX_BUFFER_OVERFLOW = 0x806122b3,
CELL_ADEC_ERROR_ATX_ILL_BLK_TYPE_ID = 0x806122b4,
CELL_ADEC_ERROR_ATX_UNPACK_CHANNEL_BLK_FAILED = 0x806122b5,
CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_1 = 0x806122b6,
CELL_ADEC_ERROR_ATX_ILL_BLK_ID_USED_2 = 0x806122b7,
CELL_ADEC_ERROR_ATX_ILLEGAL_ENC_SETTING = 0x806122b8,
CELL_ADEC_ERROR_ATX_ILLEGAL_DEC_SETTING = 0x806122b9,
CELL_ADEC_ERROR_ATX_ASSERT_NSAMPLES = 0x806122ba,
CELL_ADEC_ERROR_ATX_ILL_SYNCWORD = 0x806122bb,
CELL_ADEC_ERROR_ATX_ILL_SAMPLING_FREQ = 0x806122bc,
CELL_ADEC_ERROR_ATX_ILL_CH_CONFIG_INDEX = 0x806122bd,
CELL_ADEC_ERROR_ATX_RAW_DATA_FRAME_SIZE_OVER = 0x806122be,
CELL_ADEC_ERROR_ATX_SYNTAX_ENHANCE_LENGTH_OVER = 0x806122bf,
CELL_ADEC_ERROR_ATX_SPU_INTERNAL_FAIL = 0x806122c8,
};
enum : u32
{
CELL_ADEC_ATRACX_WORD_SZ_16BIT = 0x02,
CELL_ADEC_ATRACX_WORD_SZ_24BIT = 0x03,
CELL_ADEC_ATRACX_WORD_SZ_32BIT = 0x04,
CELL_ADEC_ATRACX_WORD_SZ_FLOAT = 0x84,
};
enum : u8
{
CELL_ADEC_ATRACX_ATS_HDR_NOTINC = 0,
CELL_ADEC_ATRACX_ATS_HDR_INC = 1,
};
enum : u8
{
ATRACX_DOWNMIX_OFF = 0,
ATRACX_DOWNMIX_ON = 1,
};
struct CellAdecParamAtracX
{
be_t<u32> sampling_freq;
be_t<u32> ch_config_idx;
be_t<u32> nch_out;
be_t<u32> nbytes;
std::array<u8, 4> extra_config_data;
be_t<u32> bw_pcm;
u8 downmix_flag;
u8 au_includes_ats_hdr_flg;
};
struct CellAdecAtracXInfo
{
be_t<u32> samplingFreq;
be_t<u32> channelConfigIndex;
be_t<u32> nbytes;
};
CHECK_SIZE(CellAdecAtracXInfo, 12);
struct AtracXdecAtsHeader
{
be_t<u16> sync_word; // 0x0fd0
be_t<u16> params; // 3 bits: sample rate, 3 bits: channel config, 10 bits: (nbytes / 8) - 1
u8 extra_config_data[4];
};
CHECK_SIZE(AtracXdecAtsHeader, 8);
enum class AtracXdecCmdType : u32
{
invalid,
start_seq,
decode_au,
end_seq,
close,
};
struct AtracXdecCmd
{
be_t<AtracXdecCmdType> type;
be_t<s32> pcm_handle;
vm::bcptr<u8> au_start_addr;
be_t<u32> au_size;
vm::bptr<void> pcm_start_addr; // Unused
be_t<u32> pcm_size; // Unused
CellAdecParamAtracX atracx_param;
AtracXdecCmd() = default; // cellAdecOpen()
AtracXdecCmd(AtracXdecCmdType&& type) // cellAdecEndSeq(), cellAdecClose()
: type(type)
{
}
AtracXdecCmd(AtracXdecCmdType&& type, const CellAdecParamAtracX& atracx_param) // cellAdecStartSeq()
: type(type), atracx_param(atracx_param)
{
}
AtracXdecCmd(AtracXdecCmdType&& type, const s32& pcm_handle, const CellAdecAuInfo& au_info) // cellAdecDecodeAu()
: type(type), pcm_handle(pcm_handle), au_start_addr(au_info.startAddr), au_size(au_info.size)
{
}
};
CHECK_SIZE(AtracXdecCmd, 0x34);
struct AtracXdecDecoder
{
be_t<u32> sampling_freq;
be_t<u32> ch_config_idx;
be_t<u32> nch_in;
be_t<u32> nch_blocks;
be_t<u32> nbytes;
be_t<u32> nch_out;
be_t<u32> bw_pcm;
be_t<u32> nbytes_128_aligned;
be_t<u32> status;
be_t<u32> pcm_output_size;
const vm::bptr<u8> work_mem;
// HLE exclusive
b8 config_is_set = false; // For savestates
const AVCodec* codec;
AVCodecContext* ctx;
AVPacket* packet;
AVFrame* frame;
u8 spurs_stuff[84]; // 120 bytes on LLE, pointers to CellSpurs, CellSpursTaskset, etc.
be_t<u32> spurs_task_id; // CellSpursTaskId
AtracXdecDecoder(vm::ptr<u8> work_mem) : work_mem(work_mem) {}
void alloc_avcodec();
void free_avcodec();
void init_avcodec();
error_code set_config_info(u32 sampling_freq, u32 ch_config_idx, u32 nbytes);
error_code init_decode(u32 bw_pcm, u32 nch_out);
error_code parse_ats_header(vm::cptr<u8> au_start_addr);
};
CHECK_SIZE(AtracXdecDecoder, 0xa8);
struct AtracXdecContext
{
be_t<u64> thread_id; // sys_ppu_thread_t
shared_mutex queue_mutex; // sys_mutex_t
cond_variable queue_not_empty; // sys_cond_t
AdecCmdQueue<AtracXdecCmd> cmd_queue;
shared_mutex output_mutex; // sys_mutex_t
cond_variable output_consumed; // sys_cond_t
be_t<u32> output_locked = false;
be_t<u32> run_thread_mutex; // sys_mutex_t
be_t<u32> run_thread_cond; // sys_cond_t
be_t<u32> run_thread = true;
const AdecCb<AdecNotifyAuDone> notify_au_done;
const AdecCb<AdecNotifyPcmOut> notify_pcm_out;
const AdecCb<AdecNotifyError> notify_error;
const AdecCb<AdecNotifySeqDone> notify_seq_done;
const vm::bptr<u8> work_mem;
// HLE exclusive
u64 cmd_counter = 0; // For debugging
AtracXdecCmd cmd; // For savestates; if savestate was created while processing a decode command, we need to save the current command
b8 skip_getting_command = false; // For savestates; skips getting a new command from the queue
b8 skip_next_frame; // Needed to emulate behavior of LLE SPU program, it doesn't output the first frame after a sequence reset or error
u8 spurs_stuff[58]; // 120 bytes on LLE, pointers to CellSpurs, CellSpursTaskset, etc.
CellAdecParamAtracX atracx_param;
u8 reserved;
b8 first_decode;
AtracXdecDecoder decoder;
AtracXdecContext(vm::ptr<AdecNotifyAuDone> notifyAuDone, vm::ptr<void> notifyAuDoneArg, vm::ptr<AdecNotifyPcmOut> notifyPcmOut, vm::ptr<void> notifyPcmOutArg,
vm::ptr<AdecNotifyError> notifyError, vm::ptr<void> notifyErrorArg, vm::ptr<AdecNotifySeqDone> notifySeqDone, vm::ptr<void> notifySeqDoneArg, vm::bptr<u8> work_mem)
: notify_au_done{ notifyAuDone, notifyAuDoneArg }
, notify_pcm_out{ notifyPcmOut, notifyPcmOutArg }
, notify_error{ notifyError, notifyErrorArg }
, notify_seq_done{ notifySeqDone, notifySeqDoneArg }
, work_mem(work_mem)
, decoder(work_mem)
{
}
void exec(ppu_thread& ppu);
template <AtracXdecCmdType type>
error_code send_command(ppu_thread& ppu, auto&&... args);
};
static_assert(std::is_standard_layout_v<AtracXdecContext>);
CHECK_SIZE_ALIGN(AtracXdecContext, 0x268, 8);
constexpr u32 ATXDEC_SPURS_STRUCTS_SIZE = 0x1cf00; // CellSpurs, CellSpursTaskset, context, etc.
constexpr u16 ATXDEC_SAMPLES_PER_FRAME = 0x800;
constexpr u16 ATXDEC_MAX_FRAME_LENGTH = 0x2000;
constexpr std::array<u8, 8> ATXDEC_NCH_BLOCKS_MAP = { 0, 1, 1, 2, 3, 4, 5, 5 };
// Expected output channel order
// - for 1 to 7 channels: Front Left, Center, Front Right, Rear Left, Rear Right, Rear Center, LFE
// - for 8 channels: Front Left, Front Right, Center, LFE, Rear Left, Rear Right, Side Left, Side Right
// FFmpeg output
// - ver <= 5.1.2: Front Left, Front Right, Center, Rear Left, Rear Right, Rear Center, Side Left, Side Right, LFE
// - ver >= 5.1.3: Front Left, Front Right, Center, LFE, Rear Left, Rear Right, Rear Center, Side Left, Side Right
constexpr u8 ATXDEC_AVCODEC_CH_MAP[7][8] =
{
{ 0 },
{ 0, 1 },
{ 0, 2, 1 },
{ 0, 2, 1, 3 },
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(59, 51, 101)
{ 0, 2, 1, 3, 4, 5 },
{ 0, 2, 1, 3, 4, 5, 6 },
{ 0, 1, 2, 4, 5, 6, 7, 3 }
#else
{ 0, 2, 1, 4, 5, 3 },
{ 0, 2, 1, 4, 5, 6, 3 },
{ 0, 1, 2, 3, 4, 5, 6, 7 }
#endif
};

View File

@ -184,6 +184,7 @@ static void ppu_initialize_modules(ppu_linkage_info* link, utils::serial* ar = n
&ppu_module_manager::cellAdec,
&ppu_module_manager::cellAtrac,
&ppu_module_manager::cellAtracMulti,
&ppu_module_manager::cellAtracXdec,
&ppu_module_manager::cellAudio,
&ppu_module_manager::cellAvconfExt,
&ppu_module_manager::cellAuthDialogUtility,

View File

@ -168,6 +168,7 @@ public:
static const ppu_static_module cellAdec;
static const ppu_static_module cellAtrac;
static const ppu_static_module cellAtracMulti;
static const ppu_static_module cellAtracXdec;
static const ppu_static_module cellAudio;
static const ppu_static_module cellAvconfExt;
static const ppu_static_module cellAuthDialogUtility;

View File

@ -47,7 +47,7 @@ extern const std::map<std::string_view, int> g_prx_list
{ "libat3multidec.sprx", 0 },
{ "libatrac3multi.sprx", 0 },
{ "libatrac3plus.sprx", 0 },
{ "libatxdec.sprx", 0 },
{ "libatxdec.sprx", 1 },
{ "libatxdec2.sprx", 0 },
{ "libaudio.sprx", 1 },
{ "libavcdec.sprx", 0 },

View File

@ -27,4 +27,16 @@ vec3 compute2x2DownsampleWeights(const in float coord, const in float uv_step, c
return vec3(1.0 - (computed_weights.x + computed_weights.y), computed_weights.xy);
}
vec2 texture2DMSCoord(const in vec2 coords, const in uint flags)
{
if (0u == (flags & (WRAP_S_MASK | WRAP_T_MASK)))
{
return coords;
}
const vec2 wrapped_coords = mod(coords, vec2(1.0));
const bvec2 wrap_control_mask = bvec2(uvec2(flags) & uvec2(WRAP_S_MASK, WRAP_T_MASK));
return _select(coords, wrapped_coords, wrap_control_mask);
}
)"

View File

@ -1,16 +1,4 @@
R"(
vec2 texture2DMSCoord(const in vec2 coords, const in uint flags)
{
if (0u == (flags & (WRAP_S_MASK | WRAP_T_MASK)))
{
return coords;
}
const vec2 wrapped_coords = mod(coords, vec2(1.0));
const bvec2 wrap_control_mask = bvec2(uvec2(flags) & uvec2(WRAP_S_MASK, WRAP_T_MASK));
return _select(coords, wrapped_coords, wrap_control_mask);
}
vec4 texelFetch2DMS(in _MSAA_SAMPLER_TYPE_ tex, const in vec2 sample_count, const in ivec2 icoords, const in int index, const in ivec2 offset)
{
const vec2 resolve_coords = vec2(icoords + offset);

View File

@ -280,6 +280,7 @@
<ClCompile Include="Emu\Cell\Modules\cellAdec.cpp" />
<ClCompile Include="Emu\Cell\Modules\cellAtrac.cpp" />
<ClCompile Include="Emu\Cell\Modules\cellAtracMulti.cpp" />
<ClCompile Include="Emu\Cell\Modules\cellAtracXdec.cpp" />
<ClCompile Include="Emu\Cell\Modules\cellAudio.cpp" />
<ClCompile Include="Emu\Cell\Modules\cellAudioOut.cpp" />
<ClCompile Include="Emu\Cell\Modules\cellAuthDialog.cpp" />
@ -793,6 +794,7 @@
<ClInclude Include="Emu\Cell\Modules\cellAdec.h" />
<ClInclude Include="Emu\Cell\Modules\cellAtrac.h" />
<ClInclude Include="Emu\Cell\Modules\cellAtracMulti.h" />
<ClInclude Include="Emu\Cell\Modules\cellAtracXdec.h" />
<ClInclude Include="Emu\Cell\Modules\cellAudio.h" />
<ClInclude Include="Emu\Cell\Modules\cellAudioIn.h" />
<ClInclude Include="Emu\Cell\Modules\cellAudioOut.h" />

View File

@ -351,6 +351,9 @@
<ClCompile Include="Emu\Cell\Modules\cellAtracMulti.cpp">
<Filter>Emu\Cell\Modules</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\Modules\cellAtracXdec.cpp">
<Filter>Emu\Cell\Modules</Filter>
</ClCompile>
<ClCompile Include="Emu\Cell\Modules\cellAudio.cpp">
<Filter>Emu\Cell\Modules</Filter>
</ClCompile>
@ -1585,6 +1588,9 @@
<ClInclude Include="Emu\Cell\Modules\cellAtracMulti.h">
<Filter>Emu\Cell\Modules</Filter>
</ClInclude>
<ClInclude Include="Emu\Cell\Modules\cellAtracXdec.h">
<Filter>Emu\Cell\Modules</Filter>
</ClInclude>
<ClInclude Include="Emu\Cell\Modules\cellAudio.h">
<Filter>Emu\Cell\Modules</Filter>
</ClInclude>