Merge pull request #237 from DrChat/audio_decoding

Cleanup Audio System Code
This commit is contained in:
Ben Vanik 2015-06-03 09:16:07 -07:00
commit 08fc703b73
6 changed files with 96 additions and 68 deletions

View File

@ -75,7 +75,7 @@ int AudioDecoder::Initialize() {
// Initialize these to 0. They'll actually be set later. // Initialize these to 0. They'll actually be set later.
context_->channels = 0; context_->channels = 0;
context_->sample_rate = 0; context_->sample_rate = 0;
context_->block_align = XMAContextData::kBytesPerBlock; context_->block_align = XMAContextData::kBytesPerPacket;
// Extra data passed to the decoder // Extra data passed to the decoder
context_->extradata_size = 18; 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 AudioDecoder::PreparePacket(uint8_t *input, size_t seq_offset, size_t size,
int sample_rate, int channels) { int sample_rate, int channels) {
if (size != XMAContextData::kBytesPerBlock) { if (size != XMAContextData::kBytesPerPacket) {
// Invalid packet size! // Invalid packet size!
assert_always(); assert_always();
return 1; return 1;
@ -116,7 +116,7 @@ int AudioDecoder::PreparePacket(uint8_t *input, size_t seq_offset, size_t size,
(*((int *)packet_data_) & 0xFFFEFF08); (*((int *)packet_data_) & 0xFFFEFF08);
packet_->data = packet_data_; packet_->data = packet_data_;
packet_->size = XMAContextData::kBytesPerBlock; packet_->size = XMAContextData::kBytesPerPacket;
// Re-initialize the context with new sample rate and channels // Re-initialize the context with new sample rate and channels
if (context_->sample_rate != sample_rate || context_->channels != channels) { if (context_->sample_rate != sample_rate || context_->channels != channels) {

View File

@ -55,7 +55,7 @@ class AudioDecoder {
uint8_t* current_frame_; uint8_t* current_frame_;
uint32_t frame_samples_size_; uint32_t frame_samples_size_;
uint8_t packet_data_[XMAContextData::kBytesPerBlock]; uint8_t packet_data_[XMAContextData::kBytesPerPacket];
}; };
} // namespace apu } // namespace apu

View File

@ -333,13 +333,7 @@ void AudioSystem::UnregisterClient(size_t index) {
void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) { void AudioSystem::ProcessXmaContext(XMAContext& context, XMAContextData& data) {
SCOPE_profile_cpu_f("apu"); SCOPE_profile_cpu_f("apu");
// Translate pointers for future use. // Translate this 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;
uint8_t* out = memory()->TranslatePhysical(data.output_buffer_ptr); uint8_t* out = memory()->TranslatePhysical(data.output_buffer_ptr);
// What I see: // 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_write_offset_bytes = data.output_buffer_write_offset * 256;
uint32_t output_read_offset_bytes = data.output_buffer_read_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 size_t output_remaining_bytes
= out_buffer.DistanceToOffset(output_read_offset_bytes); = output_buffer.DistanceToOffset(output_read_offset_bytes);
if (!output_remaining_bytes) { if (!output_remaining_bytes) {
// Can't write any more data. Break. // 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, read_bytes = context.decoder->DecodePacket(tmp_buff, 0,
output_remaining_bytes); output_remaining_bytes);
if (read_bytes >= 0) { if (read_bytes >= 0) {
out_buffer.Write(tmp_buff, read_bytes); output_buffer.Write(tmp_buff, read_bytes);
// Ok. // Ok.
break; 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 we need more data and the input buffers have it, grab it.
if (read_bytes) { if (read_bytes) {
// Still outputting. // Haven't finished with current packet.
continue; continue;
} else if (data.input_buffer_0_valid || data.input_buffer_1_valid) { } else if (data.input_buffer_0_valid || data.input_buffer_1_valid) {
// Done with previous packet, so grab a new one. // Done with previous packet, so grab a new one.
int sample_rate = 0; int ret = PrepareXMAPacket(context, data);
if (data.sample_rate == 0) { if (ret <= 0) {
sample_rate = 24000; // No more data (but may have prepared a packet)
} 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).
data.input_buffer_0_valid = 0; data.input_buffer_0_valid = 0;
data.input_buffer_1_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 // free60 may be useful here, however it looks like it's using a different
// piece of hardware: // piece of hardware:
// https://github.com/Free60Project/libxenon/blob/master/libxenon/drivers/xenon_sound/sound.c // 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, XELOGAPU("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_packet_count + data.input_buffer_1_packet_count) (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. // 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;
@ -597,6 +614,7 @@ void AudioSystem::WriteRegister(uint32_t addr, uint64_t value) {
data.input_buffer_0_valid = 0; data.input_buffer_0_valid = 0;
data.input_buffer_1_valid = 0; data.input_buffer_1_valid = 0;
data.output_buffer_valid = 0; data.output_buffer_valid = 0;
data.output_buffer_read_offset = 31;
data.Store(context_ptr); data.Store(context_ptr);
context.lock.unlock(); context.lock.unlock();

View File

@ -39,7 +39,7 @@ class AudioDecoder;
// http://pastebin.com/9amqJ2kQ // http://pastebin.com/9amqJ2kQ
struct XMAContextData { struct XMAContextData {
static const uint32_t kSize = 64; 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 kSamplesPerFrame = 512;
static const uint32_t kSamplesPerSubframe = 128; static const uint32_t kSamplesPerSubframe = 128;
@ -155,6 +155,7 @@ class AudioSystem {
void DecoderThreadMain(); void DecoderThreadMain();
void ProcessXmaContext(XMAContext& context, XMAContextData& data); void ProcessXmaContext(XMAContext& context, XMAContextData& data);
int PrepareXMAPacket(XMAContext& context, XMAContextData& data);
static uint64_t MMIOReadRegisterThunk(void* ppc_context, AudioSystem* as, static uint64_t MMIOReadRegisterThunk(void* ppc_context, AudioSystem* as,
uint32_t addr) { uint32_t addr) {

View File

@ -16,7 +16,7 @@ RingBuffer::RingBuffer(uint8_t *raw_buffer, size_t size, size_t write_offset)
size_(size), size_(size),
write_offset_(write_offset) {} 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 bytes_written = 0;
size_t input_offset = 0; size_t input_offset = 0;
size_t bytes_to_write = 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; input_offset = bytes_to_write;
write_offset_ += bytes_to_write; write_offset_ += bytes_to_write;
bytes_written += bytes_to_write;
// Wraparound (begin -> num_bytes) // Wraparound (begin -> num_bytes)
if (input_offset < num_bytes) { if (input_offset < num_bytes) {
bytes_to_write = num_bytes - input_offset; bytes_to_write = num_bytes - input_offset;
std::memcpy(raw_buffer_, buffer + input_offset, bytes_to_write); std::memcpy(raw_buffer_, buffer + input_offset, bytes_to_write);
write_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) { 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_) { if (offset < size_ && offset >= write_offset_) {
// Doesn't wraparound. // Doesn't wraparound.
return offset - write_offset_; return offset - write_offset_;

View File

@ -20,7 +20,7 @@ class RingBuffer {
public: public:
RingBuffer(uint8_t *raw_buffer, size_t size, size_t write_offset = 0); 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); size_t DistanceToOffset(size_t offset);