ppsspp/Core/HLE/sceMp3.cpp
Unknown W. Brackets b8342fb8ec SaveState: Rename ChunkFile files to Serialize.
Makes more sense and less weird than ChunkFileDoMap, etc.
2020-08-10 08:04:05 +00:00

724 lines
24 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <map>
#include <algorithm>
#include "Common/Serialize/SerializeFuncs.h"
#include "Common/Serialize/SerializeMap.h"
#include "Core/Config.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceMp3.h"
#include "Core/HW/MediaEngine.h"
#include "Core/MemMap.h"
#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_RESET_FRAME = 0x80671501;
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:
int mp3StreamStart;
int mp3StreamEnd;
u32 mp3Buf;
int mp3BufSize;
u32 mp3PcmBuf;
int mp3PcmBufSize;
int readPosition;
int bufferRead;
int bufferWrite;
int bufferAvailable;
int mp3DecodedBytes;
int mp3LoopNum;
int mp3MaxSamples;
int mp3SumDecodedSamples;
int mp3Channels;
int mp3Bitrate;
int mp3SamplingRate;
int mp3Version;
void DoState(PointerWrap &p) {
auto s = p.Section("Mp3Context", 1);
if (!s)
return;
Do(p, mp3StreamStart);
Do(p, mp3StreamEnd);
Do(p, mp3Buf);
Do(p, mp3BufSize);
Do(p, mp3PcmBuf);
Do(p, mp3PcmBufSize);
Do(p, readPosition);
Do(p, bufferRead);
Do(p, bufferWrite);
Do(p, bufferAvailable);
Do(p, mp3DecodedBytes);
Do(p, mp3LoopNum);
Do(p, mp3MaxSamples);
Do(p, mp3SumDecodedSamples);
Do(p, mp3Channels);
Do(p, mp3Bitrate);
Do(p, mp3SamplingRate);
Do(p, mp3Version);
};
};
static std::map<u32, AuCtx *> mp3Map;
static const int mp3DecodeDelay = 2400;
static bool resourceInited = false;
static AuCtx *getMp3Ctx(u32 mp3) {
if (mp3Map.find(mp3) == mp3Map.end())
return NULL;
return mp3Map[mp3];
}
void __Mp3Shutdown() {
for (auto it = mp3Map.begin(), end = mp3Map.end(); it != end; ++it) {
delete it->second;
}
mp3Map.clear();
}
void __Mp3DoState(PointerWrap &p) {
auto s = p.Section("sceMp3", 0, 3);
if (!s)
return;
if (s >= 2) {
Do(p, mp3Map);
} else {
std::map<u32, Mp3Context *> mp3Map_old;
Do(p, mp3Map_old); // read old map
for (auto it = mp3Map_old.begin(), end = mp3Map_old.end(); it != end; ++it) {
auto mp3 = new AuCtx;
u32 id = it->first;
auto mp3_old = it->second;
mp3->AuBuf = mp3_old->mp3Buf;
mp3->AuBufSize = mp3_old->mp3BufSize;
mp3->PCMBuf = mp3_old->mp3PcmBuf;
mp3->PCMBufSize = mp3_old->mp3PcmBufSize;
mp3->BitRate = mp3_old->mp3Bitrate;
mp3->Channels = mp3_old->mp3Channels;
mp3->endPos = mp3_old->mp3StreamEnd;
mp3->startPos = mp3_old->mp3StreamStart;
mp3->LoopNum = mp3_old->mp3LoopNum;
mp3->SamplingRate = mp3_old->mp3SamplingRate;
mp3->freq = mp3->SamplingRate;
mp3->SumDecodedSamples = mp3_old->mp3SumDecodedSamples;
mp3->Version = mp3_old->mp3Version;
mp3->MaxOutputSample = mp3_old->mp3MaxSamples;
mp3->readPos = mp3_old->readPosition;
mp3->AuBufAvailable = 0; // reset to read from file
mp3->askedReadSize = 0;
mp3->audioType = PSP_CODEC_MP3;
mp3->decoder = new SimpleAudio(mp3->audioType);
mp3Map[id] = mp3;
}
}
if (s >= 3) {
Do(p, resourceInited);
} else {
// Previous behavior acted as if it was already inited.
resourceInited = true;
}
}
static int sceMp3Decode(u32 mp3, u32 outPcmPtr) {
DEBUG_LOG(ME, "sceMp3Decode(%08x,%08x)", mp3, outPcmPtr);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
int pcmBytes = ctx->AuDecode(outPcmPtr);
if (pcmBytes > 0) {
// decode data successfully, delay thread
return hleDelayResult(pcmBytes, "mp3 decode", mp3DecodeDelay);
}
return pcmBytes;
}
static int sceMp3ResetPlayPosition(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 || ctx->AuBuf == 0) {
return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "not yet init");
}
return hleLogSuccessI(ME, ctx->AuResetPlayPosition());
}
static int sceMp3CheckStreamDataNeeded(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 hleLogSuccessI(ME, ctx->AuCheckStreamDataNeeded());
}
static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) {
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;
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.
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->SumDecodedSamples = 0;
Au->LoopNum = -1;
Au->AuBufAvailable = 0;
Au->readPos = Au->startPos;
Au->audioType = PSP_CODEC_MP3;
Au->decoder = new SimpleAudio(Au->audioType);
int handle = (int)mp3Map.size();
mp3Map[handle] = Au;
return hleLogSuccessI(ME, handle);
}
static int sceMp3InitResource() {
// 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() {
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) {
if (bitval == 0 || bitval == 1 || bitval == 2) { // Stereo / Joint Stereo / Dual Channel.
return 2;
}
else if (bitval == 3) { // Mono.
return 1;
}
else {
return -1;
}
}
static int __CalculateMp3SampleRates(int bitval, int mp3version) {
if (mp3version == 3) { // MPEG Version 1
int valuemapping[] = { 44100, 48000, 32000, -1 };
return valuemapping[bitval];
}
else if (mp3version == 2) { // MPEG Version 2
int valuemapping[] = { 22050, 24000, 16000, -1 };
return valuemapping[bitval];
}
else if (mp3version == 0) { // MPEG Version 2.5
int valuemapping[] = { 11025, 12000, 8000, -1 };
return valuemapping[bitval];
}
else {
return -1;
}
}
static int __CalculateMp3Bitrates(int bitval, int mp3version, int mp3layer) {
if (mp3version == 3) { // MPEG Version 1
if (mp3layer == 3) { // Layer I
int valuemapping[] = { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 };
return valuemapping[bitval];
}
else if (mp3layer == 2) { // Layer II
int valuemapping[] = { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 };
return valuemapping[bitval];
}
else if (mp3layer == 1) { // Layer III
int valuemapping[] = { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 };
return valuemapping[bitval];
}
else {
return -1;
}
}
else if (mp3version == 2 || mp3version == 0) { // MPEG Version 2 or 2.5
if (mp3layer == 3) { // Layer I
int valuemapping[] = { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 };
return valuemapping[bitval];
}
else if (mp3layer == 1 || mp3layer == 2) { // Layer II or III
int valuemapping[] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 };
return valuemapping[bitval];
}
else {
return -1;
}
}
else {
return -1;
}
}
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 FindMp3Header(AuCtx *ctx, int &header, int end) {
u32 addr = ctx->AuBuf + ctx->AuStreamWorkareaSize();
if (Memory::IsValidRange(addr, end)) {
u8 *ptr = Memory::GetPointerUnchecked(addr);
for (int offset = 0; offset < end; ++offset) {
// If we hit valid sync bits, then we've found a header.
if (ptr[offset] == 0xFF && (ptr[offset + 1] & 0xC0) == 0xC0) {
header = bswap32(Memory::Read_U32(addr + offset));
return offset;
}
}
}
return -1;
}
static int sceMp3Init(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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");
}
static const int PARSE_DELAY_MS = 500;
// First, let's search for the MP3 header. It can be offset by at most 1439 bytes.
// If we have an ID3 tag, we'll get past it based on frame sync. Don't modify startPos.
int header = 0;
if (FindMp3Header(ctx, header, 1440) < 0)
return hleDelayResult(hleLogWarning(ME, ERROR_AVCODEC_INVALID_DATA, "no header found"), "mp3 init", PARSE_DELAY_MS);
// Parse the Mp3 header
int layerBits = (header >> 17) & 0x3;
int versionBits = (header >> 19) & 0x3;
ctx->SamplingRate = __CalculateMp3SampleRates((header >> 10) & 0x3, versionBits);
ctx->Channels = __CalculateMp3Channels((header >> 6) & 0x3);
ctx->BitRate = __CalculateMp3Bitrates((header >> 12) & 0xF, versionBits, layerBits);
ctx->MaxOutputSample = CalculateMp3SamplesPerFrame(versionBits, layerBits);
ctx->freq = ctx->SamplingRate;
DEBUG_LOG(ME, "sceMp3Init(): channels=%i, samplerate=%iHz, bitrate=%ikbps", ctx->Channels, ctx->SamplingRate, ctx->BitRate);
if (layerBits != 1) {
// TODO: Should return ERROR_AVCODEC_INVALID_DATA.
WARN_LOG_REPORT(ME, "sceMp3Init: invalid data: not layer 3");
}
if (versionBits != 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", versionBits, layerBits, (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", versionBits, layerBits, (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");
}
// Based on bitrate, we can calculate the frame size in bytes.
// Note: this doesn't correctly handle padding or slot size, but the PSP doesn't either.
uint32_t bytesPerSecond = (ctx->MaxOutputSample / 8) * ctx->BitRate * 1000;
// The frame count ignores the upper bits of these sizes, although they are used in cases.
uint64_t totalBytes = (ctx->endPos & 0xFFFFFFFF) - (ctx->startPos & 0xFFFFFFFF);
ctx->FrameNum = (int)((totalBytes * ctx->SamplingRate) / bytesPerSecond);
ctx->Version = versionBits;
// for mp3, if required freq is 48000, reset resampling Frequency to 48000 seems get better sound quality (e.g. Miku Custom BGM)
// TODO: Isn't this backwards? Woudln't we want to read as 48kHz and resample to 44.1kHz?
if (ctx->freq == 48000) {
ctx->decoder->SetResampleFrequency(ctx->freq);
}
return hleDelayResult(hleLogSuccessI(ME, 0), "mp3 init", PARSE_DELAY_MS);
}
static int sceMp3GetLoopNum(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 hleLogSuccessI(ME, ctx->AuGetLoopNum());
}
static int sceMp3GetMaxOutputSample(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 hleLogSuccessI(ME, ctx->AuGetMaxOutputSample());
}
static int sceMp3GetSumDecodedSample(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 hleLogSuccessI(ME, ctx->AuGetSumDecodedSample());
}
static int sceMp3SetLoopNum(u32 mp3, int loop) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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");
}
if (loop < 0)
loop = -1;
return hleLogSuccessI(ME, ctx->AuSetLoopNum(loop));
}
static int sceMp3GetMp3ChannelNum(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 hleLogSuccessI(ME, ctx->AuGetChannelNum());
}
static int sceMp3GetBitRate(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 hleLogSuccessI(ME, ctx->AuGetBitRate());
}
static int sceMp3GetSamplingRate(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 hleLogSuccessI(ME, ctx->AuGetSamplingRate());
}
static int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 hleLogSuccessI(ME, ctx->AuGetInfoToAddStreamData(dstPtr, towritePtr, srcposPtr));
}
static int sceMp3NotifyAddStreamData(u32 mp3, int size) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 hleLogSuccessI(ME, ctx->AuNotifyAddStreamData(size));
}
static int sceMp3ReleaseMp3Handle(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
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");
}
// Intentionally a zero result.
return hleLogDebug(ME, 0, "double free ignored");
}
static u32 sceMp3EndEntry() {
ERROR_LOG_REPORT(ME, "UNIMPL sceMp3EndEntry(...)");
return 0;
}
static u32 sceMp3StartEntry() {
ERROR_LOG_REPORT(ME, "UNIMPL sceMp3StartEntry(...)");
return 0;
}
static u32 sceMp3GetFrameNum(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 || ctx->AuBuf == 0) {
return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "not yet init");
}
return hleLogSuccessI(ME, ctx->AuGetFrameNum());
}
static u32 sceMp3GetMPEGVersion(u32 mp3) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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");
}
// Tests have not revealed how to expose more than "3" here as a result.
return hleReportDebug(ME, ctx->AuGetVersion());
}
static u32 sceMp3ResetPlayPositionByFrame(u32 mp3, u32 frame) {
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
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 || ctx->AuBuf == 0) {
return hleLogError(ME, ERROR_MP3_NOT_YET_INIT_HANDLE, "not yet init");
}
if ((int)frame >= ctx->AuGetFrameNum()) {
return hleLogError(ME, ERROR_MP3_BAD_RESET_FRAME, "bad frame position");
}
return hleLogSuccessI(ME, ctx->AuResetPlayPositionByFrame(frame));
}
static u32 sceMp3LowLevelInit(u32 mp3, u32 unk) {
INFO_LOG(ME, "sceMp3LowLevelInit(%i, %i)", mp3, unk);
auto ctx = new AuCtx;
ctx->audioType = PSP_CODEC_MP3;
// create mp3 decoder
ctx->decoder = new SimpleAudio(ctx->audioType);
// close the audio if mp3 already exists.
if (mp3Map.find(mp3) != mp3Map.end()) {
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;
}
static u32 sceMp3LowLevelDecode(u32 mp3, u32 sourceAddr, u32 sourceBytesConsumedAddr, u32 samplesAddr, u32 sampleBytesAddr) {
// sourceAddr: input mp3 stream buffer
// sourceBytesConsumedAddr: consumed bytes decoded in source
// samplesAddr: output pcm buffer
// sampleBytesAddr: output pcm size
DEBUG_LOG(ME, "sceMp3LowLevelDecode(%08x, %08x, %08x, %08x, %08x)", mp3, sourceAddr, sourceBytesConsumedAddr, samplesAddr, sampleBytesAddr);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
if (!Memory::IsValidAddress(sourceAddr) || !Memory::IsValidAddress(sourceBytesConsumedAddr) ||
!Memory::IsValidAddress(samplesAddr) || !Memory::IsValidAddress(sampleBytesAddr)) {
ERROR_LOG(ME, "sceMp3LowLevelDecode(%08x, %08x, %08x, %08x, %08x) : invalid address in args", mp3, sourceAddr, sourceBytesConsumedAddr, samplesAddr, sampleBytesAddr);
return -1;
}
auto inbuff = Memory::GetPointer(sourceAddr);
auto outbuff = Memory::GetPointer(samplesAddr);
int outpcmbytes = 0;
ctx->decoder->Decode((void*)inbuff, 4096, outbuff, &outpcmbytes);
Memory::Write_U32(ctx->decoder->GetSourcePos(), sourceBytesConsumedAddr);
Memory::Write_U32(outpcmbytes, sampleBytesAddr);
return 0;
}
const HLEFunction sceMp3[] = {
{0X07EC321A, &WrapU_U<sceMp3ReserveMp3Handle>, "sceMp3ReserveMp3Handle", 'x', "x" },
{0X0DB149F4, &WrapI_UI<sceMp3NotifyAddStreamData>, "sceMp3NotifyAddStreamData", 'i', "xi" },
{0X2A368661, &WrapI_U<sceMp3ResetPlayPosition>, "sceMp3ResetPlayPosition", 'i', "x" },
{0X354D27EA, &WrapI_U<sceMp3GetSumDecodedSample>, "sceMp3GetSumDecodedSample", 'i', "x" },
{0X35750070, &WrapI_V<sceMp3InitResource>, "sceMp3InitResource", 'i', "" },
{0X3C2FA058, &WrapI_V<sceMp3TermResource>, "sceMp3TermResource", 'i', "" },
{0X3CEF484F, &WrapI_UI<sceMp3SetLoopNum>, "sceMp3SetLoopNum", 'i', "xi" },
{0X44E07129, &WrapI_U<sceMp3Init>, "sceMp3Init", 'i', "x" },
{0X732B042A, &WrapU_V<sceMp3EndEntry>, "sceMp3EndEntry", 'x', "" },
{0X7F696782, &WrapI_U<sceMp3GetMp3ChannelNum>, "sceMp3GetMp3ChannelNum", 'i', "x" },
{0X87677E40, &WrapI_U<sceMp3GetBitRate>, "sceMp3GetBitRate", 'i', "x" },
{0X87C263D1, &WrapI_U<sceMp3GetMaxOutputSample>, "sceMp3GetMaxOutputSample", 'i', "x" },
{0X8AB81558, &WrapU_V<sceMp3StartEntry>, "sceMp3StartEntry", 'x', "" },
{0X8F450998, &WrapI_U<sceMp3GetSamplingRate>, "sceMp3GetSamplingRate", 'i', "x" },
{0XA703FE0F, &WrapI_UUUU<sceMp3GetInfoToAddStreamData>, "sceMp3GetInfoToAddStreamData", 'i', "xppp" },
{0XD021C0FB, &WrapI_UU<sceMp3Decode>, "sceMp3Decode", 'i', "xx" },
{0XD0A56296, &WrapI_U<sceMp3CheckStreamDataNeeded>, "sceMp3CheckStreamDataNeeded", 'i', "x" },
{0XD8F54A51, &WrapI_U<sceMp3GetLoopNum>, "sceMp3GetLoopNum", 'i', "x" },
{0XF5478233, &WrapI_U<sceMp3ReleaseMp3Handle>, "sceMp3ReleaseMp3Handle", 'i', "x" },
{0XAE6D2027, &WrapU_U<sceMp3GetMPEGVersion>, "sceMp3GetMPEGVersion", 'x', "x" },
{0X3548AEC8, &WrapU_U<sceMp3GetFrameNum>, "sceMp3GetFrameNum", 'i', "x" },
{0X0840E808, &WrapU_UU<sceMp3ResetPlayPositionByFrame>, "sceMp3ResetPlayPositionByFrame", 'i', "xi" },
{0X1B839B83, &WrapU_UU<sceMp3LowLevelInit>, "sceMp3LowLevelInit", 'x', "xx" },
{0XE3EE2C81, &WrapU_UUUUU<sceMp3LowLevelDecode>, "sceMp3LowLevelDecode", 'x', "xxxxx"}
};
void Register_sceMp3() {
RegisterModule("sceMp3", ARRAY_SIZE(sceMp3), sceMp3);
}