From 11c63ca969f39481ccf75c204e21948f4fb41efa Mon Sep 17 00:00:00 2001 From: David <25727384+ogniK5377@users.noreply.github.com> Date: Tue, 21 Apr 2020 12:57:30 +1000 Subject: [PATCH] audio_renderer: Preliminary BehaviorInfo (#3736) * audio_renderer: Preliminary BehaviorInfo * clang format * Fixed IsRevisionSupported * fixed IsValidRevision * Fixed logic error & spelling errors & crash * Addressed issues --- src/audio_core/CMakeLists.txt | 3 + src/audio_core/audio_renderer.cpp | 32 +++++--- src/audio_core/audio_renderer.h | 5 +- src/audio_core/behavior_info.cpp | 100 ++++++++++++++++++++++++ src/audio_core/behavior_info.h | 66 ++++++++++++++++ src/audio_core/common.h | 47 +++++++++++ src/core/hle/service/audio/audren_u.cpp | 9 ++- 7 files changed, 249 insertions(+), 13 deletions(-) create mode 100644 src/audio_core/behavior_info.cpp create mode 100644 src/audio_core/behavior_info.h create mode 100644 src/audio_core/common.h diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index c381dbe1dc..5ef38a337b 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -7,9 +7,12 @@ add_library(audio_core STATIC audio_out.h audio_renderer.cpp audio_renderer.h + behavior_info.cpp + behavior_info.h buffer.h codec.cpp codec.h + common.h null_sink.h sink.h sink_details.cpp diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 7a9dc61d49..d18ef69404 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -6,6 +6,7 @@ #include "audio_core/audio_out.h" #include "audio_core/audio_renderer.h" #include "audio_core/codec.h" +#include "audio_core/common.h" #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" @@ -79,7 +80,7 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory std::size_t instance_number) : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), effects(params.effect_count), memory{memory_} { - + behavior_info.SetUserRevision(params.revision); audio_out = std::make_unique(); stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, fmt::format("AudioRenderer-Instance{}", instance_number), @@ -109,17 +110,17 @@ Stream::State AudioRenderer::GetStreamState() const { return stream->GetState(); } -static constexpr u32 VersionFromRevision(u32_le rev) { - // "REV7" -> 7 - return ((rev >> 24) & 0xff) - 0x30; -} - -std::vector AudioRenderer::UpdateAudioRenderer(const std::vector& input_params) { +ResultVal> AudioRenderer::UpdateAudioRenderer(const std::vector& input_params) { // Copy UpdateDataHeader struct UpdateDataHeader config{}; std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader)); u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4); + if (!behavior_info.UpdateInput(input_params, sizeof(UpdateDataHeader))) { + LOG_ERROR(Audio, "Failed to update behavior info input parameters"); + return Audren::ERR_INVALID_PARAMETERS; + } + // Copy MemoryPoolInfo structs std::vector mem_pool_info(memory_pool_count); std::memcpy(mem_pool_info.data(), @@ -173,8 +174,7 @@ std::vector AudioRenderer::UpdateAudioRenderer(const std::vector& input_ // Copy output header UpdateDataHeader response_data{worker_params}; std::vector output_params(response_data.total_size); - const auto audren_revision = VersionFromRevision(config.revision); - if (audren_revision >= 5) { + if (behavior_info.IsElapsedFrameCountSupported()) { response_data.frame_count = 0x10; response_data.total_size += 0x10; } @@ -200,7 +200,19 @@ std::vector AudioRenderer::UpdateAudioRenderer(const std::vector& input_ sizeof(EffectOutStatus)); effect_out_status_offset += sizeof(EffectOutStatus); } - return output_params; + + // Update behavior info output + const std::size_t behavior_out_status_offset{ + sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + + response_data.effects_size + response_data.sinks_size + + response_data.performance_manager_size}; + + if (!behavior_info.UpdateOutput(output_params, behavior_out_status_offset)) { + LOG_ERROR(Audio, "Failed to update behavior info output parameters"); + return Audren::ERR_INVALID_PARAMETERS; + } + + return MakeResult(output_params); } void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 62faf9f19a..b42770faef 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h @@ -8,11 +8,13 @@ #include #include +#include "audio_core/behavior_info.h" #include "audio_core/stream.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/kernel/object.h" +#include "core/hle/result.h" namespace Core::Timing { class CoreTiming; @@ -226,7 +228,7 @@ public: std::shared_ptr buffer_event, std::size_t instance_number); ~AudioRenderer(); - std::vector UpdateAudioRenderer(const std::vector& input_params); + ResultVal> UpdateAudioRenderer(const std::vector& input_params); void QueueMixedBuffer(Buffer::Tag tag); void ReleaseAndQueueBuffers(); u32 GetSampleRate() const; @@ -237,6 +239,7 @@ public: private: class EffectState; class VoiceState; + BehaviorInfo behavior_info{}; AudioRendererParameter worker_params; std::shared_ptr buffer_event; diff --git a/src/audio_core/behavior_info.cpp b/src/audio_core/behavior_info.cpp new file mode 100644 index 0000000000..94b7a3bf1c --- /dev/null +++ b/src/audio_core/behavior_info.cpp @@ -0,0 +1,100 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "audio_core/behavior_info.h" +#include "audio_core/common.h" +#include "common/logging/log.h" + +namespace AudioCore { + +BehaviorInfo::BehaviorInfo() : process_revision(CURRENT_PROCESS_REVISION) {} +BehaviorInfo::~BehaviorInfo() = default; + +bool BehaviorInfo::UpdateInput(const std::vector& buffer, std::size_t offset) { + if (!CanConsumeBuffer(buffer.size(), offset, sizeof(InParams))) { + LOG_ERROR(Audio, "Buffer is an invalid size!"); + return false; + } + InParams params{}; + std::memcpy(¶ms, buffer.data() + offset, sizeof(InParams)); + + if (!IsValidRevision(params.revision)) { + LOG_ERROR(Audio, "Invalid input revision, revision=0x{:08X}", params.revision); + return false; + } + + if (user_revision != params.revision) { + LOG_ERROR(Audio, + "User revision differs from input revision, expecting 0x{:08X} but got 0x{:08X}", + user_revision, params.revision); + return false; + } + + ClearError(); + UpdateFlags(params.flags); + + // TODO(ogniK): Check input params size when InfoUpdater is used + + return true; +} + +bool BehaviorInfo::UpdateOutput(std::vector& buffer, std::size_t offset) { + if (!CanConsumeBuffer(buffer.size(), offset, sizeof(OutParams))) { + LOG_ERROR(Audio, "Buffer is an invalid size!"); + return false; + } + + OutParams params{}; + std::memcpy(params.errors.data(), errors.data(), sizeof(ErrorInfo) * errors.size()); + params.error_count = static_cast(error_count); + std::memcpy(buffer.data() + offset, ¶ms, sizeof(OutParams)); + return true; +} + +void BehaviorInfo::ClearError() { + error_count = 0; +} + +void BehaviorInfo::UpdateFlags(u64_le dest_flags) { + flags = dest_flags; +} + +void BehaviorInfo::SetUserRevision(u32_le revision) { + user_revision = revision; +} + +bool BehaviorInfo::IsAdpcmLoopContextBugFixed() const { + return IsRevisionSupported(2, user_revision); +} + +bool BehaviorInfo::IsSplitterSupported() const { + return IsRevisionSupported(2, user_revision); +} + +bool BehaviorInfo::IsLongSizePreDelaySupported() const { + return IsRevisionSupported(3, user_revision); +} + +bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const { + return IsRevisionSupported(5, user_revision); +} + +bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const { + return IsRevisionSupported(4, user_revision); +} + +bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const { + return IsRevisionSupported(1, user_revision); +} + +bool BehaviorInfo::IsElapsedFrameCountSupported() const { + return IsRevisionSupported(5, user_revision); +} + +bool BehaviorInfo::IsMemoryPoolForceMappingEnabled() const { + return (flags & 1) != 0; +} + +} // namespace AudioCore diff --git a/src/audio_core/behavior_info.h b/src/audio_core/behavior_info.h new file mode 100644 index 0000000000..c5e91ab392 --- /dev/null +++ b/src/audio_core/behavior_info.h @@ -0,0 +1,66 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +#include +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" + +namespace AudioCore { +class BehaviorInfo { +public: + explicit BehaviorInfo(); + ~BehaviorInfo(); + + bool UpdateInput(const std::vector& buffer, std::size_t offset); + bool UpdateOutput(std::vector& buffer, std::size_t offset); + + void ClearError(); + void UpdateFlags(u64_le dest_flags); + void SetUserRevision(u32_le revision); + + bool IsAdpcmLoopContextBugFixed() const; + bool IsSplitterSupported() const; + bool IsLongSizePreDelaySupported() const; + bool IsAudioRenererProcessingTimeLimit80PercentSupported() const; + bool IsAudioRenererProcessingTimeLimit75PercentSupported() const; + bool IsAudioRenererProcessingTimeLimit70PercentSupported() const; + bool IsElapsedFrameCountSupported() const; + bool IsMemoryPoolForceMappingEnabled() const; + +private: + u32_le process_revision{}; + u32_le user_revision{}; + u64_le flags{}; + + struct ErrorInfo { + u32_le result{}; + INSERT_PADDING_WORDS(1); + u64_le result_info{}; + }; + static_assert(sizeof(ErrorInfo) == 0x10, "ErrorInfo is an invalid size"); + + std::array errors{}; + std::size_t error_count{}; + + struct InParams { + u32_le revision{}; + u32_le padding{}; + u64_le flags{}; + }; + static_assert(sizeof(InParams) == 0x10, "InParams is an invalid size"); + + struct OutParams { + std::array errors{}; + u32_le error_count{}; + INSERT_PADDING_BYTES(12); + }; + static_assert(sizeof(OutParams) == 0xb0, "OutParams is an invalid size"); +}; + +} // namespace AudioCore diff --git a/src/audio_core/common.h b/src/audio_core/common.h new file mode 100644 index 0000000000..98478b66b1 --- /dev/null +++ b/src/audio_core/common.h @@ -0,0 +1,47 @@ +// Copyright 2020 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/result.h" + +namespace AudioCore { +namespace Audren { +constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41}; +} + +constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); + +static constexpr u32 VersionFromRevision(u32_le rev) { + // "REV7" -> 7 + return ((rev >> 24) & 0xff) - 0x30; +} + +static constexpr bool IsRevisionSupported(u32 required, u32_le user_revision) { + const auto base = VersionFromRevision(user_revision); + return required <= base; +} + +static constexpr bool IsValidRevision(u32_le revision) { + const auto base = VersionFromRevision(revision); + constexpr auto max_rev = VersionFromRevision(CURRENT_PROCESS_REVISION); + return base <= max_rev; +} + +static constexpr bool CanConsumeBuffer(std::size_t size, std::size_t offset, std::size_t required) { + if (offset > size) { + return false; + } + if (size < required) { + return false; + } + if ((size - offset) < required) { + return false; + } + return true; +} + +} // namespace AudioCore diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 07dd2caecf..d8359abaa1 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp @@ -94,9 +94,14 @@ private: void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_Audio, "(STUBBED) called"); - ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer())); + auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer()); + + if (result.Succeeded()) { + ctx.WriteBuffer(result.Unwrap()); + } + IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(RESULT_SUCCESS); + rb.Push(result.Code()); } void Start(Kernel::HLERequestContext& ctx) {