From 826609cd600be295176814d3b1afc5e951f63f93 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Wed, 3 Jun 2015 11:12:55 -0500 Subject: [PATCH] Cleanup some audio system code. --- src/xenia/apu/audio_decoder.cc | 6 +- src/xenia/apu/audio_decoder.h | 2 +- src/xenia/apu/audio_system.cc | 138 +++++++++++++++++++-------------- src/xenia/apu/audio_system.h | 3 +- src/xenia/base/ring_buffer.cc | 13 +++- src/xenia/base/ring_buffer.h | 2 +- 6 files changed, 96 insertions(+), 68 deletions(-) diff --git a/src/xenia/apu/audio_decoder.cc b/src/xenia/apu/audio_decoder.cc index 5d0830fd5..83dd74f57 100644 --- a/src/xenia/apu/audio_decoder.cc +++ b/src/xenia/apu/audio_decoder.cc @@ -75,7 +75,7 @@ int AudioDecoder::Initialize() { // Initialize these to 0. They'll actually be set later. context_->channels = 0; context_->sample_rate = 0; - context_->block_align = XMAContextData::kBytesPerBlock; + context_->block_align = XMAContextData::kBytesPerPacket; // Extra data passed to the decoder context_->extradata_size = 18; @@ -99,7 +99,7 @@ int AudioDecoder::Initialize() { int AudioDecoder::PreparePacket(uint8_t *input, size_t seq_offset, size_t size, int sample_rate, int channels) { - if (size != XMAContextData::kBytesPerBlock) { + if (size != XMAContextData::kBytesPerPacket) { // Invalid packet size! assert_always(); return 1; @@ -116,7 +116,7 @@ int AudioDecoder::PreparePacket(uint8_t *input, size_t seq_offset, size_t size, (*((int *)packet_data_) & 0xFFFEFF08); packet_->data = packet_data_; - packet_->size = XMAContextData::kBytesPerBlock; + packet_->size = XMAContextData::kBytesPerPacket; // Re-initialize the context with new sample rate and channels if (context_->sample_rate != sample_rate || context_->channels != channels) { diff --git a/src/xenia/apu/audio_decoder.h b/src/xenia/apu/audio_decoder.h index 9a3a687d4..b62a7308d 100644 --- a/src/xenia/apu/audio_decoder.h +++ b/src/xenia/apu/audio_decoder.h @@ -55,7 +55,7 @@ class AudioDecoder { uint8_t* current_frame_; uint32_t frame_samples_size_; - uint8_t packet_data_[XMAContextData::kBytesPerBlock]; + uint8_t packet_data_[XMAContextData::kBytesPerPacket]; }; } // namespace apu diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index f51f65ad2..2bcfcfbf0 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -333,13 +333,7 @@ void AudioSystem::UnregisterClient(size_t index) { void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { SCOPE_profile_cpu_f("apu"); - // Translate pointers for future use. - uint8_t* in0 = data.input_buffer_0_valid - ? memory()->TranslatePhysical(data.input_buffer_0_ptr) - : nullptr; - uint8_t* in1 = data.input_buffer_1_valid - ? memory()->TranslatePhysical(data.input_buffer_1_ptr) - : nullptr; + // Translate this for future use. uint8_t* out = memory()->TranslatePhysical(data.output_buffer_ptr); // What I see: @@ -371,9 +365,9 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { uint32_t output_write_offset_bytes = data.output_buffer_write_offset * 256; uint32_t output_read_offset_bytes = data.output_buffer_read_offset * 256; - RingBuffer out_buffer(out, output_size_bytes, output_write_offset_bytes); + RingBuffer output_buffer(out, output_size_bytes, output_write_offset_bytes); size_t output_remaining_bytes - = out_buffer.DistanceToOffset(output_read_offset_bytes); + = output_buffer.DistanceToOffset(output_read_offset_bytes); if (!output_remaining_bytes) { // Can't write any more data. Break. @@ -392,7 +386,7 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { read_bytes = context.decoder->DecodePacket(tmp_buff, 0, output_remaining_bytes); if (read_bytes >= 0) { - out_buffer.Write(tmp_buff, read_bytes); + output_buffer.Write(tmp_buff, read_bytes); // Ok. break; @@ -426,58 +420,13 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { // If we need more data and the input buffers have it, grab it. if (read_bytes) { - // Still outputting. + // Haven't finished with current packet. continue; } else if (data.input_buffer_0_valid || data.input_buffer_1_valid) { // Done with previous packet, so grab a new one. - 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 channels = data.is_stereo ? 2 : 1; - - // See if we've finished with the input. - // Block count is in packets, so expand by packet size. - uint32_t input_size_0_bytes = (data.input_buffer_0_packet_count) * 2048; - uint32_t input_size_1_bytes = (data.input_buffer_1_packet_count) * 2048; - - // Total input size - uint32_t input_size_bytes = input_size_0_bytes + input_size_1_bytes; - - // Input read offset is in bits. Typically starts at 32 (4 bytes). - // "Sequence" offset - used internally for WMA Pro decoder. - // Just the read offset. - uint32_t seq_offset_bytes = (data.input_buffer_read_offset & ~0x7FF) / 8; - - if (seq_offset_bytes < input_size_bytes) { - // Setup input offset and input buffer. - uint32_t input_offset_bytes = seq_offset_bytes; - auto input_buffer = in0; - - if (seq_offset_bytes >= input_size_0_bytes) { - // Size overlap, select input buffer 1. - input_offset_bytes -= input_size_0_bytes; - input_buffer = in1; - } - - // Still have data to read. - auto packet = input_buffer + input_offset_bytes; - context.decoder->PreparePacket(packet, seq_offset_bytes, 2048, - sample_rate, channels); - data.input_buffer_read_offset += 2048 * 8; - if (seq_offset_bytes + 2048 >= input_size_bytes) { - // Used the last of the data. - data.input_buffer_0_valid = 0; - data.input_buffer_1_valid = 0; - } - } else { - // No more data available (for now). + int ret = PrepareXMAPacket(context, data); + if (ret <= 0) { + // No more data (but may have prepared a packet) data.input_buffer_0_valid = 0; data.input_buffer_1_valid = 0; } @@ -488,6 +437,74 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { } } +int AudioSystem::PrepareXMAPacket(XMAContext &context, XMAContextData &data) { + // Translate pointers for future use. + uint8_t* in0 = data.input_buffer_0_valid + ? memory()->TranslatePhysical(data.input_buffer_0_ptr) + : nullptr; + uint8_t* in1 = data.input_buffer_1_valid + ? 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 channels = data.is_stereo ? 2 : 1; + + // See if we've finished with the input. + // Block count is in packets, so expand by packet size. + uint32_t input_size_0_bytes = (data.input_buffer_0_packet_count) * 2048; + uint32_t input_size_1_bytes = (data.input_buffer_1_packet_count) * 2048; + + // Total input size + uint32_t input_size_bytes = input_size_0_bytes + input_size_1_bytes; + + // Input read offset is in bits. Typically starts at 32 (4 bytes). + // "Sequence" offset - used internally for WMA Pro decoder. + // Just the read offset. + uint32_t seq_offset_bytes = (data.input_buffer_read_offset & ~0x7FF) / 8; + uint32_t input_remaining_bytes = input_size_bytes - seq_offset_bytes; + + if (seq_offset_bytes < input_size_bytes) { + // Setup input offset and input buffer. + uint32_t input_offset_bytes = seq_offset_bytes; + auto input_buffer = in0; + + if (seq_offset_bytes >= input_size_0_bytes) { + // Size overlap, select input buffer 1. + // TODO: This needs testing. + input_offset_bytes -= input_size_0_bytes; + input_buffer = in1; + } + + // Still have data to read. + auto packet = input_buffer + input_offset_bytes; + context.decoder->PreparePacket(packet, seq_offset_bytes, + XMAContextData::kBytesPerPacket, + sample_rate, channels); + data.input_buffer_read_offset += XMAContextData::kBytesPerPacket * 8; + + input_remaining_bytes -= XMAContextData::kBytesPerPacket; + if (input_remaining_bytes <= 0) { + // Used the last of the data but prepared a packet + return 0; + } + } else { + // No more data available and no packet prepared. + return -1; + } + + + return input_remaining_bytes; +} + // free60 may be useful here, however it looks like it's using a different // piece of hardware: // https://github.com/Free60Project/libxenon/blob/master/libxenon/drivers/xenon_sound/sound.c @@ -547,7 +564,7 @@ void AudioSystem::WriteRegister(uint32_t addr, uint64_t value) { XELOGAPU("AudioSystem: kicking context %d (%d/%d bytes)", context_id, (data.input_buffer_read_offset & ~0x7FF) / 8, (data.input_buffer_0_packet_count + data.input_buffer_1_packet_count) - * XMAContextData::kBytesPerBlock); + * XMAContextData::kBytesPerPacket); // Reset valid flags so our audio decoder knows to process this one. data.input_buffer_0_valid = data.input_buffer_0_ptr != 0; @@ -597,6 +614,7 @@ void AudioSystem::WriteRegister(uint32_t addr, uint64_t value) { data.input_buffer_0_valid = 0; data.input_buffer_1_valid = 0; data.output_buffer_valid = 0; + data.output_buffer_read_offset = 31; data.Store(context_ptr); context.lock.unlock(); diff --git a/src/xenia/apu/audio_system.h b/src/xenia/apu/audio_system.h index b65cd4225..b2d3b01f4 100644 --- a/src/xenia/apu/audio_system.h +++ b/src/xenia/apu/audio_system.h @@ -39,7 +39,7 @@ class AudioDecoder; // http://pastebin.com/9amqJ2kQ struct XMAContextData { static const uint32_t kSize = 64; - static const uint32_t kBytesPerBlock = 2048; + static const uint32_t kBytesPerPacket = 2048; static const uint32_t kSamplesPerFrame = 512; static const uint32_t kSamplesPerSubframe = 128; @@ -155,6 +155,7 @@ class AudioSystem { void DecoderThreadMain(); void ProcessXmaContext(XMAContext& context, XMAContextData& data); + int PrepareXMAPacket(XMAContext& context, XMAContextData& data); static uint64_t MMIOReadRegisterThunk(void* ppc_context, AudioSystem* as, uint32_t addr) { diff --git a/src/xenia/base/ring_buffer.cc b/src/xenia/base/ring_buffer.cc index 48953f745..216e965d1 100644 --- a/src/xenia/base/ring_buffer.cc +++ b/src/xenia/base/ring_buffer.cc @@ -16,7 +16,7 @@ RingBuffer::RingBuffer(uint8_t *raw_buffer, size_t size, size_t write_offset) size_(size), write_offset_(write_offset) {} -int RingBuffer::Write(uint8_t *buffer, size_t num_bytes) { +size_t RingBuffer::Write(uint8_t *buffer, size_t num_bytes) { size_t bytes_written = 0; size_t input_offset = 0; size_t bytes_to_write = 0; @@ -29,18 +29,27 @@ int RingBuffer::Write(uint8_t *buffer, size_t num_bytes) { input_offset = bytes_to_write; write_offset_ += bytes_to_write; + bytes_written += bytes_to_write; + // Wraparound (begin -> num_bytes) if (input_offset < num_bytes) { bytes_to_write = num_bytes - input_offset; std::memcpy(raw_buffer_, buffer + input_offset, bytes_to_write); write_offset_ = bytes_to_write; + + bytes_written += bytes_to_write; } - return 0; + return bytes_written; } size_t RingBuffer::DistanceToOffset(size_t offset) { + // Make sure the offset is in range. + if (offset > size_) { + offset %= size_; + } + if (offset < size_ && offset >= write_offset_) { // Doesn't wraparound. return offset - write_offset_; diff --git a/src/xenia/base/ring_buffer.h b/src/xenia/base/ring_buffer.h index 1d68b114a..50c3e52f1 100644 --- a/src/xenia/base/ring_buffer.h +++ b/src/xenia/base/ring_buffer.h @@ -20,7 +20,7 @@ class RingBuffer { public: RingBuffer(uint8_t *raw_buffer, size_t size, size_t write_offset = 0); - int Write(uint8_t *buffer, size_t num_bytes); + size_t Write(uint8_t *buffer, size_t num_bytes); size_t DistanceToOffset(size_t offset);