From 93763c166f87cd51c3bcf13ac0600eb9cf3156d3 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 30 May 2015 23:52:22 -0500 Subject: [PATCH 1/3] input_buffer_*_block_count -> input_buffer_*_packet_count --- src/xenia/apu/audio_system.cc | 13 +++++-------- src/xenia/apu/audio_system.h | 9 ++++----- src/xenia/kernel/xboxkrnl_audio_xma.cc | 12 ++++++++---- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index 24eaaf437..f38b28000 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -389,7 +389,7 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { // Sometimes the decoder will fail on a packet. I think it's // looking for cross-packet frames and failing. If you run it again // on the same packet it'll work though. - XELOGAPU("APU failed to decode packet (returned %.8X)", -read_bytes); + XELOGAPU("AudioSystem: libav failed to decode packet (returned %.8X)", -read_bytes); --decode_attempts_remaining; } } @@ -426,10 +426,8 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { // 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_block_count) * 2048; - uint32_t input_size_1_bytes = - (data.input_buffer_1_block_count) * 2048; + 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; @@ -529,10 +527,9 @@ void AudioSystem::WriteRegister(uint32_t addr, uint64_t value) { auto context_ptr = memory()->TranslateVirtual(context.guest_ptr); XMAContextData data(context_ptr); - XELOGAPU( - "AudioSystem: kicking context %d (%d/%d bytes)", context_id, + XELOGAPU("AudioSystem: kicking context %d (%d/%d bytes)", context_id, (data.input_buffer_read_offset & ~0x7FF) / 8, - (data.input_buffer_0_block_count + data.input_buffer_1_block_count) + (data.input_buffer_0_packet_count + data.input_buffer_1_packet_count) * XMAContextData::kBytesPerBlock); // Reset valid flags so our audio decoder knows to process this one. diff --git a/src/xenia/apu/audio_system.h b/src/xenia/apu/audio_system.h index 25bd2f9db..db5732c27 100644 --- a/src/xenia/apu/audio_system.h +++ b/src/xenia/apu/audio_system.h @@ -44,9 +44,8 @@ struct XMAContextData { static const uint32_t kSamplesPerSubframe = 128; // DWORD 0 - uint32_t input_buffer_0_block_count : 12; // XMASetInputBuffer0, number of - // 2KB blocks. AKA SizeRead0 - // Maximum 4095 packets. + uint32_t input_buffer_0_packet_count : 12; // XMASetInputBuffer0, number of + // 2KB packets. Max 4095 packets. uint32_t loop_count : 8; // +12bit, XMASetLoopData NumLoops uint32_t input_buffer_0_valid : 1; // +20bit, XMAIsInputBuffer0Valid uint32_t input_buffer_1_valid : 1; // +21bit, XMAIsInputBuffer1Valid @@ -56,8 +55,8 @@ struct XMAContextData { // AKA OffsetWrite // DWORD 1 - uint32_t input_buffer_1_block_count : 12; // XMASetInputBuffer1, number of - // 2KB blocks. + uint32_t input_buffer_1_packet_count : 12; // XMASetInputBuffer1, number of + // 2KB packets. Max 4095 packets. uint32_t loop_subframe_end : 2; // +12bit, XMASetLoopData uint32_t unk_dword_1_a : 3; // ? might be loop_subframe_skip uint32_t loop_subframe_skip : 3; // +17bit, XMASetLoopData might be diff --git a/src/xenia/kernel/xboxkrnl_audio_xma.cc b/src/xenia/kernel/xboxkrnl_audio_xma.cc index f3994d605..fa81d2c47 100644 --- a/src/xenia/kernel/xboxkrnl_audio_xma.cc +++ b/src/xenia/kernel/xboxkrnl_audio_xma.cc @@ -104,9 +104,9 @@ SHIM_CALL XMAInitializeContext_shim(PPCContext* ppc_context, XMAContextData context(SHIM_MEM_ADDR(context_ptr)); context.input_buffer_0_ptr = SHIM_MEM_32(context_init_ptr + 0 * 4); - context.input_buffer_0_block_count = SHIM_MEM_32(context_init_ptr + 1 * 4); + context.input_buffer_0_packet_count = SHIM_MEM_32(context_init_ptr + 1 * 4); context.input_buffer_1_ptr = SHIM_MEM_32(context_init_ptr + 2 * 4); - context.input_buffer_1_block_count = SHIM_MEM_32(context_init_ptr + 3 * 4); + context.input_buffer_1_packet_count = SHIM_MEM_32(context_init_ptr + 3 * 4); context.input_buffer_read_offset = SHIM_MEM_32(context_init_ptr + 4 * 4); context.output_buffer_ptr = SHIM_MEM_32(context_init_ptr + 5 * 4); context.output_buffer_block_count = SHIM_MEM_32(context_init_ptr + 6 * 4); @@ -191,7 +191,7 @@ SHIM_CALL XMASetInputBuffer0_shim(PPCContext* ppc_context, XMAContextData context(SHIM_MEM_ADDR(context_ptr)); context.input_buffer_0_ptr = buffer_ptr; - context.input_buffer_0_block_count = block_count; + context.input_buffer_0_packet_count = block_count; context.input_buffer_read_offset = 32; // in bits context.input_buffer_0_valid = buffer_ptr ? 1 : 0; @@ -240,7 +240,7 @@ SHIM_CALL XMASetInputBuffer1_shim(PPCContext* ppc_context, XMAContextData context(SHIM_MEM_ADDR(context_ptr)); context.input_buffer_1_ptr = buffer_ptr; - context.input_buffer_1_block_count = block_count; + context.input_buffer_1_packet_count = block_count; context.input_buffer_read_offset = 32; // in bits context.input_buffer_1_valid = buffer_ptr ? 1 : 0; @@ -419,17 +419,21 @@ void xe::kernel::xboxkrnl::RegisterAudioXmaExports( SHIM_SET_MAPPING("xboxkrnl.exe", XMASetLoopData, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetInputBufferReadOffset, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBufferReadOffset, state); + SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer0Valid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0Valid, state); + SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer1Valid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1Valid, state); + SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsOutputBufferValid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferValid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferReadOffset, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferReadOffset, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferWriteOffset, state); + SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetPacketMetadata, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAEnableContext, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMADisableContext, state); From c780d5528d673c27f201bf0e16c74de05925c0fa Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sun, 31 May 2015 13:48:14 -0500 Subject: [PATCH 2/3] Remove unused bits_per_frame_ field. --- src/xenia/apu/audio_decoder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xenia/apu/audio_decoder.h b/src/xenia/apu/audio_decoder.h index bf08b2442..9a3a687d4 100644 --- a/src/xenia/apu/audio_decoder.h +++ b/src/xenia/apu/audio_decoder.h @@ -45,12 +45,12 @@ class AudioDecoder { int DecodePacket(uint8_t* output, size_t offset, size_t size); private: + // libav structures AVCodec* codec_; AVCodecContext* context_; AVFrame* decoded_frame_; AVPacket* packet_; - uint8_t bits_per_frame_; size_t current_frame_pos_; uint8_t* current_frame_; uint32_t frame_samples_size_; From 77e245dc51f5b17df9154304c18042ef80a56c12 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Tue, 2 Jun 2015 00:42:26 -0500 Subject: [PATCH 3/3] Fix audio decoding. --- src/xenia/apu/audio_system.cc | 84 +++++++++++++++++++++++++++++++---- src/xenia/apu/audio_system.h | 6 ++- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/src/xenia/apu/audio_system.cc b/src/xenia/apu/audio_system.cc index f38b28000..c7ba2077c 100644 --- a/src/xenia/apu/audio_system.cc +++ b/src/xenia/apu/audio_system.cc @@ -360,13 +360,38 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { // 3 - 48 kHz ? // SPUs also support stereo decoding. (data.is_stereo) + bool output_written_bytes = false; + while (data.output_buffer_valid) { // Check the output buffer - we cannot decode anything else if it's // unavailable. // 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_size_bytes = data.output_buffer_block_count * 256; - uint32_t output_offset_bytes = data.output_buffer_write_offset * 256; - uint32_t output_remaining_bytes = output_size_bytes - output_offset_bytes; + uint32_t output_write_offset_bytes = data.output_buffer_write_offset * 256; + uint32_t output_read_offset_bytes = data.output_buffer_read_offset * 256; + + uint32_t output_remaining_bytes = 0; + bool output_wraparound = false; + bool output_all = false; + + if (output_write_offset_bytes < output_read_offset_bytes) { + // Case 1: write -> read + output_remaining_bytes = + output_read_offset_bytes - output_write_offset_bytes; + } else if (output_read_offset_bytes < output_write_offset_bytes) { + // Case 2: write -> end -> read + output_remaining_bytes = output_size_bytes - output_write_offset_bytes; + output_remaining_bytes += output_read_offset_bytes; + + // Doesn't count if it's 0! + output_wraparound = true; + } else if (!output_written_bytes) { + output_remaining_bytes = output_size_bytes; + output_all = true; + } + if (!output_remaining_bytes) { // Can't write any more data. Break. // The game will kick us again with a new output buffer later. @@ -380,8 +405,45 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { int read_bytes = 0; int decode_attempts_remaining = 3; while (decode_attempts_remaining) { - read_bytes = context.decoder->DecodePacket(out, output_offset_bytes, - output_remaining_bytes); + // TODO: We need a ringbuffer util class! + if (output_all) { + read_bytes = context.decoder->DecodePacket(out, + output_write_offset_bytes, + output_size_bytes + - output_write_offset_bytes); + } else if (output_wraparound) { + // write -> end + int r1 = context.decoder->DecodePacket(out, + output_write_offset_bytes, + output_size_bytes + - output_write_offset_bytes); + if (r1 < 0) { + --decode_attempts_remaining; + continue; + } + + // begin -> read + // FIXME: If it fails here this'll break stuff + int r2 = context.decoder->DecodePacket(out, 0, + output_read_offset_bytes); + if (r2 < 0) { + --decode_attempts_remaining; + continue; + } + + read_bytes = r1 + r2; + } else { + // write -> read + read_bytes = context.decoder->DecodePacket(out, + output_write_offset_bytes, + output_read_offset_bytes + - output_write_offset_bytes); + } + + if (read_bytes > 0) { + output_written_bytes = true; + } + if (read_bytes >= 0) { // Ok. break; @@ -389,11 +451,13 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { // Sometimes the decoder will fail on a packet. I think it's // looking for cross-packet frames and failing. If you run it again // on the same packet it'll work though. - XELOGAPU("AudioSystem: libav failed to decode packet (returned %.8X)", -read_bytes); --decode_attempts_remaining; } } + if (!decode_attempts_remaining) { + XELOGAPU("AudioSystem: libav failed to decode packet (returned %.8X)", -read_bytes); + // Failed out. if (data.input_buffer_0_valid || data.input_buffer_1_valid) { // There's new data available - maybe we'll be ok if we decode it? @@ -404,7 +468,12 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { break; } } + data.output_buffer_write_offset += uint32_t(read_bytes) / 256; + if (data.output_buffer_write_offset > data.output_buffer_block_count) { + // Wraparound! + data.output_buffer_write_offset -= data.output_buffer_block_count; + } // If we need more data and the input buffers have it, grab it. if (read_bytes) { @@ -435,8 +504,7 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { // 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 seq_offset_bytes = (data.input_buffer_read_offset & ~0x7FF) / 8; if (seq_offset_bytes < input_size_bytes) { // Setup input offset and input buffer. @@ -535,7 +603,7 @@ void AudioSystem::WriteRegister(uint32_t addr, uint64_t value) { // Reset valid flags so our audio decoder knows to process this one. data.input_buffer_0_valid = data.input_buffer_0_ptr != 0; data.input_buffer_1_valid = data.input_buffer_1_ptr != 0; - data.output_buffer_write_offset = 0; + //data.output_buffer_write_offset = 0; data.Store(context_ptr); diff --git a/src/xenia/apu/audio_system.h b/src/xenia/apu/audio_system.h index db5732c27..3bc5e6ff4 100644 --- a/src/xenia/apu/audio_system.h +++ b/src/xenia/apu/audio_system.h @@ -46,6 +46,7 @@ struct XMAContextData { // DWORD 0 uint32_t input_buffer_0_packet_count : 12; // XMASetInputBuffer0, number of // 2KB packets. Max 4095 packets. + // These packets form a block. uint32_t loop_count : 8; // +12bit, XMASetLoopData NumLoops uint32_t input_buffer_0_valid : 1; // +20bit, XMAIsInputBuffer0Valid uint32_t input_buffer_1_valid : 1; // +21bit, XMAIsInputBuffer1Valid @@ -57,6 +58,7 @@ struct XMAContextData { // DWORD 1 uint32_t input_buffer_1_packet_count : 12; // XMASetInputBuffer1, number of // 2KB packets. Max 4095 packets. + // These packets form a block. uint32_t loop_subframe_end : 2; // +12bit, XMASetLoopData uint32_t unk_dword_1_a : 3; // ? might be loop_subframe_skip uint32_t loop_subframe_skip : 3; // +17bit, XMASetLoopData might be @@ -64,8 +66,8 @@ struct XMAContextData { uint32_t subframe_decode_count : 4; // +20bit might be subframe_skip_count uint32_t unk_dword_1_b : 3; // ? NumSubframesToSkip/NumChannels(?) uint32_t sample_rate : 2; // +27bit enum of sample rates - uint32_t is_stereo : 1; // +29bit might be NumChannels - uint32_t unk_dword_1_c : 1; // ? part of NumChannels? + uint32_t is_stereo : 1; // +29bit + uint32_t unk_dword_1_c : 1; // +29bit uint32_t output_buffer_valid : 1; // +31bit, XMAIsOutputBufferValid // DWORD 2