summaryrefslogtreecommitdiff
path: root/src/audio_core/renderer
diff options
context:
space:
mode:
authorZephyron <zephyron@citron-emu.org>2025-02-04 16:32:59 +1000
committerZephyron <zephyron@citron-emu.org>2025-02-04 16:32:59 +1000
commit4cc01f6c7169d1840a87cfc4183889eb2ab350a6 (patch)
tree85a822992fa4bf508933ab7661c2112e9127e251 /src/audio_core/renderer
parent89ecb641f1dc9debc41297dcd3eb9a8f44cdbbfa (diff)
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.
Diffstat (limited to 'src/audio_core/renderer')
-rw-r--r--src/audio_core/renderer/audio_renderer.h15
-rw-r--r--src/audio_core/renderer/compressor.h39
-rw-r--r--src/audio_core/renderer/effect/compressor.cpp46
-rw-r--r--src/audio_core/renderer/effect/compressor.h49
-rw-r--r--src/audio_core/renderer/splitter.h26
5 files changed, 175 insertions, 0 deletions
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<const u8> input, std::span<u8> performance,
std::span<u8> output);
+ bool IsCompressorStatisticsSupported() const {
+ return revision >= static_cast<u32>(AudioRendererRevision::Rev13);
+ }
+
+ bool IsSplitterPrevVolumeResetSupported() const {
+ return revision >= static_cast<u32>(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 <array>
+#include "common/common_types.h"
+
+namespace AudioCore::Renderer {
+
+struct CompressorStatistics {
+ float maximum_mean{};
+ float minimum_gain{1.0f};
+ std::array<float, 6> 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 <cmath>
#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<f32> output_buffer, std::span<const f32> 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<u16>(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<float>(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 <array>
+#include <span>
#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<float, 6> 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<f32> output_buffer, std::span<const f32> input_buffer);
+
+ bool IsEnabled() const {
+ return effect_enabled;
+ }
+
+private:
+ CompressorParameter parameter;
+ CompressorStatistics statistics;
+ std::size_t sample_count;
+ bool effect_enabled{false};
+ std::span<u8> 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<float, MaxMixBuffers> mix_volumes{};
+ std::array<float, MaxMixBuffers> prev_mix_volumes{};
+};
+
+} // namespace AudioCore::Renderer