Files
citra/src/audio_core/hle/aac_decoder.cpp

166 lines
5.7 KiB
C++

// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <neaacdec.h>
#include "audio_core/hle/aac_decoder.h"
namespace AudioCore::HLE {
AACDecoder::AACDecoder(Memory::MemorySystem& memory) : memory(memory) {
decoder = NeAACDecOpen();
if (decoder == nullptr) {
LOG_CRITICAL(Audio_DSP, "Could not open FAAD2 decoder.");
return;
}
auto config = NeAACDecGetCurrentConfiguration(decoder);
config->defObjectType = LC;
config->outputFormat = FAAD_FMT_16BIT;
if (!NeAACDecSetConfiguration(decoder, config)) {
LOG_CRITICAL(Audio_DSP, "Could not configure FAAD2 decoder.");
NeAACDecClose(decoder);
decoder = nullptr;
return;
}
LOG_INFO(Audio_DSP, "Created FAAD2 AAC decoder.");
}
AACDecoder::~AACDecoder() {
if (decoder) {
NeAACDecClose(decoder);
decoder = nullptr;
LOG_INFO(Audio_DSP, "Destroyed FAAD2 AAC decoder.");
}
}
BinaryMessage AACDecoder::ProcessRequest(const BinaryMessage& request) {
if (request.header.codec != DecoderCodec::DecodeAAC) {
LOG_ERROR(Audio_DSP, "AAC decoder received unsupported codec: {}",
static_cast<u16>(request.header.codec));
return {
.header =
{
.result = ResultStatus::Error,
},
};
}
switch (request.header.cmd) {
case DecoderCommand::Init: {
BinaryMessage response = request;
response.header.result = ResultStatus::Success;
return response;
}
case DecoderCommand::EncodeDecode: {
return Decode(request);
}
case DecoderCommand::Shutdown:
case DecoderCommand::SaveState:
case DecoderCommand::LoadState: {
LOG_WARNING(Audio_DSP, "Got unimplemented AAC binary request: {}",
static_cast<u16>(request.header.cmd));
BinaryMessage response = request;
response.header.result = ResultStatus::Success;
return response;
}
default:
LOG_ERROR(Audio_DSP, "Got unknown AAC binary request: {}",
static_cast<u16>(request.header.cmd));
return {
.header =
{
.result = ResultStatus::Error,
},
};
}
}
BinaryMessage AACDecoder::Decode(const BinaryMessage& request) {
BinaryMessage response{};
response.header.codec = request.header.codec;
response.header.cmd = request.header.cmd;
response.decode_aac_response.size = request.decode_aac_request.size;
// This is a hack to continue games when a failure occurs.
response.decode_aac_response.sample_rate = DecoderSampleRate::Rate48000;
response.decode_aac_response.num_channels = 2;
response.decode_aac_response.num_samples = 1024;
if (decoder == nullptr) {
return response;
}
if (request.decode_aac_request.src_addr < Memory::FCRAM_PADDR ||
request.decode_aac_request.src_addr + request.decode_aac_request.size >
Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) {
LOG_ERROR(Audio_DSP, "Got out of bounds src_addr {:08x}",
request.decode_aac_request.src_addr);
return response;
}
u8* data = memory.GetFCRAMPointer(request.decode_aac_request.src_addr - Memory::FCRAM_PADDR);
u32 data_len = request.decode_aac_request.size;
unsigned long sample_rate;
u8 num_channels;
auto init_result = NeAACDecInit(decoder, data, data_len, &sample_rate, &num_channels);
if (init_result < 0) {
LOG_ERROR(Audio_DSP, "Could not initialize FAAD2 AAC decoder for request: {}", init_result);
return response;
}
// Advance past the frame header if needed.
data += init_result;
data_len -= init_result;
std::array<std::vector<s16>, 2> out_streams;
while (data_len > 0) {
NeAACDecFrameInfo frame_info;
auto curr_sample_buffer =
static_cast<s16*>(NeAACDecDecode(decoder, &frame_info, data, data_len));
if (curr_sample_buffer == nullptr || frame_info.error != 0) {
LOG_ERROR(Audio_DSP, "Failed to decode AAC buffer using FAAD2: {}", frame_info.error);
return response;
}
// Split the decode result into channels.
u32 num_samples = frame_info.samples / frame_info.channels;
for (u32 sample = 0; sample < num_samples; sample++) {
for (u32 ch = 0; ch < frame_info.channels; ch++) {
out_streams[ch].push_back(curr_sample_buffer[(sample * frame_info.channels) + ch]);
}
}
data += frame_info.bytesconsumed;
data_len -= frame_info.bytesconsumed;
}
// Transfer the decoded buffer from vector to the FCRAM.
for (std::size_t ch = 0; ch < out_streams.size(); ch++) {
if (out_streams[ch].empty()) {
continue;
}
auto byte_size = out_streams[ch].size() * sizeof(s16);
auto dst = ch == 0 ? request.decode_aac_request.dst_addr_ch0
: request.decode_aac_request.dst_addr_ch1;
if (dst < Memory::FCRAM_PADDR ||
dst + byte_size > Memory::FCRAM_PADDR + Memory::FCRAM_SIZE) {
LOG_ERROR(Audio_DSP, "Got out of bounds dst_addr_ch{} {:08x}", ch, dst);
return response;
}
std::memcpy(memory.GetFCRAMPointer(dst - Memory::FCRAM_PADDR), out_streams[ch].data(),
byte_size);
}
// Set the output frame info.
response.decode_aac_response.sample_rate = GetSampleRateEnum(sample_rate);
response.decode_aac_response.num_channels = num_channels;
response.decode_aac_response.num_samples = static_cast<u32_le>(out_streams[0].size());
return response;
}
} // namespace AudioCore::HLE