CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Core/HW/SimpleAudioDec.cpp
Views: 1401
// Copyright (c) 2013- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617#include <algorithm>18#include <cmath>1920#include "Common/Serialize/SerializeFuncs.h"21#include "Core/Config.h"22#include "Core/Debugger/MemBlockInfo.h"23#include "Core/HLE/FunctionWrappers.h"24#include "Core/HW/SimpleAudioDec.h"25#include "Core/HW/MediaEngine.h"26#include "Core/HW/BufferQueue.h"27#include "Core/HW/Atrac3Standalone.h"2829#include "ext/minimp3/minimp3.h"3031#ifdef USE_FFMPEG3233extern "C" {34#include "libavformat/avformat.h"35#include "libswresample/swresample.h"36#include "libavutil/samplefmt.h"37#include "libavcodec/avcodec.h"38#include "libavutil/version.h"3940#include "Core/FFMPEGCompat.h"41}4243#else4445extern "C" {46struct AVCodec;47struct AVCodecContext;48struct SwrContext;49struct AVFrame;50}5152#endif // USE_FFMPEG5354// AAC decoder candidates:55// * https://github.com/mstorsjo/fdk-aac/tree/master5657// h.264 decoder candidates:58// * https://github.com/meerkat-cv/h264_decoder59// * https://github.com/shengbinmeng/ffmpeg-h264-dec6061// minimp3-based decoder.62class MiniMp3Audio : public AudioDecoder {63public:64MiniMp3Audio() {65mp3dec_init(&mp3_);66}67~MiniMp3Audio() {}6869bool Decode(const uint8_t* inbuf, int inbytes, int *inbytesConsumed, int outputChannels, int16_t *outbuf, int *outSamples) override {70_dbg_assert_(outputChannels == 2);7172mp3dec_frame_info_t info{};73int samplesWritten = mp3dec_decode_frame(&mp3_, inbuf, inbytes, (mp3d_sample_t *)outbuf, &info);74*inbytesConsumed = info.frame_bytes;75*outSamples = samplesWritten;76return true;77}7879bool IsOK() const override { return true; }80void SetChannels(int channels) override {81// Hmm. ignore for now.82}8384PSPAudioType GetAudioType() const override { return PSP_CODEC_MP3; }8586private:87// We use the lowest-level API.88mp3dec_t mp3_{};89};9091// FFMPEG-based decoder. TODO: Replace with individual codecs.92// Based on http://ffmpeg.org/doxygen/trunk/doc_2examples_2decoding_encoding_8c-example.html#_a1393class FFmpegAudioDecoder : public AudioDecoder {94public:95FFmpegAudioDecoder(PSPAudioType audioType, int sampleRateHz = 44100, int channels = 2);96~FFmpegAudioDecoder();9798bool Decode(const uint8_t* inbuf, int inbytes, int *inbytesConsumed, int outputChannels, int16_t *outbuf, int *outSamples) override;99bool IsOK() const override {100#ifdef USE_FFMPEG101return codec_ != 0;102#else103return 0;104#endif105}106107void SetChannels(int channels) override;108109// These two are only here because of save states.110PSPAudioType GetAudioType() const override { return audioType; }111112private:113bool OpenCodec(int block_align);114115PSPAudioType audioType;116int sample_rate_;117int channels_;118119AVFrame *frame_ = nullptr;120AVCodec *codec_ = nullptr;121AVCodecContext *codecCtx_ = nullptr;122SwrContext *swrCtx_ = nullptr;123124bool codecOpen_ = false;125};126127AudioDecoder *CreateAudioDecoder(PSPAudioType audioType, int sampleRateHz, int channels, size_t blockAlign, const uint8_t *extraData, size_t extraDataSize) {128switch (audioType) {129case PSP_CODEC_MP3:130return new MiniMp3Audio();131case PSP_CODEC_AT3:132return CreateAtrac3Audio(channels, blockAlign, extraData, extraDataSize);133case PSP_CODEC_AT3PLUS:134return CreateAtrac3PlusAudio(channels, blockAlign);135default:136// Only AAC falls back to FFMPEG now.137return new FFmpegAudioDecoder(audioType, sampleRateHz, channels);138}139}140141static int GetAudioCodecID(int audioType) {142#ifdef USE_FFMPEG143switch (audioType) {144case PSP_CODEC_AAC:145return AV_CODEC_ID_AAC;146case PSP_CODEC_AT3:147return AV_CODEC_ID_ATRAC3;148case PSP_CODEC_AT3PLUS:149return AV_CODEC_ID_ATRAC3P;150case PSP_CODEC_MP3:151return AV_CODEC_ID_MP3;152default:153return AV_CODEC_ID_NONE;154}155#else156return 0;157#endif // USE_FFMPEG158}159160FFmpegAudioDecoder::FFmpegAudioDecoder(PSPAudioType audioType, int sampleRateHz, int channels)161: audioType(audioType), sample_rate_(sampleRateHz), channels_(channels) {162163#ifdef USE_FFMPEG164#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 18, 100)165avcodec_register_all();166#endif167#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(58, 12, 100)168av_register_all();169#endif170InitFFmpeg();171172frame_ = av_frame_alloc();173174// Get AUDIO Codec ctx175int audioCodecId = GetAudioCodecID(audioType);176if (!audioCodecId) {177ERROR_LOG(Log::ME, "This version of FFMPEG does not support Audio codec type: %08x. Update your submodule.", audioType);178return;179}180// Find decoder181codec_ = avcodec_find_decoder((AVCodecID)audioCodecId);182if (!codec_) {183// Eh, we shouldn't even have managed to compile. But meh.184ERROR_LOG(Log::ME, "This version of FFMPEG does not support AV_CODEC_ctx for audio (%s). Update your submodule.", GetCodecName(audioType));185return;186}187// Allocate codec context188codecCtx_ = avcodec_alloc_context3(codec_);189if (!codecCtx_) {190ERROR_LOG(Log::ME, "Failed to allocate a codec context");191return;192}193codecCtx_->channels = channels_;194codecCtx_->channel_layout = channels_ == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;195codecCtx_->sample_rate = sample_rate_;196codecOpen_ = false;197#endif // USE_FFMPEG198}199200bool FFmpegAudioDecoder::OpenCodec(int block_align) {201#ifdef USE_FFMPEG202// Some versions of FFmpeg require this set. May be set in SetExtraData(), but optional.203// When decoding, we decode by packet, so we know the size.204if (codecCtx_->block_align == 0) {205codecCtx_->block_align = block_align;206}207208AVDictionary *opts = 0;209int retval = avcodec_open2(codecCtx_, codec_, &opts);210if (retval < 0) {211ERROR_LOG(Log::ME, "Failed to open codec: retval = %i", retval);212}213av_dict_free(&opts);214codecOpen_ = true;215return retval >= 0;216#else217return false;218#endif // USE_FFMPEG219}220221void FFmpegAudioDecoder::SetChannels(int channels) {222if (channels_ == channels) {223// Do nothing, already set.224return;225}226#ifdef USE_FFMPEG227228if (codecOpen_) {229ERROR_LOG(Log::ME, "Codec already open, cannot change channels");230} else {231channels_ = channels;232codecCtx_->channels = channels_;233codecCtx_->channel_layout = channels_ == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;234}235#endif236}237238FFmpegAudioDecoder::~FFmpegAudioDecoder() {239#ifdef USE_FFMPEG240swr_free(&swrCtx_);241av_frame_free(&frame_);242#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 52, 0)243avcodec_free_context(&codecCtx_);244#else245// Future versions may add other things to free, but avcodec_free_context didn't exist yet here.246avcodec_close(codecCtx_);247av_freep(&codecCtx_->extradata);248av_freep(&codecCtx_->subtitle_header);249av_freep(&codecCtx_);250#endif251codec_ = 0;252#endif // USE_FFMPEG253}254255// Decodes a single input frame.256bool FFmpegAudioDecoder::Decode(const uint8_t *inbuf, int inbytes, int *inbytesConsumed, int outputChannels, int16_t *outbuf, int *outSamples) {257#ifdef USE_FFMPEG258if (!codecOpen_) {259OpenCodec(inbytes);260}261262AVPacket packet;263av_init_packet(&packet);264packet.data = (uint8_t *)(inbuf);265packet.size = inbytes;266267int got_frame = 0;268av_frame_unref(frame_);269270if (outSamples) {271*outSamples = 0;272}273if (inbytesConsumed) {274*inbytesConsumed = 0;275}276#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 48, 101)277if (inbytes != 0) {278int err = avcodec_send_packet(codecCtx_, &packet);279if (err < 0) {280ERROR_LOG(Log::ME, "Error sending audio frame to decoder (%d bytes): %d (%08x)", inbytes, err, err);281return false;282}283}284int err = avcodec_receive_frame(codecCtx_, frame_);285int len = 0;286if (err >= 0) {287len = frame_->pkt_size;288got_frame = 1;289} else if (err != AVERROR(EAGAIN)) {290len = err;291}292#else293int len = avcodec_decode_audio4(codecCtx_, frame_, &got_frame, &packet);294#endif295#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)296av_packet_unref(&packet);297#else298av_free_packet(&packet);299#endif300301if (len < 0) {302ERROR_LOG(Log::ME, "Error decoding Audio frame (%i bytes): %i (%08x)", inbytes, len, len);303return false;304}305306// get bytes consumed in source307*inbytesConsumed = len;308309if (got_frame) {310// Initializing the sample rate convert. We will use it to convert float output into int.311_dbg_assert_(outputChannels == 2);312int64_t wanted_channel_layout = AV_CH_LAYOUT_STEREO; // we want stereo output layout313int64_t dec_channel_layout = frame_->channel_layout; // decoded channel layout314315if (!swrCtx_) {316swrCtx_ = swr_alloc_set_opts(317swrCtx_,318wanted_channel_layout,319AV_SAMPLE_FMT_S16,320codecCtx_->sample_rate,321dec_channel_layout,322codecCtx_->sample_fmt,323codecCtx_->sample_rate,3240,325NULL);326327if (!swrCtx_ || swr_init(swrCtx_) < 0) {328ERROR_LOG(Log::ME, "swr_init: Failed to initialize the resampling context");329avcodec_close(codecCtx_);330codec_ = 0;331return false;332}333}334335// convert audio to AV_SAMPLE_FMT_S16336int swrRet = 0;337if (outbuf != nullptr) {338swrRet = swr_convert(swrCtx_, (uint8_t **)&outbuf, frame_->nb_samples, (const u8 **)frame_->extended_data, frame_->nb_samples);339}340if (swrRet < 0) {341ERROR_LOG(Log::ME, "swr_convert: Error while converting: %d", swrRet);342return false;343}344// output stereo samples per frame345*outSamples = swrRet;346347// Save outbuf into pcm audio, you can uncomment this line to save and check the decoded audio into pcm file.348// SaveAudio("dump.pcm", outbuf, *outbytes);349}350return true;351#else352// Zero bytes output. No need to memset.353*outbytes = 0;354return true;355#endif // USE_FFMPEG356}357358void AudioClose(AudioDecoder **ctx) {359#ifdef USE_FFMPEG360delete *ctx;361*ctx = 0;362#endif // USE_FFMPEG363}364365void AudioClose(FFmpegAudioDecoder **ctx) {366#ifdef USE_FFMPEG367delete *ctx;368*ctx = 0;369#endif // USE_FFMPEG370}371372static const char *const codecNames[4] = {373"AT3+", "AT3", "MP3", "AAC",374};375376const char *GetCodecName(int codec) {377if (codec >= PSP_CODEC_AT3PLUS && codec <= PSP_CODEC_AAC) {378return codecNames[codec - PSP_CODEC_AT3PLUS];379} else {380return "(unk)";381}382};383384bool IsValidCodec(PSPAudioType codec){385if (codec >= PSP_CODEC_AT3PLUS && codec <= PSP_CODEC_AAC) {386return true;387}388return false;389}390391392// sceAu module starts from here393394AuCtx::AuCtx() {395}396397AuCtx::~AuCtx() {398if (decoder) {399AudioClose(&decoder);400decoder = nullptr;401}402}403404size_t AuCtx::FindNextMp3Sync() {405for (size_t i = 0; i < sourcebuff.size() - 2; ++i) {406if ((sourcebuff[i] & 0xFF) == 0xFF && (sourcebuff[i + 1] & 0xC0) == 0xC0) {407return i;408}409}410return 0;411}412413// return output pcm size, <0 error414u32 AuCtx::AuDecode(u32 pcmAddr) {415u32 outptr = PCMBuf + nextOutputHalf * PCMBufSize / 2;416auto outbuf = Memory::GetPointerWriteRange(outptr, PCMBufSize / 2);417int outpcmbufsize = 0;418419if (pcmAddr)420Memory::Write_U32(outptr, pcmAddr);421422// Decode a single frame in sourcebuff and output into PCMBuf.423if (!sourcebuff.empty()) {424// FFmpeg doesn't seem to search for a sync for us, so let's do that.425int nextSync = 0;426if (decoder->GetAudioType() == PSP_CODEC_MP3) {427nextSync = (int)FindNextMp3Sync();428}429int inbytesConsumed = 0;430int outSamples = 0;431decoder->Decode(&sourcebuff[nextSync], (int)sourcebuff.size() - nextSync, &inbytesConsumed, 2, (int16_t *)outbuf, &outSamples);432outpcmbufsize = outSamples * 2 * sizeof(int16_t);433434if (outpcmbufsize == 0) {435// Nothing was output, hopefully we're at the end of the stream.436AuBufAvailable = 0;437sourcebuff.clear();438} else {439// Update our total decoded samples, but don't count stereo.440SumDecodedSamples += outSamples;441// get consumed source length442int srcPos = inbytesConsumed + nextSync;443// remove the consumed source444if (srcPos > 0)445sourcebuff.erase(sourcebuff.begin(), sourcebuff.begin() + srcPos);446// reduce the available Aubuff size447// (the available buff size is now used to know if we can read again from file and how many to read)448AuBufAvailable -= srcPos;449}450}451452bool end = readPos - AuBufAvailable >= (int64_t)endPos;453if (end && LoopNum != 0) {454// When looping, start the sum back off at zero and reset readPos to the start.455SumDecodedSamples = 0;456readPos = startPos;457if (LoopNum > 0)458LoopNum--;459}460461if (outpcmbufsize == 0 && !end) {462// If we didn't decode anything, we fill this half of the buffer with zeros.463outpcmbufsize = PCMBufSize / 2;464if (outbuf != nullptr)465memset(outbuf, 0, outpcmbufsize);466} else if ((u32)outpcmbufsize < PCMBufSize) {467// TODO: Not sure it actually zeros this out.468if (outbuf != nullptr)469memset(outbuf + outpcmbufsize, 0, PCMBufSize / 2 - outpcmbufsize);470}471472if (outpcmbufsize != 0)473NotifyMemInfo(MemBlockFlags::WRITE, outptr, outpcmbufsize, "AuDecode");474475nextOutputHalf ^= 1;476return outpcmbufsize;477}478479// return 1 to read more data stream, 0 don't read480int AuCtx::AuCheckStreamDataNeeded() {481// If we would ask for bytes, then some are needed.482if (AuStreamBytesNeeded() > 0) {483return 1;484}485return 0;486}487488int AuCtx::AuStreamBytesNeeded() {489if (decoder->GetAudioType() == PSP_CODEC_MP3) {490// The endPos and readPos are not considered, except when you've read to the end.491if (readPos >= endPos)492return 0;493// Account for the workarea.494int offset = AuStreamWorkareaSize();495return (int)AuBufSize - AuBufAvailable - offset;496}497498// TODO: Untested. Maybe similar to MP3.499return std::min((int)AuBufSize - AuBufAvailable, (int)endPos - readPos);500}501502int AuCtx::AuStreamWorkareaSize() {503// Note that this is 31 bytes more than the max layer 3 frame size.504if (decoder->GetAudioType() == PSP_CODEC_MP3)505return 0x05c0;506return 0;507}508509// check how many bytes we have read from source file510u32 AuCtx::AuNotifyAddStreamData(int size) {511int offset = AuStreamWorkareaSize();512513if (askedReadSize != 0) {514// Old save state, numbers already adjusted.515int diffsize = size - askedReadSize;516// Notify the real read size517if (diffsize != 0) {518readPos += diffsize;519AuBufAvailable += diffsize;520}521askedReadSize = 0;522} else {523readPos += size;524AuBufAvailable += size;525}526527if (Memory::IsValidRange(AuBuf, size)) {528sourcebuff.resize(sourcebuff.size() + size);529Memory::MemcpyUnchecked(&sourcebuff[sourcebuff.size() - size], AuBuf + offset, size);530}531532return 0;533}534535// read from stream position srcPos of size bytes into buff536// buff, size and srcPos are all pointers537u32 AuCtx::AuGetInfoToAddStreamData(u32 bufPtr, u32 sizePtr, u32 srcPosPtr) {538int readsize = AuStreamBytesNeeded();539int offset = AuStreamWorkareaSize();540541// we can recharge AuBuf from its beginning542if (readsize != 0) {543if (Memory::IsValidAddress(bufPtr))544Memory::WriteUnchecked_U32(AuBuf + offset, bufPtr);545if (Memory::IsValidAddress(sizePtr))546Memory::WriteUnchecked_U32(readsize, sizePtr);547if (Memory::IsValidAddress(srcPosPtr))548Memory::WriteUnchecked_U32(readPos, srcPosPtr);549} else {550if (Memory::IsValidAddress(bufPtr))551Memory::WriteUnchecked_U32(0, bufPtr);552if (Memory::IsValidAddress(sizePtr))553Memory::WriteUnchecked_U32(0, sizePtr);554if (Memory::IsValidAddress(srcPosPtr))555Memory::WriteUnchecked_U32(0, srcPosPtr);556}557558// Just for old save states.559askedReadSize = 0;560return 0;561}562563u32 AuCtx::AuResetPlayPositionByFrame(int frame) {564// Note: this doesn't correctly handle padding or slot size, but the PSP doesn't either.565uint32_t bytesPerSecond = (MaxOutputSample / 8) * BitRate * 1000;566readPos = startPos + (frame * bytesPerSecond) / SamplingRate;567// Not sure why, but it seems to consistently seek 1 before, maybe in case it's off slightly.568if (frame != 0)569readPos -= 1;570SumDecodedSamples = frame * MaxOutputSample;571AuBufAvailable = 0;572sourcebuff.clear();573return 0;574}575576u32 AuCtx::AuResetPlayPosition() {577readPos = startPos;578SumDecodedSamples = 0;579AuBufAvailable = 0;580sourcebuff.clear();581return 0;582}583584void AuCtx::DoState(PointerWrap &p) {585auto s = p.Section("AuContext", 0, 2);586if (!s)587return;588589Do(p, startPos);590Do(p, endPos);591Do(p, AuBuf);592Do(p, AuBufSize);593Do(p, PCMBuf);594Do(p, PCMBufSize);595Do(p, freq);596Do(p, SumDecodedSamples);597Do(p, LoopNum);598Do(p, Channels);599Do(p, MaxOutputSample);600Do(p, readPos);601int audioType = decoder ? (int)decoder->GetAudioType() : 0;602Do(p, audioType);603Do(p, BitRate);604Do(p, SamplingRate);605Do(p, askedReadSize);606int dummy = 0;607Do(p, dummy);608Do(p, FrameNum);609610if (s < 2) {611AuBufAvailable = 0;612Version = 3;613} else {614Do(p, Version);615Do(p, AuBufAvailable);616Do(p, sourcebuff);617Do(p, nextOutputHalf);618}619620if (p.mode == p.MODE_READ) {621decoder = CreateAudioDecoder((PSPAudioType)audioType);622}623}624625626