diff --git a/Core/HLE/sceAudiocodec.cpp b/Core/HLE/sceAudiocodec.cpp index e795a272a0..b249d1ed5f 100644 --- a/Core/HLE/sceAudiocodec.cpp +++ b/Core/HLE/sceAudiocodec.cpp @@ -19,6 +19,7 @@ #include "sceAudiocodec.h" #include "Core/Reporting.h" +#include "Core/HW/SimpleMp3Dec.h" enum { PSP_CODEC_AT3PLUS = 0x00001000, @@ -48,19 +49,38 @@ struct AudioCodecContext { u32_le outDataPtr; // 8 u32_le audioSamplesPerFrame; // 9 u32_le inDataSizeAgain; // 10 ?? -}; +}; + +SimpleMP3* mp3; int sceAudiocodecInit(u32 ctxPtr, int codec) { + if (codec == PSP_CODEC_MP3){ + // Initialize MP3 audio decoder. + mp3 = MP3Create(); + DEBUG_LOG(ME, "sceAudiocodecInit(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec)); + return 0; + } ERROR_LOG_REPORT(ME, "UNIMPL sceAudiocodecInit(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec)); return 0; } int sceAudiocodecDecode(u32 ctxPtr, int codec) { - ERROR_LOG_REPORT(ME, "UNIMPL sceAudiocodecDecode(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec)); - if (!ctxPtr) { + if (!ctxPtr){ + ERROR_LOG_REPORT(ME, "sceAudiocodecDecode(%08x, %i (%s)) got NULL pointer", ctxPtr, codec, GetCodecName(codec)); return -1; } - auto ctx = PSPPointer::Create(ctxPtr); + if (codec == PSP_CODEC_MP3){ + //Use SimpleMp3Dec to decode Mp3 audio + // Get AudioCodecContext + AudioCodecContext* ctx = new AudioCodecContext; + Memory::ReadStruct(ctxPtr, ctx); + int outbytes = 0; + // Decode Mp3 audio + MP3Decode(mp3, Memory::GetPointer(ctx->inDataPtr), ctx->inDataSize, &outbytes, Memory::GetPointer(ctx->outDataPtr)); + DEBUG_LOG(ME, "sceAudiocodecDec(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec)); + return 0; + } + ERROR_LOG_REPORT(ME, "UNIMPL sceAudiocodecDecode(%08x, %i (%s))", ctxPtr, codec, GetCodecName(codec)); return 0; } diff --git a/Core/HW/SimpleMp3Dec.cpp b/Core/HW/SimpleMp3Dec.cpp new file mode 100644 index 0000000000..1d0a3c48a3 --- /dev/null +++ b/Core/HW/SimpleMp3Dec.cpp @@ -0,0 +1,192 @@ +// Copyright (c) 2013- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "Core/HW/SimpleMP3Dec.h" +#include "Core/HW/MediaEngine.h" +#include "Core/HW/BufferQueue.h" + +#ifdef USE_FFMPEG + +extern "C" { +#include +#include +#include +} + +struct SimpleMP3 { +public: + SimpleMP3(); + ~SimpleMP3(); + + bool Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbytes); + bool IsOK() const { return codec_ != 0; } + +private: + AVFrame *frame_; + AVCodec *codec_; + AVCodecContext *codecCtx_; + SwrContext *swrCtx_; +}; + +SimpleMP3::SimpleMP3() +: codec_(0), +codecCtx_(0), +swrCtx_(0) { + frame_ = av_frame_alloc(); + + codec_ = avcodec_find_decoder(AV_CODEC_ID_MP3); + 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_MP3 (MP3). Update your submodule."); + return; + } + + codecCtx_ = avcodec_alloc_context3(codec_); + if (!codecCtx_) { + ERROR_LOG(ME, "Failed to allocate a codec context"); + return; + } + + codecCtx_->channels = 2; + codecCtx_->channel_layout = AV_CH_LAYOUT_STEREO; + + AVDictionary *opts = 0; + av_dict_set(&opts, "channels", "2", 0); + av_dict_set(&opts, "sample_rate", "44100", 0); + if (avcodec_open2(codecCtx_, codec_, &opts) < 0) { + ERROR_LOG(ME, "Failed to open codec"); + return; + } + + av_dict_free(&opts); + + // Initializing the sample rate convert. We only really use it to convert float output + // into int. + int wanted_channels = 2; + int64_t wanted_channel_layout = av_get_default_channel_layout(wanted_channels); + int64_t dec_channel_layout = av_get_default_channel_layout(2); + + swrCtx_ = swr_alloc_set_opts( + swrCtx_, + wanted_channel_layout, + AV_SAMPLE_FMT_S16, + codecCtx_->sample_rate, + dec_channel_layout, + codecCtx_->sample_fmt, + codecCtx_->sample_rate, + 0, + NULL); + + if (!swrCtx_ || swr_init(swrCtx_) < 0) { + ERROR_LOG(ME, "swr_init: Failed to initialize the resampling context"); + avcodec_close(codecCtx_); + codec_ = 0; + return; + } +} + +SimpleMP3::~SimpleMP3() { + if (frame_) + av_frame_free(&frame_); + if (codecCtx_) + avcodec_close(codecCtx_); + codecCtx_ = 0; + codec_ = 0; + if (swrCtx_) + swr_free(&swrCtx_); +} + +// Input is a single MP3 packet. +bool SimpleMP3::Decode(void* inbuf, int inbytes, uint8_t *outbuf, int *outbytes) { +#ifdef USE_FFMPEG + AVPacket packet = { 0 }; + av_init_packet(&packet); + packet.data = static_cast(inbuf); + packet.size = inbytes; + + *outbytes = 0; + + int got_frame = 0; + av_frame_unref(frame_); + + int len = avcodec_decode_audio4(codecCtx_, frame_, &got_frame, &packet); + if (len < 0) { + ERROR_LOG(ME, "Error decoding Mp3 frame"); + // TODO: cleanup + return false; + } + + if (got_frame) { + int data_size = av_samples_get_buffer_size( + NULL, + codecCtx_->channels, + frame_->nb_samples, + codecCtx_->sample_fmt, 1); + int numSamples = frame_->nb_samples; + int swrRet = swr_convert(swrCtx_, &outbuf, numSamples, (const u8 **)frame_->extended_data, numSamples); + if (swrRet < 0) { + ERROR_LOG(ME, "swr_convert: Error while converting %d", swrRet); + return false; + } + // We always convert to stereo. + __AdjustBGMVolume((s16 *)outbuf, numSamples * 2); + } + + return true; +#else + // Zero bytes output. No need to memset. + *outbytes = 0; + return true; +#endif // USE_FFMPEG +} + +#endif // USE_FFMPEG + +// "C" wrapper + +SimpleMP3 *MP3Create() { +#ifdef USE_FFMPEG + avcodec_register_all(); + av_register_all(); + InitFFmpeg(); + + SimpleMP3 *MP3 = new SimpleMP3(); + if (!MP3->IsOK()) { + delete MP3; + return 0; + } + return MP3; +#else + return 0; +#endif // USE_FFMPEG +} + +bool MP3Decode(SimpleMP3 *ctx, void* inbuf, int inbytes, int *outbytes, uint8_t *outbuf) { +#ifdef USE_FFMPEG + return ctx->Decode(inbuf, inbytes, outbuf, outbytes); +#else + *outbytes = 0; + return true; +#endif +} + +void MP3Close(SimpleMP3 **ctx) { +#ifdef USE_FFMPEG + delete *ctx; + *ctx = 0; +#endif // USE_FFMPEG +} diff --git a/Core/HW/SimpleMp3Dec.h b/Core/HW/SimpleMp3Dec.h new file mode 100644 index 0000000000..7b0aa1022f --- /dev/null +++ b/Core/HW/SimpleMp3Dec.h @@ -0,0 +1,39 @@ +// Copyright (c) 2013- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include + +#include "base/basictypes.h" + +// Wraps FFMPEG in a nice interface that's drop-in compatible with +// the old one. Decodes packet by packet - does NOT demux. That's done by +// MpegDemux. Only decodes Atrac3+, not regular Atrac3. + +// Based on http://ffmpeg.org/doxygen/trunk/doc_2examples_2decoding_encoding_8c-example.html#_a13 + +// Ideally, Maxim's Atrac3+ decoder would be available as a standalone library +// that we could link, as that would be totally sufficient for the use case here. +// However, it will be maintained as a part of FFMPEG so that's the way we'll go +// for simplicity and sanity. + +struct SimpleMP3; + +SimpleMP3 *MP3Create(); +bool MP3Decode(SimpleMP3 *ctx, void* inbuf, int inbytes, int *outbytes, uint8_t* outbuf); +void MP3Close(SimpleMP3 **ctx);