commit
88fd97f30b
|
@ -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_;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue