From f2b2a22687ad2c5a87cb20e2935ffe020e7fd24d Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 22 Aug 2015 11:10:16 -0500 Subject: [PATCH 1/9] Add gflags to projects that need it only --- premake5.lua | 17 +++++++++-------- src/xenia/app/premake5.lua | 4 ++++ src/xenia/apu/nop/premake5.lua | 1 + src/xenia/apu/premake5.lua | 7 ++++--- src/xenia/apu/xaudio2/premake5.lua | 1 + src/xenia/base/premake5.lua | 2 ++ src/xenia/cpu/backend/x64/premake5.lua | 1 + src/xenia/cpu/frontend/testing/premake5.lua | 3 +++ src/xenia/cpu/premake5.lua | 1 + src/xenia/cpu/testing/premake5.lua | 1 + src/xenia/debug/premake5.lua | 1 + src/xenia/debug/ui/premake5.lua | 3 +++ src/xenia/gpu/gl4/premake5.lua | 4 ++++ src/xenia/gpu/premake5.lua | 1 + src/xenia/hid/nop/premake5.lua | 1 + src/xenia/hid/premake5.lua | 1 + src/xenia/hid/winkey/premake5.lua | 1 + src/xenia/hid/xinput/premake5.lua | 1 + src/xenia/kernel/premake5.lua | 1 + src/xenia/premake5.lua | 1 + src/xenia/ui/gl/premake5.lua | 1 + src/xenia/ui/premake5.lua | 1 + src/xenia/vfs/premake5.lua | 1 + 23 files changed, 45 insertions(+), 11 deletions(-) diff --git a/premake5.lua b/premake5.lua index 75999667d..47ea4af6c 100644 --- a/premake5.lua +++ b/premake5.lua @@ -8,7 +8,6 @@ includedirs({ ".", "src", "third_party", - "build_tools/third_party/gflags/src", }) defines({ @@ -146,6 +145,15 @@ solution("xenia") end configurations({"Checked", "Debug", "Release"}) + -- Include third party files first so they don't have to deal with gflags. + include("third_party/capstone.lua") + include("third_party/elemental-forms") + include("third_party/glew.lua") + include("third_party/imgui.lua") + include("third_party/libav.lua") + include("third_party/xxhash.lua") + include("build_tools/third_party/gflags.lua") + include("src/xenia") include("src/xenia/app") include("src/xenia/apu") @@ -169,10 +177,3 @@ solution("xenia") include("src/xenia/hid/winkey") include("src/xenia/hid/xinput") end - - include("third_party/capstone.lua") - include("third_party/elemental-forms") - include("third_party/glew.lua") - include("third_party/imgui.lua") - include("third_party/xxhash.lua") - include("build_tools/third_party/gflags.lua") diff --git a/src/xenia/app/premake5.lua b/src/xenia/app/premake5.lua index 1de6feb17..7fb8001bc 100644 --- a/src/xenia/app/premake5.lua +++ b/src/xenia/app/premake5.lua @@ -32,6 +32,7 @@ project("xenia-app") }) includedirs({ project_root.."/third_party/elemental-forms/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() files({ @@ -52,6 +53,8 @@ project("xenia-app") "xenia-hid-winkey", "xenia-hid-xinput", }) + +--[[ filter("configurations:Checked") local libav_root = "../third_party/libav-xma-bin/lib/Debug" linkoptions({ @@ -64,6 +67,7 @@ project("xenia-app") libav_root.."/libavcodec.a", libav_root.."/libavutil.a", }) +]] filter("platforms:Windows") -- Only create the .user file if it doesn't already exist. diff --git a/src/xenia/apu/nop/premake5.lua b/src/xenia/apu/nop/premake5.lua index 068234586..b11bdacc5 100644 --- a/src/xenia/apu/nop/premake5.lua +++ b/src/xenia/apu/nop/premake5.lua @@ -13,5 +13,6 @@ project("xenia-apu-nop") defines({ }) includedirs({ + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/apu/premake5.lua b/src/xenia/apu/premake5.lua index 68ed44140..8fa1b6d42 100644 --- a/src/xenia/apu/premake5.lua +++ b/src/xenia/apu/premake5.lua @@ -7,14 +7,15 @@ project("xenia-apu") kind("StaticLib") language("C++") links({ - "libavcodec.a", - "libavutil.a", + "libavcodec", + "libavutil", "xenia-base", }) defines({ }) includedirs({ - project_root.."/third_party/libav-xma-bin/include/", + project_root.."/third_party/libav/include/", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/apu/xaudio2/premake5.lua b/src/xenia/apu/xaudio2/premake5.lua index 7054e7419..8eb33a010 100644 --- a/src/xenia/apu/xaudio2/premake5.lua +++ b/src/xenia/apu/xaudio2/premake5.lua @@ -12,5 +12,6 @@ project("xenia-apu-xaudio2") defines({ }) includedirs({ + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/base/premake5.lua b/src/xenia/base/premake5.lua index 155c68613..0499fc945 100644 --- a/src/xenia/base/premake5.lua +++ b/src/xenia/base/premake5.lua @@ -8,6 +8,7 @@ project("xenia-base") defines({ }) includedirs({ + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() removefiles({"main_*.cc"}) @@ -17,6 +18,7 @@ project("xenia-base") test_suite("xenia-base-tests", project_root, ".", { includedirs = { + project_root.."/build_tools/third_party/gflags/src", }, links = { "xenia-base", diff --git a/src/xenia/cpu/backend/x64/premake5.lua b/src/xenia/cpu/backend/x64/premake5.lua index 795946cc9..1ad85ce55 100644 --- a/src/xenia/cpu/backend/x64/premake5.lua +++ b/src/xenia/cpu/backend/x64/premake5.lua @@ -21,5 +21,6 @@ project("xenia-cpu-backend-x64") }) includedirs({ project_root.."/third_party/capstone/include", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/cpu/frontend/testing/premake5.lua b/src/xenia/cpu/frontend/testing/premake5.lua index afce34fba..ffb8f38e1 100644 --- a/src/xenia/cpu/frontend/testing/premake5.lua +++ b/src/xenia/cpu/frontend/testing/premake5.lua @@ -24,6 +24,9 @@ project("xenia-cpu-frontend-tests") files({ "*.s", }) + includedirs({ + project_root.."/build_tools/third_party/gflags/src", + }) filter("files:*.s") flags({"ExcludeFromBuild"}) filter("platforms:Windows") diff --git a/src/xenia/cpu/premake5.lua b/src/xenia/cpu/premake5.lua index 14c731837..d92540557 100644 --- a/src/xenia/cpu/premake5.lua +++ b/src/xenia/cpu/premake5.lua @@ -11,6 +11,7 @@ project("xenia-cpu") }) includedirs({ project_root.."/third_party/llvm/include", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() local_platform_files("backend") diff --git a/src/xenia/cpu/testing/premake5.lua b/src/xenia/cpu/testing/premake5.lua index bbef37b84..796b51064 100644 --- a/src/xenia/cpu/testing/premake5.lua +++ b/src/xenia/cpu/testing/premake5.lua @@ -3,6 +3,7 @@ include(project_root.."/build_tools") test_suite("xenia-cpu-tests", project_root, ".", { includedirs = { + project_root.."/build_tools/third_party/gflags/src", }, links = { "xenia-base", diff --git a/src/xenia/debug/premake5.lua b/src/xenia/debug/premake5.lua index 0a2e8da88..8f8403580 100644 --- a/src/xenia/debug/premake5.lua +++ b/src/xenia/debug/premake5.lua @@ -13,6 +13,7 @@ project("xenia-debug") defines({ }) includedirs({ + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() recursive_platform_files("proto") diff --git a/src/xenia/debug/ui/premake5.lua b/src/xenia/debug/ui/premake5.lua index 508d4ccb8..a3d845bef 100644 --- a/src/xenia/debug/ui/premake5.lua +++ b/src/xenia/debug/ui/premake5.lua @@ -34,6 +34,7 @@ project("xenia-debug-ui") }) includedirs({ project_root.."/third_party/elemental-forms/src", + project_root.."/build_tools/third_party/gflags/src", }) recursive_platform_files() files({ @@ -48,6 +49,7 @@ project("xenia-debug-ui") project_root.."/third_party/elemental-forms", }) +--[[ filter("configurations:Checked") local libav_root = "../third_party/libav-xma-bin/lib/Debug" linkoptions({ @@ -60,6 +62,7 @@ project("xenia-debug-ui") libav_root.."/libavcodec.a", libav_root.."/libavutil.a", }) +]] filter("platforms:Windows") debugdir(project_root) diff --git a/src/xenia/gpu/gl4/premake5.lua b/src/xenia/gpu/gl4/premake5.lua index 643896d3d..d21887d88 100644 --- a/src/xenia/gpu/gl4/premake5.lua +++ b/src/xenia/gpu/gl4/premake5.lua @@ -21,6 +21,7 @@ project("xenia-gpu-gl4") }) includedirs({ project_root.."/third_party/elemental-forms/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() @@ -62,12 +63,14 @@ project("xenia-gpu-gl4-trace-viewer") }) includedirs({ project_root.."/third_party/elemental-forms/src", + project_root.."/build_tools/third_party/gflags/src", }) files({ "trace_viewer_main.cc", "../../base/main_"..platform_suffix..".cc", }) +--[[ filter("configurations:Checked") local libav_root = "../third_party/libav-xma-bin/lib/Debug" linkoptions({ @@ -80,6 +83,7 @@ project("xenia-gpu-gl4-trace-viewer") libav_root.."/libavcodec.a", libav_root.."/libavutil.a", }) +]] filter("platforms:Windows") debugdir(project_root) diff --git a/src/xenia/gpu/premake5.lua b/src/xenia/gpu/premake5.lua index 66d919f30..dc1d339f1 100644 --- a/src/xenia/gpu/premake5.lua +++ b/src/xenia/gpu/premake5.lua @@ -16,5 +16,6 @@ project("xenia-gpu") }) includedirs({ project_root.."/third_party/elemental-forms/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/hid/nop/premake5.lua b/src/xenia/hid/nop/premake5.lua index 7fec94cc3..baf417d5f 100644 --- a/src/xenia/hid/nop/premake5.lua +++ b/src/xenia/hid/nop/premake5.lua @@ -13,5 +13,6 @@ project("xenia-hid-nop") defines({ }) includedirs({ + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/hid/premake5.lua b/src/xenia/hid/premake5.lua index 45f3f7bcd..54052f94d 100644 --- a/src/xenia/hid/premake5.lua +++ b/src/xenia/hid/premake5.lua @@ -12,5 +12,6 @@ project("xenia-hid") defines({ }) includedirs({ + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/hid/winkey/premake5.lua b/src/xenia/hid/winkey/premake5.lua index b30122d2a..44736b50d 100644 --- a/src/xenia/hid/winkey/premake5.lua +++ b/src/xenia/hid/winkey/premake5.lua @@ -13,6 +13,7 @@ project("xenia-hid-winkey") defines({ }) includedirs({ + project_root.."/build_tools/third_party/gflags/src", project_root.."/third_party/elemental-forms/src", }) local_platform_files() diff --git a/src/xenia/hid/xinput/premake5.lua b/src/xenia/hid/xinput/premake5.lua index adb3f758c..568171b6f 100644 --- a/src/xenia/hid/xinput/premake5.lua +++ b/src/xenia/hid/xinput/premake5.lua @@ -13,5 +13,6 @@ project("xenia-hid-xinput") defines({ }) includedirs({ + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/kernel/premake5.lua b/src/xenia/kernel/premake5.lua index 509d39a72..89141bf4b 100644 --- a/src/xenia/kernel/premake5.lua +++ b/src/xenia/kernel/premake5.lua @@ -20,6 +20,7 @@ project("xenia-kernel") }) includedirs({ project_root.."/third_party/elemental-forms/src", + project_root.."/build_tools/third_party/gflags/src", }) recursive_platform_files() files({ diff --git a/src/xenia/premake5.lua b/src/xenia/premake5.lua index 9fc178e71..4834319c9 100644 --- a/src/xenia/premake5.lua +++ b/src/xenia/premake5.lua @@ -14,5 +14,6 @@ project("xenia-core") }) includedirs({ project_root.."/third_party/elemental-forms/src", + project_root.."/build_tools/third_party/gflags/src", }) files({"*.h", "*.cc"}) diff --git a/src/xenia/ui/gl/premake5.lua b/src/xenia/ui/gl/premake5.lua index 4d773ce9a..346f7ade0 100644 --- a/src/xenia/ui/gl/premake5.lua +++ b/src/xenia/ui/gl/premake5.lua @@ -18,5 +18,6 @@ project("xenia-ui-gl") }) includedirs({ project_root.."/third_party/elemental-forms/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/ui/premake5.lua b/src/xenia/ui/premake5.lua index 9e4fed1ae..119458987 100644 --- a/src/xenia/ui/premake5.lua +++ b/src/xenia/ui/premake5.lua @@ -14,5 +14,6 @@ project("xenia-ui") }) includedirs({ project_root.."/third_party/elemental-forms/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/vfs/premake5.lua b/src/xenia/vfs/premake5.lua index b5659dcf8..9a5b2b03c 100644 --- a/src/xenia/vfs/premake5.lua +++ b/src/xenia/vfs/premake5.lua @@ -12,5 +12,6 @@ project("xenia-vfs") defines({ }) includedirs({ + project_root.."/build_tools/third_party/gflags/src", }) recursive_platform_files() From 0f9cd8cfb3cdd6f8a88e49b591835097d1b5eac3 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 22 Aug 2015 11:11:57 -0500 Subject: [PATCH 2/9] New WIP audio decoder --- .gitmodules | 3 + src/xenia/apu/xma_context.cc | 340 +++++++++++++++++-------- src/xenia/apu/xma_context.h | 15 +- src/xenia/kernel/xboxkrnl_audio_xma.cc | 2 +- third_party/libav | 1 + 5 files changed, 245 insertions(+), 116 deletions(-) create mode 160000 third_party/libav diff --git a/.gitmodules b/.gitmodules index bd40f6179..5757ad53b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -19,3 +19,6 @@ [submodule "build_tools"] path = build_tools url = https://github.com/xenia-project/build-tools.git +[submodule "third_party/libav"] + path = third_party/libav + url = https://github.com/xenia-project/libav.git diff --git a/src/xenia/apu/xma_context.cc b/src/xenia/apu/xma_context.cc index 7fbe08f17..2bc1c6e55 100644 --- a/src/xenia/apu/xma_context.cc +++ b/src/xenia/apu/xma_context.cc @@ -13,13 +13,17 @@ #include #include "xenia/apu/xma_decoder.h" +#include "xenia/apu/xma_helpers.h" #include "xenia/base/logging.h" #include "xenia/base/ring_buffer.h" #include "xenia/profiling.h" extern "C" { #include "libavcodec/avcodec.h" +#include "libavcodec/xma2dec.h" #include "libavutil/channel_layout.h" + +extern AVCodec ff_xma2_decoder; } // extern "C" // Credits for most of this code goes to: @@ -50,14 +54,8 @@ int XmaContext::Setup(uint32_t id, Memory* memory, uint32_t guest_ptr) { memory_ = memory; guest_ptr_ = guest_ptr; - static bool avcodec_initialized = false; - if (!avcodec_initialized) { - avcodec_register_all(); - avcodec_initialized = true; - } - // Allocate important stuff. - codec_ = avcodec_find_decoder(AV_CODEC_ID_WMAPRO); + codec_ = &ff_xma2_decoder; if (!codec_) { return 1; } @@ -91,7 +89,7 @@ int XmaContext::Setup(uint32_t id, Memory* memory, uint32_t guest_ptr) { // Current frame stuff whatever // samples per frame * 2 max channels * output bytes - current_frame_ = new uint8_t[kSamplesPerFrame * 2 * 2]; + current_frame_ = new uint8_t[kSamplesPerFrame * kBytesPerSample * 2]; current_frame_pos_ = 0; frame_samples_size_ = 0; @@ -119,11 +117,10 @@ void XmaContext::Enable() { auto context_ptr = memory()->TranslateVirtual(guest_ptr()); XMA_CONTEXT_DATA data(context_ptr); - XELOGAPU( - "XmaContext: kicking context %d (%d/%d bytes)", id(), - (data.input_buffer_read_offset & ~0x7FF) / 8, - (data.input_buffer_0_packet_count + data.input_buffer_1_packet_count) * - kBytesPerPacket); + XELOGAPU("XmaContext: kicking context %d (%d/%d bits)", id(), + data.input_buffer_read_offset, (data.input_buffer_0_packet_count + + data.input_buffer_1_packet_count) * + kBytesPerPacket * 8); data.Store(context_ptr); @@ -227,6 +224,26 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { return; } + // XAudio Loops + // loop_count: + // - XAUDIO2_MAX_LOOP_COUNT = 254 + // - XAUDIO2_LOOP_INFINITE = 255 + // loop_start/loop_end are bit offsets to a specific frame + //assert_true(data->loop_count == 0); + + // 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; + + size_t input_buffer_0_size = + data->input_buffer_0_packet_count * kBytesPerPacket; + size_t input_buffer_1_size = + data->input_buffer_1_packet_count * kBytesPerPacket; + // 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. @@ -246,76 +263,175 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { // Decode until we can't write any more data. while (output_remaining_bytes > 0) { - // This'll copy audio samples into the output buffer. - // The samples need to be 2 bytes long! - // Copies one frame at a time, so keep calling this until size == 0. - int written_bytes = 0; - int decode_attempts_remaining = 3; - - uint8_t work_buffer[kOutputMaxSizeBytes]; - while (decode_attempts_remaining) { - size_t read_bytes = 0; - written_bytes = - DecodePacket(work_buffer, 0, output_remaining_bytes, &read_bytes); - if (written_bytes >= 0) { - // assert_true((written_bytes % 256) == 0); - auto written_bytes_rb = output_rb.Write(work_buffer, written_bytes); - assert_true(written_bytes == written_bytes_rb); - - // Ok. - break; - } else if (read_bytes % 2048 == 0) { - // 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. - --decode_attempts_remaining; - } else { - // Failed in the middle of a packet, do not retry! - decode_attempts_remaining = 0; - break; - } - } - - if (!decode_attempts_remaining) { - XELOGAPU("XmaContext: libav failed to decode packet (returned %.8X)", - -written_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? - written_bytes = 0; - DiscardPacket(); - } else { - // No data and hosed - bail. - break; - } - } - - data->output_buffer_write_offset = output_rb.write_offset() / 256; - output_remaining_bytes -= written_bytes; - - // If we need more data and the input buffers have it, grab it. - if (written_bytes) { - // Haven't finished with current packet. - continue; - } else if (data->input_buffer_0_valid || data->input_buffer_1_valid) { - // Done with previous packet, so grab a new one. - int ret = StartPacket(data); - if (ret <= 0) { - // No more data (but may have prepared a packet) - data->input_buffer_0_valid = 0; - data->input_buffer_1_valid = 0; - } - } else { - // Decoder is out of data and there's no more to give. + if (!data->input_buffer_0_valid && !data->input_buffer_1_valid) { + // Out of data. break; } + + int num_channels = data->is_stereo ? 2 : 1; + + // Check if we have part of a frame waiting (and the game hasn't jumped + // around) + if (current_frame_pos_ && + last_input_read_pos_ == data->input_buffer_read_offset) { + size_t to_write = std::min( + output_remaining_bytes, + ((size_t)kBytesPerFrame * num_channels - current_frame_pos_)); + output_rb.Write(current_frame_, to_write); + + current_frame_pos_ += to_write; + if (current_frame_pos_ >= kBytesPerFrame * num_channels) { + current_frame_pos_ = 0; + } + + data->output_buffer_write_offset = output_rb.write_offset() / 256; + output_remaining_bytes -= to_write; + continue; + } + + int block_last_frame = 0; // last frame in block? + int got_frame = 0; // successfully decoded a frame? + int frame_size = 0; + packet_->data = in0; + packet_->size = data->input_buffer_0_packet_count * 2048; + PrepareDecoder(in0, data->input_buffer_0_packet_count * 2048, + data->sample_rate, num_channels); + int len = xma2_decode_frame(context_, packet_, decoded_frame_, &got_frame, + &block_last_frame, &frame_size, + data->input_buffer_read_offset); + if (block_last_frame) { + data->input_buffer_0_valid = 0; + data->input_buffer_1_valid = 0; + data->output_buffer_valid = 0; + continue; + } + + if (len == AVERROR_EOF) { + // Screw this gtfo + data->input_buffer_0_valid = 0; + data->input_buffer_1_valid = 0; + data->output_buffer_valid = 0; + + continue; + } else if (len < 0 || !got_frame) { + // Oh no! Skip the frame and hope everything works. + data->input_buffer_read_offset += frame_size; + + continue; + } + + XELOGD("LEN: %d (%x)", len, len); + + data->input_buffer_read_offset += len; + last_input_read_pos_ = data->input_buffer_read_offset; + + // Copy to the output buffer. + // Successfully decoded a frame. + size_t written_bytes = 0; + if (got_frame) { + // Validity checks. + if (decoded_frame_->nb_samples > kSamplesPerFrame) { + return; + } else if (context_->sample_fmt != AV_SAMPLE_FMT_FLTP) { + return; + } + + // Check the returned buffer size. + if (av_samples_get_buffer_size(NULL, context_->channels, + decoded_frame_->nb_samples, + context_->sample_fmt, 1) != + context_->channels * decoded_frame_->nb_samples * sizeof(float)) { + return; + } + + // Loop through every sample, convert and drop it into the output array. + // If more than one channel, the game wants the samples from each channel + // interleaved next to each other. + uint32_t o = 0; + for (int i = 0; i < decoded_frame_->nb_samples; i++) { + for (int j = 0; j < context_->channels; j++) { + // Select the appropriate array based on the current channel. + auto sample_array = reinterpret_cast(decoded_frame_->data[j]); + + // Raw sample should be within [-1, 1]. + // Clamp it, just in case. + float raw_sample = xe::saturate(sample_array[i]); + + // Convert the sample and output it in big endian. + float scaled_sample = raw_sample * ((1 << 15) - 1); + int sample = static_cast(scaled_sample); + xe::store_and_swap(¤t_frame_[o++ * 2], + sample & 0xFFFF); + } + } + current_frame_pos_ = 0; + + if (output_remaining_bytes < kBytesPerFrame * num_channels) { + // Output buffer isn't big enough to store the entire frame! Write out a + // part of it. + current_frame_pos_ = output_remaining_bytes; + output_rb.Write(current_frame_, output_remaining_bytes); + + written_bytes = output_remaining_bytes; + } else { + output_rb.Write(current_frame_, kBytesPerFrame * num_channels); + + written_bytes = kBytesPerFrame * num_channels; + } + } + + output_remaining_bytes -= written_bytes; + data->output_buffer_write_offset = output_rb.write_offset() / 256; } // The game will kick us again with a new output buffer later. data->output_buffer_valid = 0; } +uint32_t XmaContext::GetFramePacketNumber(uint8_t* block, size_t size, + size_t bit_offset) { + size *= 8; + if (bit_offset >= size) { + // Not good :( + assert_always(); + return -1; + } + + size_t byte_offset = bit_offset >> 3; + size_t packet_number = byte_offset / kBytesPerPacket; + + return (uint32_t)packet_number; +} + +int XmaContext::PrepareDecoder(uint8_t* block, size_t size, int sample_rate, + int channels) { + // Sanity check: Packet metadata is always 1 for XMA2 + assert_true((block[2] & 0x7) == 1); + + sample_rate = GetSampleRate(sample_rate); + + // Re-initialize the context with new sample rate and channels. + if (context_->sample_rate != sample_rate || context_->channels != channels) { + // We have to reopen the codec so it'll realloc whatever data it needs. + // TODO(DrChat): Find a better way. + avcodec_close(context_); + + context_->sample_rate = sample_rate; + context_->channels = channels; + extra_data_.channel_mask = + channels == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO; + + if (avcodec_open2(context_, codec_, NULL) < 0) { + XELOGE("XmaContext: Failed to reopen libav context"); + return 1; + } + } + + av_frame_unref(decoded_frame_); + + return 0; +} + int XmaContext::StartPacket(XMA_CONTEXT_DATA* data) { // Translate pointers for future use. uint8_t* in0 = data->input_buffer_0_valid @@ -340,41 +456,49 @@ int XmaContext::StartPacket(XMA_CONTEXT_DATA* data) { // Total input size uint32_t input_size_bytes = input_size_0_bytes + input_size_1_bytes; + // Calculate the first frame offset we need to decode. + uint32_t frame_offset_bits = (data->input_buffer_read_offset % (2048 * 8)); + // 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; + // NOTE: Read offset may not be at the first frame in a packet! + uint32_t packet_offset_bytes = (data->input_buffer_read_offset & ~0x7FF) / 8; + if (packet_offset_bytes % 2048 != 0) { + packet_offset_bytes -= packet_offset_bytes % 2048; + } + uint32_t input_remaining_bytes = input_size_bytes - packet_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 && input_size_1_bytes) { - // Size overlap, select input buffer 1. - // TODO(DrChat): 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; - assert_true(input_offset_bytes % 2048 == 0); - PreparePacket(packet, seq_offset_bytes, kBytesPerPacket, sample_rate, - channels); - data->input_buffer_read_offset += kBytesPerPacket * 8; - - input_remaining_bytes -= kBytesPerPacket; - if (input_remaining_bytes <= 0) { - // Used the last of the data but prepared a packet. - return 0; - } - } else { + if (packet_offset_bytes >= input_size_bytes) { // No more data available and no packet prepared. return -1; } + // Setup input offset and input buffer. + uint32_t input_offset_bytes = packet_offset_bytes; + auto input_buffer = in0; + + if (packet_offset_bytes >= input_size_0_bytes && input_size_1_bytes) { + // Size overlap, select input buffer 1. + // TODO(DrChat): 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; + assert_true(input_offset_bytes % 2048 == 0); + PreparePacket(packet, packet_offset_bytes, kBytesPerPacket, sample_rate, + channels); + + data->input_buffer_read_offset += kBytesPerPacket * 8; + + input_remaining_bytes -= kBytesPerPacket; + if (input_remaining_bytes <= 0) { + // Used the last of the data but prepared a packet. + return 0; + } + return input_remaining_bytes; } @@ -390,15 +514,11 @@ int XmaContext::PreparePacket(uint8_t* input, size_t seq_offset, size_t size, return 1; } - std::memcpy(packet_data_, input, size); + // Packet metadata is always 1 for XMA2 + assert_true((input[2] & 0x7) == 1); - // Modify the packet header so it's WMAPro compatible. - auto int_packet_data = reinterpret_cast(packet_data_); - *int_packet_data = - (((seq_offset & 0x7800) | 0x400) >> 7) | (*int_packet_data & 0xFFFEFF08); - - packet_->data = packet_data_; - packet_->size = kBytesPerPacket; + packet_->data = input; + packet_->size = (int)size; // Re-initialize the context with new sample rate and channels. if (context_->sample_rate != sample_rate || context_->channels != channels) { diff --git a/src/xenia/apu/xma_context.h b/src/xenia/apu/xma_context.h index 06e2299b8..7298ab168 100644 --- a/src/xenia/apu/xma_context.h +++ b/src/xenia/apu/xma_context.h @@ -60,8 +60,8 @@ struct XMA_CONTEXT_DATA { 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_start : 2; // +12bit, XMASetLoopData + uint32_t loop_subframe_end : 3; // +14bit, XMASetLoopData uint32_t loop_subframe_skip : 3; // +17bit, XMASetLoopData might be // subframe_decode_count uint32_t subframe_decode_count : 4; // +20bit might be subframe_skip_count @@ -91,7 +91,7 @@ struct XMA_CONTEXT_DATA { // DWORD 7 uint32_t output_buffer_ptr; // physical address // DWORD 8 - uint32_t overlap_add_ptr; // PtrOverlapAdd(?) + uint32_t work_buffer_ptr; // PtrOverlapAdd(?) // DWORD 9 // +0bit, XMAGetOutputBufferReadOffset AKA WriteBufferOffsetRead @@ -133,6 +133,7 @@ class XmaContext { static const uint32_t kBytesPerSample = 2; static const uint32_t kSamplesPerFrame = 512; static const uint32_t kSamplesPerSubframe = 128; + static const uint32_t kBytesPerFrame = kSamplesPerFrame * kBytesPerSample; static const uint32_t kBytesPerSubframe = kSamplesPerSubframe * kBytesPerSample; @@ -165,6 +166,10 @@ class XmaContext { static int GetSampleRate(int id); void DecodePackets(XMA_CONTEXT_DATA* data); + uint32_t GetFramePacketNumber(uint8_t* block, size_t size, size_t bit_offset); + int PrepareDecoder(uint8_t* block, size_t size, int sample_rate, + int channels); + int StartPacket(XMA_CONTEXT_DATA* data); int PreparePacket(uint8_t* input, size_t seq_offset, size_t size, @@ -189,11 +194,11 @@ class XmaContext { AVPacket* packet_ = nullptr; WmaProExtraData extra_data_; + // If we didn't finish writing a frame to the output buffer, this is the offset. size_t current_frame_pos_ = 0; + uint32_t last_input_read_pos_ = 0; // Last seen read buffer pos uint8_t* current_frame_ = nullptr; uint32_t frame_samples_size_ = 0; - - uint8_t packet_data_[kBytesPerPacket]; }; } // namespace apu diff --git a/src/xenia/kernel/xboxkrnl_audio_xma.cc b/src/xenia/kernel/xboxkrnl_audio_xma.cc index a7fec8cb7..e664e7ca4 100644 --- a/src/xenia/kernel/xboxkrnl_audio_xma.cc +++ b/src/xenia/kernel/xboxkrnl_audio_xma.cc @@ -171,7 +171,7 @@ SHIM_CALL XMASetLoopData_shim(PPCContext* ppc_context, context.loop_end = loop_data->loop_end; context.loop_count = loop_data->loop_count; context.loop_subframe_end = loop_data->loop_subframe_end; - context.loop_subframe_skip = loop_data->loop_subframe_end; + context.loop_subframe_skip = loop_data->loop_subframe_skip; context.Store(SHIM_MEM_ADDR(context_ptr)); diff --git a/third_party/libav b/third_party/libav new file mode 160000 index 000000000..4752bdcdb --- /dev/null +++ b/third_party/libav @@ -0,0 +1 @@ +Subproject commit 4752bdcdbaf61afc933e667a016bca4eb389ac21 From d8ed66c3368b42fb3990cd9325e0b36bf19f47ca Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Mon, 24 Aug 2015 19:42:27 -0500 Subject: [PATCH 3/9] More improvements to the XMA decoder (and included some forgotten files) --- src/xenia/apu/debug_visualizers.natvis | 9 ++ src/xenia/apu/xma_context.cc | 178 ++++++++++++++++++------- src/xenia/apu/xma_context.h | 9 +- src/xenia/apu/xma_decoder.cc | 32 ++++- src/xenia/apu/xma_helpers.h | 43 ++++++ third_party/libav | 2 +- 6 files changed, 221 insertions(+), 52 deletions(-) create mode 100644 src/xenia/apu/debug_visualizers.natvis create mode 100644 src/xenia/apu/xma_helpers.h diff --git a/src/xenia/apu/debug_visualizers.natvis b/src/xenia/apu/debug_visualizers.natvis new file mode 100644 index 000000000..095fce6ad --- /dev/null +++ b/src/xenia/apu/debug_visualizers.natvis @@ -0,0 +1,9 @@ + + + + + + id={id_}, allocated={is_allocated_}, enabled={is_enabled_} + + + diff --git a/src/xenia/apu/xma_context.cc b/src/xenia/apu/xma_context.cc index 2bc1c6e55..17f06cabc 100644 --- a/src/xenia/apu/xma_context.cc +++ b/src/xenia/apu/xma_context.cc @@ -215,6 +215,7 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { // Quick die if there's no data. if (!data->input_buffer_0_valid && !data->input_buffer_1_valid) { + XELOGAPU("Context %d: No valid input buffers!", id()); return; } @@ -224,25 +225,36 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { return; } + assert_zero(data->unk_dword_9); + // XAudio Loops // loop_count: // - XAUDIO2_MAX_LOOP_COUNT = 254 // - XAUDIO2_LOOP_INFINITE = 255 // loop_start/loop_end are bit offsets to a specific frame - //assert_true(data->loop_count == 0); // Translate pointers for future use. + // Sometimes the game will use rolling input buffers. If they do, we cannot + // assume they form a complete block! In addition, the buffers DO NOT have + // to be sequential! + // (bit.trip runner 2 does this) + // TODO: Collect partial frames into a buffer if the game uses rolling buffers, + // and present the full frame to libav when we get it. 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* current_input_buffer = in0; size_t input_buffer_0_size = data->input_buffer_0_packet_count * kBytesPerPacket; size_t input_buffer_1_size = data->input_buffer_1_packet_count * kBytesPerPacket; + size_t current_input_size = + data->current_buffer ? input_buffer_1_size : input_buffer_0_size; + size_t input_total_size = input_buffer_0_size + input_buffer_1_size; // 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 @@ -289,50 +301,111 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { continue; } - int block_last_frame = 0; // last frame in block? - int got_frame = 0; // successfully decoded a frame? + int invalid_frame = 0; // invalid frame? + int got_frame = 0; // successfully decoded a frame? int frame_size = 0; - packet_->data = in0; - packet_->size = data->input_buffer_0_packet_count * 2048; - PrepareDecoder(in0, data->input_buffer_0_packet_count * 2048, - data->sample_rate, num_channels); + packet_->data = current_input_buffer; + packet_->size = (int)current_input_size; + PrepareDecoder(in0, current_input_size, data->sample_rate, num_channels); int len = xma2_decode_frame(context_, packet_, decoded_frame_, &got_frame, - &block_last_frame, &frame_size, + &invalid_frame, &frame_size, 1, data->input_buffer_read_offset); - if (block_last_frame) { - data->input_buffer_0_valid = 0; - data->input_buffer_1_valid = 0; - data->output_buffer_valid = 0; - continue; + if (invalid_frame) { + // Invalid frame/packet: length header is 0x7FFF + // Sometimes there's frames in the middle of the stream flagged as + // invalid. + // Double-check to make sure we're not in the middle. + uint32_t frame_byte_offset = data->input_buffer_read_offset >> 3; + uint32_t packet_number = frame_byte_offset / 2048; + if (packet_number < data->input_buffer_0_packet_count - 1) { + // Okay. Skip to the beginning of the next packet. + packet_number++; + data->input_buffer_read_offset = (packet_number * 2048 * 8) + 32; + continue; + } + + // Last frame of the block. Swap buffers if necessary. + if (data->current_buffer == 0) { + if (data->input_buffer_1_valid) { + data->current_buffer++; + } else { + // End of input. + data->input_buffer_read_offset = input_total_size * 8; + } + + data->input_buffer_0_valid = 0; + return; + } else { + // End of input. + data->current_buffer = 0; + data->input_buffer_1_valid = 0; + data->input_buffer_read_offset = input_total_size * 8; + return; + } + } else if (got_frame && len > 0) { + // Valid frame. + // Check and see if we need to loop back to any spot. + if (data->loop_count > 0 && + data->input_buffer_read_offset == data->loop_end) { + // Loop back to the beginning. + data->input_buffer_read_offset = data->loop_start; + if (data->loop_count < 255) { + data->loop_count--; + } + } else { + data->input_buffer_read_offset += len; + if (data->current_buffer == 0 && + data->input_buffer_read_offset > input_buffer_0_size * 8) { + // Overflow? Setup next buffer. + data->current_buffer++; + data->input_buffer_0_valid = 0; + } else if (data->input_buffer_read_offset > input_total_size * 8) { + // Overflow! The game will fix up the read offset. + data->current_buffer = 0; + data->input_buffer_0_valid = 0; + data->input_buffer_1_valid = 0; + } + } } - if (len == AVERROR_EOF) { - // Screw this gtfo - data->input_buffer_0_valid = 0; - data->input_buffer_1_valid = 0; - data->output_buffer_valid = 0; + if ((len < 0 || !got_frame) && frame_size != 0) { + // Oh no! Skip the frame and hope everything works. + data->input_buffer_read_offset += frame_size; + data->input_buffer_read_offset = (uint32_t)xma2_correct_frame_offset( + in0, input_buffer_0_size, data->input_buffer_read_offset); continue; } else if (len < 0 || !got_frame) { - // Oh no! Skip the frame and hope everything works. - data->input_buffer_read_offset += frame_size; - - continue; + // Did not get frame and could not get frame size. + data->input_buffer_0_valid = 0; + data->input_buffer_1_valid = 0; + return; } - XELOGD("LEN: %d (%x)", len, len); - - data->input_buffer_read_offset += len; + // Sometimes we may run up to <15 bits before the next packet. If this + // happens, we need to automatically advance to the next frame. + // We'll ask the XMA2 decoder to do this for us, since it's more qualified. + data->input_buffer_read_offset = (uint32_t)xma2_correct_frame_offset( + in0, input_buffer_0_size, data->input_buffer_read_offset); last_input_read_pos_ = data->input_buffer_read_offset; + if (data->input_buffer_read_offset == 0) { + // Invalid offset. Out of data. + data->input_buffer_0_valid = 0; + data->input_buffer_1_valid = 0; + } + // Copy to the output buffer. // Successfully decoded a frame. size_t written_bytes = 0; if (got_frame) { +#ifdef DEBUG // Validity checks. if (decoded_frame_->nb_samples > kSamplesPerFrame) { + XELOGAPU("Decoded frame has an invalid sample count!"); return; } else if (context_->sample_fmt != AV_SAMPLE_FMT_FLTP) { + XELOGAPU("libav decoder did not output floating point samples!"); return; } @@ -343,27 +416,11 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { context_->channels * decoded_frame_->nb_samples * sizeof(float)) { return; } +#endif - // Loop through every sample, convert and drop it into the output array. - // If more than one channel, the game wants the samples from each channel - // interleaved next to each other. - uint32_t o = 0; - for (int i = 0; i < decoded_frame_->nb_samples; i++) { - for (int j = 0; j < context_->channels; j++) { - // Select the appropriate array based on the current channel. - auto sample_array = reinterpret_cast(decoded_frame_->data[j]); - - // Raw sample should be within [-1, 1]. - // Clamp it, just in case. - float raw_sample = xe::saturate(sample_array[i]); - - // Convert the sample and output it in big endian. - float scaled_sample = raw_sample * ((1 << 15) - 1); - int sample = static_cast(scaled_sample); - xe::store_and_swap(¤t_frame_[o++ * 2], - sample & 0xFFFF); - } - } + // Convert the frame. + ConvertFrame((const float**)decoded_frame_->data, context_->channels, + decoded_frame_->nb_samples, current_frame_); current_frame_pos_ = 0; if (output_remaining_bytes < kBytesPerFrame * num_channels) { @@ -385,6 +442,7 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { } // The game will kick us again with a new output buffer later. + // It's important that we only invalidate this if we actually wrote to it!! data->output_buffer_valid = 0; } @@ -405,8 +463,8 @@ uint32_t XmaContext::GetFramePacketNumber(uint8_t* block, size_t size, int XmaContext::PrepareDecoder(uint8_t* block, size_t size, int sample_rate, int channels) { - // Sanity check: Packet metadata is always 1 for XMA2 - assert_true((block[2] & 0x7) == 1); + // Sanity check: Packet metadata is always 1 for XMA2/0 for XMA + assert_true((block[2] & 0x7) == 1 || (block[2] & 0x7) == 0); sample_rate = GetSampleRate(sample_rate); @@ -432,6 +490,32 @@ int XmaContext::PrepareDecoder(uint8_t* block, size_t size, int sample_rate, return 0; } +bool XmaContext::ConvertFrame(const float** samples, int num_channels, + int num_samples, uint8_t* output_buffer) { + // Loop through every sample, convert and drop it into the output array. + // If more than one channel, we need to interleave the samples from each + // channel next to each other. + // TODO: This can definitely be optimized with AVX/SSE intrinsics! + uint32_t o = 0; + for (int i = 0; i < num_samples; i++) { + for (int j = 0; j < num_channels; j++) { + // Select the appropriate array based on the current channel. + auto sample_array = samples[j]; + + // Raw sample should be within [-1, 1]. + // Clamp it, just in case. + float raw_sample = xe::saturate(sample_array[i]); + + // Convert the sample and output it in big endian. + float scaled_sample = raw_sample * ((1 << 15) - 1); + int sample = static_cast(scaled_sample); + xe::store_and_swap(&output_buffer[o++ * 2], sample & 0xFFFF); + } + } + + return true; +} + int XmaContext::StartPacket(XMA_CONTEXT_DATA* data) { // Translate pointers for future use. uint8_t* in0 = data->input_buffer_0_valid diff --git a/src/xenia/apu/xma_context.h b/src/xenia/apu/xma_context.h index 7298ab168..a0f769f38 100644 --- a/src/xenia/apu/xma_context.h +++ b/src/xenia/apu/xma_context.h @@ -60,8 +60,8 @@ struct XMA_CONTEXT_DATA { 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_start : 2; // +12bit, XMASetLoopData - uint32_t loop_subframe_end : 3; // +14bit, XMASetLoopData + uint32_t loop_subframe_start : 2; // +12bit, XMASetLoopData + uint32_t loop_subframe_end : 3; // +14bit, XMASetLoopData uint32_t loop_subframe_skip : 3; // +17bit, XMASetLoopData might be // subframe_decode_count uint32_t subframe_decode_count : 4; // +20bit might be subframe_skip_count @@ -77,10 +77,12 @@ struct XMA_CONTEXT_DATA { // DWORD 3 uint32_t loop_start : 26; // XMASetLoopData LoopStartOffset + // frame offset in bits uint32_t unk_dword_3 : 6; // ? ParserErrorStatus/ParserErrorSet(?) // DWORD 4 uint32_t loop_end : 26; // XMASetLoopData LoopEndOffset + // frame offset in bits uint32_t packet_metadata : 5; // XMAGetPacketMetadata uint32_t current_buffer : 1; // ? @@ -170,6 +172,9 @@ class XmaContext { int PrepareDecoder(uint8_t* block, size_t size, int sample_rate, int channels); + bool ConvertFrame(const float** samples, int num_channels, int num_samples, + uint8_t* output_buffer); + int StartPacket(XMA_CONTEXT_DATA* data); int PreparePacket(uint8_t* input, size_t seq_offset, size_t size, diff --git a/src/xenia/apu/xma_decoder.cc b/src/xenia/apu/xma_decoder.cc index d8bd47ef0..766ce231b 100644 --- a/src/xenia/apu/xma_decoder.cc +++ b/src/xenia/apu/xma_decoder.cc @@ -55,7 +55,34 @@ XmaDecoder::XmaDecoder(cpu::Processor* processor) XmaDecoder::~XmaDecoder() = default; void av_log_callback(void* avcl, int level, const char* fmt, va_list va) { - xe::LogLineVarargs('A', fmt, va); +#ifdef NDEBUG + if (level > AV_LOG_WARNING) { + return; + } +#endif + + char level_char = '?'; + switch (level) { + case AV_LOG_ERROR: + level_char = '!'; + break; + case AV_LOG_WARNING: + level_char = 'w'; + break; + case AV_LOG_INFO: + level_char = 'i'; + break; + case AV_LOG_VERBOSE: + level_char = 'v'; + break; + case AV_LOG_DEBUG: + level_char = 'd'; + break; + } + + StringBuffer buff; + buff.AppendVarargs(fmt, va); + xe::LogLineVarargs(level_char, "libav: %s", buff.GetString()); } X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { @@ -86,7 +113,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { } registers_.next_context = 1; - worker_running_ = true; + //worker_running_ = true; worker_thread_ = kernel::object_ref( new kernel::XHostThread(kernel_state, 128 * 1024, 0, [this]() { WorkerThreadMain(); @@ -213,6 +240,7 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) { uint32_t context_id = base_context_id + i; XmaContext& context = contexts_[context_id]; context.Enable(); + context.Work(); } } diff --git a/src/xenia/apu/xma_helpers.h b/src/xenia/apu/xma_helpers.h new file mode 100644 index 000000000..8efc9d1d1 --- /dev/null +++ b/src/xenia/apu/xma_helpers.h @@ -0,0 +1,43 @@ +/** +****************************************************************************** +* Xenia : Xbox 360 Emulator Research Project * +****************************************************************************** +* Copyright 2015 Ben Vanik. All rights reserved. * +* Released under the BSD license - see LICENSE in the root for more details. * +****************************************************************************** +*/ + +// This file contains some functions used to help parse XMA data. + +#ifndef XENIA_APU_XMA_HELPERS_H_ +#define XENIA_APU_XMA_HELPERS_H_ + +#include + +namespace xe { +namespace apu { +namespace xma { + +// Get number of frames that /begin/ in this packet. +uint32_t GetPacketFrameCount(uint8_t* packet) { + return (uint8_t)(packet[0] >> 2); +} + +// Get the first frame offset in bits +uint32_t GetPacketFrameOffset(uint8_t* packet) { + return (uint16_t)((packet[0] << 13) | (packet[1] << 5) | (packet[2] >> 3)) + 32; +} + +uint32_t GetPacketMetadata(uint8_t* packet) { + return (uint8_t)(packet[2] & 0x7); +} + +uint32_t GetPacketSkipCount(uint8_t* packet) { + return (uint8_t)(packet[3]); +} + +} // namespace xma +} // namespace apu +} // namespace xe + +#endif // XENIA_APU_XMA_HELPERS_H_ diff --git a/third_party/libav b/third_party/libav index 4752bdcdb..8be22f03d 160000 --- a/third_party/libav +++ b/third_party/libav @@ -1 +1 @@ -Subproject commit 4752bdcdbaf61afc933e667a016bca4eb389ac21 +Subproject commit 8be22f03d7e3c1663a66cc09375f840a7fc9a365 From 6c83b350030e218a848fac1cf4cedfc7d2aaa0dd Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 29 Aug 2015 20:57:42 -0500 Subject: [PATCH 4/9] Partial frame support. --- src/xenia/apu/xma_context.cc | 629 +++++++++++++++++------------------ src/xenia/apu/xma_context.h | 11 +- src/xenia/apu/xma_decoder.cc | 11 +- src/xenia/apu/xma_helpers.h | 7 +- src/xenia/base/bit_stream.cc | 143 ++++++++ src/xenia/base/bit_stream.h | 44 +++ third_party/libav.lua | 3 + 7 files changed, 518 insertions(+), 330 deletions(-) create mode 100644 src/xenia/base/bit_stream.cc create mode 100644 src/xenia/base/bit_stream.h create mode 100644 third_party/libav.lua diff --git a/src/xenia/apu/xma_context.cc b/src/xenia/apu/xma_context.cc index 17f06cabc..87ac2afa8 100644 --- a/src/xenia/apu/xma_context.cc +++ b/src/xenia/apu/xma_context.cc @@ -14,6 +14,7 @@ #include "xenia/apu/xma_decoder.h" #include "xenia/apu/xma_helpers.h" +#include "xenia/base/bit_stream.h" #include "xenia/base/logging.h" #include "xenia/base/ring_buffer.h" #include "xenia/profiling.h" @@ -87,6 +88,8 @@ int XmaContext::Setup(uint32_t id, Memory* memory, uint32_t guest_ptr) { context_->extradata_size = sizeof(extra_data_); context_->extradata = reinterpret_cast(&extra_data_); + partial_frame_buffer_.resize(2048); + // Current frame stuff whatever // samples per frame * 2 max channels * output bytes current_frame_ = new uint8_t[kSamplesPerFrame * kBytesPerSample * 2]; @@ -98,11 +101,11 @@ int XmaContext::Setup(uint32_t id, Memory* memory, uint32_t guest_ptr) { } void XmaContext::Work() { + std::lock_guard lock(lock_); if (!is_allocated() || !is_enabled()) { return; } - std::lock_guard lock(lock_); set_is_enabled(false); auto context_ptr = memory()->TranslateVirtual(guest_ptr()); @@ -117,10 +120,11 @@ void XmaContext::Enable() { auto context_ptr = memory()->TranslateVirtual(guest_ptr()); XMA_CONTEXT_DATA data(context_ptr); - XELOGAPU("XmaContext: kicking context %d (%d/%d bits)", id(), - data.input_buffer_read_offset, (data.input_buffer_0_packet_count + - data.input_buffer_1_packet_count) * - kBytesPerPacket * 8); + XELOGAPU("XmaContext: kicking context %d (buffer %d %d/%d bits)", id(), + data.current_buffer, data.input_buffer_read_offset, + (data.current_buffer == 0 ? data.input_buffer_0_packet_count + : data.input_buffer_1_packet_count) * + kBytesPerPacket * 8); data.Store(context_ptr); @@ -142,8 +146,6 @@ void XmaContext::Clear() { std::lock_guard lock(lock_); XELOGAPU("XmaContext: reset context %d", id()); - DiscardPacket(); - auto context_ptr = memory()->TranslateVirtual(guest_ptr()); XMA_CONTEXT_DATA data(context_ptr); @@ -171,8 +173,6 @@ void XmaContext::Release() { set_is_allocated(false); auto context_ptr = memory()->TranslateVirtual(guest_ptr()); std::memset(context_ptr, 0, sizeof(XMA_CONTEXT_DATA)); // Zero it. - - DiscardPacket(); } int XmaContext::GetSampleRate(int id) { @@ -190,6 +190,83 @@ int XmaContext::GetSampleRate(int id) { return 0; } +size_t XmaContext::SavePartial(uint8_t* packet, uint32_t frame_offset_bits, + size_t frame_size_bits, bool append) { + uint8_t* buff = partial_frame_buffer_.data(); + + BitStream stream(packet, 2048 * 8); + stream.SetOffset(frame_offset_bits); + + if (!append) { + // Reset the buffer. + // TODO: Probably not necessary. + std::memset(buff, 0, partial_frame_buffer_.size()); + + size_t copy_bits = (2048 * 8) - frame_offset_bits; + size_t copy_offset = stream.Copy(buff, copy_bits); + partial_frame_offset_bits_ = copy_bits; + partial_frame_start_offset_bits_ = copy_offset; + + return copy_bits; + } else { + size_t copy_bits = frame_size_bits - partial_frame_offset_bits_; + size_t copy_offset = stream.Copy( + buff + + ((partial_frame_offset_bits_ + partial_frame_start_offset_bits_) / + 8), + copy_bits); + + partial_frame_offset_bits_ += copy_bits; + + return copy_bits; + } +} + +bool XmaContext::ValidFrameOffset(uint8_t* block, size_t size_bytes, + size_t frame_offset_bits) { + uint32_t packet_num = + GetFramePacketNumber(block, size_bytes, frame_offset_bits); + uint8_t* packet = block + (packet_num * kBytesPerPacket); + size_t relative_offset_bits = frame_offset_bits % (kBytesPerPacket * 8); + + uint32_t first_frame_offset = xma::GetPacketFrameOffset(packet); + if (first_frame_offset == -1) { + // Packet only contains a partial frame, so no frames can start here. + return false; + } + + BitStream stream(packet, kBytesPerPacket * 8); + stream.SetOffset(first_frame_offset); + while (true) { + if (stream.offset_bits() == relative_offset_bits) { + return true; + } + + if (stream.BitsRemaining() < 15) { + // Not enough room for another frame header. + return false; + } + + uint64_t size = stream.Read(15); + if ((size - 15) > stream.BitsRemaining()) { + // Last frame. + return false; + } else if (size == 0x7FFF) { + // Invalid frame (and last of this packet) + return false; + } + + stream.Advance(size - 16); + + // Read the trailing bit to see if frames follow + if (stream.Read(1) == 0) { + break; + } + } + + return false; +} + void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { SCOPE_profile_cpu_f("apu"); @@ -203,22 +280,16 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { // 32bit header (big endian) // Frames are the smallest thing the SPUs can decode. - // They usually can span packets (libav handles this) + // They can and usually will span packets. // Sample rates (data.sample_rate): - // 0 - 24 kHz ? + // 0 - 24 kHz // 1 - 32 kHz - // 2 - 44.1 kHz ? - // 3 - 48 kHz ? + // 2 - 44.1 kHz + // 3 - 48 kHz // SPUs also support stereo decoding. (data.is_stereo) - // Quick die if there's no data. - if (!data->input_buffer_0_valid && !data->input_buffer_1_valid) { - XELOGAPU("Context %d: No valid input buffers!", id()); - return; - } - // Check the output buffer - we cannot decode anything else if it's // unavailable. if (!data->output_buffer_valid) { @@ -236,25 +307,28 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { // Translate pointers for future use. // Sometimes the game will use rolling input buffers. If they do, we cannot // assume they form a complete block! In addition, the buffers DO NOT have - // to be sequential! - // (bit.trip runner 2 does this) - // TODO: Collect partial frames into a buffer if the game uses rolling buffers, - // and present the full frame to libav when we get it. + // to be contiguous! 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* current_input_buffer = in0; + uint8_t* current_input_buffer = data->current_buffer ? in1 : in0; + + XELOGAPU("Processing context %d (offset %d, buffer %d, ptr %.8X)", id(), + data->input_buffer_read_offset, data->current_buffer, + current_input_buffer); size_t input_buffer_0_size = data->input_buffer_0_packet_count * kBytesPerPacket; size_t input_buffer_1_size = data->input_buffer_1_packet_count * kBytesPerPacket; + size_t input_total_size = input_buffer_0_size + input_buffer_1_size; + size_t current_input_size = data->current_buffer ? input_buffer_1_size : input_buffer_0_size; - size_t input_total_size = input_buffer_0_size + input_buffer_1_size; + size_t current_input_packet_count = current_input_size / kBytesPerPacket; // 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 @@ -272,14 +346,10 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { output_rb.set_write_offset(output_write_offset); size_t output_remaining_bytes = output_rb.write_count(); + bool output_written = false; // Decode until we can't write any more data. while (output_remaining_bytes > 0) { - if (!data->input_buffer_0_valid && !data->input_buffer_1_valid) { - // Out of data. - break; - } - int num_channels = data->is_stereo ? 2 : 1; // Check if we have part of a frame waiting (and the game hasn't jumped @@ -289,7 +359,10 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { size_t to_write = std::min( output_remaining_bytes, ((size_t)kBytesPerFrame * num_channels - current_frame_pos_)); - output_rb.Write(current_frame_, to_write); + output_rb.Write(current_frame_ + current_frame_pos_, to_write); + output_written = true; + XELOGAPU("XmaContext %d: wrote out %d bytes of left-over samples", id(), + to_write); current_frame_pos_ += to_write; if (current_frame_pos_ >= kBytesPerFrame * num_channels) { @@ -301,48 +374,189 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { continue; } + if (!data->input_buffer_0_valid && !data->input_buffer_1_valid) { + // Out of data. + break; + } + + if (data->input_buffer_read_offset == 0) { + // Invalid offset. Go ahead and set it. + uint32_t offset = xma::GetPacketFrameOffset(current_input_buffer); + if (offset == -1) { + // No more frames. + if (data->current_buffer == 0) { + data->input_buffer_0_valid = 0; + data->input_buffer_read_offset = 0; + data->current_buffer++; + } else if (data->current_buffer == 1) { + data->input_buffer_1_valid = 0; + data->input_buffer_read_offset = 0; + data->current_buffer--; + } + + // Die if we have no partial saved. + if (!partial_frame_saved_) { + return; + } + } else { + data->input_buffer_read_offset = offset; + } + } + + if (!ValidFrameOffset(current_input_buffer, current_input_size, + data->input_buffer_read_offset)) { + XELOGAPU("XmaContext %d: Invalid read offset %d!", id(), + data->input_buffer_read_offset); + if (data->current_buffer == 0) { + data->current_buffer = 1; + data->input_buffer_0_valid = 0; + } else if (data->current_buffer == 1) { + data->current_buffer = 0; + data->input_buffer_1_valid = 0; + } + + data->input_buffer_read_offset = 0; + return; + } + + // Check if we need to save a partial frame. + if (data->input_buffer_read_offset != 0 && !partial_frame_saved_ && + GetFramePacketNumber(current_input_buffer, current_input_size, + data->input_buffer_read_offset) == + current_input_packet_count - 1) { + BitStream stream(current_input_buffer, current_input_size * 8); + stream.SetOffset(data->input_buffer_read_offset); + + if (stream.BitsRemaining() >= 15) { + uint64_t frame_size = stream.Read(15); + if (data->input_buffer_read_offset + frame_size >= + current_input_size * 8 && + frame_size != 0x7FFF) { + uint32_t rel_offset = data->input_buffer_read_offset % (2048 * 8); + + // Frame is cut off! Save and exit. + partial_frame_saved_ = true; + partial_frame_size_known_ = true; + partial_frame_total_size_bits_ = frame_size; + SavePartial( + current_input_buffer + (current_input_packet_count - 1) * 2048, + rel_offset, frame_size, false); + } + } else { + // Header cut in half :/ + uint32_t rel_offset = data->input_buffer_read_offset % (2048 * 8); + + partial_frame_saved_ = true; + partial_frame_size_known_ = false; + SavePartial( + current_input_buffer + (current_input_packet_count - 1) * 2048, + rel_offset, 0, false); + } + + if (partial_frame_saved_) { + XELOGAPU("XmaContext %d: saved a partial frame", id()); + + if (data->current_buffer == 0) { + data->input_buffer_0_valid = 0; + data->input_buffer_read_offset = 0; + data->current_buffer++; + } else if (data->current_buffer == 1) { + data->input_buffer_1_valid = 0; + data->input_buffer_read_offset = 0; + data->current_buffer--; + } + + return; + } + } + + if (partial_frame_saved_ && !partial_frame_size_known_) { + // Append the rest of the header. + size_t offset = SavePartial(current_input_buffer, 32, 15, true); + + // Read the frame size. + BitStream stream(partial_frame_buffer_.data(), + 15 + partial_frame_start_offset_bits_); + stream.SetOffset(partial_frame_start_offset_bits_); + + uint64_t size = stream.Read(15); + partial_frame_size_known_ = true; + partial_frame_total_size_bits_ = size; + + // Now append the rest of the frame. + SavePartial(current_input_buffer, 32 + (uint32_t)offset, size, true); + } else if (partial_frame_saved_) { + // Append the rest of the frame. + SavePartial(current_input_buffer, 32, partial_frame_total_size_bits_, + true); + } + + // Prepare the decoder. Reinitialize if any parameters have changed. + PrepareDecoder(current_input_buffer, current_input_size, data->sample_rate, + num_channels); + + bool partial = false; + size_t bit_offset = data->input_buffer_read_offset; + if (partial_frame_saved_) { + XELOGAPU("XmaContext %d: processing saved partial frame", id()); + packet_->data = partial_frame_buffer_.data(); + packet_->size = (int)partial_frame_buffer_.size(); + + bit_offset = partial_frame_start_offset_bits_; + partial = true; + partial_frame_saved_ = false; + } else { + packet_->data = current_input_buffer; + packet_->size = (int)current_input_size; + } + int invalid_frame = 0; // invalid frame? int got_frame = 0; // successfully decoded a frame? int frame_size = 0; - packet_->data = current_input_buffer; - packet_->size = (int)current_input_size; - PrepareDecoder(in0, current_input_size, data->sample_rate, num_channels); - int len = xma2_decode_frame(context_, packet_, decoded_frame_, &got_frame, - &invalid_frame, &frame_size, 1, - data->input_buffer_read_offset); - if (invalid_frame) { - // Invalid frame/packet: length header is 0x7FFF - // Sometimes there's frames in the middle of the stream flagged as - // invalid. - // Double-check to make sure we're not in the middle. - uint32_t frame_byte_offset = data->input_buffer_read_offset >> 3; - uint32_t packet_number = frame_byte_offset / 2048; - if (packet_number < data->input_buffer_0_packet_count - 1) { - // Okay. Skip to the beginning of the next packet. - packet_number++; - data->input_buffer_read_offset = (packet_number * 2048 * 8) + 32; - continue; - } - - // Last frame of the block. Swap buffers if necessary. - if (data->current_buffer == 0) { - if (data->input_buffer_1_valid) { - data->current_buffer++; - } else { - // End of input. - data->input_buffer_read_offset = input_total_size * 8; + int len = + xma2_decode_frame(context_, packet_, decoded_frame_, &got_frame, + &invalid_frame, &frame_size, !partial, bit_offset); + if (!partial && len == 0) { + // Got the last frame of a packet. Advance the read offset to the next + // packet. + uint32_t packet_number = + GetFramePacketNumber(current_input_buffer, current_input_size, + data->input_buffer_read_offset); + if (packet_number == current_input_packet_count - 1) { + // Last packet. + if (data->current_buffer == 0) { + data->input_buffer_0_valid = 0; + data->input_buffer_read_offset = 0; + data->current_buffer = 1; + } else if (data->current_buffer == 1) { + data->input_buffer_1_valid = 0; + data->input_buffer_read_offset = 0; + data->current_buffer = 0; } - - data->input_buffer_0_valid = 0; - return; } else { - // End of input. - data->current_buffer = 0; - data->input_buffer_1_valid = 0; - data->input_buffer_read_offset = input_total_size * 8; - return; + // Advance the read offset. + packet_number++; + uint8_t* packet = current_input_buffer + (packet_number * 2048); + uint32_t first_frame_offset = xma::GetPacketFrameOffset(packet); + if (first_frame_offset == -1) { + // Invalid packet (only contained a frame partial). Out of input. + if (data->current_buffer == 0) { + data->input_buffer_0_valid = 0; + data->current_buffer = 1; + } else if (data->current_buffer == 1) { + data->input_buffer_1_valid = 0; + data->current_buffer = 0; + } + + data->input_buffer_read_offset = 0; + } else { + data->input_buffer_read_offset = + packet_number * 2048 * 8 + first_frame_offset; + } } - } else if (got_frame && len > 0) { + } + + if (got_frame) { // Valid frame. // Check and see if we need to loop back to any spot. if (data->loop_count > 0 && @@ -352,53 +566,28 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { if (data->loop_count < 255) { data->loop_count--; } - } else { + } else if (!partial && len > 0) { data->input_buffer_read_offset += len; - if (data->current_buffer == 0 && - data->input_buffer_read_offset > input_buffer_0_size * 8) { - // Overflow? Setup next buffer. - data->current_buffer++; - data->input_buffer_0_valid = 0; - } else if (data->input_buffer_read_offset > input_total_size * 8) { - // Overflow! The game will fix up the read offset. - data->current_buffer = 0; - data->input_buffer_0_valid = 0; - data->input_buffer_1_valid = 0; - } } - } - - if ((len < 0 || !got_frame) && frame_size != 0) { - // Oh no! Skip the frame and hope everything works. - data->input_buffer_read_offset += frame_size; - data->input_buffer_read_offset = (uint32_t)xma2_correct_frame_offset( - in0, input_buffer_0_size, data->input_buffer_read_offset); - - continue; - } else if (len < 0 || !got_frame) { - // Did not get frame and could not get frame size. - data->input_buffer_0_valid = 0; - data->input_buffer_1_valid = 0; + } else if (len < 0) { + // Did not get frame + XELOGAPU("libav failed to decode a frame!"); + if (frame_size && frame_size != 0x7FFF) { + data->input_buffer_read_offset += frame_size; + } else { + data->input_buffer_0_valid = 0; + data->input_buffer_1_valid = 0; + } return; } - // Sometimes we may run up to <15 bits before the next packet. If this - // happens, we need to automatically advance to the next frame. - // We'll ask the XMA2 decoder to do this for us, since it's more qualified. - data->input_buffer_read_offset = (uint32_t)xma2_correct_frame_offset( - in0, input_buffer_0_size, data->input_buffer_read_offset); last_input_read_pos_ = data->input_buffer_read_offset; - if (data->input_buffer_read_offset == 0) { - // Invalid offset. Out of data. - data->input_buffer_0_valid = 0; - data->input_buffer_1_valid = 0; - } - - // Copy to the output buffer. - // Successfully decoded a frame. - size_t written_bytes = 0; if (got_frame) { + // Successfully decoded a frame. + // Copy to the output buffer. + size_t written_bytes = 0; + #ifdef DEBUG // Validity checks. if (decoded_frame_->nb_samples > kSamplesPerFrame) { @@ -419,7 +608,7 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { #endif // Convert the frame. - ConvertFrame((const float**)decoded_frame_->data, context_->channels, + ConvertFrame((const uint8_t**)decoded_frame_->data, context_->channels, decoded_frame_->nb_samples, current_frame_); current_frame_pos_ = 0; @@ -435,15 +624,18 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { written_bytes = kBytesPerFrame * num_channels; } - } - output_remaining_bytes -= written_bytes; - data->output_buffer_write_offset = output_rb.write_offset() / 256; + output_written = true; + output_remaining_bytes -= written_bytes; + data->output_buffer_write_offset = output_rb.write_offset() / 256; + } } // The game will kick us again with a new output buffer later. // It's important that we only invalidate this if we actually wrote to it!! - data->output_buffer_valid = 0; + if (output_written) { + data->output_buffer_valid = 0; + } } uint32_t XmaContext::GetFramePacketNumber(uint8_t* block, size_t size, @@ -490,7 +682,7 @@ int XmaContext::PrepareDecoder(uint8_t* block, size_t size, int sample_rate, return 0; } -bool XmaContext::ConvertFrame(const float** samples, int num_channels, +bool XmaContext::ConvertFrame(const uint8_t** samples, int num_channels, int num_samples, uint8_t* output_buffer) { // Loop through every sample, convert and drop it into the output array. // If more than one channel, we need to interleave the samples from each @@ -500,7 +692,7 @@ bool XmaContext::ConvertFrame(const float** samples, int num_channels, for (int i = 0; i < num_samples; i++) { for (int j = 0; j < num_channels; j++) { // Select the appropriate array based on the current channel. - auto sample_array = samples[j]; + auto sample_array = reinterpret_cast(samples[j]); // Raw sample should be within [-1, 1]. // Clamp it, just in case. @@ -516,216 +708,5 @@ bool XmaContext::ConvertFrame(const float** samples, int num_channels, return true; } -int XmaContext::StartPacket(XMA_CONTEXT_DATA* 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 = GetSampleRate(data->sample_rate); - 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_valid - ? (data->input_buffer_0_packet_count) * 2048 - : 0; - uint32_t input_size_1_bytes = data->input_buffer_1_valid - ? (data->input_buffer_1_packet_count) * 2048 - : 0; - - // Total input size - uint32_t input_size_bytes = input_size_0_bytes + input_size_1_bytes; - - // Calculate the first frame offset we need to decode. - uint32_t frame_offset_bits = (data->input_buffer_read_offset % (2048 * 8)); - - // Input read offset is in bits. Typically starts at 32 (4 bytes). - // "Sequence" offset - used internally for WMA Pro decoder. - // Just the read offset. - // NOTE: Read offset may not be at the first frame in a packet! - uint32_t packet_offset_bytes = (data->input_buffer_read_offset & ~0x7FF) / 8; - if (packet_offset_bytes % 2048 != 0) { - packet_offset_bytes -= packet_offset_bytes % 2048; - } - uint32_t input_remaining_bytes = input_size_bytes - packet_offset_bytes; - - if (packet_offset_bytes >= input_size_bytes) { - // No more data available and no packet prepared. - return -1; - } - - // Setup input offset and input buffer. - uint32_t input_offset_bytes = packet_offset_bytes; - auto input_buffer = in0; - - if (packet_offset_bytes >= input_size_0_bytes && input_size_1_bytes) { - // Size overlap, select input buffer 1. - // TODO(DrChat): 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; - assert_true(input_offset_bytes % 2048 == 0); - PreparePacket(packet, packet_offset_bytes, kBytesPerPacket, sample_rate, - channels); - - data->input_buffer_read_offset += kBytesPerPacket * 8; - - input_remaining_bytes -= kBytesPerPacket; - if (input_remaining_bytes <= 0) { - // Used the last of the data but prepared a packet. - return 0; - } - - return input_remaining_bytes; -} - -int XmaContext::PreparePacket(uint8_t* input, size_t seq_offset, size_t size, - int sample_rate, int channels) { - if (size != kBytesPerPacket) { - // Invalid packet size! - assert_always(); - return 1; - } - if (packet_->size > 0 || current_frame_pos_ != frame_samples_size_) { - // Haven't finished parsing another packet. - return 1; - } - - // Packet metadata is always 1 for XMA2 - assert_true((input[2] & 0x7) == 1); - - packet_->data = input; - packet_->size = (int)size; - - // Re-initialize the context with new sample rate and channels. - if (context_->sample_rate != sample_rate || context_->channels != channels) { - // We have to reopen the codec so it'll realloc whatever data it needs. - // TODO(DrChat): Find a better way. - avcodec_close(context_); - - context_->sample_rate = sample_rate; - context_->channels = channels; - extra_data_.channel_mask = - channels == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO; - - if (avcodec_open2(context_, codec_, NULL) < 0) { - XELOGE("XmaContext: Failed to reopen libav context"); - return 1; - } - } - - return 0; -} - -void XmaContext::DiscardPacket() { - if (packet_->size > 0 || current_frame_pos_ != frame_samples_size_) { - packet_->data = 0; - packet_->size = 0; - current_frame_pos_ = frame_samples_size_; - } -} - -int XmaContext::DecodePacket(uint8_t* output, size_t output_offset, - size_t output_size, size_t* read_bytes) { - size_t to_copy = 0; - size_t original_offset = output_offset; - if (read_bytes) { - *read_bytes = 0; - } - - // We're holding onto an already-decoded frame. Copy it out. - if (current_frame_pos_ != frame_samples_size_) { - to_copy = std::min(output_size, frame_samples_size_ - current_frame_pos_); - memcpy(output + output_offset, current_frame_ + current_frame_pos_, - to_copy); - - current_frame_pos_ += to_copy; - output_size -= to_copy; - output_offset += to_copy; - } - - while (output_size > 0 && packet_->size > 0) { - int got_frame = 0; - - // Decode the current frame. - int len = - avcodec_decode_audio4(context_, decoded_frame_, &got_frame, packet_); - if (len < 0) { - // Error in codec (bad sample rate or something). - return len; - } - - if (read_bytes) { - *read_bytes += len; - } - - // Offset by decoded length. - packet_->size -= len; - packet_->data += len; - packet_->dts = packet_->pts = AV_NOPTS_VALUE; - - // Successfully decoded a frame. - if (got_frame) { - // Validity checks. - if (decoded_frame_->nb_samples > kSamplesPerFrame) { - return -2; - } else if (context_->sample_fmt != AV_SAMPLE_FMT_FLTP) { - return -3; - } - - // Check the returned buffer size. - if (av_samples_get_buffer_size(NULL, context_->channels, - decoded_frame_->nb_samples, - context_->sample_fmt, 1) != - context_->channels * decoded_frame_->nb_samples * sizeof(float)) { - return -4; - } - - // Loop through every sample, convert and drop it into the output array. - // If more than one channel, the game wants the samples from each channel - // interleaved next to each other. - uint32_t o = 0; - for (int i = 0; i < decoded_frame_->nb_samples; i++) { - for (int j = 0; j < context_->channels; j++) { - // Select the appropriate array based on the current channel. - auto sample_array = reinterpret_cast(decoded_frame_->data[j]); - - // Raw sample should be within [-1, 1]. - // Clamp it, just in case. - float raw_sample = xe::saturate(sample_array[i]); - - // Convert the sample and output it in big endian. - float scaled_sample = raw_sample * ((1 << 15) - 1); - int sample = static_cast(scaled_sample); - xe::store_and_swap(¤t_frame_[o++ * 2], - sample & 0xFFFF); - } - } - current_frame_pos_ = 0; - - // Total size of the frame's samples. - // Magic number 2 is sizeof an output sample. - frame_samples_size_ = context_->channels * decoded_frame_->nb_samples * 2; - - to_copy = std::min(output_size, (size_t)(frame_samples_size_)); - std::memcpy(output + output_offset, current_frame_, to_copy); - - current_frame_pos_ += to_copy; - output_size -= to_copy; - output_offset += to_copy; - } - } - - // Return number of bytes written. - return static_cast(output_offset - original_offset); -} - } // namespace apu } // namespace xe diff --git a/src/xenia/apu/xma_context.h b/src/xenia/apu/xma_context.h index a0f769f38..86eca8c1c 100644 --- a/src/xenia/apu/xma_context.h +++ b/src/xenia/apu/xma_context.h @@ -167,12 +167,14 @@ class XmaContext { private: static int GetSampleRate(int id); + size_t SavePartial(uint8_t* packet, uint32_t frame_offset_bits, size_t frame_size_bits, bool append); + bool ValidFrameOffset(uint8_t* block, size_t size_bytes, size_t frame_offset_bits); void DecodePackets(XMA_CONTEXT_DATA* data); uint32_t GetFramePacketNumber(uint8_t* block, size_t size, size_t bit_offset); int PrepareDecoder(uint8_t* block, size_t size, int sample_rate, int channels); - bool ConvertFrame(const float** samples, int num_channels, int num_samples, + bool ConvertFrame(const uint8_t** samples, int num_channels, int num_samples, uint8_t* output_buffer); int StartPacket(XMA_CONTEXT_DATA* data); @@ -199,6 +201,13 @@ class XmaContext { AVPacket* packet_ = nullptr; WmaProExtraData extra_data_; + bool partial_frame_saved_ = false; + bool partial_frame_size_known_ = false; + size_t partial_frame_total_size_bits_ = 0; + size_t partial_frame_start_offset_bits_ = 0; + size_t partial_frame_offset_bits_ = 0; // blah internal don't use this + std::vector partial_frame_buffer_; + // If we didn't finish writing a frame to the output buffer, this is the offset. size_t current_frame_pos_ = 0; uint32_t last_input_read_pos_ = 0; // Last seen read buffer pos diff --git a/src/xenia/apu/xma_decoder.cc b/src/xenia/apu/xma_decoder.cc index 766ce231b..aaa0edf90 100644 --- a/src/xenia/apu/xma_decoder.cc +++ b/src/xenia/apu/xma_decoder.cc @@ -82,7 +82,7 @@ void av_log_callback(void* avcl, int level, const char* fmt, va_list va) { StringBuffer buff; buff.AppendVarargs(fmt, va); - xe::LogLineVarargs(level_char, "libav: %s", buff.GetString()); + xe::LogLineFormat(level_char, "libav: %s", buff.GetString()); } X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { @@ -113,7 +113,7 @@ X_STATUS XmaDecoder::Setup(kernel::KernelState* kernel_state) { } registers_.next_context = 1; - //worker_running_ = true; + worker_running_ = true; worker_thread_ = kernel::object_ref( new kernel::XHostThread(kernel_state, 128 * 1024, 0, [this]() { WorkerThreadMain(); @@ -131,6 +131,11 @@ void XmaDecoder::WorkerThreadMain() { for (uint32_t n = 0; n < kContextCount; n++) { XmaContext& context = contexts_[n]; context.Work(); + + // TODO: Need thread safety to do this. + // Probably not too important though. + //registers_.current_context = n; + //registers_.next_context = (n + 1) % kContextCount; } } } @@ -209,7 +214,6 @@ uint32_t XmaDecoder::ReadRegister(uint32_t addr) { // number registers_.current_context = registers_.next_context; registers_.next_context = (registers_.next_context + 1) % kContextCount; - value = registers_.current_context; } value = xe::byte_swap(value); @@ -240,7 +244,6 @@ void XmaDecoder::WriteRegister(uint32_t addr, uint32_t value) { uint32_t context_id = base_context_id + i; XmaContext& context = contexts_[context_id]; context.Enable(); - context.Work(); } } diff --git a/src/xenia/apu/xma_helpers.h b/src/xenia/apu/xma_helpers.h index 8efc9d1d1..bf0f593ac 100644 --- a/src/xenia/apu/xma_helpers.h +++ b/src/xenia/apu/xma_helpers.h @@ -25,7 +25,12 @@ uint32_t GetPacketFrameCount(uint8_t* packet) { // Get the first frame offset in bits uint32_t GetPacketFrameOffset(uint8_t* packet) { - return (uint16_t)((packet[0] << 13) | (packet[1] << 5) | (packet[2] >> 3)) + 32; + uint32_t val = (uint16_t)(((packet[0] & 0x3) << 13) | (packet[1] << 5) | (packet[2] >> 3)); + if (val == 0x7FFF) { + return -1; + } else { + return val + 32; + } } uint32_t GetPacketMetadata(uint8_t* packet) { diff --git a/src/xenia/base/bit_stream.cc b/src/xenia/base/bit_stream.cc new file mode 100644 index 000000000..2a073109e --- /dev/null +++ b/src/xenia/base/bit_stream.cc @@ -0,0 +1,143 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/bit_stream.h" + +#include + +#include "xenia/base/assert.h" +#include "xenia/base/byte_order.h" + +namespace xe { +BitStream::BitStream(uint8_t* buffer, size_t size_in_bits) + : buffer_(buffer), size_bits_(size_in_bits) {} + +BitStream::~BitStream() {} + +void BitStream::SetOffset(size_t offset_bits) { + assert_false(offset_bits > size_bits_); + offset_bits_ = std::min(offset_bits, size_bits_); +} + +size_t BitStream::BitsRemaining() { return size_bits_ - offset_bits_; } + +uint64_t BitStream::Peek(size_t num_bits) { + // FYI: The reason we can't copy more than 57 bits is: + // 57 = 7 * 8 + 1 - that can only span a maximum of 8 bytes. + // We can't read in 9 bytes (easily), so we limit it. + assert_false(num_bits > 57); + assert_false(offset_bits_ + num_bits > size_bits_); + + size_t offset_bytes = offset_bits_ >> 3; + size_t rel_offset_bits = offset_bits_ - (offset_bytes << 3); + + // offset --> + // ..[junk]..| target bits |....[junk]............. + uint64_t bits = *(uint64_t*)(buffer_ + offset_bytes); + + // We need the data in little endian. + // TODO: Have a flag specifying endianness of data? + bits = xe::byte_swap(bits); + + // Shift right + // .....[junk]........| target bits | + bits >>= 64 - (rel_offset_bits + num_bits); + + // AND with mask + // ...................| target bits | + bits &= (1 << num_bits) - 1; + + return bits; +} + +uint64_t BitStream::Read(size_t num_bits) { + uint64_t val = Peek(num_bits); + Advance(num_bits); + + return val; +} + +// TODO: This is totally not tested! +bool BitStream::Write(uint64_t val, size_t num_bits) { + assert_false(num_bits > 57); + assert_false(offset_bits_ + num_bits >= size_bits_); + + size_t offset_bytes = offset_bits_ >> 3; + size_t rel_offset_bits = offset_bits_ - (offset_bytes << 3); + + // Construct a mask + uint64_t mask = (1 << num_bits) - 1; + mask <<= 64 - (rel_offset_bits + num_bits); + mask = ~mask; + + // Shift the value left into position. + val <<= 64 - (rel_offset_bits + num_bits); + + // offset -----> + // ....[junk]...| target bits w/ junk |....[junk]...... + uint64_t bits = *(uint64_t*)(buffer_ + offset_bytes); + + // AND with mask + // ....[junk]...| target bits (0) |........[junk]...... + bits &= mask; + + // OR with val + // ....[junk]...| target bits (val) |......[junk]...... + bits |= val; + + // Store into the bitstream. + *(uint64_t*)(buffer_ + offset_bytes) = bits; + + // Advance the bitstream forward. + Advance(num_bits); + + return true; +} + +size_t BitStream::Copy(uint8_t* dest_buffer, size_t num_bits) { + size_t offset_bytes = offset_bits_ >> 3; + size_t rel_offset_bits = offset_bits_ - (offset_bytes << 3); + size_t bits_left = num_bits; + size_t out_offset_bytes = 0; + + // First: Copy the first few bits up to a byte boundary. + if (rel_offset_bits) { + uint64_t bits = Peek(8 - rel_offset_bits); + dest_buffer[out_offset_bytes] |= (uint8_t)bits; + + bits_left -= 8 - rel_offset_bits; + Advance(8 - rel_offset_bits); + out_offset_bytes++; + } + + // Second: Use memcpy for the bytes left. + if (bits_left >= 8) { + std::memcpy(dest_buffer + out_offset_bytes, + buffer_ + offset_bytes + out_offset_bytes, bits_left / 8); + out_offset_bytes += (bits_left / 8); + Advance((bits_left / 8) * 8); + bits_left -= (bits_left / 8) * 8; + } + + // Third: Copy the last few bits. + if (bits_left) { + uint64_t bits = Peek(bits_left); + bits <<= 8 - bits_left; + + dest_buffer[out_offset_bytes] |= (uint8_t)bits; + Advance(bits_left); + } + + // Return the bit offset to the copied bits. + return rel_offset_bits; +} + +void BitStream::Advance(size_t num_bits) { SetOffset(offset_bits_ + num_bits); } + +} // namespace xe \ No newline at end of file diff --git a/src/xenia/base/bit_stream.h b/src/xenia/base/bit_stream.h new file mode 100644 index 000000000..64cc2704b --- /dev/null +++ b/src/xenia/base/bit_stream.h @@ -0,0 +1,44 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_BASE_BIT_STREAM_H_ +#define XENIA_BASE_BIT_STREAM_H_ + +#include + +namespace xe { +class BitStream { + public: + BitStream(uint8_t* buffer, size_t size_in_bits); + ~BitStream(); + + const uint8_t* buffer() const { return buffer_; } + uint8_t* buffer() { return buffer_; } + size_t offset_bits() const { return offset_bits_; } + size_t size_bits() const { return size_bits_; } + + void Advance(size_t num_bits); + void SetOffset(size_t offset_bits); + size_t BitsRemaining(); + + // Note: num_bits MUST be in the range 0-57 (inclusive) + uint64_t Peek(size_t num_bits); + uint64_t Read(size_t num_bits); + bool Write(uint64_t val, size_t num_bits); // TODO: Not tested! + + size_t Copy(uint8_t* dest_buffer, size_t num_bits); + + private: + uint8_t* buffer_ = nullptr; + size_t offset_bits_ = 0; + size_t size_bits_ = 0; +}; +} // namespace xe + +#endif // XENIA_BASE_BIT_STREAM_H_ \ No newline at end of file diff --git a/third_party/libav.lua b/third_party/libav.lua new file mode 100644 index 000000000..3341a696c --- /dev/null +++ b/third_party/libav.lua @@ -0,0 +1,3 @@ +group("third_party") + include("libav/libavcodec/premake5.lua") + include("libav/libavutil/premake5.lua") From 3d684ac304b75bcbee775885db5e2ceff7db9b7c Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 29 Aug 2015 22:02:02 -0500 Subject: [PATCH 5/9] Fixup the premake files --- src/xenia/app/premake5.lua | 17 +---------------- src/xenia/apu/nop/premake5.lua | 2 +- src/xenia/apu/premake5.lua | 2 +- src/xenia/apu/xaudio2/premake5.lua | 2 +- src/xenia/base/premake5.lua | 4 ++-- src/xenia/cpu/frontend/testing/premake5.lua | 2 +- src/xenia/cpu/premake5.lua | 2 +- src/xenia/cpu/testing/premake5.lua | 2 +- src/xenia/debug/premake5.lua | 2 +- src/xenia/debug/ui/premake5.lua | 17 +---------------- src/xenia/gpu/gl4/premake5.lua | 19 ++----------------- src/xenia/gpu/premake5.lua | 2 +- src/xenia/hid/premake5.lua | 2 +- src/xenia/kernel/premake5.lua | 2 +- src/xenia/premake5.lua | 2 +- src/xenia/ui/gl/premake5.lua | 2 +- src/xenia/ui/premake5.lua | 2 +- src/xenia/vfs/premake5.lua | 2 +- 18 files changed, 20 insertions(+), 65 deletions(-) diff --git a/src/xenia/app/premake5.lua b/src/xenia/app/premake5.lua index 7fb8001bc..a7746755e 100644 --- a/src/xenia/app/premake5.lua +++ b/src/xenia/app/premake5.lua @@ -32,7 +32,7 @@ project("xenia-app") }) includedirs({ project_root.."/third_party/elemental-forms/src", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() files({ @@ -54,21 +54,6 @@ project("xenia-app") "xenia-hid-xinput", }) ---[[ - filter("configurations:Checked") - local libav_root = "../third_party/libav-xma-bin/lib/Debug" - linkoptions({ - libav_root.."/libavcodec.a", - libav_root.."/libavutil.a", - }) - filter("configurations:Debug or Release") - local libav_root = "../third_party/libav-xma-bin/lib/Release" - linkoptions({ - libav_root.."/libavcodec.a", - libav_root.."/libavutil.a", - }) -]] - filter("platforms:Windows") -- Only create the .user file if it doesn't already exist. local user_file = project_root.."/build/xenia-app.vcxproj.user" diff --git a/src/xenia/apu/nop/premake5.lua b/src/xenia/apu/nop/premake5.lua index b11bdacc5..276404fb1 100644 --- a/src/xenia/apu/nop/premake5.lua +++ b/src/xenia/apu/nop/premake5.lua @@ -13,6 +13,6 @@ project("xenia-apu-nop") defines({ }) includedirs({ - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/apu/premake5.lua b/src/xenia/apu/premake5.lua index 8fa1b6d42..647bf02d6 100644 --- a/src/xenia/apu/premake5.lua +++ b/src/xenia/apu/premake5.lua @@ -15,7 +15,7 @@ project("xenia-apu") }) includedirs({ project_root.."/third_party/libav/include/", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/apu/xaudio2/premake5.lua b/src/xenia/apu/xaudio2/premake5.lua index 8eb33a010..466268609 100644 --- a/src/xenia/apu/xaudio2/premake5.lua +++ b/src/xenia/apu/xaudio2/premake5.lua @@ -12,6 +12,6 @@ project("xenia-apu-xaudio2") defines({ }) includedirs({ - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/base/premake5.lua b/src/xenia/base/premake5.lua index 0499fc945..21ed7720e 100644 --- a/src/xenia/base/premake5.lua +++ b/src/xenia/base/premake5.lua @@ -8,7 +8,7 @@ project("xenia-base") defines({ }) includedirs({ - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() removefiles({"main_*.cc"}) @@ -18,7 +18,7 @@ project("xenia-base") test_suite("xenia-base-tests", project_root, ".", { includedirs = { - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }, links = { "xenia-base", diff --git a/src/xenia/cpu/frontend/testing/premake5.lua b/src/xenia/cpu/frontend/testing/premake5.lua index ffb8f38e1..74f0a39fc 100644 --- a/src/xenia/cpu/frontend/testing/premake5.lua +++ b/src/xenia/cpu/frontend/testing/premake5.lua @@ -25,7 +25,7 @@ project("xenia-cpu-frontend-tests") "*.s", }) includedirs({ - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) filter("files:*.s") flags({"ExcludeFromBuild"}) diff --git a/src/xenia/cpu/premake5.lua b/src/xenia/cpu/premake5.lua index d92540557..15f6a00a2 100644 --- a/src/xenia/cpu/premake5.lua +++ b/src/xenia/cpu/premake5.lua @@ -11,7 +11,7 @@ project("xenia-cpu") }) includedirs({ project_root.."/third_party/llvm/include", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() local_platform_files("backend") diff --git a/src/xenia/cpu/testing/premake5.lua b/src/xenia/cpu/testing/premake5.lua index 796b51064..fa3df2df2 100644 --- a/src/xenia/cpu/testing/premake5.lua +++ b/src/xenia/cpu/testing/premake5.lua @@ -3,7 +3,7 @@ include(project_root.."/build_tools") test_suite("xenia-cpu-tests", project_root, ".", { includedirs = { - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }, links = { "xenia-base", diff --git a/src/xenia/debug/premake5.lua b/src/xenia/debug/premake5.lua index 8f8403580..ad6670fc5 100644 --- a/src/xenia/debug/premake5.lua +++ b/src/xenia/debug/premake5.lua @@ -13,7 +13,7 @@ project("xenia-debug") defines({ }) includedirs({ - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() recursive_platform_files("proto") diff --git a/src/xenia/debug/ui/premake5.lua b/src/xenia/debug/ui/premake5.lua index a3d845bef..e7f24d51c 100644 --- a/src/xenia/debug/ui/premake5.lua +++ b/src/xenia/debug/ui/premake5.lua @@ -34,7 +34,7 @@ project("xenia-debug-ui") }) includedirs({ project_root.."/third_party/elemental-forms/src", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) recursive_platform_files() files({ @@ -49,21 +49,6 @@ project("xenia-debug-ui") project_root.."/third_party/elemental-forms", }) ---[[ - filter("configurations:Checked") - local libav_root = "../third_party/libav-xma-bin/lib/Debug" - linkoptions({ - libav_root.."/libavcodec.a", - libav_root.."/libavutil.a", - }) - filter("configurations:Debug or Release") - local libav_root = "../third_party/libav-xma-bin/lib/Release" - linkoptions({ - libav_root.."/libavcodec.a", - libav_root.."/libavutil.a", - }) -]] - filter("platforms:Windows") debugdir(project_root) debugargs({ diff --git a/src/xenia/gpu/gl4/premake5.lua b/src/xenia/gpu/gl4/premake5.lua index d21887d88..435e3542f 100644 --- a/src/xenia/gpu/gl4/premake5.lua +++ b/src/xenia/gpu/gl4/premake5.lua @@ -21,7 +21,7 @@ project("xenia-gpu-gl4") }) includedirs({ project_root.."/third_party/elemental-forms/src", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() @@ -63,28 +63,13 @@ project("xenia-gpu-gl4-trace-viewer") }) includedirs({ project_root.."/third_party/elemental-forms/src", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) files({ "trace_viewer_main.cc", "../../base/main_"..platform_suffix..".cc", }) ---[[ - filter("configurations:Checked") - local libav_root = "../third_party/libav-xma-bin/lib/Debug" - linkoptions({ - libav_root.."/libavcodec.a", - libav_root.."/libavutil.a", - }) - filter("configurations:Debug or Release") - local libav_root = "../third_party/libav-xma-bin/lib/Release" - linkoptions({ - libav_root.."/libavcodec.a", - libav_root.."/libavutil.a", - }) -]] - filter("platforms:Windows") debugdir(project_root) debugargs({ diff --git a/src/xenia/gpu/premake5.lua b/src/xenia/gpu/premake5.lua index dc1d339f1..521a04869 100644 --- a/src/xenia/gpu/premake5.lua +++ b/src/xenia/gpu/premake5.lua @@ -16,6 +16,6 @@ project("xenia-gpu") }) includedirs({ project_root.."/third_party/elemental-forms/src", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/hid/premake5.lua b/src/xenia/hid/premake5.lua index 54052f94d..4904d9efd 100644 --- a/src/xenia/hid/premake5.lua +++ b/src/xenia/hid/premake5.lua @@ -12,6 +12,6 @@ project("xenia-hid") defines({ }) includedirs({ - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/kernel/premake5.lua b/src/xenia/kernel/premake5.lua index 89141bf4b..e26ea9ccb 100644 --- a/src/xenia/kernel/premake5.lua +++ b/src/xenia/kernel/premake5.lua @@ -20,7 +20,7 @@ project("xenia-kernel") }) includedirs({ project_root.."/third_party/elemental-forms/src", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) recursive_platform_files() files({ diff --git a/src/xenia/premake5.lua b/src/xenia/premake5.lua index 4834319c9..f5d11a6fc 100644 --- a/src/xenia/premake5.lua +++ b/src/xenia/premake5.lua @@ -14,6 +14,6 @@ project("xenia-core") }) includedirs({ project_root.."/third_party/elemental-forms/src", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) files({"*.h", "*.cc"}) diff --git a/src/xenia/ui/gl/premake5.lua b/src/xenia/ui/gl/premake5.lua index 346f7ade0..dab3c9a42 100644 --- a/src/xenia/ui/gl/premake5.lua +++ b/src/xenia/ui/gl/premake5.lua @@ -18,6 +18,6 @@ project("xenia-ui-gl") }) includedirs({ project_root.."/third_party/elemental-forms/src", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/ui/premake5.lua b/src/xenia/ui/premake5.lua index 119458987..43697e222 100644 --- a/src/xenia/ui/premake5.lua +++ b/src/xenia/ui/premake5.lua @@ -14,6 +14,6 @@ project("xenia-ui") }) includedirs({ project_root.."/third_party/elemental-forms/src", - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) local_platform_files() diff --git a/src/xenia/vfs/premake5.lua b/src/xenia/vfs/premake5.lua index 9a5b2b03c..dc2331f56 100644 --- a/src/xenia/vfs/premake5.lua +++ b/src/xenia/vfs/premake5.lua @@ -12,6 +12,6 @@ project("xenia-vfs") defines({ }) includedirs({ - project_root.."/build_tools/third_party/gflags/src", + project_root.."/build_tools/third_party/gflags/src", }) recursive_platform_files() From e3d87b1715bc22a3bab3a6f8b5dfe641ac86ce90 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 29 Aug 2015 22:05:33 -0500 Subject: [PATCH 6/9] Whoops (formatting) --- src/xenia/apu/xma_helpers.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/xenia/apu/xma_helpers.h b/src/xenia/apu/xma_helpers.h index bf0f593ac..50a6d0678 100644 --- a/src/xenia/apu/xma_helpers.h +++ b/src/xenia/apu/xma_helpers.h @@ -1,11 +1,11 @@ /** -****************************************************************************** -* Xenia : Xbox 360 Emulator Research Project * -****************************************************************************** -* Copyright 2015 Ben Vanik. All rights reserved. * -* Released under the BSD license - see LICENSE in the root for more details. * -****************************************************************************** -*/ + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ // This file contains some functions used to help parse XMA data. From afa013f4bff32650ded8fa8e312a2d441f03cd75 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 29 Aug 2015 22:11:59 -0500 Subject: [PATCH 7/9] libav_verbose flag --- src/xenia/apu/xma_decoder.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/xenia/apu/xma_decoder.cc b/src/xenia/apu/xma_decoder.cc index aaa0edf90..0c73236db 100644 --- a/src/xenia/apu/xma_decoder.cc +++ b/src/xenia/apu/xma_decoder.cc @@ -7,8 +7,11 @@ ****************************************************************************** */ -#include "xenia/apu/xma_context.h" #include "xenia/apu/xma_decoder.h" + +#include + +#include "xenia/apu/xma_context.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" #include "xenia/base/ring_buffer.h" @@ -46,6 +49,8 @@ extern "C" { // do this, it's likely they are either passing the context to XAudio or // using the XMA* functions. +DEFINE_bool(libav_verbose, false, "Verbose libav output (debug and above)"); + namespace xe { namespace apu { @@ -55,11 +60,9 @@ XmaDecoder::XmaDecoder(cpu::Processor* processor) XmaDecoder::~XmaDecoder() = default; void av_log_callback(void* avcl, int level, const char* fmt, va_list va) { -#ifdef NDEBUG - if (level > AV_LOG_WARNING) { + if (!FLAGS_libav_verbose && level > AV_LOG_WARNING) { return; } -#endif char level_char = '?'; switch (level) { From 50535b07c384c8191370026cc8472de63d1517e5 Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 29 Aug 2015 22:14:20 -0500 Subject: [PATCH 8/9] Change validity checks to assert statements. --- src/xenia/apu/xma_context.cc | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/xenia/apu/xma_context.cc b/src/xenia/apu/xma_context.cc index 87ac2afa8..333366220 100644 --- a/src/xenia/apu/xma_context.cc +++ b/src/xenia/apu/xma_context.cc @@ -588,24 +588,15 @@ void XmaContext::DecodePackets(XMA_CONTEXT_DATA* data) { // Copy to the output buffer. size_t written_bytes = 0; -#ifdef DEBUG // Validity checks. - if (decoded_frame_->nb_samples > kSamplesPerFrame) { - XELOGAPU("Decoded frame has an invalid sample count!"); - return; - } else if (context_->sample_fmt != AV_SAMPLE_FMT_FLTP) { - XELOGAPU("libav decoder did not output floating point samples!"); - return; - } + assert(decoded_frame_->nb_samples <= kSamplesPerFrame); + assert(context_->sample_fmt == AV_SAMPLE_FMT_FLTP); // Check the returned buffer size. - if (av_samples_get_buffer_size(NULL, context_->channels, - decoded_frame_->nb_samples, - context_->sample_fmt, 1) != - context_->channels * decoded_frame_->nb_samples * sizeof(float)) { - return; - } -#endif + assert(av_samples_get_buffer_size(NULL, context_->channels, + decoded_frame_->nb_samples, + context_->sample_fmt, 1) == + context_->channels * decoded_frame_->nb_samples * sizeof(float)); // Convert the frame. ConvertFrame((const uint8_t**)decoded_frame_->data, context_->channels, From f80e5fc98d268c263ccf4ada3f4e53559f0b4dda Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sat, 29 Aug 2015 22:21:25 -0500 Subject: [PATCH 9/9] Linting --- src/xenia/apu/xma_context.h | 13 ++++++++----- src/xenia/apu/xma_decoder.cc | 4 ++-- src/xenia/apu/xma_helpers.h | 13 ++++++------- src/xenia/base/bit_stream.h | 2 +- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/xenia/apu/xma_context.h b/src/xenia/apu/xma_context.h index 86eca8c1c..96d059188 100644 --- a/src/xenia/apu/xma_context.h +++ b/src/xenia/apu/xma_context.h @@ -167,8 +167,10 @@ class XmaContext { private: static int GetSampleRate(int id); - size_t SavePartial(uint8_t* packet, uint32_t frame_offset_bits, size_t frame_size_bits, bool append); - bool ValidFrameOffset(uint8_t* block, size_t size_bytes, size_t frame_offset_bits); + size_t SavePartial(uint8_t* packet, uint32_t frame_offset_bits, + size_t frame_size_bits, bool append); + bool ValidFrameOffset(uint8_t* block, size_t size_bytes, + size_t frame_offset_bits); void DecodePackets(XMA_CONTEXT_DATA* data); uint32_t GetFramePacketNumber(uint8_t* block, size_t size, size_t bit_offset); int PrepareDecoder(uint8_t* block, size_t size, int sample_rate, @@ -205,12 +207,13 @@ class XmaContext { bool partial_frame_size_known_ = false; size_t partial_frame_total_size_bits_ = 0; size_t partial_frame_start_offset_bits_ = 0; - size_t partial_frame_offset_bits_ = 0; // blah internal don't use this + size_t partial_frame_offset_bits_ = 0; // blah internal don't use this std::vector partial_frame_buffer_; - // If we didn't finish writing a frame to the output buffer, this is the offset. + // If we didn't finish writing a frame to the output buffer, this is the + // offset. size_t current_frame_pos_ = 0; - uint32_t last_input_read_pos_ = 0; // Last seen read buffer pos + uint32_t last_input_read_pos_ = 0; // Last seen read buffer pos uint8_t* current_frame_ = nullptr; uint32_t frame_samples_size_ = 0; }; diff --git a/src/xenia/apu/xma_decoder.cc b/src/xenia/apu/xma_decoder.cc index 0c73236db..a33968ef9 100644 --- a/src/xenia/apu/xma_decoder.cc +++ b/src/xenia/apu/xma_decoder.cc @@ -137,8 +137,8 @@ void XmaDecoder::WorkerThreadMain() { // TODO: Need thread safety to do this. // Probably not too important though. - //registers_.current_context = n; - //registers_.next_context = (n + 1) % kContextCount; + // registers_.current_context = n; + // registers_.next_context = (n + 1) % kContextCount; } } } diff --git a/src/xenia/apu/xma_helpers.h b/src/xenia/apu/xma_helpers.h index 50a6d0678..c22c70c25 100644 --- a/src/xenia/apu/xma_helpers.h +++ b/src/xenia/apu/xma_helpers.h @@ -25,7 +25,8 @@ uint32_t GetPacketFrameCount(uint8_t* packet) { // Get the first frame offset in bits uint32_t GetPacketFrameOffset(uint8_t* packet) { - uint32_t val = (uint16_t)(((packet[0] & 0x3) << 13) | (packet[1] << 5) | (packet[2] >> 3)); + uint32_t val = (uint16_t)(((packet[0] & 0x3) << 13) | (packet[1] << 5) | + (packet[2] >> 3)); if (val == 0x7FFF) { return -1; } else { @@ -37,12 +38,10 @@ uint32_t GetPacketMetadata(uint8_t* packet) { return (uint8_t)(packet[2] & 0x7); } -uint32_t GetPacketSkipCount(uint8_t* packet) { - return (uint8_t)(packet[3]); -} +uint32_t GetPacketSkipCount(uint8_t* packet) { return (uint8_t)(packet[3]); } -} // namespace xma -} // namespace apu -} // namespace xe +} // namespace xma +} // namespace apu +} // namespace xe #endif // XENIA_APU_XMA_HELPERS_H_ diff --git a/src/xenia/base/bit_stream.h b/src/xenia/base/bit_stream.h index 64cc2704b..ad84379d4 100644 --- a/src/xenia/base/bit_stream.h +++ b/src/xenia/base/bit_stream.h @@ -30,7 +30,7 @@ class BitStream { // Note: num_bits MUST be in the range 0-57 (inclusive) uint64_t Peek(size_t num_bits); uint64_t Read(size_t num_bits); - bool Write(uint64_t val, size_t num_bits); // TODO: Not tested! + bool Write(uint64_t val, size_t num_bits); // TODO: Not tested! size_t Copy(uint8_t* dest_buffer, size_t num_bits);