Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

a proper branch for fixing issues of sceMp3 #5827

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 139 additions & 109 deletions Core/HLE/sceMp3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
extern "C" {
#include <libavutil/opt.h>
#include <libavformat/avformat.h>
//#include <libavutil/timestamp.h> // iOS build is not happy with this one.
//#include <libavutil/timestamp.h> // iOS build is not happy with this one.
#include <libswresample/swresample.h>
#include <libavutil/samplefmt.h>
}
Expand All @@ -46,7 +46,7 @@ int __Mp3InitContext(Mp3Context *ctx);
struct Mp3Context {
Mp3Context()
#ifdef USE_FFMPEG
: avformat_context(NULL), avio_context(NULL), resampler_context(NULL) {
: avformat_context(NULL), avio_context(NULL), resampler_context(NULL) {
#else
{
#endif
Expand Down Expand Up @@ -149,83 +149,70 @@ void __Mp3DoState(PointerWrap &p) {
}

int sceMp3Decode(u32 mp3, u32 outPcmPtr) {
DEBUG_LOG(ME, "sceMp3Decode(%08x,%08x)", mp3, outPcmPtr);

Mp3Context *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;
int bytespcm = 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);
AVFrame *frame = av_frame_alloc();
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;
AVPacket packet;
av_init_packet(&packet);

if ((ret = av_read_frame(ctx->avformat_context, &packet)) < 0){
av_free_packet(&packet);
break;
}
if (packet.stream_index == ctx->audio_stream_index) {
av_frame_unref(&frame);
av_frame_unref(frame);
got_frame = 0;
ret = avcodec_decode_audio4(ctx->decoder_context, &frame, &got_frame, &packet);
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);
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);
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;
av_free_packet(&packet);
break;
}
__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);
// always convert to stereo pcm
__AdjustBGMVolume((s16 *)out, frame->nb_samples * 2);

//memcpy(Memory::GetPointer(ctx->mp3PcmBuf + bytesdecoded), audio_dst_data, decoded);
// the decoded size
bytesdecoded += decoded;
// av_freep(&audio_dst_data[0]);
// the output pcm size
bytespcm += ret * 2 * 2;
// the decoded source data size, always increasing even for looping
ctx->mp3DecodedBytes = packet.pos;
}
}
av_free_packet(&packet);
} // end while
av_frame_free(&frame);
// if no decoded output, we don't need to play
if (bytesdecoded > 0){
Memory::Write_U32(ctx->mp3PcmBuf, outPcmPtr);
}
Memory::Write_U32(ctx->mp3PcmBuf, outPcmPtr);
#endif

#if 0 && defined(_DEBUG)
#if 0 && defined(_DEBUG)
char fileName[256];
sprintf(fileName, "out.wav", mp3);

Expand All @@ -240,14 +227,13 @@ int sceMp3Decode(u32 mp3, u32 outPcmPtr) {

fclose(file);
}
#endif
// 2 bytes per channel and we have frame.channels in mp3 source
// learn japanese v0.9 frame.channels = 0
if (frame.channels == 0)
frame.channels = 2;
ctx->mp3SumDecodedSamples += bytesdecoded / (2 * frame.channels);
#endif

// 2 bytes per channel and we have ctx->mp3Channels in mp3 source
ctx->mp3SumDecodedSamples += bytesdecoded / (2 * ctx->mp3Channels);

return bytesdecoded;
DEBUG_LOG(ME, "%08x = sceMp3Decode(%08x,%08x)", bytespcm, mp3, outPcmPtr);
return bytespcm;
}

int sceMp3ResetPlayPosition(u32 mp3) {
Expand Down Expand Up @@ -277,47 +263,89 @@ int sceMp3CheckStreamDataNeeded(u32 mp3) {

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) {
static int pbufpos = 0;
// if we still have available data in mp3Buf
if (ctx->bufferAvailable) {
// Maximum bytes we can read
int to_read = std::min(ctx->bufferAvailable, buf_size);
int to_read = std::min(ctx->bufferAvailable, buf_size - pbufpos);

// Don't read past the end if the buffer loops
// Don't read past the end of mp3Buf if loops
to_read = std::min(ctx->mp3BufSize - ctx->bufferRead, to_read);
memcpy(buf + res, Memory::GetCharPointer(ctx->mp3Buf + ctx->bufferRead), to_read);

// we will fill the ffmpeg's buffer from the current position
memcpy(buf + pbufpos, Memory::GetCharPointer(ctx->mp3Buf + ctx->bufferRead), to_read);

ctx->bufferRead += to_read;
// if mp3Buf is full read, we reset the read position to its begining
if (ctx->bufferRead == ctx->mp3BufSize)
ctx->bufferRead = 0;
ctx->bufferAvailable -= to_read;
buf_size -= to_read;
res += to_read;
}

if (ctx->bufferAvailable == 0) {
pbufpos += to_read;
// if ffmpeg buffer is full charged, we reset pointer to its begining
if (pbufpos == buf_size)
pbufpos = 0;
res = to_read;
} // otherwise, we have no data in mp3Buf
else {
ctx->bufferRead = 0;
ctx->bufferWrite = 0;
// if the mp3 file have not been all decoded, we should not stop but continue
// we can control the loops here. If the mp3 file has been fully decoded, then return zero to stop loops, return buf_size to continue loops.
int looped = ctx->mp3DecodedBytes / (ctx->mp3StreamEnd - ctx->mp3StreamStart); // number of time we have looped
if (ctx->mp3LoopNum == -1){ // if loop all the time
return res == 0 ? buf_size : res; // always looping
}
else if (ctx->mp3LoopNum > 0 && ctx->mp3LoopNum - looped < -1){ // if loop more than once
return 0; // stop playing immediately when number of loops reached
}
else if (ctx->mp3LoopNum == 0 && looped == 0){ // only play once and still not stopped
// if res == 0, i.e. we have copied everything from mp3Buff to ffmpeg, but the decoding still not finished (maybe due to latency).
// Thus we can not return 0 to stop playing, we must return buf_size until the decoding is finishes (i.e., looped == 1).
return res == 0 ? ctx->mp3BufSize : res;
}
}
return res;
}

#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);
}
/*
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;
}

fwrite(buf, 1, res, file);
if (ctx->bufferAvailable == 0) {
ctx->bufferRead = 0;
ctx->bufferWrite = 0;
// if the mp3 file have not been all decoded, we should not stop but continue
// we can control the loop times here. If the mp3 file has been fully decoded, then return zero to stop, return buf_size to loop.
int looped = ctx->mp3DecodedBytes / (ctx->mp3StreamEnd - ctx->mp3StreamStart);
if (ctx->mp3LoopNum == -1){ // loop all the time
return res == 0 ? ctx->mp3BufSize : res; // always looping
}
else if (ctx->mp3LoopNum > 0 && ctx->mp3LoopNum - looped < -1){ // loop more than once
return 0; // stop playing immediately when number of loops reached
}
else if (ctx->mp3LoopNum == 0 && looped == 0){ // only play once and still not stopped
// if res == 0, i.e. we have copied everything from mp3Buff to ffmpeg, but the decoding still not finished due to latency.
// Thus we can not return 0 to stop playing, we must return buf_size until the decoding is finishes (looped!=0).
return res == 0 ? ctx->mp3BufSize : res;
}
}
*/

fclose(file);
}
#endif

return res;
}

u32 sceMp3ReserveMp3Handle(u32 mp3Addr) {
DEBUG_LOG(ME, "sceMp3ReserveMp3Handle(%08x)", mp3Addr);
Expand All @@ -327,7 +355,8 @@ u32 sceMp3ReserveMp3Handle(u32 mp3Addr) {

if (!Memory::IsValidAddress(mp3Addr)) {
WARN_LOG_REPORT(ME, "sceMp3ReserveMp3Handle(%08x): invalid address", mp3Addr)
} else {
}
else {
ctx->mp3StreamStart = Memory::Read_U64(mp3Addr);
ctx->mp3StreamEnd = Memory::Read_U64(mp3Addr + 8);
ctx->mp3Buf = Memory::Read_U32(mp3Addr + 16);
Expand All @@ -336,10 +365,10 @@ u32 sceMp3ReserveMp3Handle(u32 mp3Addr) {
ctx->mp3PcmBufSize = Memory::Read_U32(mp3Addr + 28);
}
ctx->readPosition = ctx->mp3StreamStart;
ctx->mp3MaxSamples = ctx->mp3PcmBufSize / 4 ;
ctx->mp3MaxSamples = ctx->mp3PcmBufSize / 4;
ctx->mp3DecodedBytes = 0;
ctx->mp3SumDecodedSamples = 0;
ctx->mp3LoopNum = -1;
ctx->mp3LoopNum = 0;
ctx->mp3Channels = 2;
ctx->mp3Bitrate = 128;
ctx->mp3SamplingRate = 44100;
Expand Down Expand Up @@ -390,7 +419,8 @@ int __Mp3InitContext(Mp3Context *ctx) {
if (ret < 0) {
if (ret == AVERROR_DECODER_NOT_FOUND) {
ERROR_LOG(HLE, "av_find_best_stream: No appropriate decoder found");
} else {
}
else {
ERROR_LOG(HLE, "av_find_best_stream: Cannot find an audio stream in the input file %d", ret);
}
return -1;
Expand All @@ -404,10 +434,11 @@ int __Mp3InitContext(Mp3Context *ctx) {
return -1;
}

// always convert to PCM S16LE, 44100Hz, stereo
ctx->resampler_context = swr_alloc_set_opts(NULL,
ctx->decoder_context->channel_layout,
AV_CH_LAYOUT_STEREO,
AV_SAMPLE_FMT_S16,
ctx->decoder_context->sample_rate,
44100,
ctx->decoder_context->channel_layout,
ctx->decoder_context->sample_fmt,
ctx->decoder_context->sample_rate,
Expand Down Expand Up @@ -451,7 +482,7 @@ int sceMp3Init(u32 mp3) {
ctx->mp3Channels = ctx->decoder_context->channels;
ctx->mp3Bitrate = ctx->decoder_context->bit_rate / 1000;
INFO_LOG(ME, "sceMp3Init(): channels=%i, samplerate=%ikHz, bitrate=%ikbps", ctx->mp3Channels, ctx->mp3SamplingRate, ctx->mp3Bitrate);

av_dump_format(ctx->avformat_context, 0, "mp3", 0);
#endif

Expand Down Expand Up @@ -553,7 +584,8 @@ int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcpos
if (ctx->readPosition < ctx->mp3StreamEnd) {
buf = ctx->mp3Buf + ctx->bufferWrite;
max_write = std::min(ctx->mp3BufSize - ctx->bufferWrite, ctx->mp3BufSize - ctx->bufferAvailable);
} else {
}
else {
buf = 0;
max_write = 0;
}
Expand Down Expand Up @@ -584,10 +616,8 @@ int sceMp3NotifyAddStreamData(u32 mp3, int size) {
if (ctx->bufferWrite >= ctx->mp3BufSize)
ctx->bufferWrite %= ctx->mp3BufSize;

if (ctx->readPosition >= ctx->mp3StreamEnd && ctx->mp3LoopNum != 0) {
if (ctx->readPosition >= ctx->mp3StreamEnd) {
ctx->readPosition = ctx->mp3StreamStart;
if (ctx->mp3LoopNum > 0)
ctx->mp3LoopNum--;
}
return 0;
}
Expand Down Expand Up @@ -656,30 +686,30 @@ u32 sceMp3LowLevelDecode() {
}

const HLEFunction sceMp3[] = {
{0x07EC321A,WrapU_U<sceMp3ReserveMp3Handle>,"sceMp3ReserveMp3Handle"},
{0x0DB149F4,WrapI_UI<sceMp3NotifyAddStreamData>,"sceMp3NotifyAddStreamData"},
{0x2A368661,WrapI_U<sceMp3ResetPlayPosition>,"sceMp3ResetPlayPosition"},
{0x354D27EA,WrapI_U<sceMp3GetSumDecodedSample>,"sceMp3GetSumDecodedSample"},
{0x35750070,WrapI_V<sceMp3InitResource>,"sceMp3InitResource"},
{0x3C2FA058,WrapI_V<sceMp3TermResource>,"sceMp3TermResource"},
{0x3CEF484F,WrapI_UI<sceMp3SetLoopNum>,"sceMp3SetLoopNum"},
{0x44E07129,WrapI_U<sceMp3Init>,"sceMp3Init"},
{0x732B042A,WrapU_V<sceMp3EndEntry>,"sceMp3EndEntry"},
{0x7F696782,WrapI_U<sceMp3GetMp3ChannelNum>,"sceMp3GetMp3ChannelNum"},
{0x87677E40,WrapI_U<sceMp3GetBitRate>,"sceMp3GetBitRate"},
{0x87C263D1,WrapI_U<sceMp3GetMaxOutputSample>,"sceMp3GetMaxOutputSample"},
{0x8AB81558,WrapU_V<sceMp3StartEntry>,"sceMp3StartEntry"},
{0x8F450998,WrapI_U<sceMp3GetSamplingRate>,"sceMp3GetSamplingRate"},
{0xA703FE0F,WrapI_UUUU<sceMp3GetInfoToAddStreamData>,"sceMp3GetInfoToAddStreamData"},
{0xD021C0FB,WrapI_UU<sceMp3Decode>,"sceMp3Decode"},
{0xD0A56296,WrapI_U<sceMp3CheckStreamDataNeeded>,"sceMp3CheckStreamDataNeeded"},
{0xD8F54A51,WrapI_U<sceMp3GetLoopNum>,"sceMp3GetLoopNum"},
{0xF5478233,WrapI_U<sceMp3ReleaseMp3Handle>,"sceMp3ReleaseMp3Handle"},
{0xAE6D2027,WrapU_U<sceMp3GetMPEGVersion>,"sceMp3GetMPEGVersion"},
{0x3548AEC8,WrapU_U<sceMp3GetFrameNum>,"sceMp3GetFrameNum"},
{0x0840e808,WrapU_UI<sceMp3ResetPlayPositionByFrame>,"sceMp3ResetPlayPositionByFrame"},
{0x1b839b83,WrapU_V<sceMp3LowLevelInit>,"sceMp3LowLevelInit"},
{0xe3ee2c81,WrapU_V<sceMp3LowLevelDecode>,"sceMp3LowLevelDecode"}
{ 0x07EC321A, WrapU_U<sceMp3ReserveMp3Handle>, "sceMp3ReserveMp3Handle" },
{ 0x0DB149F4, WrapI_UI<sceMp3NotifyAddStreamData>, "sceMp3NotifyAddStreamData" },
{ 0x2A368661, WrapI_U<sceMp3ResetPlayPosition>, "sceMp3ResetPlayPosition" },
{ 0x354D27EA, WrapI_U<sceMp3GetSumDecodedSample>, "sceMp3GetSumDecodedSample" },
{ 0x35750070, WrapI_V<sceMp3InitResource>, "sceMp3InitResource" },
{ 0x3C2FA058, WrapI_V<sceMp3TermResource>, "sceMp3TermResource" },
{ 0x3CEF484F, WrapI_UI<sceMp3SetLoopNum>, "sceMp3SetLoopNum" },
{ 0x44E07129, WrapI_U<sceMp3Init>, "sceMp3Init" },
{ 0x732B042A, WrapU_V<sceMp3EndEntry>, "sceMp3EndEntry" },
{ 0x7F696782, WrapI_U<sceMp3GetMp3ChannelNum>, "sceMp3GetMp3ChannelNum" },
{ 0x87677E40, WrapI_U<sceMp3GetBitRate>, "sceMp3GetBitRate" },
{ 0x87C263D1, WrapI_U<sceMp3GetMaxOutputSample>, "sceMp3GetMaxOutputSample" },
{ 0x8AB81558, WrapU_V<sceMp3StartEntry>, "sceMp3StartEntry" },
{ 0x8F450998, WrapI_U<sceMp3GetSamplingRate>, "sceMp3GetSamplingRate" },
{ 0xA703FE0F, WrapI_UUUU<sceMp3GetInfoToAddStreamData>, "sceMp3GetInfoToAddStreamData" },
{ 0xD021C0FB, WrapI_UU<sceMp3Decode>, "sceMp3Decode" },
{ 0xD0A56296, WrapI_U<sceMp3CheckStreamDataNeeded>, "sceMp3CheckStreamDataNeeded" },
{ 0xD8F54A51, WrapI_U<sceMp3GetLoopNum>, "sceMp3GetLoopNum" },
{ 0xF5478233, WrapI_U<sceMp3ReleaseMp3Handle>, "sceMp3ReleaseMp3Handle" },
{ 0xAE6D2027, WrapU_U<sceMp3GetMPEGVersion>, "sceMp3GetMPEGVersion" },
{ 0x3548AEC8, WrapU_U<sceMp3GetFrameNum>, "sceMp3GetFrameNum" },
{ 0x0840e808, WrapU_UI<sceMp3ResetPlayPositionByFrame>, "sceMp3ResetPlayPositionByFrame" },
{ 0x1b839b83, WrapU_V<sceMp3LowLevelInit>, "sceMp3LowLevelInit" },
{ 0xe3ee2c81, WrapU_V<sceMp3LowLevelDecode>, "sceMp3LowLevelDecode" }
};

void Register_sceMp3() {
Expand Down