From 75ef95b1b80189b92a3748b34a2f36cf599d099b Mon Sep 17 00:00:00 2001 From: gibbed Date: Sun, 21 Jun 2015 08:33:49 -0500 Subject: [PATCH] More XMA decoder cleanup. --- src/xenia/apu/xma_context.cc | 90 ++++++++++++++++++------------------ src/xenia/apu/xma_context.h | 39 ++++++++++++---- src/xenia/apu/xma_decoder.h | 3 -- 3 files changed, 74 insertions(+), 58 deletions(-) diff --git a/src/xenia/apu/xma_context.cc b/src/xenia/apu/xma_context.cc index 7d6257cc8..d9eef95aa 100644 --- a/src/xenia/apu/xma_context.cc +++ b/src/xenia/apu/xma_context.cc @@ -17,6 +17,7 @@ extern "C" { #include "libavcodec/avcodec.h" +#include "libavutil/channel_layout.h" } // Credits for most of this code goes to: @@ -26,9 +27,11 @@ namespace xe { namespace apu { XmaContext::XmaContext() - : guest_ptr_(0) + : id_(0) + , guest_ptr_(0) , is_allocated_(false) , is_enabled_(false) + , decoding_packet_(false) , codec_(nullptr) , context_(nullptr) , decoded_frame_(nullptr) @@ -36,9 +39,6 @@ XmaContext::XmaContext() XmaContext::~XmaContext() { if (context_) { - if (context_->extradata) { - delete [] context_->extradata; - } if (avcodec_is_open(context_)) { avcodec_close(context_); } @@ -85,21 +85,20 @@ int XmaContext::Setup(uint32_t id, Memory* memory, uint32_t guest_ptr) { // Initialize these to 0. They'll actually be set later. context_->channels = 0; context_->sample_rate = 0; - context_->block_align = XMA_CONTEXT_DATA::kBytesPerPacket; + context_->block_align = kBytesPerPacket; // Extra data passed to the decoder - context_->extradata_size = 18; - context_->extradata = new uint8_t[context_->extradata_size]; - std::memset(context_->extradata, 0, context_->extradata_size); + std::memset(&extra_data_, 0, sizeof(extra_data_)); + extra_data_.bits_per_sample = 16; + extra_data_.channel_mask = AV_CH_FRONT_RIGHT; + extra_data_.decode_flags = 0x10D6; - *(short *)(context_->extradata) = 0x10; // bits per sample - *(int *)(context_->extradata + 2) = 1; // channel mask - *(short *)(context_->extradata + 14) = 0x10D6; // decode flags + context_->extradata_size = sizeof(extra_data_); + context_->extradata = (uint8_t*)&extra_data_; // Current frame stuff whatever // samples per frame * 2 max channels * output bytes - current_frame_ = - new uint8_t[XMA_CONTEXT_DATA::kSamplesPerFrame * 2 * 2]; + current_frame_ = new uint8_t[kSamplesPerFrame * 2 * 2]; current_frame_pos_ = 0; frame_samples_size_ = 0; @@ -117,7 +116,7 @@ void XmaContext::Work() { auto context_ptr = memory()->TranslateVirtual(guest_ptr()); XMA_CONTEXT_DATA data(context_ptr); - Process(data); + DecodePackets(data); data.Store(context_ptr); } @@ -130,7 +129,7 @@ void XmaContext::Enable() { XELOGAPU("XmaContext: kicking context %d (%d/%d bytes)", id(), (data.input_buffer_read_offset & ~0x7FF) / 8, (data.input_buffer_0_packet_count + data.input_buffer_1_packet_count) - * XMA_CONTEXT_DATA::kBytesPerPacket); + * kBytesPerPacket); // Reset valid flags so our audio decoder knows to process this one. data.input_buffer_0_valid = data.input_buffer_0_ptr != 0; @@ -189,7 +188,18 @@ void XmaContext::Release() { DiscardPacket(); } -void XmaContext::Process(XMA_CONTEXT_DATA& data) { +int XmaContext::GetSampleRate(int id) { + switch (id) { + case 0: return 24000; + case 1: return 32000; + case 2: return 44100; + case 3: return 48000; + } + assert_always(); + return 0; +} + +void XmaContext::DecodePackets(XMA_CONTEXT_DATA& data) { SCOPE_profile_cpu_f("apu"); // What I see: @@ -218,15 +228,13 @@ void XmaContext::Process(XMA_CONTEXT_DATA& data) { return; } - // Translate this for future use. - uint8_t* output_buffer = memory()->TranslatePhysical(data.output_buffer_ptr); - // Output buffers are in raw PCM samples, 256 bytes per block. // Output buffer is a ring buffer. We need to write from the write offset // to the read offset. - uint32_t output_capacity = data.output_buffer_block_count * 256; - uint32_t output_read_offset = data.output_buffer_read_offset * 256; - uint32_t output_write_offset = data.output_buffer_write_offset * 256; + uint8_t* output_buffer = memory()->TranslatePhysical(data.output_buffer_ptr); + uint32_t output_capacity = data.output_buffer_block_count * kBytesPerSubframe; + uint32_t output_read_offset = data.output_buffer_read_offset * kBytesPerSubframe; + uint32_t output_write_offset = data.output_buffer_write_offset * kBytesPerSubframe; RingBuffer output_rb(output_buffer, output_capacity); output_rb.set_read_offset(output_read_offset); @@ -242,7 +250,7 @@ void XmaContext::Process(XMA_CONTEXT_DATA& data) { int read_bytes = 0; int decode_attempts_remaining = 3; - uint8_t work_buffer[XMA_CONTEXT_DATA::kOutputMaxSizeBytes]; + uint8_t work_buffer[kOutputMaxSizeBytes]; while (decode_attempts_remaining) { read_bytes = DecodePacket(work_buffer, 0, output_remaining_bytes); if (read_bytes >= 0) { @@ -283,7 +291,7 @@ void XmaContext::Process(XMA_CONTEXT_DATA& data) { continue; } else if (data.input_buffer_0_valid || data.input_buffer_1_valid) { // Done with previous packet, so grab a new one. - int ret = PreparePacket(data); + int ret = StartPacket(data); if (ret <= 0) { // No more data (but may have prepared a packet) data.input_buffer_0_valid = 0; @@ -299,8 +307,7 @@ void XmaContext::Process(XMA_CONTEXT_DATA& data) { data.output_buffer_valid = 0; } - -int XmaContext::PreparePacket(XMA_CONTEXT_DATA &data) { +int XmaContext::StartPacket(XMA_CONTEXT_DATA &data) { // Translate pointers for future use. uint8_t* in0 = data.input_buffer_0_valid ? memory()->TranslatePhysical(data.input_buffer_0_ptr) @@ -309,16 +316,7 @@ int XmaContext::PreparePacket(XMA_CONTEXT_DATA &data) { ? memory()->TranslatePhysical(data.input_buffer_1_ptr) : nullptr; - int sample_rate = 0; - if (data.sample_rate == 0) { - sample_rate = 24000; - } else if (data.sample_rate == 1) { - sample_rate = 32000; - } else if (data.sample_rate == 2) { - sample_rate = 44100; - } else if (data.sample_rate == 3) { - sample_rate = 48000; - } + int sample_rate = GetSampleRate(data.sample_rate); int channels = data.is_stereo ? 2 : 1; // See if we've finished with the input. @@ -351,11 +349,11 @@ int XmaContext::PreparePacket(XMA_CONTEXT_DATA &data) { auto packet = input_buffer + input_offset_bytes; assert_true(input_offset_bytes % 2048 == 0); PreparePacket(packet, seq_offset_bytes, - XMA_CONTEXT_DATA::kBytesPerPacket, + kBytesPerPacket, sample_rate, channels); - data.input_buffer_read_offset += XMA_CONTEXT_DATA::kBytesPerPacket * 8; + data.input_buffer_read_offset += kBytesPerPacket * 8; - input_remaining_bytes -= XMA_CONTEXT_DATA::kBytesPerPacket; + input_remaining_bytes -= kBytesPerPacket; if (input_remaining_bytes <= 0) { // Used the last of the data but prepared a packet return 0; @@ -370,7 +368,7 @@ int XmaContext::PreparePacket(XMA_CONTEXT_DATA &data) { int XmaContext::PreparePacket(uint8_t *input, size_t seq_offset, size_t size, int sample_rate, int channels) { - if (size != XMA_CONTEXT_DATA::kBytesPerPacket) { + if (size != kBytesPerPacket) { // Invalid packet size! assert_always(); return 1; @@ -387,16 +385,18 @@ int XmaContext::PreparePacket(uint8_t *input, size_t seq_offset, size_t size, (*((int *)packet_data_) & 0xFFFEFF08); packet_->data = packet_data_; - packet_->size = XMA_CONTEXT_DATA::kBytesPerPacket; + packet_->size = kBytesPerPacket; // Re-initialize the context with new sample rate and channels if (context_->sample_rate != sample_rate || context_->channels != channels) { - context_->sample_rate = sample_rate; - context_->channels = channels; - // We have to reopen the codec so it'll realloc whatever data it needs. // TODO: Find a better way. avcodec_close(context_); + + context_->sample_rate = sample_rate; + context_->channels = channels; + extra_data_.channel_mask = channels == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO; + if (avcodec_open2(context_, codec_, NULL) < 0) { XELOGE("XmaContext: Failed to reopen libav context"); return 1; @@ -449,7 +449,7 @@ int XmaContext::DecodePacket(uint8_t *output, size_t output_offset, // Successfully decoded a frame if (got_frame) { // Validity checks. - if (decoded_frame_->nb_samples > XMA_CONTEXT_DATA::kSamplesPerFrame) { + if (decoded_frame_->nb_samples > kSamplesPerFrame) { return -2; } else if (context_->sample_fmt != AV_SAMPLE_FMT_FLTP) { return -3; diff --git a/src/xenia/apu/xma_context.h b/src/xenia/apu/xma_context.h index a9fe5108a..183c9e6bc 100644 --- a/src/xenia/apu/xma_context.h +++ b/src/xenia/apu/xma_context.h @@ -44,13 +44,6 @@ namespace apu { // Appears to be dumped in order (for the most part) struct XMA_CONTEXT_DATA { - static const uint32_t kBytesPerPacket = 2048; - static const uint32_t kSamplesPerFrame = 512; - static const uint32_t kSamplesPerSubframe = 128; - - static const uint32_t kOutputBytesPerBlock = 256; - static const uint32_t kOutputMaxSizeBytes = 31 * kOutputBytesPerBlock; - // DWORD 0 uint32_t input_buffer_0_packet_count : 12; // XMASetInputBuffer0, number of // 2KB packets. Max 4095 packets. @@ -122,8 +115,29 @@ struct XMA_CONTEXT_DATA { }; static_assert_size(XMA_CONTEXT_DATA, 64); +#pragma pack(push, 1) +struct WmaProExtraData { + uint16_t bits_per_sample; + uint32_t channel_mask; + uint8_t unk06[8]; + uint16_t decode_flags; + uint8_t unk10[2]; +}; +static_assert_size(WmaProExtraData, 18); +#pragma pack(pop) + class XmaContext { public: + static const uint32_t kBytesPerPacket = 2048; + + static const uint32_t kBytesPerSample = 2; + static const uint32_t kSamplesPerFrame = 512; + static const uint32_t kSamplesPerSubframe = 128; + static const uint32_t kBytesPerSubframe = kSamplesPerSubframe * kBytesPerSample; + + static const uint32_t kOutputBytesPerBlock = 256; + static const uint32_t kOutputMaxSizeBytes = 31 * kOutputBytesPerBlock; + XmaContext(); ~XmaContext(); @@ -147,8 +161,10 @@ class XmaContext { void set_is_enabled(bool is_enabled) { is_enabled_ = is_enabled; } private: - void Process(XMA_CONTEXT_DATA& data); - int PreparePacket(XMA_CONTEXT_DATA &data); + static int GetSampleRate(int id); + + void DecodePackets(XMA_CONTEXT_DATA& data); + int StartPacket(XMA_CONTEXT_DATA &data); int PreparePacket(uint8_t* input, size_t seq_offset, size_t size, int sample_rate, int channels); @@ -164,17 +180,20 @@ class XmaContext { bool is_allocated_; bool is_enabled_; + bool decoding_packet_; + // libav structures AVCodec* codec_; AVCodecContext* context_; AVFrame* decoded_frame_; AVPacket* packet_; + WmaProExtraData extra_data_; size_t current_frame_pos_; uint8_t* current_frame_; uint32_t frame_samples_size_; - uint8_t packet_data_[XMA_CONTEXT_DATA::kBytesPerPacket]; + uint8_t packet_data_[kBytesPerPacket]; }; } // namespace apu diff --git a/src/xenia/apu/xma_decoder.h b/src/xenia/apu/xma_decoder.h index f25aba1da..ecae584a4 100644 --- a/src/xenia/apu/xma_decoder.h +++ b/src/xenia/apu/xma_decoder.h @@ -58,9 +58,6 @@ class XmaDecoder { private: void WorkerThreadMain(); - void ProcessContext(XmaContext& context, XMA_CONTEXT_DATA& data); - int PreparePacket(XmaContext& context, XMA_CONTEXT_DATA& data); - static uint64_t MMIOReadRegisterThunk(void* ppc_context, XmaDecoder* as, uint32_t addr) { return as->ReadRegister(addr);