From 92829d47fd605d21f49bfdee56f3c83e7e01b338 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 Apr 2019 18:08:54 -0700 Subject: [PATCH 01/12] Mp3: Better match resched on resource init/term. It always seems to return 0, though. --- Core/HLE/sceMp3.cpp | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index f6ff940636..72dd408b44 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -80,9 +80,9 @@ public: }; }; -static std::map mp3Map_old; static std::map mp3Map; static const int mp3DecodeDelay = 4000; +static bool resourceInited = false; static AuCtx *getMp3Ctx(u32 mp3) { if (mp3Map.find(mp3) == mp3Map.end()) @@ -98,14 +98,14 @@ void __Mp3Shutdown() { } void __Mp3DoState(PointerWrap &p) { - auto s = p.Section("sceMp3", 0, 2); + auto s = p.Section("sceMp3", 0, 3); if (!s) return; - if (s >= 2){ + if (s >= 2) { p.Do(mp3Map); - } - if (s <= 1 && p.mode == p.MODE_READ){ + } else { + std::map mp3Map_old; p.Do(mp3Map_old); // read old map for (auto it = mp3Map_old.begin(), end = mp3Map_old.end(); it != end; ++it) { auto mp3 = new AuCtx; @@ -135,6 +135,13 @@ void __Mp3DoState(PointerWrap &p) { mp3Map[id] = mp3; } } + + if (s >= 3) { + p.Do(resourceInited); + } else { + // Previous behavior acted as if it was already inited. + resourceInited = true; + } } static int sceMp3Decode(u32 mp3, u32 outPcmPtr) { @@ -219,15 +226,20 @@ static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { } static int sceMp3InitResource() { - WARN_LOG(ME, "UNIMPL: sceMp3InitResource"); - // Do nothing here - return 0; + // TODO: Could validate the utility modules have been loaded? + if (resourceInited) { + return hleLogSuccessI(ME, 0); + } + resourceInited = true; + return hleLogSuccessI(ME, hleDelayResult(0, "mp3 resource init", 200)); } static int sceMp3TermResource() { - WARN_LOG(ME, "UNIMPL: sceMp3TermResource"); - // Do nothing here - return 0; + if (!resourceInited) { + return hleLogSuccessI(ME, 0); + } + resourceInited = false; + return hleLogSuccessI(ME, hleDelayResult(0, "mp3 resource term", 100)); } static int __CalculateMp3Channels(int bitval) { From 4ae2108629fa4e9f9f32768a0eea8b2d453d728a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 Apr 2019 19:54:34 -0700 Subject: [PATCH 02/12] Mp3: Fail to reserve if resource not inited. Also, add a required parameter for sceMp3LowLevelInit, since it fails unless proper values are passed for this on a PSP. --- Core/HLE/sceMp3.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 72dd408b44..36187b1500 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -27,6 +27,7 @@ #include "Core/Reporting.h" #include "Core/HW/SimpleAudioDec.h" +static const u32 ERROR_MP3_RESOURCE_NOT_INIT = 0x80671201; struct Mp3Context { public: @@ -186,6 +187,10 @@ static int sceMp3CheckStreamDataNeeded(u32 mp3) { } static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { + if (!resourceInited) { + return hleLogError(ME, ERROR_MP3_RESOURCE_NOT_INIT, "sceMp3InitResource must be called first"); + } + INFO_LOG(ME, "sceMp3ReserveMp3Handle(%08x)", mp3Addr); if (!Memory::IsValidAddress(mp3Addr)){ ERROR_LOG(ME, "sceMp3ReserveMp3Handle(%08x) invalid address %08x", mp3Addr, mp3Addr); @@ -526,8 +531,8 @@ static u32 sceMp3ResetPlayPositionByFrame(u32 mp3, int position) { return ctx->AuResetPlayPositionByFrame(position); } -static u32 sceMp3LowLevelInit(u32 mp3) { - INFO_LOG(ME, "sceMp3LowLevelInit(%i)", mp3); +static u32 sceMp3LowLevelInit(u32 mp3, u32 unk) { + INFO_LOG(ME, "sceMp3LowLevelInit(%i, %i)", mp3, unk); auto ctx = new AuCtx; ctx->audioType = PSP_CODEC_MP3; @@ -597,7 +602,7 @@ const HLEFunction sceMp3[] = { {0XAE6D2027, &WrapU_U, "sceMp3GetMPEGVersion", 'x', "x" }, {0X3548AEC8, &WrapU_U, "sceMp3GetFrameNum", 'x', "x" }, {0X0840E808, &WrapU_UI, "sceMp3ResetPlayPositionByFrame", 'x', "xi" }, - {0X1B839B83, &WrapU_U, "sceMp3LowLevelInit", 'x', "x" }, + {0X1B839B83, &WrapU_UU, "sceMp3LowLevelInit", 'x', "xx" }, {0XE3EE2C81, &WrapU_UUUUU, "sceMp3LowLevelDecode", 'x', "xxxxx"} }; From 351a10bd69bce40700a8c17671f6599908543fa0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 21 Apr 2019 22:07:46 -0700 Subject: [PATCH 03/12] Mp3: Allow allocating two mp3 handles. The handles are always 0 or 1, not pointers, which might be relied upon by some games. Two could be used with the same pointer. This also makes NULL a valid pointer, which is used for low level mp3s. --- Core/HLE/sceMp3.cpp | 71 +++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 36187b1500..ee76f0b6c4 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -27,7 +27,11 @@ #include "Core/Reporting.h" #include "Core/HW/SimpleAudioDec.h" -static const u32 ERROR_MP3_RESOURCE_NOT_INIT = 0x80671201; +static const u32 ERROR_MP3_NO_RESOURCE_AVAIL = 0x80671201; +static const u32 ERROR_MP3_BAD_ADDR = 0x80671002; +static const u32 ERROR_MP3_BAD_SIZE = 0x80671003; +static const int AU_BUF_MIN_SIZE = 8192; +static const int PCM_BUF_MIN_SIZE = 9216; struct Mp3Context { public: @@ -188,27 +192,49 @@ static int sceMp3CheckStreamDataNeeded(u32 mp3) { static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { if (!resourceInited) { - return hleLogError(ME, ERROR_MP3_RESOURCE_NOT_INIT, "sceMp3InitResource must be called first"); + return hleLogError(ME, ERROR_MP3_NO_RESOURCE_AVAIL, "sceMp3InitResource must be called first"); } - - INFO_LOG(ME, "sceMp3ReserveMp3Handle(%08x)", mp3Addr); - if (!Memory::IsValidAddress(mp3Addr)){ - ERROR_LOG(ME, "sceMp3ReserveMp3Handle(%08x) invalid address %08x", mp3Addr, mp3Addr); - return -1; + if (mp3Map.size() >= 2) { + return hleLogError(ME, ERROR_MP3_NO_RESOURCE_AVAIL, "no free handles"); + } + if (mp3Addr != 0 && !Memory::IsValidRange(mp3Addr, 32)) { + // The PSP would crash, but might as well return a proper error. + return hleLogError(ME, SCE_KERNEL_ERROR_INVALID_POINTER, "bad mp3 pointer"); } AuCtx *Au = new AuCtx; - Au->startPos = Memory::Read_U64(mp3Addr); // Audio stream start position. - Au->endPos = Memory::Read_U32(mp3Addr + 8); // Audio stream end position. - Au->AuBuf = Memory::Read_U32(mp3Addr + 16); // Input Au data buffer. - Au->AuBufSize = Memory::Read_U32(mp3Addr + 20); // Input Au data buffer size. - Au->PCMBuf = Memory::Read_U32(mp3Addr + 24); // Output PCM data buffer. - Au->PCMBufSize = Memory::Read_U32(mp3Addr + 28); // Output PCM data buffer size. + if (mp3Addr) { + Au->startPos = Memory::Read_U64(mp3Addr); // Audio stream start position. + Au->endPos = Memory::Read_U64(mp3Addr + 8); // Audio stream end position. + Au->AuBuf = Memory::Read_U32(mp3Addr + 16); // Input Au data buffer. + Au->AuBufSize = Memory::Read_U32(mp3Addr + 20); // Input Au data buffer size. + Au->PCMBuf = Memory::Read_U32(mp3Addr + 24); // Output PCM data buffer. + Au->PCMBufSize = Memory::Read_U32(mp3Addr + 28); // Output PCM data buffer size. - DEBUG_LOG(ME, "startPos %llx endPos %llx mp3buf %08x mp3bufSize %08x PCMbuf %08x PCMbufSize %08x", - Au->startPos, Au->endPos, Au->AuBuf, Au->AuBufSize, Au->PCMBuf, Au->PCMBufSize); + if (Au->startPos >= Au->endPos) { + delete Au; + return hleLogError(ME, ERROR_MP3_BAD_SIZE, "start must be before end"); + } + if (!Au->AuBuf || !Au->PCMBuf) { + delete Au; + return hleLogError(ME, ERROR_MP3_BAD_ADDR, "invalid buffer addresses"); + } + if ((int)Au->AuBufSize < AU_BUF_MIN_SIZE || (int)Au->PCMBufSize < PCM_BUF_MIN_SIZE) { + delete Au; + return hleLogError(ME, ERROR_MP3_BAD_SIZE, "buffers too small"); + } + + DEBUG_LOG(ME, "startPos %llx endPos %llx mp3buf %08x mp3bufSize %08x PCMbuf %08x PCMbufSize %08x", + Au->startPos, Au->endPos, Au->AuBuf, Au->AuBufSize, Au->PCMBuf, Au->PCMBufSize); + } else { + Au->startPos = 0; + Au->endPos = 0; + Au->AuBuf = 0; + Au->AuBufSize = 0; + Au->PCMBuf = 0; + Au->PCMBufSize = 0; + } - Au->audioType = PSP_CODEC_MP3; Au->Channels = 2; Au->SumDecodedSamples = 0; Au->MaxOutputSample = Au->PCMBufSize / 4; @@ -216,18 +242,13 @@ static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { Au->AuBufAvailable = 0; Au->readPos = Au->startPos; - // create Au decoder + Au->audioType = PSP_CODEC_MP3; Au->decoder = new SimpleAudio(Au->audioType); - // close the audio if mp3Addr already exist. - if (mp3Map.find(mp3Addr) != mp3Map.end()) { - delete mp3Map[mp3Addr]; - mp3Map.erase(mp3Addr); - } + int handle = (int)mp3Map.size(); + mp3Map[handle] = Au; - mp3Map[mp3Addr] = Au; - - return mp3Addr; + return hleLogSuccessI(ME, handle); } static int sceMp3InitResource() { From 4563d9487bd8528db2ef29ab41d26f161ba0c34d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 22 Apr 2019 22:57:05 -0700 Subject: [PATCH 04/12] Mp3: Better errors in sceMp3GetInfoToAddStreamData. --- Core/HLE/sceMp3.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index ee76f0b6c4..9e278b0ccf 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -27,11 +27,14 @@ #include "Core/Reporting.h" #include "Core/HW/SimpleAudioDec.h" +static const u32 ERROR_MP3_INVALID_HANDLE = 0x80671101; +static const u32 ERROR_MP3_UNRESERVED_HANDLE = 0x80671102; static const u32 ERROR_MP3_NO_RESOURCE_AVAIL = 0x80671201; static const u32 ERROR_MP3_BAD_ADDR = 0x80671002; static const u32 ERROR_MP3_BAD_SIZE = 0x80671003; static const int AU_BUF_MIN_SIZE = 8192; static const int PCM_BUF_MIN_SIZE = 9216; +static const size_t MP3_MAX_HANDLES = 2; struct Mp3Context { public: @@ -194,7 +197,7 @@ static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { if (!resourceInited) { return hleLogError(ME, ERROR_MP3_NO_RESOURCE_AVAIL, "sceMp3InitResource must be called first"); } - if (mp3Map.size() >= 2) { + if (mp3Map.size() >= MP3_MAX_HANDLES) { return hleLogError(ME, ERROR_MP3_NO_RESOURCE_AVAIL, "no free handles"); } if (mp3Addr != 0 && !Memory::IsValidRange(mp3Addr, 32)) { @@ -472,15 +475,16 @@ static int sceMp3GetSamplingRate(u32 mp3) { } static int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) { - DEBUG_LOG(ME, "sceMp3GetInfoToAddStreamData(%08X, %08X, %08X, %08X)", mp3, dstPtr, towritePtr, srcposPtr); - AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 > MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "unreserved handle"); + } else if (ctx->AuBuf == 0) { + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "incorrect handle type"); } - return ctx->AuGetInfoToAddStreamData(dstPtr, towritePtr, srcposPtr); + return hleLogSuccessI(ME, ctx->AuGetInfoToAddStreamData(dstPtr, towritePtr, srcposPtr)); } static int sceMp3NotifyAddStreamData(u32 mp3, int size) { @@ -615,7 +619,7 @@ const HLEFunction sceMp3[] = { {0X87C263D1, &WrapI_U, "sceMp3GetMaxOutputSample", 'i', "x" }, {0X8AB81558, &WrapU_V, "sceMp3StartEntry", 'x', "" }, {0X8F450998, &WrapI_U, "sceMp3GetSamplingRate", 'i', "x" }, - {0XA703FE0F, &WrapI_UUUU, "sceMp3GetInfoToAddStreamData", 'i', "xxxx" }, + {0XA703FE0F, &WrapI_UUUU, "sceMp3GetInfoToAddStreamData", 'i', "xppp" }, {0XD021C0FB, &WrapI_UU, "sceMp3Decode", 'i', "xx" }, {0XD0A56296, &WrapI_U, "sceMp3CheckStreamDataNeeded", 'i', "x" }, {0XD8F54A51, &WrapI_U, "sceMp3GetLoopNum", 'i', "x" }, From 5b9a3e6ca867a2e45e738e6889c7283de6704f03 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Mon, 22 Apr 2019 22:57:49 -0700 Subject: [PATCH 05/12] Mp3: Improve handle releasing behavior. --- Core/HLE/sceMp3.cpp | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 9e278b0ccf..dfa8e6337d 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -27,7 +27,7 @@ #include "Core/Reporting.h" #include "Core/HW/SimpleAudioDec.h" -static const u32 ERROR_MP3_INVALID_HANDLE = 0x80671101; +static const u32 ERROR_MP3_INVALID_HANDLE = 0x80671001; static const u32 ERROR_MP3_UNRESERVED_HANDLE = 0x80671102; static const u32 ERROR_MP3_NO_RESOURCE_AVAIL = 0x80671201; static const u32 ERROR_MP3_BAD_ADDR = 0x80671002; @@ -267,6 +267,13 @@ static int sceMp3TermResource() { if (!resourceInited) { return hleLogSuccessI(ME, 0); } + + // Free any handles that are still open. + for (auto au : mp3Map) { + delete au.second; + } + mp3Map.clear(); + resourceInited = false; return hleLogSuccessI(ME, hleDelayResult(0, "mp3 resource term", 100)); } @@ -477,7 +484,7 @@ static int sceMp3GetSamplingRate(u32 mp3) { static int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) { AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - if (mp3 > MP3_MAX_HANDLES) + if (mp3 >= MP3_MAX_HANDLES) return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "unreserved handle"); } else if (ctx->AuBuf == 0) { @@ -500,18 +507,17 @@ static int sceMp3NotifyAddStreamData(u32 mp3, int size) { } static int sceMp3ReleaseMp3Handle(u32 mp3) { - INFO_LOG(ME, "sceMp3ReleaseMp3Handle(%08X)", mp3); - AuCtx *ctx = getMp3Ctx(mp3); - if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (ctx) { + delete ctx; + mp3Map.erase(mp3); + return hleLogSuccessI(ME, 0); + } else if (mp3 >= MP3_MAX_HANDLES) { + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); } - delete ctx; - mp3Map.erase(mp3); - - return 0; + // Intentionally a zero result. + return hleLogDebug(ME, 0, "double free ignored"); } static u32 sceMp3EndEntry() { From 709c9dc93cd5148dc23ee4e701371ab310eab0a0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 23 Apr 2019 19:48:01 -0700 Subject: [PATCH 06/12] Mp3: Fix errors on sceMp3NotifyAddStreamData. --- Core/HLE/sceMp3.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index dfa8e6337d..b53b6f1d2d 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -495,15 +495,16 @@ static int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 } static int sceMp3NotifyAddStreamData(u32 mp3, int size) { - DEBUG_LOG(ME, "sceMp3NotifyAddStreamData(%08X, %i)", mp3, size); - AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 >= MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "unreserved handle"); + } else if (ctx->AuBuf == 0) { + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "incorrect handle type"); } - return ctx->AuNotifyAddStreamData(size); + return hleLogSuccessI(ME, ctx->AuNotifyAddStreamData(size)); } static int sceMp3ReleaseMp3Handle(u32 mp3) { From f1eaf9dc0e5bd43cb3e273fc5639d173c2f444f0 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 23 Apr 2019 20:18:16 -0700 Subject: [PATCH 07/12] Mp3: Don't change buffer accounting until add. If we just ask what we should add, that is meant to stay static until we do actually add it. This also reduces the max we ask for at a time, which better matches correct behavior and might impact game behavior. --- Core/HLE/sceMp3.cpp | 1 - Core/HW/SimpleAudioDec.cpp | 78 ++++++++++++++++++++++++-------------- Core/HW/SimpleAudioDec.h | 3 +- 3 files changed, 51 insertions(+), 31 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index b53b6f1d2d..a474d91ec9 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -136,7 +136,6 @@ void __Mp3DoState(PointerWrap &p) { mp3->readPos = mp3_old->readPosition; mp3->AuBufAvailable = 0; // reset to read from file mp3->askedReadSize = 0; - mp3->realReadSize = 0; mp3->audioType = PSP_CODEC_MP3; mp3->decoder = new SimpleAudio(mp3->audioType); diff --git a/Core/HW/SimpleAudioDec.cpp b/Core/HW/SimpleAudioDec.cpp index 05d36541f8..6f9c039104 100644 --- a/Core/HW/SimpleAudioDec.cpp +++ b/Core/HW/SimpleAudioDec.cpp @@ -306,7 +306,6 @@ AuCtx::AuCtx() { SumDecodedSamples = 0; MaxOutputSample = 0; askedReadSize = 0; - realReadSize = 0; audioType = 0; FrameNum = 0; }; @@ -394,20 +393,26 @@ int AuCtx::AuCheckStreamDataNeeded() } // check how many bytes we have read from source file -u32 AuCtx::AuNotifyAddStreamData(int size) -{ - realReadSize = size; - int diffsize = realReadSize - askedReadSize; - // Notify the real read size - if (diffsize != 0){ - readPos += diffsize; - AuBufAvailable += diffsize; +u32 AuCtx::AuNotifyAddStreamData(int size) { + if (askedReadSize != 0) { + // Old save state, numbers already adjusted. + int diffsize = size - askedReadSize; + // Notify the real read size + if (diffsize != 0) { + readPos += diffsize; + AuBufAvailable += diffsize; + } + askedReadSize = 0; + } else { + readPos += size; + AuBufAvailable += size; } - // append AuBuf into sourcebuff - sourcebuff.append((const char*)Memory::GetPointer(AuBuf), size); + if (Memory::IsValidRange(AuBuf, size)) { + sourcebuff.append((const char *)Memory::GetPointer(AuBuf), size); + } - if (readPos >= (int)endPos && LoopNum != 0){ + if (readPos >= (int)endPos && LoopNum != 0) { // if we need loop, reset readPos readPos = startPos; // reset LoopNum @@ -421,24 +426,40 @@ u32 AuCtx::AuNotifyAddStreamData(int size) // read from stream position srcPos of size bytes into buff // buff, size and srcPos are all pointers -u32 AuCtx::AuGetInfoToAddStreamData(u32 buff, u32 size, u32 srcPos) -{ - // you can not read beyond file size and the buffer size - int readsize = std::min((int)AuBufSize - AuBufAvailable, (int)endPos - readPos); +u32 AuCtx::AuGetInfoToAddStreamData(u32 bufPtr, u32 sizePtr, u32 srcPosPtr) { + int readsize; + int offset = 0; + if (audioType == PSP_CODEC_MP3) { + // Account for the workarea. + offset = 0x05c0; + // The endPos and readPos are not considered, except when you've read to the end. + readsize = (int)AuBufSize - AuBufAvailable - offset; + if (readPos >= endPos) + readsize = 0; + } else { + // TODO: Untested. Maybe similar to MP3. + readsize = std::min((int)AuBufSize - AuBufAvailable, (int)endPos - readPos); + } // we can recharge AuBuf from its beginning - if (Memory::IsValidAddress(buff)) - Memory::Write_U32(AuBuf, buff); - if (Memory::IsValidAddress(size)) - Memory::Write_U32(readsize, size); - if (Memory::IsValidAddress(srcPos)) - Memory::Write_U32(readPos, srcPos); - - // preset the readPos and available size, they will be notified later in NotifyAddStreamData. - askedReadSize = readsize; - readPos += askedReadSize; - AuBufAvailable += askedReadSize; + if (readsize != 0) { + if (Memory::IsValidAddress(bufPtr)) + Memory::Write_U32(AuBuf, bufPtr); + if (Memory::IsValidAddress(sizePtr)) + Memory::Write_U32(readsize, sizePtr); + if (Memory::IsValidAddress(srcPosPtr)) + Memory::Write_U32(readPos, srcPosPtr); + } else { + if (Memory::IsValidAddress(bufPtr)) + Memory::Write_U32(0, bufPtr); + if (Memory::IsValidAddress(sizePtr)) + Memory::Write_U32(0, sizePtr); + if (Memory::IsValidAddress(srcPosPtr)) + Memory::Write_U32(0, srcPosPtr); + } + // Just for old save states. + askedReadSize = 0; return 0; } @@ -473,7 +494,8 @@ void AuCtx::DoState(PointerWrap &p) { p.Do(BitRate); p.Do(SamplingRate); p.Do(askedReadSize); - p.Do(realReadSize); + int dummy = 0; + p.Do(dummy); p.Do(FrameNum); if (p.mode == p.MODE_READ) { diff --git a/Core/HW/SimpleAudioDec.h b/Core/HW/SimpleAudioDec.h index 22ec180a85..63c7c181a1 100644 --- a/Core/HW/SimpleAudioDec.h +++ b/Core/HW/SimpleAudioDec.h @@ -105,7 +105,7 @@ public: u32 AuSetLoopNum(int loop); u32 AuGetLoopNum(); - u32 AuGetInfoToAddStreamData(u32 buff, u32 size, u32 srcPos); + u32 AuGetInfoToAddStreamData(u32 bufPtr, u32 sizePtr, u32 srcPosPtr); u32 AuGetMaxOutputSample() const { return MaxOutputSample; } u32 AuGetSumDecodedSample() const { return SumDecodedSamples; } int AuGetChannelNum() const { return Channels; } @@ -149,7 +149,6 @@ public: int AuBufAvailable; // the available buffer of AuBuf to be able to recharge data int readPos; // read position in audio source file int askedReadSize; // the size of data requied to be read from file by the game - int realReadSize; // the really read size from file private: std::string sourcebuff; // source buffer From f9863c3be2cd23fe09f8a5e44f7631e83dd9cd3f Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 23 Apr 2019 20:42:13 -0700 Subject: [PATCH 08/12] Mp3: Align CheckStreamDataNeeded with InfoToAdd. May cause problems if they don't match. --- Core/HW/SimpleAudioDec.cpp | 33 +++++++++++++++++++-------------- Core/HW/SimpleAudioDec.h | 1 + 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/Core/HW/SimpleAudioDec.cpp b/Core/HW/SimpleAudioDec.cpp index 6f9c039104..4a26c01999 100644 --- a/Core/HW/SimpleAudioDec.cpp +++ b/Core/HW/SimpleAudioDec.cpp @@ -383,15 +383,28 @@ u32 AuCtx::AuSetLoopNum(int loop) } // return 1 to read more data stream, 0 don't read -int AuCtx::AuCheckStreamDataNeeded() -{ - // if we have no available Au buffer, and the current read position in source file is not the end of stream, then we can read - if (AuBufAvailable < (int)AuBufSize && readPos < (int)endPos){ +int AuCtx::AuCheckStreamDataNeeded() { + // If we would ask for bytes, then some are needed. + if (AuStreamBytesNeeded() != 0) { return 1; } return 0; } +int AuCtx::AuStreamBytesNeeded() { + if (audioType == PSP_CODEC_MP3) { + // The endPos and readPos are not considered, except when you've read to the end. + if (readPos >= endPos) + return 0; + // Account for the workarea. + int offset = 0x05c0; + return (int)AuBufSize - AuBufAvailable - offset; + } + + // TODO: Untested. Maybe similar to MP3. + return std::min((int)AuBufSize - AuBufAvailable, (int)endPos - readPos); +} + // check how many bytes we have read from source file u32 AuCtx::AuNotifyAddStreamData(int size) { if (askedReadSize != 0) { @@ -427,24 +440,16 @@ u32 AuCtx::AuNotifyAddStreamData(int size) { // read from stream position srcPos of size bytes into buff // buff, size and srcPos are all pointers u32 AuCtx::AuGetInfoToAddStreamData(u32 bufPtr, u32 sizePtr, u32 srcPosPtr) { - int readsize; + int readsize = AuStreamBytesNeeded(); int offset = 0; if (audioType == PSP_CODEC_MP3) { - // Account for the workarea. offset = 0x05c0; - // The endPos and readPos are not considered, except when you've read to the end. - readsize = (int)AuBufSize - AuBufAvailable - offset; - if (readPos >= endPos) - readsize = 0; - } else { - // TODO: Untested. Maybe similar to MP3. - readsize = std::min((int)AuBufSize - AuBufAvailable, (int)endPos - readPos); } // we can recharge AuBuf from its beginning if (readsize != 0) { if (Memory::IsValidAddress(bufPtr)) - Memory::Write_U32(AuBuf, bufPtr); + Memory::Write_U32(AuBuf + offset, bufPtr); if (Memory::IsValidAddress(sizePtr)) Memory::Write_U32(readsize, sizePtr); if (Memory::IsValidAddress(srcPosPtr)) diff --git a/Core/HW/SimpleAudioDec.h b/Core/HW/SimpleAudioDec.h index 63c7c181a1..182d2dc7bf 100644 --- a/Core/HW/SimpleAudioDec.h +++ b/Core/HW/SimpleAudioDec.h @@ -99,6 +99,7 @@ public: u32 AuNotifyAddStreamData(int size); int AuCheckStreamDataNeeded(); + int AuStreamBytesNeeded(); u32 AuResetPlayPosition(); u32 AuResetPlayPositionByFrame(int position); From e4d271289733847ee21c6940cba36a6784dd853d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 23 Apr 2019 20:49:45 -0700 Subject: [PATCH 09/12] Mp3: Apply offset to stream read buffer. Handle errors for sceMp3CheckStreamDataNeeded as well. --- Core/HLE/sceMp3.cpp | 19 +++++++++++-------- Core/HW/SimpleAudioDec.cpp | 18 ++++++++++++------ Core/HW/SimpleAudioDec.h | 1 + 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index a474d91ec9..7ffb6e4711 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -181,15 +181,16 @@ static int sceMp3ResetPlayPosition(u32 mp3) { } static int sceMp3CheckStreamDataNeeded(u32 mp3) { - DEBUG_LOG(ME, "sceMp3CheckStreamDataNeeded(%08x)", mp3); - AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 >= MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "unreserved handle"); + } else if (ctx->AuBuf == 0) { + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "incorrect handle type"); } - return ctx->AuCheckStreamDataNeeded(); + return hleLogSuccessI(ME, ctx->AuCheckStreamDataNeeded()); } static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { @@ -344,15 +345,17 @@ static int __CalculateMp3Bitrates(int bitval, int mp3version, int mp3layer) { } static int __ParseMp3Header(AuCtx *ctx, bool *isID3) { - int header = bswap32(Memory::Read_U32(ctx->AuBuf)); + u32 ptr = ctx->AuBuf + ctx->AuStreamWorkareaSize(); + int header = bswap32(Memory::Read_U32(ptr)); // ID3 tag , can be seen in Hanayaka Nari Wa ga Ichizoku. static const int ID3 = 0x49443300; if ((header & 0xFFFFFF00) == ID3) { *isID3 = true; - int size = bswap32(Memory::Read_U32(ctx->AuBuf + ctx->startPos + 6)); + // TODO: Can we count on startPos being read already? What if it's past the buffer size? + int size = bswap32(Memory::Read_U32(ptr + ctx->startPos + 6)); // Highest bit of each byte has to be ignored (format: 0x7F7F7F7F) size = (size & 0x7F) | ((size & 0x7F00) >> 1) | ((size & 0x7F0000) >> 2) | ((size & 0x7F000000) >> 3); - header = bswap32(Memory::Read_U32(ctx->AuBuf + ctx->startPos + 10 + size)); + header = bswap32(Memory::Read_U32(ptr + ctx->startPos + 10 + size)); } return header; } diff --git a/Core/HW/SimpleAudioDec.cpp b/Core/HW/SimpleAudioDec.cpp index 4a26c01999..b8062ca56e 100644 --- a/Core/HW/SimpleAudioDec.cpp +++ b/Core/HW/SimpleAudioDec.cpp @@ -397,7 +397,7 @@ int AuCtx::AuStreamBytesNeeded() { if (readPos >= endPos) return 0; // Account for the workarea. - int offset = 0x05c0; + int offset = AuStreamWorkareaSize(); return (int)AuBufSize - AuBufAvailable - offset; } @@ -405,8 +405,17 @@ int AuCtx::AuStreamBytesNeeded() { return std::min((int)AuBufSize - AuBufAvailable, (int)endPos - readPos); } +int AuCtx::AuStreamWorkareaSize() { + // Note that this is 31 bytes more than the max layer 3 frame size. + if (audioType == PSP_CODEC_MP3) + return 0x05c0; + return 0; +} + // check how many bytes we have read from source file u32 AuCtx::AuNotifyAddStreamData(int size) { + int offset = AuStreamWorkareaSize(); + if (askedReadSize != 0) { // Old save state, numbers already adjusted. int diffsize = size - askedReadSize; @@ -422,7 +431,7 @@ u32 AuCtx::AuNotifyAddStreamData(int size) { } if (Memory::IsValidRange(AuBuf, size)) { - sourcebuff.append((const char *)Memory::GetPointer(AuBuf), size); + sourcebuff.append((const char *)Memory::GetPointer(AuBuf + offset), size); } if (readPos >= (int)endPos && LoopNum != 0) { @@ -441,10 +450,7 @@ u32 AuCtx::AuNotifyAddStreamData(int size) { // buff, size and srcPos are all pointers u32 AuCtx::AuGetInfoToAddStreamData(u32 bufPtr, u32 sizePtr, u32 srcPosPtr) { int readsize = AuStreamBytesNeeded(); - int offset = 0; - if (audioType == PSP_CODEC_MP3) { - offset = 0x05c0; - } + int offset = AuStreamWorkareaSize(); // we can recharge AuBuf from its beginning if (readsize != 0) { diff --git a/Core/HW/SimpleAudioDec.h b/Core/HW/SimpleAudioDec.h index 182d2dc7bf..c41707c3a5 100644 --- a/Core/HW/SimpleAudioDec.h +++ b/Core/HW/SimpleAudioDec.h @@ -100,6 +100,7 @@ public: u32 AuNotifyAddStreamData(int size); int AuCheckStreamDataNeeded(); int AuStreamBytesNeeded(); + int AuStreamWorkareaSize(); u32 AuResetPlayPosition(); u32 AuResetPlayPositionByFrame(int position); From 5f82258fa1d6b291427817ac9d1cb8d1f4e0001a Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Apr 2019 09:16:21 -0700 Subject: [PATCH 10/12] Mp3: Detect offset headers, reject invalid data. ID3 handling not really tested. Left some error codes disabled in case people are happily playing some custom BGM that the PSP didn't support. --- Core/HLE/sceMp3.cpp | 61 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 7ffb6e4711..10d8afaf60 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -32,6 +32,7 @@ static const u32 ERROR_MP3_UNRESERVED_HANDLE = 0x80671102; static const u32 ERROR_MP3_NO_RESOURCE_AVAIL = 0x80671201; static const u32 ERROR_MP3_BAD_ADDR = 0x80671002; static const u32 ERROR_MP3_BAD_SIZE = 0x80671003; +static const u32 ERROR_AVCODEC_INVALID_DATA = 0x807f00fd; static const int AU_BUF_MIN_SIZE = 8192; static const int PCM_BUF_MIN_SIZE = 9216; static const size_t MP3_MAX_HANDLES = 2; @@ -344,8 +345,8 @@ static int __CalculateMp3Bitrates(int bitval, int mp3version, int mp3layer) { } } -static int __ParseMp3Header(AuCtx *ctx, bool *isID3) { - u32 ptr = ctx->AuBuf + ctx->AuStreamWorkareaSize(); +static int ParseMp3Header(AuCtx *ctx, int offset, bool *isID3) { + u32 ptr = ctx->AuBuf + ctx->AuStreamWorkareaSize() + offset; int header = bswap32(Memory::Read_U32(ptr)); // ID3 tag , can be seen in Hanayaka Nari Wa ga Ichizoku. static const int ID3 = 0x49443300; @@ -361,17 +362,36 @@ static int __ParseMp3Header(AuCtx *ctx, bool *isID3) { } static int sceMp3Init(u32 mp3) { - INFO_LOG(ME, "sceMp3Init(%08x)", mp3); - AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 >= MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "unreserved handle"); + } else if (ctx->AuBuf == 0) { + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "incorrect handle type"); + } + + // First, let's search for the MP3 header. It can be offset by at most 1439 bytes. + bool hasID3Tag = false; + int header = 0; + for (int offset = 0; offset < 1440; ++offset) { + header = ParseMp3Header(ctx, offset, &hasID3Tag); + // If we hit valid sync bits, then we've found a header. + if (((header >> 21) & 0x0FFE) == 0x0FFE) { + // Ignore the data before that. + ctx->EatSourceBuff(offset); + break; + } + } + + static const int PARSE_DELAY_MS = 500; + + // Couldn't find a header after all? + if (((header >> 21) & 0x0FFE) != 0x0FFE) { + return hleDelayResult(hleLogWarning(ME, ERROR_AVCODEC_INVALID_DATA, "no header found"), "mp3 init", PARSE_DELAY_MS); } // Parse the Mp3 header - bool hasID3Tag = false; - int header = __ParseMp3Header(ctx, &hasID3Tag); int layer = (header >> 17) & 0x3; ctx->Version = ((header >> 19) & 0x3); ctx->SamplingRate = __CalculateMp3SampleRates((header >> 10) & 0x3, ctx->Version); @@ -379,8 +399,6 @@ static int sceMp3Init(u32 mp3) { ctx->BitRate = __CalculateMp3Bitrates((header >> 12) & 0xF, ctx->Version, layer); ctx->freq = ctx->SamplingRate; - INFO_LOG(ME, "sceMp3Init(): channels=%i, samplerate=%iHz, bitrate=%ikbps", ctx->Channels, ctx->SamplingRate, ctx->BitRate); - // for mp3, if required freq is 48000, reset resampling Frequency to 48000 seems get better sound quality (e.g. Miku Custom BGM) if (ctx->freq == 48000) { ctx->decoder->SetResampleFrequency(ctx->freq); @@ -394,10 +412,31 @@ static int sceMp3Init(u32 mp3) { ctx->EatSourceBuff(0x400); } else { // if no ID3 tag, we will decode from the begining of the file + // TODO: This seems wrong, since it's an init parameter? ctx->startPos = 0; } - return 0; + DEBUG_LOG(ME, "sceMp3Init(): channels=%i, samplerate=%iHz, bitrate=%ikbps", ctx->Channels, ctx->SamplingRate, ctx->BitRate); + + if (layer != 1) { + // TODO: Should return ERROR_AVCODEC_INVALID_DATA. + WARN_LOG_REPORT(ME, "sceMp3Init: invalid data: not layer 3"); + } + if (ctx->Version != 3) { + // TODO: Should return 0x80671301 (unsupported version?) + WARN_LOG_REPORT(ME, "sceMp3Init: invalid data: not MPEG v1"); + } + if (ctx->BitRate == 0 || ctx->BitRate == -1) { + return hleDelayResult(hleReportError(ME, ERROR_AVCODEC_INVALID_DATA, "invalid bitrate v%d l%d rate %04x", ctx->Version, layer, (header >> 12) & 0xF), "mp3 init", PARSE_DELAY_MS); + } + if (ctx->SamplingRate == -1) { + return hleDelayResult(hleReportError(ME, ERROR_AVCODEC_INVALID_DATA, "invalid sample rate v%d l%d rate %02x", ctx->Version, layer, (header >> 10) & 0x3), "mp3 init", PARSE_DELAY_MS); + } else if (ctx->SamplingRate != 44100) { + // TODO: Should return 0x80671302 (unsupported sample rate?) + WARN_LOG_REPORT(ME, "sceMp3Init: invalid data: not 44.1kHz"); + } + + return hleDelayResult(hleLogSuccessI(ME, 0), "mp3 init", PARSE_DELAY_MS); } static int sceMp3GetLoopNum(u32 mp3) { From be6710ea4a30a9a224a28313d55472aaf0d5dcbd Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Apr 2019 14:13:46 -0700 Subject: [PATCH 11/12] Mp3: Fix errors for a bunch of init getters. --- Core/HLE/sceMp3.cpp | 60 +++++++++++++++++++++++++------------- Core/HW/SimpleAudioDec.cpp | 5 ---- Core/HW/SimpleAudioDec.h | 10 +++---- 3 files changed, 44 insertions(+), 31 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 10d8afaf60..a1f09e77e9 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -29,6 +29,7 @@ static const u32 ERROR_MP3_INVALID_HANDLE = 0x80671001; static const u32 ERROR_MP3_UNRESERVED_HANDLE = 0x80671102; +static const u32 ERROR_MP3_NOT_YET_INIT_HANDLE = 0x80671103; static const u32 ERROR_MP3_NO_RESOURCE_AVAIL = 0x80671201; static const u32 ERROR_MP3_BAD_ADDR = 0x80671002; static const u32 ERROR_MP3_BAD_SIZE = 0x80671003; @@ -239,7 +240,6 @@ static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { Au->PCMBufSize = 0; } - Au->Channels = 2; Au->SumDecodedSamples = 0; Au->MaxOutputSample = Au->PCMBufSize / 4; Au->LoopNum = -1; @@ -487,39 +487,48 @@ static int sceMp3SetLoopNum(u32 mp3, int loop) { } static int sceMp3GetMp3ChannelNum(u32 mp3) { - INFO_LOG(ME, "sceMp3GetMp3ChannelNum(%08X)", mp3); - AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 >= MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "unreserved handle"); + } else if (ctx->Version < 0) { + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "not yet init"); + } else if (ctx->AuBuf == 0) { + return hleLogWarning(ME, 0, "no channel available for low level"); } - return ctx->AuGetChannelNum(); + return hleLogSuccessI(ME, ctx->AuGetChannelNum()); } static int sceMp3GetBitRate(u32 mp3) { - INFO_LOG(ME, "sceMp3GetBitRate(%08X)", mp3); - AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 >= MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "unreserved handle"); + } else if (ctx->Version < 0) { + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "not yet init"); + } else if (ctx->AuBuf == 0) { + return hleLogWarning(ME, 0, "no bitrate available for low level"); } - return ctx->AuGetBitRate(); + return hleLogSuccessI(ME, ctx->AuGetBitRate()); } static int sceMp3GetSamplingRate(u32 mp3) { - INFO_LOG(ME, "sceMp3GetSamplingRate(%08X)", mp3); - AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 >= MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "unreserved handle"); + } else if (ctx->Version < 0) { + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "not yet init"); + } else if (ctx->AuBuf == 0) { + return hleLogWarning(ME, 0, "no sample rate available for low level"); } - return ctx->AuGetSamplingRate(); + return hleLogSuccessI(ME, ctx->AuGetSamplingRate()); } static int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) { @@ -583,14 +592,20 @@ static u32 sceMp3GetFrameNum(u32 mp3) { } static u32 sceMp3GetMPEGVersion(u32 mp3) { - INFO_LOG(ME, "sceMp3GetMPEGVersion(%08x)", mp3); AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 >= MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "unreserved handle"); + } else if (ctx->Version < 0) { + // Seems to be the wrong error code. + return hleLogError(ME, ERROR_MP3_UNRESERVED_HANDLE, "not yet init"); + } else if (ctx->AuBuf == 0) { + return hleLogWarning(ME, 0, "no MPEG version available for low level"); } - return ctx->AuGetVersion(); + // Tests have not revealed how to expose more than "3" here as a result. + return hleReportDebug(ME, ctx->AuGetVersion()); } static u32 sceMp3ResetPlayPositionByFrame(u32 mp3, int position) { @@ -617,8 +632,11 @@ static u32 sceMp3LowLevelInit(u32 mp3, u32 unk) { delete mp3Map[mp3]; mp3Map.erase(mp3); } - mp3Map[mp3] = ctx; + + // Indicate that we've run low level init by setting version to 1. + ctx->Version = 1; + return 0; } diff --git a/Core/HW/SimpleAudioDec.cpp b/Core/HW/SimpleAudioDec.cpp index b8062ca56e..3a55ec71fa 100644 --- a/Core/HW/SimpleAudioDec.cpp +++ b/Core/HW/SimpleAudioDec.cpp @@ -298,11 +298,6 @@ AuCtx::AuCtx() { PCMBuf = 0; PCMBufSize = 2048; AuBufAvailable = 0; - SamplingRate = 44100; - freq = SamplingRate; - BitRate = 0; - Channels = 2; - Version = 0; SumDecodedSamples = 0; MaxOutputSample = 0; askedReadSize = 0; diff --git a/Core/HW/SimpleAudioDec.h b/Core/HW/SimpleAudioDec.h index c41707c3a5..1894da0838 100644 --- a/Core/HW/SimpleAudioDec.h +++ b/Core/HW/SimpleAudioDec.h @@ -129,11 +129,11 @@ public: u32 AuBufSize; u32 PCMBuf; u32 PCMBufSize; - int freq; - int BitRate; - int SamplingRate; - int Channels; - int Version; + int freq = -1; + int BitRate = 0; + int SamplingRate = -1; + int Channels = 0; + int Version = -1; // State variables. These should be relatively easy to move into private. u32 SumDecodedSamples; From 0d6570e8ad6e8c435ea1785ca9f9c8ddfa2f7dc2 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Wed, 24 Apr 2019 14:31:12 -0700 Subject: [PATCH 12/12] Mp3: Correct sceMp3GetMaxOutputSample. Was based on output buffer size, rather than frame size, before. Fixes choppy audio in Velocity. --- Core/HLE/sceMp3.cpp | 26 +++++++++++++++++++++----- Core/HW/SimpleAudioDec.cpp | 1 - Core/HW/SimpleAudioDec.h | 2 +- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index a1f09e77e9..94ac217f77 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -241,7 +241,6 @@ static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) { } Au->SumDecodedSamples = 0; - Au->MaxOutputSample = Au->PCMBufSize / 4; Au->LoopNum = -1; Au->AuBufAvailable = 0; Au->readPos = Au->startPos; @@ -345,6 +344,18 @@ static int __CalculateMp3Bitrates(int bitval, int mp3version, int mp3layer) { } } +static int CalculateMp3SamplesPerFrame(int versionBits, int layerBits) { + if (versionBits == 1 || layerBits == 0) { + return -1; + } else if (layerBits == 3) { + return 384; + } else if (layerBits == 2 || versionBits == 3) { + return 1152; + } else { + return 576; + } +} + static int ParseMp3Header(AuCtx *ctx, int offset, bool *isID3) { u32 ptr = ctx->AuBuf + ctx->AuStreamWorkareaSize() + offset; int header = bswap32(Memory::Read_U32(ptr)); @@ -397,6 +408,7 @@ static int sceMp3Init(u32 mp3) { ctx->SamplingRate = __CalculateMp3SampleRates((header >> 10) & 0x3, ctx->Version); ctx->Channels = __CalculateMp3Channels((header >> 6) & 0x3); ctx->BitRate = __CalculateMp3Bitrates((header >> 12) & 0xF, ctx->Version, layer); + ctx->MaxOutputSample = CalculateMp3SamplesPerFrame(ctx->Version, layer); ctx->freq = ctx->SamplingRate; // for mp3, if required freq is 48000, reset resampling Frequency to 48000 seems get better sound quality (e.g. Miku Custom BGM) @@ -452,14 +464,18 @@ static int sceMp3GetLoopNum(u32 mp3) { } static int sceMp3GetMaxOutputSample(u32 mp3) { - DEBUG_LOG(ME, "sceMp3GetMaxOutputSample(%08x)", mp3); AuCtx *ctx = getMp3Ctx(mp3); if (!ctx) { - ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3); - return -1; + if (mp3 >= MP3_MAX_HANDLES) + return hleLogError(ME, ERROR_MP3_INVALID_HANDLE, "invalid handle"); + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "unreserved handle"); + } else if (ctx->Version < 0) { + return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "not yet init"); + } else if (ctx->AuBuf == 0) { + return hleLogWarning(ME, 0, "no channel available for low level"); } - return ctx->AuGetMaxOutputSample(); + return hleLogSuccessI(ME, ctx->AuGetMaxOutputSample()); } static int sceMp3GetSumDecodedSample(u32 mp3) { diff --git a/Core/HW/SimpleAudioDec.cpp b/Core/HW/SimpleAudioDec.cpp index 3a55ec71fa..739733fa15 100644 --- a/Core/HW/SimpleAudioDec.cpp +++ b/Core/HW/SimpleAudioDec.cpp @@ -299,7 +299,6 @@ AuCtx::AuCtx() { PCMBufSize = 2048; AuBufAvailable = 0; SumDecodedSamples = 0; - MaxOutputSample = 0; askedReadSize = 0; audioType = 0; FrameNum = 0; diff --git a/Core/HW/SimpleAudioDec.h b/Core/HW/SimpleAudioDec.h index 1894da0838..e7f9b1c1c1 100644 --- a/Core/HW/SimpleAudioDec.h +++ b/Core/HW/SimpleAudioDec.h @@ -138,7 +138,7 @@ public: // State variables. These should be relatively easy to move into private. u32 SumDecodedSamples; int LoopNum; - u32 MaxOutputSample; + u32 MaxOutputSample = 0; int FrameNum; // number of decoded frame // Au decoder