Merge pull request #5839 from kaienfr/Universal_Audio_Branch

Universal audio branch
This commit is contained in:
Henrik Rydgård 2014-04-14 14:00:21 +02:00
commit c7ddd4564c
9 changed files with 883 additions and 481 deletions

View file

@ -148,6 +148,9 @@ public:
int iSFXVolume;
int iBGMVolume;
// Audio Hack
bool bSoundSpeedHack;
// UI
bool bShowDebuggerOnLoad;
int iShowFPSCounter;

View file

@ -89,3 +89,4 @@ struct AudioChannel
extern AudioChannel chans[PSP_AUDIO_CHANNEL_MAX + 1];
void Register_sceAudio();

View file

@ -78,6 +78,7 @@
#include "sceVaudio.h"
#include "sceHeap.h"
#include "sceDmac.h"
#include "sceMp4.h"
#include "../Util/PPGeDraw.h"
@ -164,6 +165,7 @@ void __KernelShutdown()
__AudioCodecShutdown();
__VideoPmpShutdown();
__AACShutdown();
__NetShutdown();
__NetAdhocShutdown();
__FontShutdown();
@ -264,6 +266,7 @@ void __KernelDoState(PointerWrap &p)
__CheatDoState(p);
__sceAudiocodecDoState(p);
__VideoPmpDoState(p);
__AACDoState(p);
}
{

View file

@ -25,49 +25,34 @@
#include "Core/HW/MediaEngine.h"
#include "Core/MemMap.h"
#include "Core/Reporting.h"
#include "Core/HW/SimpleAudioDec.h"
static const int ID3 = 0x49443300;
#ifdef USE_FFMPEG
#ifndef PRId64
#define PRId64 "%llu"
#endif
extern "C" {
#include <libavutil/opt.h>
#include <libavformat/avformat.h>
//#include <libavutil/timestamp.h> // iOS build is not happy with this one.
#include <libswresample/swresample.h>
#include <libavutil/samplefmt.h>
}
#endif
struct Mp3Context;
int __Mp3InitContext(Mp3Context *ctx);
struct Mp3Context {
Mp3Context()
#ifdef USE_FFMPEG
: avformat_context(NULL), avio_context(NULL), resampler_context(NULL) {
#else
{
#endif
}
public:
~Mp3Context() {
#ifdef USE_FFMPEG
if (avio_context != NULL) {
av_free(avio_context->buffer);
av_free(avio_context);
}
if (avformat_context != NULL) {
avformat_free_context(avformat_context);
}
if (resampler_context != NULL) {
swr_free(&resampler_context);
}
#endif
}
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);
@ -92,44 +77,14 @@ struct Mp3Context {
p.Do(mp3Bitrate);
p.Do(mp3SamplingRate);
p.Do(mp3Version);
__Mp3InitContext(this);
}
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;
#ifdef USE_FFMPEG
AVFormatContext *avformat_context;
AVIOContext *avio_context;
AVCodecContext *decoder_context;
SwrContext *resampler_context;
int audio_stream_index;
#endif
};
};
static std::map<u32, Mp3Context *> mp3Map;
static std::map<u32, Mp3Context *> mp3Map_old;
static std::map<u32, AuCtx *> mp3Map;
Mp3Context *getMp3Ctx(u32 mp3) {
AuCtx *getMp3Ctx(u32 mp3) {
if (mp3Map.find(mp3) == mp3Map.end())
return NULL;
return mp3Map[mp3];
@ -143,207 +98,118 @@ void __Mp3Shutdown() {
}
void __Mp3DoState(PointerWrap &p) {
auto s = p.Section("sceMp3", 0, 1);
auto s = p.Section("sceMp3", 0, 2);
if (!s)
return;
p.Do(mp3Map);
if (s >= 2){
p.Do(mp3Map);
}
if (s <= 1 && p.mode == p.MODE_READ){
p.Do(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->realReadSize = 0;
mp3->audioType = PSP_CODEC_MP3;
mp3->decoder = new SimpleAudio(mp3->audioType);
mp3Map[id] = mp3;
}
}
}
int sceMp3Decode(u32 mp3, u32 outPcmPtr) {
DEBUG_LOG(ME, "sceMp3Decode(%08x,%08x)", mp3, outPcmPtr);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
// Nothing to decode
if (ctx->bufferAvailable == 0 || ctx->readPosition >= ctx->mp3StreamEnd) {
return 0;
}
int bytesdecoded = 0;
#ifndef USE_FFMPEG
Memory::Memset(ctx->mp3PcmBuf, 0, ctx->mp3PcmBufSize);
Memory::Write_U32(ctx->mp3PcmBuf, outPcmPtr);
#else
AVFrame frame;
memset(&frame, 0, sizeof(frame));
AVPacket packet;
av_init_packet(&packet);
int got_frame = 0, ret;
static int audio_frame_count = 0;
while (!got_frame) {
if ((ret = av_read_frame(ctx->avformat_context, &packet)) < 0)
break;
if (packet.stream_index == ctx->audio_stream_index) {
av_frame_unref(&frame);
got_frame = 0;
ret = avcodec_decode_audio4(ctx->decoder_context, &frame, &got_frame, &packet);
if (ret < 0) {
ERROR_LOG(ME, "avcodec_decode_audio4: Error decoding audio %d", ret);
continue;
}
if (got_frame) {
//char buf[1024] = "";
//av_ts_make_time_string(buf, frame.pts, &ctx->decoder_context->time_base);
//DEBUG_LOG(ME, "audio_frame n:%d nb_samples:%d pts:%s", audio_frame_count++, frame.nb_samples, buf);
/*
u8 *audio_dst_data;
int audio_dst_linesize;
ret = av_samples_alloc(&audio_dst_data, &audio_dst_linesize, frame.channels, frame.nb_samples, (AVSampleFormat)frame.format, 1);
if (ret < 0) {
ERROR_LOG(ME, "av_samples_alloc: Could not allocate audio buffer %d", ret);
return -1;
}
*/
int decoded = av_samples_get_buffer_size(NULL, frame.channels, frame.nb_samples, (AVSampleFormat)frame.format, 1);
u8* out = Memory::GetPointer(ctx->mp3PcmBuf + bytesdecoded);
ret = swr_convert(ctx->resampler_context, &out, frame.nb_samples, (const u8**)frame.extended_data, frame.nb_samples);
if (ret < 0) {
ERROR_LOG(ME, "swr_convert: Error while converting %d", ret);
return -1;
}
__AdjustBGMVolume((s16 *)out, frame.nb_samples * frame.channels);
//av_samples_copy(&audio_dst_data, frame.data, 0, 0, frame.nb_samples, frame.channels, (AVSampleFormat)frame.format);
//memcpy(Memory::GetPointer(ctx->mp3PcmBuf + bytesdecoded), audio_dst_data, decoded);
bytesdecoded += decoded;
// av_freep(&audio_dst_data[0]);
}
}
av_free_packet(&packet);
}
Memory::Write_U32(ctx->mp3PcmBuf, outPcmPtr);
#endif
#if 0 && defined(_DEBUG)
char fileName[256];
sprintf(fileName, "out.wav", mp3);
FILE * file = fopen(fileName, "a+b");
if (file) {
if (!Memory::IsValidAddress(ctx->mp3Buf)) {
ERROR_LOG(ME, "sceMp3Decode mp3Buf %08X is not a valid address!", ctx->mp3Buf);
}
//u8 * ptr = Memory::GetPointer(ctx->mp3Buf);
fwrite(Memory::GetPointer(ctx->mp3PcmBuf), 1, bytesdecoded, file);
fclose(file);
}
#endif
// 2 bytes per channel and we use ctx->mp3Channels here
ctx->mp3SumDecodedSamples += bytesdecoded / (2 * ctx->mp3Channels);
return bytesdecoded;
return ctx->AuDecode(outPcmPtr);
}
int sceMp3ResetPlayPosition(u32 mp3) {
DEBUG_LOG(ME, "SceMp3ResetPlayPosition(%08x)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
ctx->readPosition = ctx->mp3StreamStart;
return 0;
return ctx->AuResetPlayPosition();
}
int sceMp3CheckStreamDataNeeded(u32 mp3) {
DEBUG_LOG(ME, "sceMp3CheckStreamDataNeeded(%08x)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
return ctx->bufferAvailable != ctx->mp3BufSize && ctx->readPosition < ctx->mp3StreamEnd;
}
static int readFunc(void *opaque, uint8_t *buf, int buf_size) {
Mp3Context *ctx = static_cast<Mp3Context*>(opaque);
int res = 0;
while (ctx->bufferAvailable && buf_size) {
// Maximum bytes we can read
int to_read = std::min(ctx->bufferAvailable, buf_size);
// Don't read past the end if the buffer loops
to_read = std::min(ctx->mp3BufSize - ctx->bufferRead, to_read);
memcpy(buf + res, Memory::GetCharPointer(ctx->mp3Buf + ctx->bufferRead), to_read);
ctx->bufferRead += to_read;
if (ctx->bufferRead == ctx->mp3BufSize)
ctx->bufferRead = 0;
ctx->bufferAvailable -= to_read;
buf_size -= to_read;
res += to_read;
}
if (ctx->bufferAvailable == 0) {
ctx->bufferRead = 0;
ctx->bufferWrite = 0;
}
#if 0 && defined(_DEBUG)
char fileName[256];
sprintf(fileName, "out.mp3");
FILE * file = fopen(fileName, "a+b");
if (file) {
if (!Memory::IsValidAddress(ctx->mp3Buf)) {
ERROR_LOG(ME, "sceMp3Decode mp3Buf %08X is not a valid address!", ctx->mp3Buf);
}
fwrite(buf, 1, res, file);
fclose(file);
}
#endif
return res;
return ctx->AuCheckStreamDataNeeded();
}
u32 sceMp3ReserveMp3Handle(u32 mp3Addr) {
DEBUG_LOG(ME, "sceMp3ReserveMp3Handle(%08x)", mp3Addr);
Mp3Context *ctx = new Mp3Context;
memset(ctx, 0, sizeof(Mp3Context));
if (!Memory::IsValidAddress(mp3Addr)) {
WARN_LOG_REPORT(ME, "sceMp3ReserveMp3Handle(%08x): invalid address", mp3Addr)
} else {
ctx->mp3StreamStart = Memory::Read_U64(mp3Addr);
ctx->mp3StreamEnd = Memory::Read_U64(mp3Addr + 8);
ctx->mp3Buf = Memory::Read_U32(mp3Addr + 16);
ctx->mp3BufSize = Memory::Read_U32(mp3Addr + 20);
ctx->mp3PcmBuf = Memory::Read_U32(mp3Addr + 24);
ctx->mp3PcmBufSize = Memory::Read_U32(mp3Addr + 28);
INFO_LOG(ME, "sceMp3ReserveMp3Handle(%08x)", mp3Addr);
if (!Memory::IsValidAddress(mp3Addr)){
ERROR_LOG(ME, "sceMp3ReserveMp3Handle(%08x) invalid address %08x", mp3Addr, mp3Addr);
return -1;
}
ctx->readPosition = ctx->mp3StreamStart;
ctx->mp3MaxSamples = ctx->mp3PcmBufSize / 4 ;
ctx->mp3DecodedBytes = 0;
ctx->mp3SumDecodedSamples = 0;
ctx->mp3LoopNum = -1;
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.
DEBUG_LOG(ME, "startPos %x endPos %x mp3buf %08x mp3bufSize %08x PCMbuf %08x PCMbufSize %08x",
Au->startPos, Au->endPos, Au->AuBuf, Au->AuBufSize, Au->PCMBuf, Au->PCMBufSize);
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->decoder = new SimpleAudio(Au->audioType);
// close the audio if mp3Addr already exist.
if (mp3Map.find(mp3Addr) != mp3Map.end()) {
delete mp3Map[mp3Addr];
mp3Map.erase(mp3Addr);
}
mp3Map[mp3Addr] = ctx;
mp3Map[mp3Addr] = Au;
return mp3Addr;
}
@ -359,76 +225,14 @@ int sceMp3TermResource() {
return 0;
}
int __Mp3InitContext(Mp3Context *ctx) {
#ifdef USE_FFMPEG
InitFFmpeg();
u8 *avio_buffer = (u8*)(av_malloc(ctx->mp3BufSize));
ctx->avio_context = avio_alloc_context(avio_buffer, ctx->mp3BufSize, 0, (void*)ctx, readFunc, NULL, NULL);
ctx->avformat_context = avformat_alloc_context();
ctx->avformat_context->pb = ctx->avio_context;
int ret;
// Load audio buffer
if ((ret = avformat_open_input(&ctx->avformat_context, NULL, av_find_input_format("mp3"), NULL)) < 0) {
ERROR_LOG(ME, "avformat_open_input: Cannot open input %d", ret);
return -1;
}
if ((ret = avformat_find_stream_info(ctx->avformat_context, NULL)) < 0) {
ERROR_LOG(ME, "avformat_find_stream_info: Cannot find stream information %d", ret);
return -1;
}
AVCodec *dec;
// Select the audio stream
ret = av_find_best_stream(ctx->avformat_context, AVMEDIA_TYPE_AUDIO, -1, -1, &dec, 0);
if (ret < 0) {
if (ret == AVERROR_DECODER_NOT_FOUND) {
ERROR_LOG(HLE, "av_find_best_stream: No appropriate decoder found");
} else {
ERROR_LOG(HLE, "av_find_best_stream: Cannot find an audio stream in the input file %d", ret);
}
return -1;
}
ctx->audio_stream_index = ret;
ctx->decoder_context = ctx->avformat_context->streams[ctx->audio_stream_index]->codec;
// Init the audio decoder
if ((ret = avcodec_open2(ctx->decoder_context, dec, NULL)) < 0) {
ERROR_LOG(ME, "avcodec_open2: Cannot open audio decoder %d", ret);
return -1;
}
ctx->resampler_context = swr_alloc_set_opts(NULL,
ctx->decoder_context->channel_layout,
AV_SAMPLE_FMT_S16,
ctx->decoder_context->sample_rate,
ctx->decoder_context->channel_layout,
ctx->decoder_context->sample_fmt,
ctx->decoder_context->sample_rate,
0, NULL);
if (!ctx->resampler_context) {
ERROR_LOG(ME, "Could not allocate resampler context %d", ret);
return -1;
}
if ((ret = swr_init(ctx->resampler_context)) < 0) {
ERROR_LOG(ME, "Failed to initialize the resampling context %d", ret);
return -1;
}
return 0;
#endif
}
int __CalculateMp3Channels(int bitval) {
if (bitval == 0 || bitval == 1 || bitval == 2) { // Stereo / Joint Stereo / Dual Channel.
return 2;
} else if (bitval == 3) { // Mono.
}
else if (bitval == 3) { // Mono.
return 1;
} else {
}
else {
return -1;
}
}
@ -437,13 +241,16 @@ 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
}
else if (mp3version == 2) { // MPEG Version 2
int valuemapping[] = { 22050, 24000, 16000, -1 };
return valuemapping[bitval];
} else if (mp3version == 0) { // MPEG Version 2.5
}
else if (mp3version == 0) { // MPEG Version 2.5
int valuemapping[] = { 11025, 12000, 8000, -1 };
return valuemapping[bitval];
} else {
}
else {
return -1;
}
}
@ -453,216 +260,214 @@ int __CalculateMp3Bitrates(int bitval, int mp3version, int mp3layer) {
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
}
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
}
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 {
}
else {
return -1;
}
} else if (mp3version == 2 || mp3version == 0) { // MPEG Version 2 or 2.5
}
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
}
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 {
}
else {
return -1;
}
} else {
}
else {
return -1;
}
}
int __ParseMp3Header(Mp3Context *ctx) {
int header = bswap32(Memory::Read_U32(ctx->mp3Buf));
int __ParseMp3Header(AuCtx *ctx, bool *isID3) {
int header = bswap32(Memory::Read_U32(ctx->AuBuf));
// ID3 tag , can be seen in Hanayaka Nari Wa ga Ichizoku.
static const int ID3 = 0x49443300;
if ((header & 0xFFFFFF00) == ID3) {
int size = bswap32(Memory::Read_U32(ctx->mp3Buf + ctx->mp3StreamStart + 6));
*isID3 = true;
int size = bswap32(Memory::Read_U32(ctx->AuBuf + 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->mp3Buf + ctx->mp3StreamStart + 10 + size));
header = bswap32(Memory::Read_U32(ctx->AuBuf + ctx->startPos + 10 + size));
}
return header;
}
int sceMp3Init(u32 mp3) {
DEBUG_LOG(ME, "sceMp3Init(%08x)", mp3);
INFO_LOG(ME, "sceMp3Init(%08x)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
// Parse the Mp3 header
int header = __ParseMp3Header(ctx);
bool isID3 = false;
int header = __ParseMp3Header(ctx, &isID3);
int layer = (header >> 17) & 0x3;
ctx->mp3Version = ((header >> 19) & 0x3);
ctx->mp3SamplingRate = __CalculateMp3SampleRates((header >> 10) & 0x3, ctx->mp3Version);
ctx->mp3Channels = __CalculateMp3Channels((header >> 6) & 0x3);
ctx->mp3Bitrate = __CalculateMp3Bitrates((header >> 12) & 0xF, ctx->mp3Version, layer);
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->freq = ctx->SamplingRate;
INFO_LOG(ME, "sceMp3Init(): channels=%i, samplerate=%ikHz, bitrate=%ikbps", ctx->mp3Channels, ctx->mp3SamplingRate, ctx->mp3Bitrate);
INFO_LOG(ME, "sceMp3Init(): channels=%i, samplerate=%iHz, bitrate=%ikbps", ctx->Channels, ctx->SamplingRate, ctx->BitRate);
#ifdef USE_FFMPEG
int ret = __Mp3InitContext(ctx);
if (ret != 0)
return ret;
av_dump_format(ctx->avformat_context, 0, "mp3", 0);
#endif
// Read information from source via ffmpeg and re-create codec context
// This is an automatic method without knowledge in audio file format
// ctx->AuCreateCodecContextFromSource();
// INFO_LOG(ME, "sceMp3Init() ffmpeg: 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);
}
// For mp3 file, if ID3 tag is detected, we must move startPos to 0x400 (stream start position), remove 0x400 bytes of the sourcebuff, and reduce the available buffer size by 0x400
// this is very important for ID3 tag mp3, since our universal audio decoder is for decoding stream part only.
if (isID3){
// if get ID3 tage, we will decode from 0x400
ctx->startPos = 0x400;
ctx->sourcebuff.erase(0, 0x400);
ctx->AuBufAvailable -= 0x400;
}
else{
// if no ID3 tag, we will decode from the begining of the file
ctx->startPos = 0;
}
return 0;
}
int sceMp3GetLoopNum(u32 mp3) {
DEBUG_LOG(ME, "sceMp3GetLoopNum(%08x)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
return ctx->mp3LoopNum;
return ctx->AuGetLoopNum();
}
int sceMp3GetMaxOutputSample(u32 mp3)
{
DEBUG_LOG(ME, "sceMp3GetMaxOutputSample(%08x)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
return ctx->mp3MaxSamples;
return ctx->AuGetMaxOutputSample();
}
int sceMp3GetSumDecodedSample(u32 mp3) {
ERROR_LOG_REPORT(ME, "UNIMPL sceMp3GetSumDecodedSample(%08X)", mp3);
INFO_LOG(ME, "sceMp3GetSumDecodedSample(%08X)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
return ctx->mp3SumDecodedSamples;
return ctx->AuGetSumDecodedSample();
}
int sceMp3SetLoopNum(u32 mp3, int loop) {
INFO_LOG(ME, "sceMp3SetLoopNum(%08X, %i)", mp3, loop);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
ctx->mp3LoopNum = loop;
return 0;
return ctx->AuSetLoopNum(loop);
}
int sceMp3GetMp3ChannelNum(u32 mp3) {
DEBUG_LOG(ME, "sceMp3GetMp3ChannelNum(%08X)", mp3);
INFO_LOG(ME, "sceMp3GetMp3ChannelNum(%08X)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
return ctx->mp3Channels;
return ctx->AuGetChannelNum();
}
int sceMp3GetBitRate(u32 mp3) {
DEBUG_LOG(ME, "sceMp3GetBitRate(%08X)", mp3);
INFO_LOG(ME, "sceMp3GetBitRate(%08X)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
return ctx->mp3Bitrate;
return ctx->AuGetBitRate();
}
int sceMp3GetSamplingRate(u32 mp3) {
DEBUG_LOG(ME, "sceMp3GetSamplingRate(%08X)", mp3);
INFO_LOG(ME, "sceMp3GetSamplingRate(%08X)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
return ctx->mp3SamplingRate;
return ctx->AuGetSamplingRate();
}
int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) {
INFO_LOG(ME, "sceMp3GetInfoToAddStreamData(%08X, %08X, %08X, %08X)", mp3, dstPtr, towritePtr, srcposPtr);
DEBUG_LOG(ME, "sceMp3GetInfoToAddStreamData(%08X, %08X, %08X, %08X)", mp3, dstPtr, towritePtr, srcposPtr);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
u32 buf, max_write;
if (ctx->readPosition < ctx->mp3StreamEnd) {
buf = ctx->mp3Buf + ctx->bufferWrite;
max_write = std::min(ctx->mp3BufSize - ctx->bufferWrite, ctx->mp3BufSize - ctx->bufferAvailable);
} else {
buf = 0;
max_write = 0;
}
if (Memory::IsValidAddress(dstPtr))
Memory::Write_U32(buf, dstPtr);
if (Memory::IsValidAddress(towritePtr))
Memory::Write_U32(max_write, towritePtr);
if (Memory::IsValidAddress(srcposPtr))
Memory::Write_U32(ctx->readPosition, srcposPtr);
return 0;
return ctx->AuGetInfoToAddStreamData(dstPtr, towritePtr, srcposPtr);
}
int sceMp3NotifyAddStreamData(u32 mp3, int size) {
INFO_LOG(ME, "sceMp3NotifyAddStreamData(%08X, %i)", mp3, size);
DEBUG_LOG(ME, "sceMp3NotifyAddStreamData(%08X, %i)", mp3, size);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
ctx->readPosition += size;
ctx->bufferAvailable += size;
ctx->bufferWrite += size;
if (ctx->bufferWrite >= ctx->mp3BufSize)
ctx->bufferWrite %= ctx->mp3BufSize;
if (ctx->readPosition >= ctx->mp3StreamEnd && ctx->mp3LoopNum != 0) {
ctx->readPosition = ctx->mp3StreamStart;
if (ctx->mp3LoopNum > 0)
ctx->mp3LoopNum--;
}
return 0;
return ctx->AuNotifyAddStreamData(size);
}
int sceMp3ReleaseMp3Handle(u32 mp3) {
DEBUG_LOG(ME, "sceMp3ReleaseMp3Handle(%08X)", mp3);
INFO_LOG(ME, "sceMp3ReleaseMp3Handle(%08X)", mp3);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
mp3Map.erase(mp3Map.find(mp3));
delete ctx;
mp3Map.erase(mp3);
return 0;
}
@ -678,40 +483,82 @@ u32 sceMp3StartEntry() {
}
u32 sceMp3GetFrameNum(u32 mp3) {
ERROR_LOG_REPORT(ME, "UNIMPL sceMp3GetFrameNum(%08x)", mp3);
return 0;
INFO_LOG(ME, "sceMp3GetFrameNum(%08x)", mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
return ctx->AuGetFrameNum();
}
u32 sceMp3GetMPEGVersion(u32 mp3) {
DEBUG_LOG(ME, "sceMp3GetMPEGVersion(%08x)", mp3);
Mp3Context *ctx = getMp3Ctx(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;
}
return ctx->mp3Version;
return ctx->AuGetVersion();
}
u32 sceMp3ResetPlayPositionByFrame(u32 mp3, int position) {
DEBUG_LOG(ME, "sceMp3ResetPlayPositionByFrame(%08x, %i)", mp3, position);
Mp3Context *ctx = getMp3Ctx(mp3);
AuCtx *ctx = getMp3Ctx(mp3);
if (!ctx) {
ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
return -1;
}
ctx->readPosition = position;
return 0;
return ctx->AuResetPlayPositionByFrame(position);
}
u32 sceMp3LowLevelInit(u32 mp3, u32 paramsAddr) {
ERROR_LOG_REPORT(ME, "UNIMPL sceMp3LowLevelInit(%08x, %08x)", mp3, paramsAddr);
u32 sceMp3LowLevelInit(u32 mp3) {
INFO_LOG(ME, "sceMp3LowLevelInit(%i)", mp3);
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;
return 0;
}
u32 sceMp3LowLevelDecode(u32 mp3, u32 sourceAddr, u32 sourceBytesConsumedAddr, u32 samplesAddr, u32 sampleBytesAddr) {
ERROR_LOG_REPORT(ME, "UNIMPL sceMp3LowLevelDecode(%08x, %08x, %08x, %08x, %08x)", mp3, sourceAddr, sourceBytesConsumedAddr, samplesAddr, 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;
}
@ -738,7 +585,7 @@ const HLEFunction sceMp3[] = {
{0xAE6D2027,WrapU_U<sceMp3GetMPEGVersion>,"sceMp3GetMPEGVersion"},
{0x3548AEC8,WrapU_U<sceMp3GetFrameNum>,"sceMp3GetFrameNum"},
{0x0840e808,WrapU_UI<sceMp3ResetPlayPositionByFrame>,"sceMp3ResetPlayPositionByFrame"},
{0x1b839b83,WrapU_UU<sceMp3LowLevelInit>,"sceMp3LowLevelInit"},
{0x1b839b83,WrapU_U<sceMp3LowLevelInit>,"sceMp3LowLevelInit"},
{0xe3ee2c81,WrapU_UUUUU<sceMp3LowLevelDecode>,"sceMp3LowLevelDecode"}
};

View file

@ -19,11 +19,34 @@
#include "Core/HLE/FunctionWrappers.h"
#include "Core/Reporting.h"
#include "Core/HLE/sceMp4.h"
#include "Core/HW/SimpleAudioDec.h"
static std::map<u32, AuCtx*> aacMap;
AuCtx *getAacCtx(u32 id) {
if (aacMap.find(id) == aacMap.end())
return NULL;
return aacMap[id];
}
void __AACShutdown() {
for (auto it = aacMap.begin(), end = aacMap.end(); it != end; it++) {
delete it->second;
}
aacMap.clear();
}
void __AACDoState(PointerWrap &p) {
auto s = p.Section("sceAAC", 0, 1);
if (!s)
return;
p.Do(aacMap);
}
u32 sceMp4Init()
{
ERROR_LOG_REPORT(ME, "UNIMPL sceMp4Init()");
INFO_LOG(ME, "sceMp4Init()");
return 0;
}
@ -135,52 +158,88 @@ u32 sceMp4SearchSyncSampleNum()
return 0;
}
u32 sceAacInit(u32 parameters, u32 unknown1, u32 unknown2, u32 unknown3)
// sceAac module starts from here
u32 sceAacExit(u32 id)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceAacInit(parameters %08x, unknown1 %08x, unknown2 %08x, unknown3 %08x)", unknown1, unknown2, unknown3);
if (!Memory::IsValidAddress(parameters)){
ERROR_LOG(ME, "sceAacInit() AAC Invalid parameters address %08x", parameters);
return ERROR_AAC_INVALID_ADDRESS;
INFO_LOG(ME, "sceAacExit(id %i)", id);
if (aacMap.find(id) != aacMap.end()) {
delete aacMap[id];
aacMap.erase(id);
}
u64 startPos = (u64)Memory::Read_U32(parameters) << 32 | Memory::Read_U32(parameters + 4); // Audio data frame start position.
u64 endPos = (u64)Memory::Read_U32(parameters + 8) << 32 | Memory::Read_U32(parameters + 12); // Audio data frame end position.
int bufferAddr = Memory::Read_U32(parameters + 16); // Input AAC data buffer.
int bufferSize = Memory::Read_U32(parameters + 20); // Input AAC data buffer size.
int outputAddr = Memory::Read_U32(parameters + 24); // Output PCM data buffer.
int outputSize = Memory::Read_U32(parameters + 28); // Output PCM data buffer size.
int freq = Memory::Read_U32(parameters + 32); // Frequency.
int reserved = Memory::Read_U32(parameters + 36); // Always null.
if (bufferAddr == 0 || outputAddr == 0) {
ERROR_LOG(ME, "sceAacInit() AAC INVALID ADDRESS %08x", bufferAddr);
return ERROR_AAC_INVALID_ADDRESS;
else{
ERROR_LOG(ME, "%s: bad aac id %08x", __FUNCTION__, id);
return -1;
}
if (startPos < 0 || startPos > endPos) {
ERROR_LOG(ME, "sceAacInit() AAC INVALID startPos %i", startPos);
return ERROR_AAC_INVALID_PARAMETER;
}
if (bufferSize < 8192 || outputSize < 8192 || reserved != 0) {
ERROR_LOG(ME, "sceAacInit() AAC INVALID PARAMETER, bufferSize %i outputSize %i reserved %i", bufferSize, outputSize, reserved);
return ERROR_AAC_INVALID_PARAMETER;
}
if (freq != 44100 && freq != 32000 && freq != 48000 && freq != 24000) {
ERROR_LOG(ME, "sceAacInit() AAC INVALID freq %i", freq);
return ERROR_AAC_INVALID_PARAMETER;
}
//To Do
return 0;
}
u32 sceAacExit()
u32 sceAacInit(u32 id)
{
ERROR_LOG(ME, "UNIMPL sceAacExit()");
return 0;
INFO_LOG(ME, "UNIMPL sceAacInit(%08x)", id);
if (!Memory::IsValidAddress(id)){
ERROR_LOG(ME, "sceAacInit() AAC Invalid id address %08x", id);
return ERROR_AAC_INVALID_ADDRESS;
}
AuCtx *aac = new AuCtx;
aac->startPos = Memory::Read_U64(id); // Audio stream start position.
aac->endPos = Memory::Read_U32(id + 8); // Audio stream end position.
aac->AuBuf = Memory::Read_U32(id + 16); // Input AAC data buffer.
aac->AuBufSize = Memory::Read_U32(id + 20); // Input AAC data buffer size.
aac->PCMBuf = Memory::Read_U32(id + 24); // Output PCM data buffer.
aac->PCMBufSize = Memory::Read_U32(id + 28); // Output PCM data buffer size.
aac->freq = Memory::Read_U32(id + 32); // Frequency.
if (aac->AuBuf == 0 || aac->PCMBuf == 0) {
ERROR_LOG(ME, "sceAacInit() AAC INVALID ADDRESS AuBuf %08x PCMBuf %08x", aac->AuBuf, aac->PCMBuf);
delete aac;
return ERROR_AAC_INVALID_ADDRESS;
}
if (aac->startPos < 0 || aac->startPos > aac->endPos) {
ERROR_LOG(ME, "sceAacInit() AAC INVALID startPos %i endPos %i", aac->startPos, aac->endPos);
delete aac;
return ERROR_AAC_INVALID_PARAMETER;
}
if (aac->AuBufSize < 8192 || aac->PCMBufSize < 8192) {
ERROR_LOG(ME, "sceAacInit() AAC INVALID PARAMETER, bufferSize %i outputSize %i reserved %i", aac->AuBufSize, aac->PCMBufSize);
delete aac;
return ERROR_AAC_INVALID_PARAMETER;
}
if (aac->freq != 24000 && aac->freq != 32000 && aac->freq != 44100 && aac->freq != 48000) {
ERROR_LOG(ME, "sceAacInit() AAC INVALID freq %i", aac->freq);
delete aac;
return ERROR_AAC_INVALID_PARAMETER;
}
DEBUG_LOG(ME, "startPos %x endPos %x AuBuf %08x AuBufSize %08x PCMbuf %08x PCMbufSize %08x freq %d",
aac->startPos, aac->endPos, aac->AuBuf, aac->AuBufSize, aac->PCMBuf, aac->PCMBufSize, aac->freq);
aac->Channels = 2;
aac->SumDecodedSamples = 0;
aac->MaxOutputSample = aac->PCMBufSize / 4;
aac->LoopNum = -1;
aac->AuBufAvailable = 0;
aac->MaxOutputSample = 0;
aac->readPos = aac->startPos;
aac->audioType = PSP_CODEC_AAC;
// create aac decoder
aac->decoder = new SimpleAudio(aac->audioType);
// close the audio if id already exist.
if (aacMap.find(id) != aacMap.end()) {
delete aacMap[id];
aacMap.erase(id);
}
aacMap[id] = aac;
return id;
}
u32 sceAacInitResource(u32 numberIds)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceAacInitResource(%i)", numberIds);
// Do nothing here
INFO_LOG_REPORT(ME, "sceAacInitResource(%i)", numberIds);
return 0;
}
@ -190,58 +249,118 @@ u32 sceAacTermResource()
return 0;
}
u32 sceAacDecode(u32 id, u32 bufferAddress)
u32 sceAacDecode(u32 id, u32 pcmAddr)
{
ERROR_LOG(ME, "UNIMPL sceAacDecode(id %i, bufferAddress %08x)", id, bufferAddress);
return 0;
// return the size of output pcm, <0 error
DEBUG_LOG(ME, "sceAacDecode(id %i, bufferAddress %08x)", id, pcmAddr);
auto ctx = getAacCtx(id);
if (!ctx) {
ERROR_LOG(ME, "%s: bad aac id %08x", __FUNCTION__, id);
return -1;
}
return ctx->AuDecode(pcmAddr);
}
u32 sceAacGetLoopNum()
u32 sceAacGetLoopNum(u32 id)
{
ERROR_LOG(ME, "UNIMPL sceAacGetLoopNum()");
return 0;
INFO_LOG(ME, "sceAacGetLoopNum(id %i)", id);
auto ctx = getAacCtx(id);
if (!ctx) {
ERROR_LOG(ME, "%s: bad aac id %08x", __FUNCTION__, id);
return -1;
}
return ctx->AuGetLoopNum();
}
u32 sceAacSetLoopNum()
u32 sceAacSetLoopNum(u32 id, int loop)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceAacSetLoopNum()");
return 0;
INFO_LOG(ME, "sceAacSetLoopNum(id %i,loop %d)", id, loop);
auto ctx = getAacCtx(id);
if (!ctx) {
ERROR_LOG(ME, "%s: bad aac id %08x", __FUNCTION__, id);
return -1;
}
return ctx->AuSetLoopNum(loop);
}
u32 sceAacCheckStreamDataNeeded(u32 id)
int sceAacCheckStreamDataNeeded(u32 id)
{
ERROR_LOG(ME, "UNIMPL sceAacCheckStreamDataNeeded(%i)", id);
return 0;
// return 1 to read more data stream, 0 don't read, <0 error
DEBUG_LOG(ME, "sceAacCheckStreamDataNeeded(%i)", id);
auto ctx = getAacCtx(id);
if (!ctx) {
ERROR_LOG(ME, "%s: bad aac id %08x", __FUNCTION__, id);
return -1;
}
return ctx->AuCheckStreamDataNeeded();
}
u32 sceAacNotifyAddStreamData()
u32 sceAacNotifyAddStreamData(u32 id, int size)
{
ERROR_LOG(ME, "UNIMPL sceAacNotifyAddStreamData()");
return 0;
// check how many bytes we have read from source file
DEBUG_LOG(ME, "sceAacNotifyAddStreamData(%i, %08x)", id, size);
auto ctx = getAacCtx(id);
if (!ctx) {
ERROR_LOG(ME, "%s: bad aac id %08x", __FUNCTION__, id);
return -1;
}
return ctx->AuNotifyAddStreamData(size);
}
u32 sceAacGetInfoToAddStreamData()
u32 sceAacGetInfoToAddStreamData(u32 id, u32 buff, u32 size, u32 srcPos)
{
ERROR_LOG(ME, "UNIMPL sceAacGetInfoToAddStreamData()");
return 0;
// read from stream position srcPos of size bytes into buff
DEBUG_LOG(ME, "sceAacGetInfoToAddStreamData(%08X, %08X, %08X, %08X)", id, buff, size, srcPos);
auto ctx = getAacCtx(id);
if (!ctx) {
ERROR_LOG(ME, "%s: bad aac handle %08x", __FUNCTION__, id);
return -1;
}
return ctx->AuGetInfoToAddStreamData(buff, size, srcPos);
}
u32 sceAacGetMaxOutputSample()
u32 sceAacGetMaxOutputSample(u32 id)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceAacGetMaxOutputSample()");
return 0;
DEBUG_LOG(ME, "sceAacGetMaxOutputSample(id %i)", id);
auto ctx = getAacCtx(id);
if (!ctx) {
ERROR_LOG(ME, "%s: bad aac id %08x", __FUNCTION__, id);
return -1;
}
return ctx->AuGetMaxOutputSample();
}
u32 sceAacGetSumDecodedSample(u32 id)
{
ERROR_LOG(ME, "UNIMPL sceAacGetSumDecodedSample(%i)", id);
return 0;
DEBUG_LOG(ME, "sceAacGetSumDecodedSample(id %i)", id);
auto ctx = getAacCtx(id);
if (!ctx) {
ERROR_LOG(ME, "%s: bad aac id %08x", __FUNCTION__, id);
return -1;
}
return ctx->AuGetSumDecodedSample();
}
u32 sceAacResetPlayPosition()
u32 sceAacResetPlayPosition(u32 id)
{
ERROR_LOG_REPORT(ME, "UNIMPL sceAacResetPlayPosition()");
return 0;
INFO_LOG(ME, "sceAacResetPlayPosition(id %i)", id);
auto ctx = getAacCtx(id);
if (!ctx) {
ERROR_LOG(ME, "%s: bad aac id %08x", __FUNCTION__, id);
return -1;
}
return ctx->AuResetPlayPosition();
}
const HLEFunction sceMp4[] =
@ -289,19 +408,19 @@ const HLEFunction sceMp4[] =
// 395
const HLEFunction sceAac[] = {
{0xE0C89ACA, WrapU_UUUU<sceAacInit>, "sceAacInit"},
{0x33B8C009, WrapU_V<sceAacExit>, "sceAacExit"},
{0xE0C89ACA, WrapU_U<sceAacInit>, "sceAacInit"},
{0x33B8C009, WrapU_U<sceAacExit>, "sceAacExit"},
{0x5CFFC57C, WrapU_U<sceAacInitResource>, "sceAacInitResource"},
{0x23D35CAE, WrapU_V<sceAacTermResource>, "sceAacTermResource"},
{0x7E4CFEE4, WrapU_UU<sceAacDecode>, "sceAacDecode"},
{0x523347D9, WrapU_V<sceAacGetLoopNum>, "sceAacGetLoopNum"},
{0xBBDD6403, WrapU_V<sceAacSetLoopNum>, "sceAacSetLoopNum"},
{0xD7C51541, WrapU_U<sceAacCheckStreamDataNeeded>, "sceAacCheckStreamDataNeeded"},
{0xAC6DCBE3, WrapU_V<sceAacNotifyAddStreamData>, "sceAacNotifyAddStreamData"},
{0x02098C69, WrapU_V<sceAacGetInfoToAddStreamData>, "sceAacGetInfoToAddStreamData"},
{0x6DC7758A, WrapU_V<sceAacGetMaxOutputSample>, "sceAacGetMaxOutputSample"},
{0x523347D9, WrapU_U<sceAacGetLoopNum>, "sceAacGetLoopNum"},
{0xBBDD6403, WrapU_UI<sceAacSetLoopNum>, "sceAacSetLoopNum"},
{0xD7C51541, WrapI_U<sceAacCheckStreamDataNeeded>, "sceAacCheckStreamDataNeeded"},
{0xAC6DCBE3, WrapU_UI<sceAacNotifyAddStreamData>, "sceAacNotifyAddStreamData"},
{0x02098C69, WrapU_UUUU<sceAacGetInfoToAddStreamData>, "sceAacGetInfoToAddStreamData"},
{0x6DC7758A, WrapU_U<sceAacGetMaxOutputSample>, "sceAacGetMaxOutputSample"},
{0x506BF66C, WrapU_U<sceAacGetSumDecodedSample>, "sceAacGetSumDecodedSample"},
{0xD2DA2BBA, WrapU_V<sceAacResetPlayPosition>, "sceAacResetPlayPosition"},
{0xD2DA2BBA, WrapU_U<sceAacResetPlayPosition>, "sceAacResetPlayPosition"},
};
void Register_sceMp4()

View file

@ -23,3 +23,5 @@ enum {
};
void Register_sceMp4();
void __AACShutdown();
void __AACDoState(PointerWrap &p);

View file

@ -15,6 +15,9 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "Core/Config.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HW/SimpleAudioDec.h"
#include "Core/HW/MediaEngine.h"
#include "Core/HW/BufferQueue.h"
@ -31,7 +34,7 @@ extern "C" {
bool SimpleAudio::GetAudioCodecID(int audioType){
#ifdef USE_FFMPEG
switch (audioType)
{
case PSP_CODEC_AAC:
@ -60,12 +63,12 @@ bool SimpleAudio::GetAudioCodecID(int audioType){
}
SimpleAudio::SimpleAudio(int audioType)
: codec_(0), codecCtx_(0), swrCtx_(0), audioType(audioType){
: codec_(0), codecCtx_(0), swrCtx_(0), audioType(audioType), outSamples(0), wanted_resample_freq(44100){
#ifdef USE_FFMPEG
avcodec_register_all();
av_register_all();
InitFFmpeg();
frame_ = av_frame_alloc();
// Get Audio Codec ID
@ -102,7 +105,7 @@ SimpleAudio::SimpleAudio(int audioType)
SimpleAudio::SimpleAudio(u32 ctxPtr, int audioType)
: codec_(0), codecCtx_(0), swrCtx_(0), ctxPtr(ctxPtr), audioType(audioType){
: codec_(0), codecCtx_(0), swrCtx_(0), ctxPtr(ctxPtr), audioType(audioType), outSamples(0), wanted_resample_freq(44100){
#ifdef USE_FFMPEG
avcodec_register_all();
av_register_all();
@ -110,7 +113,7 @@ SimpleAudio::SimpleAudio(u32 ctxPtr, int audioType)
frame_ = av_frame_alloc();
// Get Audio Codec ID
// Get Audio Codec ctx
if (!GetAudioCodecID(audioType)){
ERROR_LOG(ME, "This version of FFMPEG does not support Audio codec type: %08x. Update your submodule.", audioType);
return;
@ -119,7 +122,7 @@ SimpleAudio::SimpleAudio(u32 ctxPtr, int audioType)
codec_ = avcodec_find_decoder(audioCodecId);
if (!codec_) {
// Eh, we shouldn't even have managed to compile. But meh.
ERROR_LOG(ME, "This version of FFMPEG does not support AV_CODEC_ID for audio (%s). Update your submodule.",GetCodecName(audioType));
ERROR_LOG(ME, "This version of FFMPEG does not support AV_CODEC_ctx for audio (%s). Update your submodule.", GetCodecName(audioType));
return;
}
// Allocate codec context
@ -135,6 +138,7 @@ SimpleAudio::SimpleAudio(u32 ctxPtr, int audioType)
AVDictionary *opts = 0;
if (avcodec_open2(codecCtx_, codec_, &opts) < 0) {
ERROR_LOG(ME, "Failed to open codec");
av_dict_free(&opts);
return;
}
@ -142,16 +146,44 @@ SimpleAudio::SimpleAudio(u32 ctxPtr, int audioType)
#endif // USE_FFMPEG
}
bool SimpleAudio::ResetCodecCtx(int channels, int samplerate){
#ifdef USE_FFMPEG
if (codecCtx_)
avcodec_close(codecCtx_);
// Find decoder
codec_ = avcodec_find_decoder(audioCodecId);
if (!codec_) {
// Eh, we shouldn't even have managed to compile. But meh.
ERROR_LOG(ME, "This version of FFMPEG does not support AV_CODEC_ctx for audio (%s). Update your submodule.", GetCodecName(audioType));
return false;
}
codecCtx_->channels = channels;
codecCtx_->channel_layout = channels==2?AV_CH_LAYOUT_STEREO:AV_CH_LAYOUT_MONO;
codecCtx_->sample_rate = samplerate;
// Open codec
AVDictionary *opts = 0;
if (avcodec_open2(codecCtx_, codec_, &opts) < 0) {
ERROR_LOG(ME, "Failed to open codec");
av_dict_free(&opts);
return false;
}
av_dict_free(&opts);
return true;
#endif
return false;
}
SimpleAudio::~SimpleAudio() {
#ifdef USE_FFMPEG
if (frame_)
av_frame_free(&frame_);
if (codecCtx_)
avcodec_close(codecCtx_);
frame_ = 0;
codecCtx_ = 0;
codec_ = 0;
if (swrCtx_)
swr_free(&swrCtx_);
#endif // USE_FFMPEG
}
@ -172,13 +204,20 @@ bool SimpleAudio::Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbyte
int got_frame = 0;
av_frame_unref(frame_);
*outbytes = 0;
srcPos = 0;
int len = avcodec_decode_audio4(codecCtx_, frame_, &got_frame, &packet);
if (len < 0) {
ERROR_LOG(ME, "Error decoding Audio frame");
// TODO: cleanup
return false;
}
av_free_packet(&packet);
// get bytes consumed in source
srcPos = len;
if (got_frame) {
// Initializing the sample rate convert. We will use it to convert float output into int.
int64_t wanted_channel_layout = AV_CH_LAYOUT_STEREO; // we want stereo output layout
@ -188,7 +227,7 @@ bool SimpleAudio::Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbyte
swrCtx_,
wanted_channel_layout,
AV_SAMPLE_FMT_S16,
44100,
wanted_resample_freq,
dec_channel_layout,
codecCtx_->sample_fmt,
codecCtx_->sample_rate,
@ -209,7 +248,7 @@ bool SimpleAudio::Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbyte
}
swr_free(&swrCtx_);
// output samples per frame, we should *2 since we have two channels
int outSamples = swrRet * 2;
outSamples = swrRet * 2;
// each sample occupies 2 bytes
*outbytes = outSamples * 2;
@ -227,6 +266,18 @@ bool SimpleAudio::Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbyte
#endif // USE_FFMPEG
}
int SimpleAudio::getOutSamples(){
return outSamples;
}
int SimpleAudio::getSourcePos(){
return srcPos;
}
void SimpleAudio::setResampleFrequency(int freq){
wanted_resample_freq = freq;
}
void AudioClose(SimpleAudio **ctx) {
#ifdef USE_FFMPEG
delete *ctx;
@ -241,3 +292,252 @@ bool isValidCodec(int codec){
return false;
}
// sceAu module starts from here
// return output pcm size, <0 error
u32 AuCtx::AuDecode(u32 pcmAddr)
{
if (!Memory::IsValidAddress(pcmAddr)){
ERROR_LOG(ME, "%s: output bufferAddress %08x is invalctx", __FUNCTION__, pcmAddr);
return -1;
}
auto outbuf = Memory::GetPointer(PCMBuf);
memset(outbuf, 0, PCMBufSize); // important! empty outbuf to avoid noise
u32 outpcmbufsize = 0;
int repeat = 1;
if (g_Config.bSoundSpeedHack){
repeat = 2;
}
int i = 0;
// decode frames in sourcebuff and output into PCMBuf (each time, we decode one or two frames)
// some games as Miku like one frame each time, some games like DOA like two frames each time
while (sourcebuff.size() > 0 && outpcmbufsize < PCMBufSize && i < repeat){
i++;
int pcmframesize;
// decode
decoder->Decode((void*)sourcebuff.c_str(), (int)sourcebuff.size(), outbuf, &pcmframesize);
if (pcmframesize == 0){
// no output pcm, we are at the end of the stream
AuBufAvailable = 0;
sourcebuff.clear();
if (LoopNum != 0){
// if we loop, reset readPos
readPos = startPos;
}
break;
}
// count total output pcm size
outpcmbufsize += pcmframesize;
// count total output samples
SumDecodedSamples += decoder->getOutSamples();
// get consumed source length
int srcPos = decoder->getSourcePos();
// remove the consumed source
sourcebuff.erase(0, srcPos);
// reduce the available Aubuff size
// (the available buff size is now used to know if we can read again from file and how many to read)
AuBufAvailable -= srcPos;
// move outbuff position to the current end of output
outbuf += pcmframesize;
// increase FrameNum count
FrameNum++;
}
Memory::Write_U32(PCMBuf, pcmAddr);
return outpcmbufsize;
}
u32 AuCtx::AuGetLoopNum()
{
return LoopNum;
}
u32 AuCtx::AuSetLoopNum(int loop)
{
LoopNum = loop;
return 0;
}
// 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){
return 1;
}
return 0;
}
// check how many bytes we have read from source file
u32 AuCtx::AuNotifyAddStreamData(int size)
{
realReadSize = size;
int diffszie = realReadSize - askedReadSize;
// Notify the real read size
if (diffszie != 0){
readPos += diffszie;
AuBufAvailable += diffszie;
}
// append AuBuf into sourcebuff
sourcebuff.append((const char*)Memory::GetPointer(AuBuf), size);
if (readPos >= endPos && LoopNum != 0){
// if we need loop, reset readPos
readPos = startPos;
// reset LoopNum
if (LoopNum > 0){
LoopNum--;
}
}
return 0;
}
// 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 buffersize
int readsize = std::min((int)AuBufSize - AuBufAvailable, (int)endPos - readPos);
// we can recharge AuBuf from its begining
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;
return 0;
}
u32 AuCtx::AuGetMaxOutputSample()
{
return MaxOutputSample;
}
u32 AuCtx::AuGetSumDecodedSample()
{
return SumDecodedSamples;
}
u32 AuCtx::AuResetPlayPosition()
{
readPos = startPos;
return 0;
}
int AuCtx::AuGetChannelNum(){
return Channels;
}
int AuCtx::AuGetBitRate(){
return BitRate;
}
int AuCtx::AuGetSamplingRate(){
return SamplingRate;
}
u32 AuCtx::AuResetPlayPositionByFrame(int position){
readPos = position;
return 0;
}
int AuCtx::AuGetVersion(){
return Version;
}
int AuCtx::AuGetFrameNum(){
return FrameNum;
}
static int _Readbuffer(void *opaque, uint8_t *buf, int buf_size) {
auto ctx = (AuCtx *)opaque;
int toread = std::min((int)ctx->AuBufSize, buf_size);
memcpy(buf, Memory::GetPointer(ctx->AuBuf), toread);
return toread;
}
static void closeAvioCtxandFormatCtx(AVIOContext* pAVIOCtx, AVFormatContext* pFormatCtx){
if (pAVIOCtx && pAVIOCtx->buffer)
av_free(pAVIOCtx->buffer);
if (pAVIOCtx)
av_free(pAVIOCtx);
if (pFormatCtx)
avformat_close_input(&pFormatCtx);
}
// you need at least have initialized AuBuf, AuBufSize and decoder
bool AuCtx::AuCreateCodecContextFromSource(){
u8* tempbuf = (u8*)av_malloc(AuBufSize);
auto pFormatCtx = avformat_alloc_context();
auto pAVIOCtx = avio_alloc_context(tempbuf, AuBufSize, 0, (void*)this, _Readbuffer, NULL, NULL);
pFormatCtx->pb = pAVIOCtx;
int ret;
// Load audio buffer
if ((ret = avformat_open_input((AVFormatContext**)&pFormatCtx, NULL, NULL, NULL)) != 0) {
ERROR_LOG(ME, "avformat_open_input: Cannot open input %d", ret);
closeAvioCtxandFormatCtx(pAVIOCtx,pFormatCtx);
return false;
}
if ((ret = avformat_find_stream_info(pFormatCtx, NULL)) < 0) {
ERROR_LOG(ME, "avformat_find_stream_info: Cannot find stream information %d", ret);
closeAvioCtxandFormatCtx(pAVIOCtx, pFormatCtx);
return false;
}
// reset decoder context
if (decoder->codecCtx_){
avcodec_close(decoder->codecCtx_);
av_free(decoder->codecCtx_);
}
decoder->codecCtx_ = pFormatCtx->streams[ret]->codec;
if (decoder->codec_){
decoder->codec_ = 0;
}
// select the audio stream
ret = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, &decoder->codec_, 0);
if (ret < 0) {
if (ret == AVERROR_DECODER_NOT_FOUND) {
ERROR_LOG(HLE, "av_find_best_stream: No appropriate decoder found");
}
else {
ERROR_LOG(HLE, "av_find_best_stream: Cannot find an audio stream in the input file %d", ret);
}
closeAvioCtxandFormatCtx(pAVIOCtx, pFormatCtx);
return false;
}
// close and free AVIO and AVFormat
// closeAvioCtxandFormatCtx(pAVIOCtx, pFormatCtx);
// open codec
if ((ret = avcodec_open2(decoder->codecCtx_, decoder->codec_, NULL)) < 0) {
avcodec_close(decoder->codecCtx_);
av_free(decoder->codecCtx_);
decoder->codecCtx_ = 0;
decoder->codec_ = 0;
ERROR_LOG(ME, "avcodec_open2: Cannot open audio decoder %d", ret);
return false;
}
// set audio informations
SamplingRate = decoder->codecCtx_->sample_rate;
Channels = decoder->codecCtx_->channels;
BitRate = decoder->codecCtx_->bit_rate/1000;
freq = SamplingRate;
return true;
}

View file

@ -21,6 +21,7 @@
#include "base/basictypes.h"
#include "Core/HW/MediaEngine.h"
#include "Core/HLE/sceAudio.h"
#ifdef USE_FFMPEG
@ -51,11 +52,17 @@ public:
bool Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbytes);
bool IsOK() const { return codec_ != 0; }
int getOutSamples();
int getSourcePos();
bool ResetCodecCtx(int channels, int samplerate);
void setResampleFrequency(int freq);
u32 ctxPtr;
int audioType;
int outSamples; // output samples per frame
int srcPos; // bytes consumed in source during the last decoding
int wanted_resample_freq; // wanted resampling rate/frequency
private:
#ifdef USE_FFMPEG
AVFrame *frame_;
AVCodec *codec_;
@ -67,7 +74,7 @@ private:
#endif // USE_FFMPEG
};
// audioType
enum {
PSP_CODEC_AT3PLUS = 0x00001000,
PSP_CODEC_AT3 = 0x00001001,
@ -89,3 +96,120 @@ static const char *GetCodecName(int codec) {
}
};
bool isValidCodec(int codec);
class AuCtx{
public:
// Au source informations
u64 startPos;
u64 endPos;
u32 AuBuf;
u32 AuBufSize;
u32 PCMBuf;
u32 PCMBufSize;
int freq;
int BitRate;
int SamplingRate;
int Channels;
int Version;
// audio settings
u32 SumDecodedSamples;
int LoopNum;
u32 MaxOutputSample;
int FrameNum; // number of decoded frame
// Au decoder
SimpleAudio *decoder;
// Au type
int audioType;
// buffers informations
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
std::string sourcebuff; // source buffer
AuCtx(){
decoder = NULL;
startPos = 0;
endPos = 0;
LoopNum = -1;
AuBuf = 0;
AuBufSize = 2048;
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;
};
~AuCtx(){
if (decoder){
AudioClose(&decoder);
decoder = NULL;
}
};
u32 AuExit();
u32 AuDecode(u32 pcmAddr);
u32 AuGetLoopNum();
u32 AuSetLoopNum(int loop);
int AuCheckStreamDataNeeded();
u32 AuNotifyAddStreamData(int size);
u32 AuGetInfoToAddStreamData(u32 buff, u32 size, u32 srcPos);
u32 AuGetMaxOutputSample();
u32 AuGetSumDecodedSample();
u32 AuResetPlayPosition();
int AuGetChannelNum();
int AuGetBitRate();
int AuGetSamplingRate();
u32 AuResetPlayPositionByFrame(int position);
int AuGetVersion();
int AuGetFrameNum();
bool AuCreateCodecContextFromSource();
void DoState(PointerWrap &p) {
auto s = p.Section("AuContext", 0, 1);
if (!s)
return;
p.Do(startPos);
p.Do(endPos);
p.Do(AuBuf);
p.Do(AuBufSize);
p.Do(PCMBuf);
p.Do(PCMBufSize);
p.Do(freq);
p.Do(SumDecodedSamples);
p.Do(LoopNum);
p.Do(Channels);
p.Do(MaxOutputSample);
p.Do(readPos);
p.Do(audioType);
p.Do(BitRate);
p.Do(SamplingRate);
p.Do(askedReadSize);
p.Do(realReadSize);
p.Do(FrameNum);
if (p.mode == p.MODE_READ){
decoder = new SimpleAudio(audioType);
AuBufAvailable = 0; // reset to read from file at position readPos
}
};
};

View file

@ -248,6 +248,9 @@ void GameSettingsScreen::CreateViews() {
CheckBox *lowAudio = audioSettings->Add(new CheckBox(&g_Config.bLowLatencyAudio, a->T("Low latency audio")));
lowAudio->SetEnabledPtr(&g_Config.bEnableSound);
audioSettings->Add(new ItemHeader(ms->T("Audio hacks")));
audioSettings->Add(new CheckBox(&g_Config.bSoundSpeedHack, a->T("Sound speed hack (DOA etc.)")));
// Control
ViewGroup *controlsSettingsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
LinearLayout *controlsSettings = new LinearLayout(ORIENT_VERTICAL);