diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index f6ff940636..94ac217f77 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -27,6 +27,16 @@ #include "Core/Reporting.h" #include "Core/HW/SimpleAudioDec.h" +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; +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; struct Mp3Context { public: @@ -80,9 +90,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 +108,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; @@ -128,13 +138,19 @@ 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); 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) { @@ -167,67 +183,99 @@ 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) { - INFO_LOG(ME, "sceMp3ReserveMp3Handle(%08x)", mp3Addr); - if (!Memory::IsValidAddress(mp3Addr)){ - ERROR_LOG(ME, "sceMp3ReserveMp3Handle(%08x) invalid address %08x", mp3Addr, mp3Addr); - return -1; + if (!resourceInited) { + return hleLogError(ME, ERROR_MP3_NO_RESOURCE_AVAIL, "sceMp3InitResource must be called first"); + } + if (mp3Map.size() >= MP3_MAX_HANDLES) { + 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; Au->LoopNum = -1; 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() { - 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); + } + + // 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)); } static int __CalculateMp3Channels(int bitval) { @@ -296,41 +344,73 @@ static int __CalculateMp3Bitrates(int bitval, int mp3version, int mp3layer) { } } -static int __ParseMp3Header(AuCtx *ctx, bool *isID3) { - int header = bswap32(Memory::Read_U32(ctx->AuBuf)); +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)); // 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; } 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); 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; - 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); @@ -344,10 +424,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) { @@ -363,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) { @@ -398,78 +503,88 @@ 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) { - 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) { - 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) { - 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() { @@ -493,14 +608,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) { @@ -514,8 +635,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; @@ -527,8 +648,11 @@ static u32 sceMp3LowLevelInit(u32 mp3) { 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; } @@ -577,7 +701,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" }, @@ -585,7 +709,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"} }; diff --git a/Core/HW/SimpleAudioDec.cpp b/Core/HW/SimpleAudioDec.cpp index e8644e4d19..968e84d61b 100644 --- a/Core/HW/SimpleAudioDec.cpp +++ b/Core/HW/SimpleAudioDec.cpp @@ -300,15 +300,8 @@ AuCtx::AuCtx() { PCMBuf = 0; PCMBufSize = 2048; AuBufAvailable = 0; - SamplingRate = 44100; - freq = SamplingRate; - BitRate = 0; - Channels = 2; - Version = 0; SumDecodedSamples = 0; - MaxOutputSample = 0; askedReadSize = 0; - realReadSize = 0; audioType = 0; FrameNum = 0; }; @@ -382,30 +375,58 @@ 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; } -// 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; +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 = AuStreamWorkareaSize(); + return (int)AuBufSize - AuBufAvailable - offset; } - // append AuBuf into sourcebuff - sourcebuff.append((const char*)Memory::GetPointer(AuBuf), size); + // TODO: Untested. Maybe similar to MP3. + return std::min((int)AuBufSize - AuBufAvailable, (int)endPos - readPos); +} - if (readPos >= (int)endPos && LoopNum != 0){ +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; + // Notify the real read size + if (diffsize != 0) { + readPos += diffsize; + AuBufAvailable += diffsize; + } + askedReadSize = 0; + } else { + readPos += size; + AuBufAvailable += size; + } + + if (Memory::IsValidRange(AuBuf, size)) { + sourcebuff.append((const char *)Memory::GetPointer(AuBuf + offset), size); + } + + if (readPos >= (int)endPos && LoopNum != 0) { // if we need loop, reset readPos readPos = startPos; // reset LoopNum @@ -419,24 +440,29 @@ 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 = AuStreamBytesNeeded(); + int offset = AuStreamWorkareaSize(); // 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 + offset, 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; } @@ -471,7 +497,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..e7f9b1c1c1 100644 --- a/Core/HW/SimpleAudioDec.h +++ b/Core/HW/SimpleAudioDec.h @@ -99,13 +99,15 @@ public: u32 AuNotifyAddStreamData(int size); int AuCheckStreamDataNeeded(); + int AuStreamBytesNeeded(); + int AuStreamWorkareaSize(); u32 AuResetPlayPosition(); u32 AuResetPlayPositionByFrame(int position); 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; } @@ -127,16 +129,16 @@ 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; int LoopNum; - u32 MaxOutputSample; + u32 MaxOutputSample = 0; int FrameNum; // number of decoded frame // Au decoder @@ -149,7 +151,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