2014-03-23 19:45:08 +01:00
|
|
|
// 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/.
|
|
|
|
|
|
2014-04-12 23:16:38 +02:00
|
|
|
#include <algorithm>
|
2014-06-23 22:51:22 +02:00
|
|
|
|
2020-08-10 00:12:51 -07:00
|
|
|
#include "Common/Serialize/SerializeFuncs.h"
|
2014-04-12 17:16:31 +02:00
|
|
|
#include "Core/Config.h"
|
2014-04-11 15:09:31 +02:00
|
|
|
#include "Core/HLE/FunctionWrappers.h"
|
2014-03-23 19:45:08 +01:00
|
|
|
#include "Core/HW/SimpleAudioDec.h"
|
|
|
|
|
#include "Core/HW/MediaEngine.h"
|
|
|
|
|
#include "Core/HW/BufferQueue.h"
|
|
|
|
|
|
|
|
|
|
#ifdef USE_FFMPEG
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
2015-02-03 17:46:51 -02:00
|
|
|
#include "libavformat/avformat.h"
|
|
|
|
|
#include "libswresample/swresample.h"
|
|
|
|
|
#include "libavutil/samplefmt.h"
|
2014-03-23 19:45:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // USE_FFMPEG
|
|
|
|
|
|
2015-01-18 12:59:26 -08:00
|
|
|
int SimpleAudio::GetAudioCodecID(int audioType) {
|
2014-03-23 19:45:08 +01:00
|
|
|
#ifdef USE_FFMPEG
|
2014-05-29 08:30:00 -07:00
|
|
|
switch (audioType) {
|
2014-03-23 19:45:08 +01:00
|
|
|
case PSP_CODEC_AAC:
|
2015-01-18 12:59:26 -08:00
|
|
|
return AV_CODEC_ID_AAC;
|
2014-03-23 19:45:08 +01:00
|
|
|
case PSP_CODEC_AT3:
|
2015-01-18 12:59:26 -08:00
|
|
|
return AV_CODEC_ID_ATRAC3;
|
2014-03-23 19:45:08 +01:00
|
|
|
case PSP_CODEC_AT3PLUS:
|
2015-01-18 12:59:26 -08:00
|
|
|
return AV_CODEC_ID_ATRAC3P;
|
2014-03-23 19:45:08 +01:00
|
|
|
case PSP_CODEC_MP3:
|
2015-01-18 12:59:26 -08:00
|
|
|
return AV_CODEC_ID_MP3;
|
2014-03-23 19:45:08 +01:00
|
|
|
default:
|
2015-01-18 12:59:26 -08:00
|
|
|
return AV_CODEC_ID_NONE;
|
2014-03-23 19:45:08 +01:00
|
|
|
}
|
|
|
|
|
#else
|
2015-01-18 12:59:26 -08:00
|
|
|
return 0;
|
2014-03-23 19:45:08 +01:00
|
|
|
#endif // USE_FFMPEG
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-22 18:33:09 +02:00
|
|
|
SimpleAudio::SimpleAudio(int audioType, int sample_rate, int channels)
|
2015-01-18 12:59:26 -08:00
|
|
|
: ctxPtr(0xFFFFFFFF), audioType(audioType), sample_rate_(sample_rate), channels_(channels),
|
|
|
|
|
outSamples(0), srcPos(0), wanted_resample_freq(44100), frame_(0), codec_(0), codecCtx_(0), swrCtx_(0),
|
2015-10-10 18:37:15 -07:00
|
|
|
codecOpen_(false) {
|
2014-05-29 08:30:00 -07:00
|
|
|
Init();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SimpleAudio::Init() {
|
2014-03-23 19:45:08 +01:00
|
|
|
#ifdef USE_FFMPEG
|
2014-03-29 19:36:53 +01:00
|
|
|
avcodec_register_all();
|
|
|
|
|
av_register_all();
|
|
|
|
|
InitFFmpeg();
|
|
|
|
|
|
2014-03-23 19:45:08 +01:00
|
|
|
frame_ = av_frame_alloc();
|
|
|
|
|
|
2014-04-11 15:09:31 +02:00
|
|
|
// Get Audio Codec ctx
|
2015-01-18 12:59:26 -08:00
|
|
|
int audioCodecId = GetAudioCodecID(audioType);
|
|
|
|
|
if (!audioCodecId) {
|
2014-03-23 19:45:08 +01:00
|
|
|
ERROR_LOG(ME, "This version of FFMPEG does not support Audio codec type: %08x. Update your submodule.", audioType);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Find decoder
|
2014-06-22 14:23:06 +02:00
|
|
|
codec_ = avcodec_find_decoder((AVCodecID)audioCodecId);
|
2014-03-23 19:45:08 +01:00
|
|
|
if (!codec_) {
|
|
|
|
|
// Eh, we shouldn't even have managed to compile. But meh.
|
2014-04-11 15:09:31 +02:00
|
|
|
ERROR_LOG(ME, "This version of FFMPEG does not support AV_CODEC_ctx for audio (%s). Update your submodule.", GetCodecName(audioType));
|
2014-03-23 19:45:08 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Allocate codec context
|
|
|
|
|
codecCtx_ = avcodec_alloc_context3(codec_);
|
|
|
|
|
if (!codecCtx_) {
|
|
|
|
|
ERROR_LOG(ME, "Failed to allocate a codec context");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-06-22 18:33:09 +02:00
|
|
|
codecCtx_->channels = channels_;
|
|
|
|
|
codecCtx_->channel_layout = channels_ == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;
|
|
|
|
|
codecCtx_->sample_rate = sample_rate_;
|
2015-01-18 12:59:26 -08:00
|
|
|
codecOpen_ = false;
|
2014-06-22 09:55:14 -07:00
|
|
|
#endif // USE_FFMPEG
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-18 12:59:26 -08:00
|
|
|
bool SimpleAudio::OpenCodec(int block_align) {
|
2014-06-22 09:55:14 -07:00
|
|
|
#ifdef USE_FFMPEG
|
2015-01-18 12:59:26 -08:00
|
|
|
// Some versions of FFmpeg require this set. May be set in SetExtraData(), but optional.
|
|
|
|
|
// When decoding, we decode by packet, so we know the size.
|
|
|
|
|
if (codecCtx_->block_align == 0) {
|
|
|
|
|
codecCtx_->block_align = block_align;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-23 19:45:08 +01:00
|
|
|
AVDictionary *opts = 0;
|
2014-06-23 21:42:31 +02:00
|
|
|
int retval = avcodec_open2(codecCtx_, codec_, &opts);
|
|
|
|
|
if (retval < 0) {
|
2014-06-23 22:51:22 +02:00
|
|
|
ERROR_LOG(ME, "Failed to open codec: retval = %i", retval);
|
2014-03-23 19:45:08 +01:00
|
|
|
}
|
|
|
|
|
av_dict_free(&opts);
|
2015-01-18 12:59:26 -08:00
|
|
|
codecOpen_ = true;
|
2014-06-23 21:42:31 +02:00
|
|
|
return retval >= 0;
|
2015-01-18 12:59:26 -08:00
|
|
|
#else
|
|
|
|
|
return false;
|
|
|
|
|
#endif // USE_FFMPEG
|
2014-03-23 19:45:08 +01:00
|
|
|
}
|
|
|
|
|
|
2014-06-22 09:55:14 -07:00
|
|
|
void SimpleAudio::SetExtraData(u8 *data, int size, int wav_bytes_per_packet) {
|
|
|
|
|
#ifdef USE_FFMPEG
|
|
|
|
|
if (codecCtx_) {
|
2015-10-10 18:37:15 -07:00
|
|
|
codecCtx_->extradata = (uint8_t *)av_mallocz(size);
|
2014-06-22 09:55:14 -07:00
|
|
|
codecCtx_->extradata_size = size;
|
|
|
|
|
codecCtx_->block_align = wav_bytes_per_packet;
|
2015-01-18 12:59:26 -08:00
|
|
|
codecOpen_ = false;
|
2015-10-10 18:37:15 -07:00
|
|
|
|
|
|
|
|
if (data != nullptr) {
|
|
|
|
|
memcpy(codecCtx_->extradata, data, size);
|
|
|
|
|
}
|
2014-06-22 09:55:14 -07:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-21 23:19:03 -08:00
|
|
|
void SimpleAudio::SetChannels(int channels) {
|
|
|
|
|
if (channels_ == channels) {
|
|
|
|
|
// Do nothing, already set.
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-02-24 18:59:41 +01:00
|
|
|
#ifdef USE_FFMPEG
|
2016-01-21 23:19:03 -08:00
|
|
|
|
|
|
|
|
if (codecOpen_) {
|
|
|
|
|
ERROR_LOG(ME, "Codec already open, cannot change channels");
|
|
|
|
|
} else {
|
|
|
|
|
channels_ = channels;
|
|
|
|
|
codecCtx_->channels = channels_;
|
|
|
|
|
codecCtx_->channel_layout = channels_ == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;
|
|
|
|
|
}
|
2017-02-24 18:59:41 +01:00
|
|
|
#endif
|
2016-01-21 23:19:03 -08:00
|
|
|
}
|
|
|
|
|
|
2014-03-23 19:45:08 +01:00
|
|
|
SimpleAudio::~SimpleAudio() {
|
|
|
|
|
#ifdef USE_FFMPEG
|
2015-10-10 18:37:15 -07:00
|
|
|
swr_free(&swrCtx_);
|
|
|
|
|
av_frame_free(&frame_);
|
2015-10-10 19:44:39 -07:00
|
|
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(55, 52, 0)
|
2015-10-10 18:37:15 -07:00
|
|
|
avcodec_free_context(&codecCtx_);
|
2015-10-10 19:44:39 -07:00
|
|
|
#else
|
|
|
|
|
// Future versions may add other things to free, but avcodec_free_context didn't exist yet here.
|
|
|
|
|
avcodec_close(codecCtx_);
|
|
|
|
|
av_freep(&codecCtx_->extradata);
|
|
|
|
|
av_freep(&codecCtx_->subtitle_header);
|
|
|
|
|
av_freep(&codecCtx_);
|
|
|
|
|
#endif
|
2014-03-23 19:45:08 +01:00
|
|
|
codec_ = 0;
|
|
|
|
|
#endif // USE_FFMPEG
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-22 14:01:23 +02:00
|
|
|
bool SimpleAudio::IsOK() const {
|
|
|
|
|
#ifdef USE_FFMPEG
|
|
|
|
|
return codec_ != 0;
|
|
|
|
|
#else
|
|
|
|
|
return 0;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-18 12:59:26 -08:00
|
|
|
bool SimpleAudio::Decode(void *inbuf, int inbytes, uint8_t *outbuf, int *outbytes) {
|
2014-03-23 19:45:08 +01:00
|
|
|
#ifdef USE_FFMPEG
|
2015-01-18 12:59:26 -08:00
|
|
|
if (!codecOpen_) {
|
|
|
|
|
OpenCodec(inbytes);
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-08 01:09:28 +02:00
|
|
|
AVPacket packet;
|
2014-03-23 19:45:08 +01:00
|
|
|
av_init_packet(&packet);
|
|
|
|
|
packet.data = static_cast<uint8_t *>(inbuf);
|
|
|
|
|
packet.size = inbytes;
|
|
|
|
|
|
|
|
|
|
int got_frame = 0;
|
|
|
|
|
av_frame_unref(frame_);
|
2014-04-11 15:09:31 +02:00
|
|
|
|
|
|
|
|
*outbytes = 0;
|
2014-04-12 02:12:25 +02:00
|
|
|
srcPos = 0;
|
|
|
|
|
int len = avcodec_decode_audio4(codecCtx_, frame_, &got_frame, &packet);
|
2016-05-28 10:11:53 -07:00
|
|
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 12, 100)
|
|
|
|
|
av_packet_unref(&packet);
|
|
|
|
|
#else
|
|
|
|
|
av_free_packet(&packet);
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-04-12 02:12:25 +02:00
|
|
|
if (len < 0) {
|
2014-06-22 18:33:09 +02:00
|
|
|
ERROR_LOG(ME, "Error decoding Audio frame (%i bytes): %i (%08x)", inbytes, len, len);
|
2014-03-23 19:45:08 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2014-04-12 02:12:25 +02:00
|
|
|
|
|
|
|
|
// get bytes consumed in source
|
|
|
|
|
srcPos = len;
|
2014-04-11 15:09:31 +02:00
|
|
|
|
2014-03-23 19:45:08 +01:00
|
|
|
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
|
|
|
|
|
int64_t dec_channel_layout = frame_->channel_layout; // decoded channel layout
|
|
|
|
|
|
2014-06-22 17:50:47 +02:00
|
|
|
if (!swrCtx_) {
|
|
|
|
|
swrCtx_ = swr_alloc_set_opts(
|
|
|
|
|
swrCtx_,
|
|
|
|
|
wanted_channel_layout,
|
|
|
|
|
AV_SAMPLE_FMT_S16,
|
|
|
|
|
wanted_resample_freq,
|
|
|
|
|
dec_channel_layout,
|
|
|
|
|
codecCtx_->sample_fmt,
|
|
|
|
|
codecCtx_->sample_rate,
|
|
|
|
|
0,
|
|
|
|
|
NULL);
|
2014-03-23 19:45:08 +01:00
|
|
|
|
2014-06-22 17:50:47 +02:00
|
|
|
if (!swrCtx_ || swr_init(swrCtx_) < 0) {
|
|
|
|
|
ERROR_LOG(ME, "swr_init: Failed to initialize the resampling context");
|
|
|
|
|
avcodec_close(codecCtx_);
|
|
|
|
|
codec_ = 0;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-03-23 19:45:08 +01:00
|
|
|
}
|
2014-06-22 17:50:47 +02:00
|
|
|
|
2014-03-23 19:45:08 +01:00
|
|
|
// convert audio to AV_SAMPLE_FMT_S16
|
|
|
|
|
int swrRet = swr_convert(swrCtx_, &outbuf, frame_->nb_samples, (const u8 **)frame_->extended_data, frame_->nb_samples);
|
|
|
|
|
if (swrRet < 0) {
|
2014-06-23 21:42:31 +02:00
|
|
|
ERROR_LOG(ME, "swr_convert: Error while converting: %d", swrRet);
|
2014-03-23 19:45:08 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2014-04-08 01:09:28 +02:00
|
|
|
// output samples per frame, we should *2 since we have two channels
|
2014-04-11 15:09:31 +02:00
|
|
|
outSamples = swrRet * 2;
|
2014-04-08 01:09:28 +02:00
|
|
|
|
|
|
|
|
// each sample occupies 2 bytes
|
|
|
|
|
*outbytes = outSamples * 2;
|
2014-03-23 19:45:08 +01:00
|
|
|
|
2014-04-08 01:09:28 +02:00
|
|
|
// Save outbuf into pcm audio, you can uncomment this line to save and check the decoded audio into pcm file.
|
|
|
|
|
// SaveAudio("dump.pcm", outbuf, *outbytes);
|
|
|
|
|
}
|
2014-03-23 19:45:08 +01:00
|
|
|
return true;
|
|
|
|
|
#else
|
|
|
|
|
// Zero bytes output. No need to memset.
|
|
|
|
|
*outbytes = 0;
|
|
|
|
|
return true;
|
|
|
|
|
#endif // USE_FFMPEG
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-18 12:59:26 -08:00
|
|
|
int SimpleAudio::GetOutSamples() {
|
2014-04-11 15:09:31 +02:00
|
|
|
return outSamples;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-18 12:59:26 -08:00
|
|
|
int SimpleAudio::GetSourcePos() {
|
2014-04-11 15:09:31 +02:00
|
|
|
return srcPos;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-23 19:45:08 +01:00
|
|
|
void AudioClose(SimpleAudio **ctx) {
|
|
|
|
|
#ifdef USE_FFMPEG
|
|
|
|
|
delete *ctx;
|
|
|
|
|
*ctx = 0;
|
|
|
|
|
#endif // USE_FFMPEG
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-22 14:01:23 +02:00
|
|
|
|
|
|
|
|
static const char *const codecNames[4] = {
|
|
|
|
|
"AT3+", "AT3", "MP3", "AAC",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char *GetCodecName(int codec) {
|
|
|
|
|
if (codec >= PSP_CODEC_AT3PLUS && codec <= PSP_CODEC_AAC) {
|
|
|
|
|
return codecNames[codec - PSP_CODEC_AT3PLUS];
|
|
|
|
|
} else {
|
|
|
|
|
return "(unk)";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
bool IsValidCodec(int codec){
|
2014-03-23 19:45:08 +01:00
|
|
|
if (codec >= PSP_CODEC_AT3PLUS && codec <= PSP_CODEC_AAC) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2014-04-08 01:09:28 +02:00
|
|
|
|
2014-04-11 15:09:31 +02:00
|
|
|
|
|
|
|
|
// sceAu module starts from here
|
|
|
|
|
|
2014-06-22 14:01:23 +02:00
|
|
|
AuCtx::AuCtx() {
|
|
|
|
|
decoder = NULL;
|
|
|
|
|
startPos = 0;
|
|
|
|
|
endPos = 0;
|
|
|
|
|
LoopNum = -1;
|
|
|
|
|
AuBuf = 0;
|
|
|
|
|
AuBufSize = 2048;
|
|
|
|
|
PCMBuf = 0;
|
|
|
|
|
PCMBufSize = 2048;
|
|
|
|
|
AuBufAvailable = 0;
|
|
|
|
|
SumDecodedSamples = 0;
|
|
|
|
|
askedReadSize = 0;
|
|
|
|
|
audioType = 0;
|
|
|
|
|
FrameNum = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
AuCtx::~AuCtx(){
|
|
|
|
|
if (decoder){
|
|
|
|
|
AudioClose(&decoder);
|
|
|
|
|
decoder = NULL;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2019-04-28 08:35:35 -07:00
|
|
|
size_t AuCtx::FindNextMp3Sync() {
|
|
|
|
|
if (audioType != PSP_CODEC_MP3) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sourcebuff.size() - 2; ++i) {
|
|
|
|
|
if ((sourcebuff[i] & 0xFF) == 0xFF && (sourcebuff[i + 1] & 0xC0) == 0xC0) {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-11 15:09:31 +02:00
|
|
|
// return output pcm size, <0 error
|
2019-04-28 08:35:35 -07:00
|
|
|
u32 AuCtx::AuDecode(u32 pcmAddr) {
|
2014-04-11 15:09:31 +02:00
|
|
|
if (!Memory::IsValidAddress(pcmAddr)){
|
|
|
|
|
ERROR_LOG(ME, "%s: output bufferAddress %08x is invalctx", __FUNCTION__, pcmAddr);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto outbuf = Memory::GetPointer(PCMBuf);
|
2019-04-27 07:54:58 -07:00
|
|
|
int outpcmbufsize = 0;
|
2014-04-11 15:09:31 +02:00
|
|
|
|
2019-04-27 07:54:58 -07:00
|
|
|
// Decode a single frame in sourcebuff and output into PCMBuf.
|
|
|
|
|
if (!sourcebuff.empty()) {
|
2019-04-28 08:35:35 -07:00
|
|
|
// FFmpeg doesn't seem to search for a sync for us, so let's do that.
|
|
|
|
|
int nextSync = (int)FindNextMp3Sync();
|
|
|
|
|
decoder->Decode(&sourcebuff[nextSync], (int)sourcebuff.size() - nextSync, outbuf, &outpcmbufsize);
|
|
|
|
|
|
2019-04-27 07:54:58 -07:00
|
|
|
if (outpcmbufsize == 0) {
|
2019-04-28 11:57:59 -07:00
|
|
|
// Nothing was output, hopefully we're at the end of the stream.
|
2014-04-13 14:38:42 +02:00
|
|
|
AuBufAvailable = 0;
|
|
|
|
|
sourcebuff.clear();
|
2019-04-27 07:54:58 -07:00
|
|
|
} else {
|
2019-04-28 07:30:56 -07:00
|
|
|
// Update our total decoded samples, but don't count stereo.
|
|
|
|
|
SumDecodedSamples += decoder->GetOutSamples() / 2;
|
2019-04-27 07:54:58 -07:00
|
|
|
// get consumed source length
|
2019-04-28 08:35:35 -07:00
|
|
|
int srcPos = decoder->GetSourcePos() + nextSync;
|
2019-04-27 07:54:58 -07:00
|
|
|
// remove the consumed source
|
2019-04-28 11:56:39 -07:00
|
|
|
if (srcPos > 0)
|
|
|
|
|
sourcebuff.erase(sourcebuff.begin(), sourcebuff.begin() + srcPos);
|
2019-04-27 07:54:58 -07:00
|
|
|
// 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;
|
2014-04-11 15:09:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-28 07:55:29 -07:00
|
|
|
|
2019-04-28 11:57:59 -07:00
|
|
|
bool end = readPos - AuBufAvailable >= (int64_t)endPos;
|
|
|
|
|
if (end && LoopNum != 0) {
|
2019-04-28 07:55:29 -07:00
|
|
|
// When looping, start the sum back off at zero and reset readPos to the start.
|
|
|
|
|
SumDecodedSamples = 0;
|
|
|
|
|
readPos = startPos;
|
|
|
|
|
if (LoopNum > 0)
|
|
|
|
|
LoopNum--;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-28 11:57:59 -07:00
|
|
|
if (outpcmbufsize == 0 && !end) {
|
|
|
|
|
outpcmbufsize = MaxOutputSample * 4;
|
|
|
|
|
memset(outbuf, 0, PCMBufSize);
|
|
|
|
|
} else if ((u32)outpcmbufsize < PCMBufSize) {
|
|
|
|
|
// TODO: We probably should use a rolling buffer instead.
|
|
|
|
|
memset(outbuf + outpcmbufsize, 0, PCMBufSize - outpcmbufsize);
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-11 15:09:31 +02:00
|
|
|
Memory::Write_U32(PCMBuf, pcmAddr);
|
|
|
|
|
return outpcmbufsize;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-12 02:12:25 +02:00
|
|
|
u32 AuCtx::AuGetLoopNum()
|
2014-04-11 15:09:31 +02:00
|
|
|
{
|
|
|
|
|
return LoopNum;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-12 02:12:25 +02:00
|
|
|
u32 AuCtx::AuSetLoopNum(int loop)
|
2014-04-11 15:09:31 +02:00
|
|
|
{
|
|
|
|
|
LoopNum = loop;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return 1 to read more data stream, 0 don't read
|
2019-04-23 20:42:13 -07:00
|
|
|
int AuCtx::AuCheckStreamDataNeeded() {
|
|
|
|
|
// If we would ask for bytes, then some are needed.
|
|
|
|
|
if (AuStreamBytesNeeded() != 0) {
|
2014-04-11 15:09:31 +02:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 20:42:13 -07:00
|
|
|
int AuCtx::AuStreamBytesNeeded() {
|
|
|
|
|
if (audioType == PSP_CODEC_MP3) {
|
|
|
|
|
// The endPos and readPos are not considered, except when you've read to the end.
|
|
|
|
|
if (readPos >= endPos)
|
|
|
|
|
return 0;
|
|
|
|
|
// Account for the workarea.
|
2019-04-23 20:49:45 -07:00
|
|
|
int offset = AuStreamWorkareaSize();
|
2019-04-23 20:42:13 -07:00
|
|
|
return (int)AuBufSize - AuBufAvailable - offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Untested. Maybe similar to MP3.
|
|
|
|
|
return std::min((int)AuBufSize - AuBufAvailable, (int)endPos - readPos);
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-23 20:49:45 -07:00
|
|
|
int AuCtx::AuStreamWorkareaSize() {
|
|
|
|
|
// Note that this is 31 bytes more than the max layer 3 frame size.
|
|
|
|
|
if (audioType == PSP_CODEC_MP3)
|
|
|
|
|
return 0x05c0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-11 15:09:31 +02:00
|
|
|
// check how many bytes we have read from source file
|
2019-04-23 20:18:16 -07:00
|
|
|
u32 AuCtx::AuNotifyAddStreamData(int size) {
|
2019-04-23 20:49:45 -07:00
|
|
|
int offset = AuStreamWorkareaSize();
|
|
|
|
|
|
2019-04-23 20:18:16 -07:00
|
|
|
if (askedReadSize != 0) {
|
|
|
|
|
// Old save state, numbers already adjusted.
|
|
|
|
|
int diffsize = size - askedReadSize;
|
|
|
|
|
// Notify the real read size
|
|
|
|
|
if (diffsize != 0) {
|
|
|
|
|
readPos += diffsize;
|
|
|
|
|
AuBufAvailable += diffsize;
|
|
|
|
|
}
|
|
|
|
|
askedReadSize = 0;
|
|
|
|
|
} else {
|
|
|
|
|
readPos += size;
|
|
|
|
|
AuBufAvailable += size;
|
2014-04-12 14:54:25 +02:00
|
|
|
}
|
2014-04-11 15:09:31 +02:00
|
|
|
|
2019-04-23 20:18:16 -07:00
|
|
|
if (Memory::IsValidRange(AuBuf, size)) {
|
2019-04-28 11:56:39 -07:00
|
|
|
sourcebuff.resize(sourcebuff.size() + size);
|
|
|
|
|
Memory::MemcpyUnchecked(&sourcebuff[sourcebuff.size() - size], AuBuf + offset, size);
|
2019-04-23 20:18:16 -07:00
|
|
|
}
|
2014-04-12 23:16:38 +02:00
|
|
|
|
2014-04-11 15:09:31 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// read from stream position srcPos of size bytes into buff
|
2014-04-12 14:54:25 +02:00
|
|
|
// buff, size and srcPos are all pointers
|
2019-04-23 20:18:16 -07:00
|
|
|
u32 AuCtx::AuGetInfoToAddStreamData(u32 bufPtr, u32 sizePtr, u32 srcPosPtr) {
|
2019-04-23 20:42:13 -07:00
|
|
|
int readsize = AuStreamBytesNeeded();
|
2019-04-23 20:49:45 -07:00
|
|
|
int offset = AuStreamWorkareaSize();
|
2014-04-12 23:16:38 +02:00
|
|
|
|
2014-06-22 14:01:23 +02:00
|
|
|
// we can recharge AuBuf from its beginning
|
2019-04-23 20:18:16 -07:00
|
|
|
if (readsize != 0) {
|
|
|
|
|
if (Memory::IsValidAddress(bufPtr))
|
2019-04-23 20:42:13 -07:00
|
|
|
Memory::Write_U32(AuBuf + offset, bufPtr);
|
2019-04-23 20:18:16 -07:00
|
|
|
if (Memory::IsValidAddress(sizePtr))
|
|
|
|
|
Memory::Write_U32(readsize, sizePtr);
|
|
|
|
|
if (Memory::IsValidAddress(srcPosPtr))
|
|
|
|
|
Memory::Write_U32(readPos, srcPosPtr);
|
|
|
|
|
} else {
|
|
|
|
|
if (Memory::IsValidAddress(bufPtr))
|
|
|
|
|
Memory::Write_U32(0, bufPtr);
|
|
|
|
|
if (Memory::IsValidAddress(sizePtr))
|
|
|
|
|
Memory::Write_U32(0, sizePtr);
|
|
|
|
|
if (Memory::IsValidAddress(srcPosPtr))
|
|
|
|
|
Memory::Write_U32(0, srcPosPtr);
|
|
|
|
|
}
|
2014-04-12 14:54:25 +02:00
|
|
|
|
2019-04-23 20:18:16 -07:00
|
|
|
// Just for old save states.
|
|
|
|
|
askedReadSize = 0;
|
2014-04-11 15:09:31 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-28 13:07:55 -07:00
|
|
|
u32 AuCtx::AuResetPlayPositionByFrame(int frame) {
|
|
|
|
|
// Note: this doesn't correctly handle padding or slot size, but the PSP doesn't either.
|
|
|
|
|
uint32_t bytesPerSecond = (MaxOutputSample / 8) * BitRate * 1000;
|
|
|
|
|
readPos = startPos + (frame * bytesPerSecond) / SamplingRate;
|
|
|
|
|
// Not sure why, but it seems to consistently seek 1 before, maybe in case it's off slightly.
|
|
|
|
|
if (frame != 0)
|
|
|
|
|
readPos -= 1;
|
|
|
|
|
SumDecodedSamples = frame * MaxOutputSample;
|
|
|
|
|
AuBufAvailable = 0;
|
|
|
|
|
sourcebuff.clear();
|
2014-04-11 15:09:31 +02:00
|
|
|
return 0;
|
Universal Audio Class
Based on my implementation in sceAac https://github.com/hrydgard/ppsspp/pull/5836
I've created a class AuCtx included in My SimpleAudioDec.cpp/.h which aims at providing a standard easy implementation to support all codecs in ffmpeg.
Here, I also completely re-code sceMp3 file with this class to give an example how to use this class, and it has solved all mp3 issues I've observed in the current master.
Tests on different freq and channels mp3 audios as:
Miku custom BGM (48kHz, stereo), Hanayaka Nari Wa ga Ichizoku(32kHz, mono, a little fast but better than before now), downstreet panic (44.1kHz, stereo), and learn jp09(44.1kHz, stero) are just all right.
Especially, I am very glad to see that Miku's Custom BGMs have no repetition issues in first tone any more and no longer stopped in the first second neither. :)
We will come into a new age to fast support new audio formats from now on I hope :P
2014-04-11 22:56:59 +02:00
|
|
|
}
|
|
|
|
|
|
2014-06-22 14:01:23 +02:00
|
|
|
u32 AuCtx::AuResetPlayPosition() {
|
|
|
|
|
readPos = startPos;
|
2019-04-28 11:59:07 -07:00
|
|
|
SumDecodedSamples = 0;
|
|
|
|
|
AuBufAvailable = 0;
|
|
|
|
|
sourcebuff.clear();
|
2014-06-22 14:01:23 +02:00
|
|
|
return 0;
|
Universal Audio Class
Based on my implementation in sceAac https://github.com/hrydgard/ppsspp/pull/5836
I've created a class AuCtx included in My SimpleAudioDec.cpp/.h which aims at providing a standard easy implementation to support all codecs in ffmpeg.
Here, I also completely re-code sceMp3 file with this class to give an example how to use this class, and it has solved all mp3 issues I've observed in the current master.
Tests on different freq and channels mp3 audios as:
Miku custom BGM (48kHz, stereo), Hanayaka Nari Wa ga Ichizoku(32kHz, mono, a little fast but better than before now), downstreet panic (44.1kHz, stereo), and learn jp09(44.1kHz, stero) are just all right.
Especially, I am very glad to see that Miku's Custom BGMs have no repetition issues in first tone any more and no longer stopped in the first second neither. :)
We will come into a new age to fast support new audio formats from now on I hope :P
2014-04-11 22:56:59 +02:00
|
|
|
}
|
2014-04-14 11:47:28 +02:00
|
|
|
|
2014-06-22 14:01:23 +02:00
|
|
|
void AuCtx::DoState(PointerWrap &p) {
|
|
|
|
|
auto s = p.Section("AuContext", 0, 1);
|
|
|
|
|
if (!s)
|
|
|
|
|
return;
|
|
|
|
|
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, startPos);
|
|
|
|
|
Do(p, endPos);
|
|
|
|
|
Do(p, AuBuf);
|
|
|
|
|
Do(p, AuBufSize);
|
|
|
|
|
Do(p, PCMBuf);
|
|
|
|
|
Do(p, PCMBufSize);
|
|
|
|
|
Do(p, freq);
|
|
|
|
|
Do(p, SumDecodedSamples);
|
|
|
|
|
Do(p, LoopNum);
|
|
|
|
|
Do(p, Channels);
|
|
|
|
|
Do(p, MaxOutputSample);
|
|
|
|
|
Do(p, readPos);
|
|
|
|
|
Do(p, audioType);
|
|
|
|
|
Do(p, BitRate);
|
|
|
|
|
Do(p, SamplingRate);
|
|
|
|
|
Do(p, askedReadSize);
|
2019-04-23 20:18:16 -07:00
|
|
|
int dummy = 0;
|
2020-08-09 21:20:42 -07:00
|
|
|
Do(p, dummy);
|
|
|
|
|
Do(p, FrameNum);
|
2014-06-22 14:01:23 +02:00
|
|
|
|
|
|
|
|
if (p.mode == p.MODE_READ) {
|
|
|
|
|
decoder = new SimpleAudio(audioType);
|
|
|
|
|
AuBufAvailable = 0; // reset to read from file at position readPos
|
|
|
|
|
}
|
2014-04-14 13:23:10 +02:00
|
|
|
}
|