Merge pull request #5839 from kaienfr/Universal_Audio_Branch
Universal audio branch
This commit is contained in:
commit
c7ddd4564c
9 changed files with 883 additions and 481 deletions
|
@ -148,6 +148,9 @@ public:
|
|||
int iSFXVolume;
|
||||
int iBGMVolume;
|
||||
|
||||
// Audio Hack
|
||||
bool bSoundSpeedHack;
|
||||
|
||||
// UI
|
||||
bool bShowDebuggerOnLoad;
|
||||
int iShowFPSCounter;
|
||||
|
|
|
@ -89,3 +89,4 @@ struct AudioChannel
|
|||
extern AudioChannel chans[PSP_AUDIO_CHANNEL_MAX + 1];
|
||||
|
||||
void Register_sceAudio();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -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"}
|
||||
};
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -23,3 +23,5 @@ enum {
|
|||
};
|
||||
|
||||
void Register_sceMp4();
|
||||
void __AACShutdown();
|
||||
void __AACDoState(PointerWrap &p);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue