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