Merge pull request #232 from DrChat/audio_decoding

Fix audio decoding
This commit is contained in:
Ben Vanik 2015-06-01 22:47:17 -07:00
commit 88fd97f30b
4 changed files with 97 additions and 27 deletions

View File

@ -45,12 +45,12 @@ class AudioDecoder {
int DecodePacket(uint8_t* output, size_t offset, size_t size); int DecodePacket(uint8_t* output, size_t offset, size_t size);
private: private:
// libav structures
AVCodec* codec_; AVCodec* codec_;
AVCodecContext* context_; AVCodecContext* context_;
AVFrame* decoded_frame_; AVFrame* decoded_frame_;
AVPacket* packet_; AVPacket* packet_;
uint8_t bits_per_frame_;
size_t current_frame_pos_; size_t current_frame_pos_;
uint8_t* current_frame_; uint8_t* current_frame_;
uint32_t frame_samples_size_; uint32_t frame_samples_size_;

View File

@ -360,13 +360,38 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) {
// 3 - 48 kHz ? // 3 - 48 kHz ?
// SPUs also support stereo decoding. (data.is_stereo) // SPUs also support stereo decoding. (data.is_stereo)
bool output_written_bytes = false;
while (data.output_buffer_valid) { while (data.output_buffer_valid) {
// Check the output buffer - we cannot decode anything else if it's // Check the output buffer - we cannot decode anything else if it's
// unavailable. // unavailable.
// Output buffers are in raw PCM samples, 256 bytes per block. // 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_size_bytes = data.output_buffer_block_count * 256;
uint32_t output_offset_bytes = data.output_buffer_write_offset * 256; uint32_t output_write_offset_bytes = data.output_buffer_write_offset * 256;
uint32_t output_remaining_bytes = output_size_bytes - output_offset_bytes; 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) { if (!output_remaining_bytes) {
// Can't write any more data. Break. // Can't write any more data. Break.
// The game will kick us again with a new output buffer later. // 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 read_bytes = 0;
int decode_attempts_remaining = 3; int decode_attempts_remaining = 3;
while (decode_attempts_remaining) { while (decode_attempts_remaining) {
read_bytes = context.decoder->DecodePacket(out, output_offset_bytes, // TODO: We need a ringbuffer util class!
output_remaining_bytes); 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) { if (read_bytes >= 0) {
// Ok. // Ok.
break; break;
@ -389,11 +451,13 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) {
// Sometimes the decoder will fail on a packet. I think it's // Sometimes the decoder will fail on a packet. I think it's
// looking for cross-packet frames and failing. If you run it again // looking for cross-packet frames and failing. If you run it again
// on the same packet it'll work though. // on the same packet it'll work though.
XELOGAPU("APU failed to decode packet (returned %.8X)", -read_bytes);
--decode_attempts_remaining; --decode_attempts_remaining;
} }
} }
if (!decode_attempts_remaining) { if (!decode_attempts_remaining) {
XELOGAPU("AudioSystem: libav failed to decode packet (returned %.8X)", -read_bytes);
// Failed out. // Failed out.
if (data.input_buffer_0_valid || data.input_buffer_1_valid) { 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? // 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; break;
} }
} }
data.output_buffer_write_offset += uint32_t(read_bytes) / 256; 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 we need more data and the input buffers have it, grab it.
if (read_bytes) { if (read_bytes) {
@ -426,10 +495,8 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) {
// See if we've finished with the input. // See if we've finished with the input.
// Block count is in packets, so expand by packet size. // Block count is in packets, so expand by packet size.
uint32_t input_size_0_bytes = uint32_t input_size_0_bytes = (data.input_buffer_0_packet_count) * 2048;
(data.input_buffer_0_block_count) * 2048; uint32_t input_size_1_bytes = (data.input_buffer_1_packet_count) * 2048;
uint32_t input_size_1_bytes =
(data.input_buffer_1_block_count) * 2048;
// Total input size // Total input size
uint32_t input_size_bytes = input_size_0_bytes + input_size_1_bytes; uint32_t input_size_bytes = input_size_0_bytes + input_size_1_bytes;
@ -437,8 +504,7 @@ void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) {
// Input read offset is in bits. Typically starts at 32 (4 bytes). // Input read offset is in bits. Typically starts at 32 (4 bytes).
// "Sequence" offset - used internally for WMA Pro decoder. // "Sequence" offset - used internally for WMA Pro decoder.
// Just the read offset. // Just the read offset.
uint32_t seq_offset_bytes = uint32_t seq_offset_bytes = (data.input_buffer_read_offset & ~0x7FF) / 8;
(data.input_buffer_read_offset & ~0x7FF) / 8;
if (seq_offset_bytes < input_size_bytes) { if (seq_offset_bytes < input_size_bytes) {
// Setup input offset and input buffer. // Setup input offset and input buffer.
@ -529,16 +595,15 @@ void AudioSystem::WriteRegister(uint32_t addr, uint64_t value) {
auto context_ptr = memory()->TranslateVirtual(context.guest_ptr); auto context_ptr = memory()->TranslateVirtual(context.guest_ptr);
XMAContextData data(context_ptr); XMAContextData data(context_ptr);
XELOGAPU( XELOGAPU("AudioSystem: kicking context %d (%d/%d bytes)", context_id,
"AudioSystem: kicking context %d (%d/%d bytes)", context_id,
(data.input_buffer_read_offset & ~0x7FF) / 8, (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); * XMAContextData::kBytesPerBlock);
// Reset valid flags so our audio decoder knows to process this one. // 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_0_valid = data.input_buffer_0_ptr != 0;
data.input_buffer_1_valid = data.input_buffer_1_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); data.Store(context_ptr);

View File

@ -44,9 +44,9 @@ struct XMAContextData {
static const uint32_t kSamplesPerSubframe = 128; static const uint32_t kSamplesPerSubframe = 128;
// DWORD 0 // DWORD 0
uint32_t input_buffer_0_block_count : 12; // XMASetInputBuffer0, number of uint32_t input_buffer_0_packet_count : 12; // XMASetInputBuffer0, number of
// 2KB blocks. AKA SizeRead0 // 2KB packets. Max 4095 packets.
// Maximum 4095 packets. // These packets form a block.
uint32_t loop_count : 8; // +12bit, XMASetLoopData NumLoops uint32_t loop_count : 8; // +12bit, XMASetLoopData NumLoops
uint32_t input_buffer_0_valid : 1; // +20bit, XMAIsInputBuffer0Valid uint32_t input_buffer_0_valid : 1; // +20bit, XMAIsInputBuffer0Valid
uint32_t input_buffer_1_valid : 1; // +21bit, XMAIsInputBuffer1Valid uint32_t input_buffer_1_valid : 1; // +21bit, XMAIsInputBuffer1Valid
@ -56,8 +56,9 @@ struct XMAContextData {
// AKA OffsetWrite // AKA OffsetWrite
// DWORD 1 // DWORD 1
uint32_t input_buffer_1_block_count : 12; // XMASetInputBuffer1, number of uint32_t input_buffer_1_packet_count : 12; // XMASetInputBuffer1, number of
// 2KB blocks. // 2KB packets. Max 4095 packets.
// These packets form a block.
uint32_t loop_subframe_end : 2; // +12bit, XMASetLoopData uint32_t loop_subframe_end : 2; // +12bit, XMASetLoopData
uint32_t unk_dword_1_a : 3; // ? might be loop_subframe_skip uint32_t unk_dword_1_a : 3; // ? might be loop_subframe_skip
uint32_t loop_subframe_skip : 3; // +17bit, XMASetLoopData might be uint32_t loop_subframe_skip : 3; // +17bit, XMASetLoopData might be
@ -65,8 +66,8 @@ struct XMAContextData {
uint32_t subframe_decode_count : 4; // +20bit might be subframe_skip_count uint32_t subframe_decode_count : 4; // +20bit might be subframe_skip_count
uint32_t unk_dword_1_b : 3; // ? NumSubframesToSkip/NumChannels(?) uint32_t unk_dword_1_b : 3; // ? NumSubframesToSkip/NumChannels(?)
uint32_t sample_rate : 2; // +27bit enum of sample rates uint32_t sample_rate : 2; // +27bit enum of sample rates
uint32_t is_stereo : 1; // +29bit might be NumChannels uint32_t is_stereo : 1; // +29bit
uint32_t unk_dword_1_c : 1; // ? part of NumChannels? uint32_t unk_dword_1_c : 1; // +29bit
uint32_t output_buffer_valid : 1; // +31bit, XMAIsOutputBufferValid uint32_t output_buffer_valid : 1; // +31bit, XMAIsOutputBufferValid
// DWORD 2 // DWORD 2

View File

@ -104,9 +104,9 @@ SHIM_CALL XMAInitializeContext_shim(PPCContext* ppc_context,
XMAContextData context(SHIM_MEM_ADDR(context_ptr)); XMAContextData context(SHIM_MEM_ADDR(context_ptr));
context.input_buffer_0_ptr = SHIM_MEM_32(context_init_ptr + 0 * 4); 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_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.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_ptr = SHIM_MEM_32(context_init_ptr + 5 * 4);
context.output_buffer_block_count = SHIM_MEM_32(context_init_ptr + 6 * 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)); XMAContextData context(SHIM_MEM_ADDR(context_ptr));
context.input_buffer_0_ptr = buffer_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_read_offset = 32; // in bits
context.input_buffer_0_valid = buffer_ptr ? 1 : 0; 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)); XMAContextData context(SHIM_MEM_ADDR(context_ptr));
context.input_buffer_1_ptr = buffer_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_read_offset = 32; // in bits
context.input_buffer_1_valid = buffer_ptr ? 1 : 0; 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", XMASetLoopData, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetInputBufferReadOffset, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetInputBufferReadOffset, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBufferReadOffset, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBufferReadOffset, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer0Valid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer0Valid, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0Valid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer0Valid, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer1Valid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsInputBuffer1Valid, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1Valid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetInputBuffer1Valid, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsOutputBufferValid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAIsOutputBufferValid, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferValid, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferValid, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferReadOffset, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMASetOutputBufferReadOffset, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferReadOffset, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferReadOffset, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferWriteOffset, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetOutputBufferWriteOffset, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetPacketMetadata, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAGetPacketMetadata, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMAEnableContext, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMAEnableContext, state);
SHIM_SET_MAPPING("xboxkrnl.exe", XMADisableContext, state); SHIM_SET_MAPPING("xboxkrnl.exe", XMADisableContext, state);