From 4cc01f6c7169d1840a87cfc4183889eb2ab350a6 Mon Sep 17 00:00:00 2001 From: Zephyron Date: Tue, 4 Feb 2025 16:32:59 +1000 Subject: [PATCH] audio_core/renderer: Add compressor and splitter support for Rev13 Implement new audio features available in AudioRenderer Revision 13: - Add AudioRendererRevision enum to track version-specific features - Implement CompressorEffect with statistics tracking support - Add SplitterDestination with previous volume reset functionality - Add version checks for feature compatibility The compressor provides dynamic range compression with configurable parameters and optional statistics tracking. The splitter improvements allow for more flexible volume management between audio transitions. These changes maintain compatibility with older revisions while enabling new features in Rev13. --- externals/vcpkg | 2 +- src/audio_core/renderer/audio_renderer.h | 15 ++++++ src/audio_core/renderer/compressor.h | 39 +++++++++++++++ src/audio_core/renderer/effect/compressor.cpp | 46 +++++++++++++++++ src/audio_core/renderer/effect/compressor.h | 49 +++++++++++++++++++ src/audio_core/renderer/splitter.h | 26 ++++++++++ 6 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 src/audio_core/renderer/compressor.h create mode 100644 src/audio_core/renderer/splitter.h diff --git a/externals/vcpkg b/externals/vcpkg index 2ded45cc7..561cf50a7 160000 --- a/externals/vcpkg +++ b/externals/vcpkg @@ -1 +1 @@ -Subproject commit 2ded45cc7a35667ad8d96e5e50b4a24afacafb3a +Subproject commit 561cf50a731761f9890fb720f830cb3501b8472d diff --git a/src/audio_core/renderer/audio_renderer.h b/src/audio_core/renderer/audio_renderer.h index f16adeda7..5b0875c81 100644 --- a/src/audio_core/renderer/audio_renderer.h +++ b/src/audio_core/renderer/audio_renderer.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -23,6 +24,11 @@ struct AudioRendererParameterInternal; namespace Renderer { class Manager; +enum class AudioRendererRevision { + Rev12 = 12 << 24, + Rev13 = 13 << 24, +}; + /** * Audio Renderer, wraps the main audio system and is mainly responsible for handling service calls. */ @@ -82,6 +88,14 @@ public: Result RequestUpdate(std::span input, std::span performance, std::span output); + bool IsCompressorStatisticsSupported() const { + return revision >= static_cast(AudioRendererRevision::Rev13); + } + + bool IsSplitterPrevVolumeResetSupported() const { + return revision >= static_cast(AudioRendererRevision::Rev13); + } + private: /// System core Core::System& core; @@ -93,6 +107,7 @@ private: bool system_registered{}; /// Audio render system, main driver of audio rendering System system; + u32 revision; }; } // namespace Renderer diff --git a/src/audio_core/renderer/compressor.h b/src/audio_core/renderer/compressor.h new file mode 100644 index 000000000..97f43dfae --- /dev/null +++ b/src/audio_core/renderer/compressor.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include "common/common_types.h" + +namespace AudioCore::Renderer { + +struct CompressorStatistics { + float maximum_mean{}; + float minimum_gain{1.0f}; + std::array last_samples{}; // 6 channels max + + void Reset(u16 channel_count) { + maximum_mean = 0.0f; + minimum_gain = 1.0f; + std::fill_n(last_samples.begin(), channel_count, 0.0f); + } +}; + +struct CompressorParameter { + u32 channel_count{}; + float input_gain{}; + float release_coefficient{}; + float attack_coefficient{}; + float ratio{}; + float threshold{}; + bool makeup_gain_enabled{}; + bool statistics_enabled{}; + bool statistics_reset{}; + + bool IsChannelCountValid() const { + return channel_count > 0 && channel_count <= 6; + } +}; + +} // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/compressor.cpp b/src/audio_core/renderer/effect/compressor.cpp index fea0aefcf..3b32670f7 100644 --- a/src/audio_core/renderer/effect/compressor.cpp +++ b/src/audio_core/renderer/effect/compressor.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include "audio_core/renderer/effect/compressor.h" namespace AudioCore::Renderer { @@ -37,4 +39,48 @@ CpuAddr CompressorInfo::GetWorkbuffer(s32 index) { return GetSingleBuffer(index); } +CompressorEffect::CompressorEffect(std::size_t sample_count_) : sample_count{sample_count_} {} + +void CompressorEffect::Process(std::span output_buffer, std::span input_buffer) { + if (!IsEnabled() || !parameter.IsChannelCountValid()) { + std::copy(input_buffer.begin(), input_buffer.end(), output_buffer.begin()); + return; + } + + if (!result_state.empty() && parameter.statistics_reset) { + statistics.Reset(static_cast(parameter.channel_count)); + } + + // Process audio with compressor effect + for (u32 sample_index = 0; sample_index < sample_count; sample_index++) { + float mean = 0.0f; + for (u32 channel = 0; channel < parameter.channel_count; channel++) { + const auto sample = input_buffer[sample_index * parameter.channel_count + channel]; + mean += sample * sample; + } + mean /= static_cast(parameter.channel_count); + + // Calculate compression gain + float compression_gain = 1.0f; + if (mean > parameter.threshold) { + compression_gain = parameter.threshold / mean; + compression_gain = std::pow(compression_gain, 1.0f - (1.0f / parameter.ratio)); + } + + // Apply compression + for (u32 channel = 0; channel < parameter.channel_count; channel++) { + const auto in_sample = input_buffer[sample_index * parameter.channel_count + channel]; + const auto out_sample = in_sample * compression_gain * parameter.input_gain; + output_buffer[sample_index * parameter.channel_count + channel] = out_sample; + + // Update statistics if enabled + if (parameter.statistics_enabled) { + statistics.maximum_mean = std::max(statistics.maximum_mean, mean); + statistics.minimum_gain = std::min(statistics.minimum_gain, compression_gain); + statistics.last_samples[channel] = std::abs(in_sample) * (1.0f / 32768.0f); + } + } + } +} + } // namespace AudioCore::Renderer diff --git a/src/audio_core/renderer/effect/compressor.h b/src/audio_core/renderer/effect/compressor.h index cda55c284..7074c8484 100644 --- a/src/audio_core/renderer/effect/compressor.h +++ b/src/audio_core/renderer/effect/compressor.h @@ -1,9 +1,11 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include +#include #include "audio_core/common/common.h" #include "audio_core/renderer/effect/effect_info_base.h" @@ -12,6 +14,53 @@ namespace AudioCore::Renderer { +struct CompressorStatistics { + float maximum_mean{}; + float minimum_gain{1.0f}; + std::array last_samples{}; // 6 channels max + + void Reset(u16 channel_count) { + maximum_mean = 0.0f; + minimum_gain = 1.0f; + std::fill_n(last_samples.begin(), channel_count, 0.0f); + } +}; + +struct CompressorParameter { + u32 channel_count{}; + float input_gain{}; + float release_coefficient{}; + float attack_coefficient{}; + float ratio{}; + float threshold{}; + bool makeup_gain_enabled{}; + bool statistics_enabled{}; + bool statistics_reset{}; + + bool IsChannelCountValid() const { + return channel_count > 0 && channel_count <= 6; + } +}; + +class CompressorEffect : public EffectInfoBase { +public: + explicit CompressorEffect(std::size_t sample_count_); + ~CompressorEffect() override = default; + + void Process(std::span output_buffer, std::span input_buffer); + + bool IsEnabled() const { + return effect_enabled; + } + +private: + CompressorParameter parameter; + CompressorStatistics statistics; + std::size_t sample_count; + bool effect_enabled{false}; + std::span result_state; +}; + class CompressorInfo : public EffectInfoBase { public: struct ParameterVersion1 { diff --git a/src/audio_core/renderer/splitter.h b/src/audio_core/renderer/splitter.h new file mode 100644 index 000000000..373656d44 --- /dev/null +++ b/src/audio_core/renderer/splitter.h @@ -0,0 +1,26 @@ +// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +namespace AudioCore::Renderer { + +class SplitterDestination { +public: + void Update(const SplitterDestinationParameter& parameter, bool is_prev_volume_reset_supported) { + if (is_prev_volume_reset_supported ? parameter.reset_prev_volume + : (!is_used && parameter.is_used)) { + // Reset previous mix volumes + prev_mix_volumes = parameter.mix_volumes; + mix_volumes = parameter.mix_volumes; + } + is_used = parameter.is_used; + } + +private: + bool is_used{}; + std::array mix_volumes{}; + std::array prev_mix_volumes{}; +}; + +} // namespace AudioCore::Renderer