mirror of
https://git.citron-emu.org/Citron/Citron.git
synced 2025-02-07 16:56:21 +01:00
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.
This commit is contained in:
parent
89ecb641f1
commit
4cc01f6c71
6 changed files with 176 additions and 1 deletions
2
externals/vcpkg
vendored
2
externals/vcpkg
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 2ded45cc7a35667ad8d96e5e50b4a24afacafb3a
|
||||
Subproject commit 561cf50a731761f9890fb720f830cb3501b8472d
|
|
@ -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
|
||||
|
|
39
src/audio_core/renderer/compressor.h
Normal file
39
src/audio_core/renderer/compressor.h
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
26
src/audio_core/renderer/splitter.h
Normal file
26
src/audio_core/renderer/splitter.h
Normal file
|
@ -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
|
Loading…
Reference in a new issue