early-access version 2830
This commit is contained in:
@@ -5,9 +5,9 @@
|
||||
#include "audio_core/renderer/command/command_buffer.h"
|
||||
#include "audio_core/renderer/command/command_list_header.h"
|
||||
#include "audio_core/renderer/command/command_processing_time_estimator.h"
|
||||
#include "audio_core/renderer/effect/effect_biquad_filter_info.h"
|
||||
#include "audio_core/renderer/effect/effect_delay_info.h"
|
||||
#include "audio_core/renderer/effect/effect_reverb_info.h"
|
||||
#include "audio_core/renderer/effect/biquad_filter.h"
|
||||
#include "audio_core/renderer/effect/delay.h"
|
||||
#include "audio_core/renderer/effect/reverb.h"
|
||||
#include "audio_core/renderer/memory/memory_pool_info.h"
|
||||
#include "audio_core/renderer/mix/mix_info.h"
|
||||
#include "audio_core/renderer/sink/circular_buffer_sink_info.h"
|
||||
@@ -368,9 +368,13 @@ void CommandBuffer::GenerateDelayCommand(const s32 node_id, EffectInfoBase& effe
|
||||
if (IsChannelCountValid(parameter.channel_count)) {
|
||||
const auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(DelayInfo::State))};
|
||||
if (state_buffer) {
|
||||
for (s8 channel = 0; channel < parameter.channel_count; channel++) {
|
||||
cmd.inputs[channel] = static_cast<s8>(buffer_offset + parameter.inputs[channel]);
|
||||
cmd.outputs[channel] = static_cast<s8>(buffer_offset + parameter.outputs[channel]);
|
||||
for (s16 channel = 0; channel < parameter.channel_count; channel++) {
|
||||
cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
|
||||
cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
|
||||
}
|
||||
|
||||
if (!behavior->IsDelayChannelMappingChanged() && parameter.channel_count == 6) {
|
||||
UseOldChannelMapping(cmd.inputs, cmd.outputs);
|
||||
}
|
||||
|
||||
cmd.parameter = parameter;
|
||||
@@ -506,11 +510,15 @@ void CommandBuffer::GenerateReverbCommand(const s32 node_id, EffectInfoBase& eff
|
||||
if (IsChannelCountValid(parameter.channel_count)) {
|
||||
const auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(ReverbInfo::State))};
|
||||
if (state_buffer) {
|
||||
for (s8 channel = 0; channel < parameter.channel_count; channel++) {
|
||||
for (s16 channel = 0; channel < parameter.channel_count; channel++) {
|
||||
cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
|
||||
cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
|
||||
}
|
||||
|
||||
if (!behavior->IsReverbChannelMappingChanged() && parameter.channel_count == 6) {
|
||||
UseOldChannelMapping(cmd.inputs, cmd.outputs);
|
||||
}
|
||||
|
||||
cmd.parameter = parameter;
|
||||
cmd.effect_enabled = effect_info.IsEnabled();
|
||||
cmd.state = state_buffer;
|
||||
@@ -534,11 +542,15 @@ void CommandBuffer::GenerateI3dl2ReverbCommand(const s32 node_id, EffectInfoBase
|
||||
const auto state_buffer{
|
||||
memory_pool->Translate(CpuAddr(state), sizeof(I3dl2ReverbInfo::State))};
|
||||
if (state_buffer) {
|
||||
for (s8 channel = 0; channel < parameter.channel_count; channel++) {
|
||||
for (s16 channel = 0; channel < parameter.channel_count; channel++) {
|
||||
cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
|
||||
cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
|
||||
}
|
||||
|
||||
if (!behavior->IsI3dl2ReverbChannelMappingChanged() && parameter.channel_count == 6) {
|
||||
UseOldChannelMapping(cmd.inputs, cmd.outputs);
|
||||
}
|
||||
|
||||
cmd.parameter = parameter;
|
||||
cmd.effect_enabled = effect_info.IsEnabled();
|
||||
cmd.state = state_buffer;
|
||||
@@ -675,4 +687,28 @@ void CommandBuffer::GenerateCaptureCommand(const s32 node_id, EffectInfoBase& ef
|
||||
GenerateEnd<CaptureCommand>(cmd);
|
||||
}
|
||||
|
||||
void CommandBuffer::GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& effect_info,
|
||||
s32 node_id) {
|
||||
auto& cmd{GenerateStart<CompressorCommand, CommandId::Compressor>(node_id)};
|
||||
|
||||
auto& parameter{
|
||||
*reinterpret_cast<CompressorInfo::ParameterVersion2*>(effect_info.GetParameter())};
|
||||
auto state{reinterpret_cast<CompressorInfo::State*>(effect_info.GetStateBuffer())};
|
||||
|
||||
if (IsChannelCountValid(parameter.channel_count)) {
|
||||
auto state_buffer{memory_pool->Translate(CpuAddr(state), sizeof(CompressorInfo::State))};
|
||||
if (state_buffer) {
|
||||
for (u16 channel = 0; channel < parameter.channel_count; channel++) {
|
||||
cmd.inputs[channel] = buffer_offset + parameter.inputs[channel];
|
||||
cmd.outputs[channel] = buffer_offset + parameter.outputs[channel];
|
||||
}
|
||||
cmd.parameter = parameter;
|
||||
cmd.workbuffer = state_buffer;
|
||||
cmd.enabled = effect_info.IsEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
GenerateEnd<CompressorCommand>(cmd);
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
||||
|
@@ -6,7 +6,7 @@
|
||||
#include <span>
|
||||
|
||||
#include "audio_core/renderer/command/commands.h"
|
||||
#include "audio_core/renderer/effect/effect_light_limiter_info.h"
|
||||
#include "audio_core/renderer/effect/light_limiter.h"
|
||||
#include "audio_core/renderer/performance/performance_manager.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -428,6 +428,15 @@ public:
|
||||
s16 output_index, s16 buffer_offset, u32 update_count,
|
||||
u32 count_max, u32 write_offset);
|
||||
|
||||
/**
|
||||
* Generate a compressor command, adding it to the command list.
|
||||
*
|
||||
* @param buffer_offset - Base mix buffer offset to use.
|
||||
* @param effect_info - Capture effect info to generate this command from.
|
||||
* @param node_id - Node id of the voice this command is generated for.
|
||||
*/
|
||||
void GenerateCompressorCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id);
|
||||
|
||||
/// Command list buffer generated commands will be added to
|
||||
std::span<u8> command_list{};
|
||||
/// Input sample count, unused
|
||||
|
@@ -6,11 +6,12 @@
|
||||
#include "audio_core/renderer/command/command_buffer.h"
|
||||
#include "audio_core/renderer/command/command_generator.h"
|
||||
#include "audio_core/renderer/command/command_list_header.h"
|
||||
#include "audio_core/renderer/effect/effect_aux_info.h"
|
||||
#include "audio_core/renderer/effect/effect_biquad_filter_info.h"
|
||||
#include "audio_core/renderer/effect/effect_buffer_mixer_info.h"
|
||||
#include "audio_core/renderer/effect/effect_capture_info.h"
|
||||
#include "audio_core/renderer/effect/aux_.h"
|
||||
#include "audio_core/renderer/effect/biquad_filter.h"
|
||||
#include "audio_core/renderer/effect/buffer_mixer.h"
|
||||
#include "audio_core/renderer/effect/capture.h"
|
||||
#include "audio_core/renderer/effect/effect_context.h"
|
||||
#include "audio_core/renderer/effect/light_limiter.h"
|
||||
#include "audio_core/renderer/mix/mix_context.h"
|
||||
#include "audio_core/renderer/performance/detail_aspect.h"
|
||||
#include "audio_core/renderer/performance/entry_aspect.h"
|
||||
@@ -371,7 +372,7 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset
|
||||
break;
|
||||
case EffectInfoBase::ParameterState::Updating:
|
||||
case EffectInfoBase::ParameterState::Updated:
|
||||
if (render_context.behavior->IsBiquadFilterEffectStateClaerBugFixed()) {
|
||||
if (render_context.behavior->IsBiquadFilterEffectStateClearBugFixed()) {
|
||||
needs_init = false;
|
||||
} else {
|
||||
needs_init = parameter.state == EffectInfoBase::ParameterState::Updating;
|
||||
@@ -442,6 +443,11 @@ void CommandGenerator::GenerateCaptureCommand(const s16 buffer_offset, EffectInf
|
||||
}
|
||||
}
|
||||
|
||||
void CommandGenerator::GenerateCompressorCommand(const s16 buffer_offset,
|
||||
EffectInfoBase& effect_info, const s32 node_id) {
|
||||
command_buffer.GenerateCompressorCommand(buffer_offset, effect_info, node_id);
|
||||
}
|
||||
|
||||
void CommandGenerator::GenerateEffectCommand(MixInfo& mix_info) {
|
||||
const auto effect_count{effect_context.GetCount()};
|
||||
for (u32 i = 0; i < effect_count; i++) {
|
||||
@@ -550,6 +556,17 @@ void CommandGenerator::GenerateEffectCommand(MixInfo& mix_info) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case EffectInfoBase::Type::Compressor: {
|
||||
DetailAspect capture_detail_aspect(*this, entry_type, mix_info.node_id,
|
||||
PerformanceDetailType::Unk13);
|
||||
GenerateCompressorCommand(mix_info.buffer_offset, effect_info, mix_info.node_id);
|
||||
if (capture_detail_aspect.initialized) {
|
||||
command_buffer.GeneratePerformanceCommand(
|
||||
capture_detail_aspect.node_id, PerformanceState::Stop,
|
||||
capture_detail_aspect.performance_entry_address);
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_ERROR(Service_Audio, "Invalid effect type {}",
|
||||
static_cast<u32>(effect_info.GetType()));
|
||||
|
@@ -244,6 +244,16 @@ public:
|
||||
*/
|
||||
void GenerateCaptureCommand(s16 buffer_offset, EffectInfoBase& effect_info, s32 node_id);
|
||||
|
||||
/**
|
||||
* Generate a compressor effect command.
|
||||
*
|
||||
* @param buffer_offset - Base mix buffer offset to use.
|
||||
* @param effect_info_base - Compressor effect info.
|
||||
* @param node_id - Node id of the mix this command is generated for.
|
||||
*/
|
||||
void GenerateCompressorCommand(const s16 buffer_offset, EffectInfoBase& effect_info,
|
||||
const s32 node_id);
|
||||
|
||||
/**
|
||||
* Generate all effect commands for a mix.
|
||||
*
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -43,6 +43,7 @@ public:
|
||||
virtual u32 Estimate(const LightLimiterVersion2Command& command) const = 0;
|
||||
virtual u32 Estimate(const MultiTapBiquadFilterCommand& command) const = 0;
|
||||
virtual u32 Estimate(const CaptureCommand& command) const = 0;
|
||||
virtual u32 Estimate(const CompressorCommand& command) const = 0;
|
||||
};
|
||||
|
||||
class CommandProcessingTimeEstimatorVersion1 final : public ICommandProcessingTimeEstimator {
|
||||
@@ -79,6 +80,7 @@ public:
|
||||
u32 Estimate(const LightLimiterVersion2Command& command) const override;
|
||||
u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
|
||||
u32 Estimate(const CaptureCommand& command) const override;
|
||||
u32 Estimate(const CompressorCommand& command) const override;
|
||||
|
||||
private:
|
||||
u32 sample_count{};
|
||||
@@ -119,6 +121,7 @@ public:
|
||||
u32 Estimate(const LightLimiterVersion2Command& command) const override;
|
||||
u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
|
||||
u32 Estimate(const CaptureCommand& command) const override;
|
||||
u32 Estimate(const CompressorCommand& command) const override;
|
||||
|
||||
private:
|
||||
u32 sample_count{};
|
||||
@@ -159,6 +162,7 @@ public:
|
||||
u32 Estimate(const LightLimiterVersion2Command& command) const override;
|
||||
u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
|
||||
u32 Estimate(const CaptureCommand& command) const override;
|
||||
u32 Estimate(const CompressorCommand& command) const override;
|
||||
|
||||
private:
|
||||
u32 sample_count{};
|
||||
@@ -199,6 +203,48 @@ public:
|
||||
u32 Estimate(const LightLimiterVersion2Command& command) const override;
|
||||
u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
|
||||
u32 Estimate(const CaptureCommand& command) const override;
|
||||
u32 Estimate(const CompressorCommand& command) const override;
|
||||
|
||||
private:
|
||||
u32 sample_count{};
|
||||
u32 buffer_count{};
|
||||
};
|
||||
|
||||
class CommandProcessingTimeEstimatorVersion5 final : public ICommandProcessingTimeEstimator {
|
||||
public:
|
||||
CommandProcessingTimeEstimatorVersion5(u32 sample_count_, u32 buffer_count_)
|
||||
: sample_count{sample_count_}, buffer_count{buffer_count_} {}
|
||||
|
||||
u32 Estimate(const PcmInt16DataSourceVersion1Command& command) const override;
|
||||
u32 Estimate(const PcmInt16DataSourceVersion2Command& command) const override;
|
||||
u32 Estimate(const PcmFloatDataSourceVersion1Command& command) const override;
|
||||
u32 Estimate(const PcmFloatDataSourceVersion2Command& command) const override;
|
||||
u32 Estimate(const AdpcmDataSourceVersion1Command& command) const override;
|
||||
u32 Estimate(const AdpcmDataSourceVersion2Command& command) const override;
|
||||
u32 Estimate(const VolumeCommand& command) const override;
|
||||
u32 Estimate(const VolumeRampCommand& command) const override;
|
||||
u32 Estimate(const BiquadFilterCommand& command) const override;
|
||||
u32 Estimate(const MixCommand& command) const override;
|
||||
u32 Estimate(const MixRampCommand& command) const override;
|
||||
u32 Estimate(const MixRampGroupedCommand& command) const override;
|
||||
u32 Estimate(const DepopPrepareCommand& command) const override;
|
||||
u32 Estimate(const DepopForMixBuffersCommand& command) const override;
|
||||
u32 Estimate(const DelayCommand& command) const override;
|
||||
u32 Estimate(const UpsampleCommand& command) const override;
|
||||
u32 Estimate(const DownMix6chTo2chCommand& command) const override;
|
||||
u32 Estimate(const AuxCommand& command) const override;
|
||||
u32 Estimate(const DeviceSinkCommand& command) const override;
|
||||
u32 Estimate(const CircularBufferSinkCommand& command) const override;
|
||||
u32 Estimate(const ReverbCommand& command) const override;
|
||||
u32 Estimate(const I3dl2ReverbCommand& command) const override;
|
||||
u32 Estimate(const PerformanceCommand& command) const override;
|
||||
u32 Estimate(const ClearMixBufferCommand& command) const override;
|
||||
u32 Estimate(const CopyMixBufferCommand& command) const override;
|
||||
u32 Estimate(const LightLimiterVersion1Command& command) const override;
|
||||
u32 Estimate(const LightLimiterVersion2Command& command) const override;
|
||||
u32 Estimate(const MultiTapBiquadFilterCommand& command) const override;
|
||||
u32 Estimate(const CaptureCommand& command) const override;
|
||||
u32 Estimate(const CompressorCommand& command) const override;
|
||||
|
||||
private:
|
||||
u32 sample_count{};
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include "audio_core/renderer/command/effect/aux_.h"
|
||||
#include "audio_core/renderer/command/effect/biquad_filter.h"
|
||||
#include "audio_core/renderer/command/effect/capture.h"
|
||||
#include "audio_core/renderer/command/effect/compressor.h"
|
||||
#include "audio_core/renderer/command/effect/delay.h"
|
||||
#include "audio_core/renderer/command/effect/i3dl2_reverb.h"
|
||||
#include "audio_core/renderer/command/effect/light_limiter.h"
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/effect/aux_.h"
|
||||
#include "audio_core/renderer/effect/effect_aux_info.h"
|
||||
#include "audio_core/renderer/effect/aux_.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/effect/capture.h"
|
||||
#include "audio_core/renderer/effect/effect_aux_info.h"
|
||||
#include "audio_core/renderer/effect/aux_.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
156
src/audio_core/renderer/command/effect/compressor.cpp
Executable file
156
src/audio_core/renderer/command/effect/compressor.cpp
Executable file
@@ -0,0 +1,156 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cmath>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/renderer/adsp/command_list_processor.h"
|
||||
#include "audio_core/renderer/command/effect/compressor.h"
|
||||
#include "audio_core/renderer/effect/compressor.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
||||
static void SetCompressorEffectParameter(CompressorInfo::ParameterVersion2& params,
|
||||
CompressorInfo::State& state) {
|
||||
const auto ratio{1.0f / params.compressor_ratio};
|
||||
auto makeup_gain{0.0f};
|
||||
if (params.makeup_gain_enabled) {
|
||||
makeup_gain = (params.threshold * 0.5f) * (ratio - 1.0f) - 3.0f;
|
||||
}
|
||||
state.makeup_gain = makeup_gain;
|
||||
state.unk_18 = params.unk_28;
|
||||
|
||||
const auto a{(params.out_gain + makeup_gain) / 20.0f * 3.3219f};
|
||||
const auto b{(a - std::trunc(a)) * 0.69315f};
|
||||
const auto c{std::pow(2.0f, b)};
|
||||
|
||||
state.unk_0C = (1.0f - ratio) / 6.0f;
|
||||
state.unk_14 = params.threshold + 1.5f;
|
||||
state.unk_10 = params.threshold - 1.5f;
|
||||
state.unk_20 = c;
|
||||
}
|
||||
|
||||
static void InitializeCompressorEffect(CompressorInfo::ParameterVersion2& params,
|
||||
CompressorInfo::State& state) {
|
||||
std::memset(&state, 0, sizeof(CompressorInfo::State));
|
||||
|
||||
state.unk_00 = 0;
|
||||
state.unk_04 = 1.0f;
|
||||
state.unk_08 = 1.0f;
|
||||
|
||||
SetCompressorEffectParameter(params, state);
|
||||
}
|
||||
|
||||
static void ApplyCompressorEffect(CompressorInfo::ParameterVersion2& params,
|
||||
CompressorInfo::State& state, bool enabled,
|
||||
std::vector<std::span<const s32>> input_buffers,
|
||||
std::vector<std::span<s32>> output_buffers, u32 sample_count) {
|
||||
if (enabled) {
|
||||
auto state_00{state.unk_00};
|
||||
auto state_04{state.unk_04};
|
||||
auto state_08{state.unk_08};
|
||||
auto state_18{state.unk_18};
|
||||
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
auto a{0.0f};
|
||||
for (s16 channel = 0; channel < params.channel_count; channel++) {
|
||||
const auto input_sample{Common::FixedPoint<49, 15>(input_buffers[channel][i])};
|
||||
a += (input_sample * input_sample).to_float();
|
||||
}
|
||||
|
||||
state_00 += params.unk_24 * ((a / params.channel_count) - state.unk_00);
|
||||
|
||||
auto b{-100.0f};
|
||||
auto c{0.0f};
|
||||
if (state_00 >= 1.0e-10) {
|
||||
b = std::log10(state_00) * 10.0f;
|
||||
c = 1.0f;
|
||||
}
|
||||
|
||||
if (b >= state.unk_10) {
|
||||
const auto d{b >= state.unk_14
|
||||
? ((1.0f / params.compressor_ratio) - 1.0f) *
|
||||
(b - params.threshold)
|
||||
: (b - state.unk_10) * (b - state.unk_10) * -state.unk_0C};
|
||||
const auto e{d / 20.0f * 3.3219f};
|
||||
const auto f{(e - std::trunc(e)) * 0.69315f};
|
||||
c = std::pow(2.0f, f);
|
||||
}
|
||||
|
||||
state_18 = params.unk_28;
|
||||
auto tmp{c};
|
||||
if ((state_04 - c) <= 0.08f) {
|
||||
state_18 = params.unk_2C;
|
||||
if (((state_04 - c) >= -0.08f) && (std::abs(state_08 - c) >= 0.001f)) {
|
||||
tmp = state_04;
|
||||
}
|
||||
}
|
||||
|
||||
state_04 = tmp;
|
||||
state_08 += (c - state_08) * state_18;
|
||||
|
||||
for (s16 channel = 0; channel < params.channel_count; channel++) {
|
||||
output_buffers[channel][i] = static_cast<s32>(
|
||||
static_cast<f32>(input_buffers[channel][i]) * state_08 * state.unk_20);
|
||||
}
|
||||
}
|
||||
|
||||
state.unk_00 = state_00;
|
||||
state.unk_04 = state_04;
|
||||
state.unk_08 = state_08;
|
||||
state.unk_18 = state_18;
|
||||
} else {
|
||||
for (s16 channel = 0; channel < params.channel_count; channel++) {
|
||||
if (params.inputs[channel] != params.outputs[channel]) {
|
||||
std::memcpy((char*)output_buffers[channel].data(),
|
||||
(char*)input_buffers[channel].data(),
|
||||
output_buffers[channel].size_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompressorCommand::Dump([[maybe_unused]] const ADSP::CommandListProcessor& processor,
|
||||
std::string& string) {
|
||||
string += fmt::format("CompressorCommand\n\tenabled {} \n\tinputs: ", effect_enabled);
|
||||
for (s16 i = 0; i < parameter.channel_count; i++) {
|
||||
string += fmt::format("{:02X}, ", inputs[i]);
|
||||
}
|
||||
string += "\n\toutputs: ";
|
||||
for (s16 i = 0; i < parameter.channel_count; i++) {
|
||||
string += fmt::format("{:02X}, ", outputs[i]);
|
||||
}
|
||||
string += "\n";
|
||||
}
|
||||
|
||||
void CompressorCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
|
||||
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
|
||||
|
||||
for (s16 i = 0; i < parameter.channel_count; i++) {
|
||||
input_buffers[i] = processor.mix_buffers.subspan(inputs[i] * processor.sample_count,
|
||||
processor.sample_count);
|
||||
output_buffers[i] = processor.mix_buffers.subspan(outputs[i] * processor.sample_count,
|
||||
processor.sample_count);
|
||||
}
|
||||
|
||||
auto state_{reinterpret_cast<CompressorInfo::State*>(state)};
|
||||
|
||||
if (effect_enabled) {
|
||||
if (parameter.state == CompressorInfo::ParameterState::Updating) {
|
||||
SetCompressorEffectParameter(parameter, *state_);
|
||||
} else if (parameter.state == CompressorInfo::ParameterState::Initialized) {
|
||||
InitializeCompressorEffect(parameter, *state_);
|
||||
}
|
||||
}
|
||||
|
||||
ApplyCompressorEffect(parameter, *state_, effect_enabled, input_buffers, output_buffers,
|
||||
processor.sample_count);
|
||||
}
|
||||
|
||||
bool CompressorCommand::Verify(const ADSP::CommandListProcessor& processor) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
60
src/audio_core/renderer/command/effect/compressor.h
Executable file
60
src/audio_core/renderer/command/effect/compressor.h
Executable file
@@ -0,0 +1,60 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "audio_core/renderer/effect/compressor.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
namespace ADSP {
|
||||
class CommandListProcessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* AudioRenderer command for limiting volume between a high and low threshold.
|
||||
* Version 1.
|
||||
*/
|
||||
struct CompressorCommand : ICommand {
|
||||
/**
|
||||
* Print this command's information to a string.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @param string - The string to print into.
|
||||
*/
|
||||
void Dump(const ADSP::CommandListProcessor& processor, std::string& string) override;
|
||||
|
||||
/**
|
||||
* Process this command.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
*/
|
||||
void Process(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/**
|
||||
* Verify this command's data is valid.
|
||||
*
|
||||
* @param processor - The CommandListProcessor processing this command.
|
||||
* @return True if the command is valid, otherwise false.
|
||||
*/
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Input mix buffer offsets for each channel
|
||||
std::array<s16, MaxChannels> inputs;
|
||||
/// Output mix buffer offsets for each channel
|
||||
std::array<s16, MaxChannels> outputs;
|
||||
/// Input parameters
|
||||
CompressorInfo::ParameterVersion2 parameter;
|
||||
/// State, updated each call
|
||||
CpuAddr state;
|
||||
/// Game-supplied workbuffer (Unused)
|
||||
CpuAddr workbuffer;
|
||||
/// Is this effect enabled?
|
||||
bool effect_enabled;
|
||||
};
|
||||
|
||||
} // namespace AudioCore::AudioRenderer
|
@@ -65,84 +65,78 @@ static void InitializeDelayEffect(const DelayInfo::ParameterVersion1& params,
|
||||
* Delay effect impl, according to the parameters and current state, on the input mix buffers,
|
||||
* saving the results to the output mix buffers.
|
||||
*
|
||||
* @tparam NumChannels - Number of channels to process. 1-6.
|
||||
* @param params - Input parameters to use.
|
||||
* @param state - State to use, must be initialized (see InitializeDelayEffect).
|
||||
* @param inputs - Input mix buffers to performan the delay on.
|
||||
* @param outputs - Output mix buffers to receive the delayed samples.
|
||||
* @param sample_count - Number of samples to process.
|
||||
*/
|
||||
template <size_t Channels>
|
||||
template <size_t NumChannels>
|
||||
static void ApplyDelay(const DelayInfo::ParameterVersion1& params, DelayInfo::State& state,
|
||||
std::vector<std::span<const s32>>& inputs,
|
||||
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
std::array<Common::FixedPoint<50, 14>, Channels> input_samples{};
|
||||
for (u32 channel = 0; channel < Channels; channel++) {
|
||||
input_samples[channel] = inputs[channel][i] * 64;
|
||||
for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
|
||||
std::array<Common::FixedPoint<50, 14>, NumChannels> input_samples{};
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
input_samples[channel] = inputs[channel][sample_index] * 64;
|
||||
}
|
||||
|
||||
std::array<Common::FixedPoint<50, 14>, Channels> delay_samples{};
|
||||
for (u32 channel = 0; channel < Channels; channel++) {
|
||||
std::array<Common::FixedPoint<50, 14>, NumChannels> delay_samples{};
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
delay_samples[channel] = state.delay_lines[channel].Read();
|
||||
}
|
||||
|
||||
std::array<std::array<Common::FixedPoint<18, 14>, Channels>, Channels> matrix{};
|
||||
if constexpr (Channels == 1) {
|
||||
// clang-format off
|
||||
std::array<std::array<Common::FixedPoint<18, 14>, NumChannels>, NumChannels> matrix{};
|
||||
if constexpr (NumChannels == 1) {
|
||||
matrix = {{
|
||||
{state.feedback_gain},
|
||||
}};
|
||||
} else if constexpr (Channels == 2) {
|
||||
} else if constexpr (NumChannels == 2) {
|
||||
matrix = {{
|
||||
{state.delay_feedback_gain, state.delay_feedback_cross_gain},
|
||||
{state.delay_feedback_cross_gain, state.delay_feedback_gain},
|
||||
}};
|
||||
} else if constexpr (Channels == 4) {
|
||||
} else if constexpr (NumChannels == 4) {
|
||||
matrix = {{
|
||||
{state.delay_feedback_gain, state.delay_feedback_cross_gain,
|
||||
state.delay_feedback_cross_gain, 0.0f},
|
||||
{state.delay_feedback_cross_gain, state.delay_feedback_gain, 0.0f,
|
||||
state.delay_feedback_cross_gain},
|
||||
{state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_gain,
|
||||
state.delay_feedback_cross_gain},
|
||||
{0.0f, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain,
|
||||
state.delay_feedback_gain},
|
||||
{state.delay_feedback_gain, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, 0.0f},
|
||||
{state.delay_feedback_cross_gain, state.delay_feedback_gain, 0.0f, state.delay_feedback_cross_gain},
|
||||
{state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_gain, state.delay_feedback_cross_gain},
|
||||
{0.0f, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, state.delay_feedback_gain},
|
||||
}};
|
||||
} else if constexpr (Channels == 6) {
|
||||
} else if constexpr (NumChannels == 6) {
|
||||
matrix = {{
|
||||
{state.delay_feedback_gain, 0.0f, 0.0f, 0.0f, state.delay_feedback_cross_gain,
|
||||
state.delay_feedback_cross_gain},
|
||||
{0.0f, state.delay_feedback_gain, 0.0f, state.delay_feedback_cross_gain,
|
||||
state.delay_feedback_cross_gain, 0.0f},
|
||||
{state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_gain,
|
||||
state.delay_feedback_cross_gain, 0.0f, 0.0f},
|
||||
{0.0f, state.delay_feedback_cross_gain, state.delay_feedback_cross_gain,
|
||||
state.delay_feedback_gain, 0.0f, 0.0f},
|
||||
{state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, 0.0f, 0.0f,
|
||||
state.delay_feedback_gain, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, params.feedback_gain},
|
||||
{state.delay_feedback_gain, 0.0f, state.delay_feedback_cross_gain, 0.0f, state.delay_feedback_cross_gain, 0.0f},
|
||||
{0.0f, state.delay_feedback_gain, state.delay_feedback_cross_gain, 0.0f, 0.0f, state.delay_feedback_cross_gain},
|
||||
{state.delay_feedback_cross_gain, state.delay_feedback_cross_gain, state.delay_feedback_gain, 0.0f, 0.0f, 0.0f},
|
||||
{0.0f, 0.0f, 0.0f, params.feedback_gain, 0.0f, 0.0f},
|
||||
{state.delay_feedback_cross_gain, 0.0f, 0.0f, 0.0f, state.delay_feedback_gain, state.delay_feedback_cross_gain},
|
||||
{0.0f, state.delay_feedback_cross_gain, 0.0f, 0.0f, state.delay_feedback_cross_gain, state.delay_feedback_gain},
|
||||
}};
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
std::array<Common::FixedPoint<50, 14>, Channels> gained_samples{};
|
||||
for (u32 channel = 0; channel < Channels; channel++) {
|
||||
std::array<Common::FixedPoint<50, 14>, NumChannels> gained_samples{};
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
Common::FixedPoint<50, 14> delay{};
|
||||
for (u32 j = 0; j < Channels; j++) {
|
||||
for (u32 j = 0; j < NumChannels; j++) {
|
||||
delay += delay_samples[j] * matrix[j][channel];
|
||||
}
|
||||
gained_samples[channel] = input_samples[channel] * params.in_gain + delay;
|
||||
}
|
||||
|
||||
for (u32 channel = 0; channel < Channels; channel++) {
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
state.lowpass_z[channel] = gained_samples[channel] * state.lowpass_gain +
|
||||
state.lowpass_z[channel] * state.lowpass_feedback_gain;
|
||||
state.delay_lines[channel].Write(state.lowpass_z[channel]);
|
||||
}
|
||||
|
||||
for (u32 channel = 0; channel < Channels; channel++) {
|
||||
outputs[channel][i] = (input_samples[channel] * params.dry_gain +
|
||||
delay_samples[channel] * params.wet_gain)
|
||||
.to_int_floor() /
|
||||
64;
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
outputs[channel][sample_index] = (input_samples[channel] * params.dry_gain +
|
||||
delay_samples[channel] * params.wet_gain)
|
||||
.to_int_floor() /
|
||||
64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "audio_core/renderer/effect/effect_delay_info.h"
|
||||
#include "audio_core/renderer/effect/delay.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
@@ -44,9 +44,9 @@ struct DelayCommand : ICommand {
|
||||
bool Verify(const ADSP::CommandListProcessor& processor) override;
|
||||
|
||||
/// Input mix buffer offsets for each channel
|
||||
std::array<s8, MaxChannels> inputs;
|
||||
std::array<s16, MaxChannels> inputs;
|
||||
/// Output mix buffer offsets for each channel
|
||||
std::array<s8, MaxChannels> outputs;
|
||||
std::array<s16, MaxChannels> outputs;
|
||||
/// Input parameters
|
||||
DelayInfo::ParameterVersion1 parameter;
|
||||
/// State, updated each call
|
||||
|
@@ -232,14 +232,14 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(I3dl2ReverbInfo::I3dl2DelayLi
|
||||
* Impl. Apply a I3DL2 reverb according to the current state, on the input mix buffers,
|
||||
* saving the results to the output mix buffers.
|
||||
*
|
||||
* @tparam Channels - Number of channels to process. 1-6.
|
||||
* @tparam NumChannels - Number of channels to process. 1-6.
|
||||
Inputs/outputs should have this many buffers.
|
||||
* @param state - State to use, must be initialized (see InitializeI3dl2ReverbEffect).
|
||||
* @param inputs - Input mix buffers to perform the reverb on.
|
||||
* @param outputs - Output mix buffers to receive the reverbed samples.
|
||||
* @param sample_count - Number of samples to process.
|
||||
*/
|
||||
template <size_t Channels>
|
||||
template <size_t NumChannels>
|
||||
static void ApplyI3dl2ReverbEffect(I3dl2ReverbInfo::State& state,
|
||||
std::span<std::span<const s32>> inputs,
|
||||
std::span<std::span<s32>> outputs, const u32 sample_count) {
|
||||
@@ -253,46 +253,46 @@ static void ApplyI3dl2ReverbEffect(I3dl2ReverbInfo::State& state,
|
||||
0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 3, 3, 3,
|
||||
};
|
||||
constexpr std::array<u8, I3dl2ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{
|
||||
4, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2,
|
||||
2, 0, 0, 1, 1, 1, 1, 4, 4, 4, 1, 1, 1, 0, 0, 0, 0, 5, 5, 5,
|
||||
};
|
||||
|
||||
std::span<const u8> tap_indexes{};
|
||||
if constexpr (Channels == 1) {
|
||||
if constexpr (NumChannels == 1) {
|
||||
tap_indexes = OutTapIndexes1Ch;
|
||||
} else if constexpr (Channels == 2) {
|
||||
} else if constexpr (NumChannels == 2) {
|
||||
tap_indexes = OutTapIndexes2Ch;
|
||||
} else if constexpr (Channels == 4) {
|
||||
} else if constexpr (NumChannels == 4) {
|
||||
tap_indexes = OutTapIndexes4Ch;
|
||||
} else if constexpr (Channels == 6) {
|
||||
} else if constexpr (NumChannels == 6) {
|
||||
tap_indexes = OutTapIndexes6Ch;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < sample_count; i++) {
|
||||
for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
|
||||
Common::FixedPoint<50, 14> early_to_late_tap{
|
||||
state.early_delay_line.TapOut(state.early_to_late_taps)};
|
||||
std::array<Common::FixedPoint<50, 14>, Channels> output_samples{};
|
||||
std::array<Common::FixedPoint<50, 14>, NumChannels> output_samples{};
|
||||
|
||||
for (u32 early_tap = 0; early_tap < I3dl2ReverbInfo::MaxDelayTaps; early_tap++) {
|
||||
output_samples[tap_indexes[early_tap]] +=
|
||||
state.early_delay_line.TapOut(state.early_tap_steps[early_tap]) *
|
||||
EarlyGains[early_tap];
|
||||
if constexpr (Channels == 6) {
|
||||
output_samples[5] +=
|
||||
if constexpr (NumChannels == 6) {
|
||||
output_samples[static_cast<u32>(Channels::LFE)] +=
|
||||
state.early_delay_line.TapOut(state.early_tap_steps[early_tap]) *
|
||||
EarlyGains[early_tap];
|
||||
}
|
||||
}
|
||||
|
||||
Common::FixedPoint<50, 14> current_sample{};
|
||||
for (u32 channel = 0; channel < Channels; channel++) {
|
||||
current_sample += inputs[channel][i];
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
current_sample += inputs[channel][sample_index];
|
||||
}
|
||||
|
||||
state.lowpass_0 =
|
||||
(current_sample * state.lowpass_2 + state.lowpass_0 * state.lowpass_1).to_float();
|
||||
state.early_delay_line.Tick(state.lowpass_0);
|
||||
|
||||
for (u32 channel = 0; channel < Channels; channel++) {
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
output_samples[channel] *= state.early_gain;
|
||||
}
|
||||
|
||||
@@ -321,26 +321,34 @@ static void ApplyI3dl2ReverbEffect(I3dl2ReverbInfo::State& state,
|
||||
state.fdn_delay_lines[delay_line], mix_matrix[delay_line]);
|
||||
}
|
||||
|
||||
const auto out_channels{std::min(Channels, size_t(4))};
|
||||
for (u32 channel = 0; channel < out_channels; channel++) {
|
||||
auto out_sample{output_samples[channel] + allpass_samples[channel] +
|
||||
state.dry_gain * static_cast<f32>(inputs[channel][i])};
|
||||
outputs[channel][i] =
|
||||
static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f));
|
||||
}
|
||||
if constexpr (NumChannels == 6) {
|
||||
const std::array<Common::FixedPoint<50, 14>, MaxChannels> allpass_outputs{
|
||||
allpass_samples[0], allpass_samples[1], allpass_samples[2] - allpass_samples[3],
|
||||
allpass_samples[3], allpass_samples[2], allpass_samples[3],
|
||||
};
|
||||
|
||||
if constexpr (Channels == 6) {
|
||||
auto center{
|
||||
state.center_delay_line.Tick((allpass_samples[2] - allpass_samples[3]) * 0.5f)};
|
||||
auto out_sample{static_cast<f32>(inputs[4][i]) * state.dry_gain +
|
||||
output_samples[4] * state.early_gain + center};
|
||||
outputs[4][i] =
|
||||
static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f));
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
Common::FixedPoint<50, 14> allpass{};
|
||||
|
||||
out_sample = static_cast<f32>(inputs[5][i]) * state.dry_gain +
|
||||
output_samples[5] * state.early_gain + allpass_samples[3];
|
||||
outputs[5][i] =
|
||||
static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f));
|
||||
if (channel == static_cast<u32>(Channels::Center)) {
|
||||
allpass = state.center_delay_line.Tick(allpass_outputs[channel] * 0.5f);
|
||||
} else {
|
||||
allpass = allpass_outputs[channel];
|
||||
}
|
||||
|
||||
auto out_sample{output_samples[channel] + allpass +
|
||||
state.dry_gain * static_cast<f32>(inputs[channel][sample_index])};
|
||||
|
||||
outputs[channel][sample_index] =
|
||||
static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f));
|
||||
}
|
||||
} else {
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
auto out_sample{output_samples[channel] + allpass_samples[channel] +
|
||||
state.dry_gain * static_cast<f32>(inputs[channel][sample_index])};
|
||||
outputs[channel][sample_index] =
|
||||
static_cast<s32>(std::clamp(out_sample.to_float(), -8388600.0f, 8388600.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "audio_core/renderer/effect/effect_i3dl2_info.h"
|
||||
#include "audio_core/renderer/effect/i3dl2.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
@@ -53,6 +53,15 @@ static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& p
|
||||
constexpr s64 min{std::numeric_limits<s32>::min()};
|
||||
constexpr s64 max{std::numeric_limits<s32>::max()};
|
||||
|
||||
const auto recip_estimate = [](f64 a) -> f64 {
|
||||
s32 q, s;
|
||||
f64 r;
|
||||
q = (s32)(a * 512.0); /* a in units of 1/512 rounded down */
|
||||
r = 1.0 / (((f64)q + 0.5) / 512.0); /* reciprocal r */
|
||||
s = (s32)(256.0 * r + 0.5); /* r in units of 1/256 rounded to nearest */
|
||||
return ((f64)s / 256.0);
|
||||
};
|
||||
|
||||
if (enabled) {
|
||||
if (statistics && params.statistics_reset_required) {
|
||||
for (u32 i = 0; i < params.channel_count; i++) {
|
||||
@@ -75,9 +84,17 @@ static void ApplyLightLimiterEffect(const LightLimiterInfo::ParameterVersion2& p
|
||||
state.samples_average[channel] +=
|
||||
((abs_sample - state.samples_average[channel]) * coeff).to_float();
|
||||
|
||||
// Reciprocal estimate
|
||||
auto new_average_sample{Common::FixedPoint<49, 15>(
|
||||
recip_estimate(state.samples_average[channel].to_double()))};
|
||||
if (params.processing_mode != LightLimiterInfo::ProcessingMode::Mode1) {
|
||||
// Two Newton-Raphson steps
|
||||
auto temp{2.0 - (state.samples_average[channel] * new_average_sample)};
|
||||
new_average_sample = 2.0 - (state.samples_average[channel] * temp);
|
||||
}
|
||||
|
||||
auto above_threshold{state.samples_average[channel] > params.threshold};
|
||||
auto attenuation{above_threshold ? params.threshold / state.samples_average[channel]
|
||||
: 1.0f};
|
||||
auto attenuation{above_threshold ? params.threshold * new_average_sample : 1.0f};
|
||||
coeff = attenuation < state.compression_gain[channel] ? params.attack_coeff
|
||||
: params.release_coeff;
|
||||
state.compression_gain[channel] +=
|
||||
@@ -173,8 +190,6 @@ void LightLimiterVersion2Command::Dump([[maybe_unused]] const ADSP::CommandListP
|
||||
}
|
||||
|
||||
void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& processor) {
|
||||
auto state_{reinterpret_cast<LightLimiterInfo::State*>(state)};
|
||||
|
||||
std::vector<std::span<const s32>> input_buffers(parameter.channel_count);
|
||||
std::vector<std::span<s32>> output_buffers(parameter.channel_count);
|
||||
|
||||
@@ -185,6 +200,8 @@ void LightLimiterVersion2Command::Process(const ADSP::CommandListProcessor& proc
|
||||
processor.sample_count);
|
||||
}
|
||||
|
||||
auto state_{reinterpret_cast<LightLimiterInfo::State*>(state)};
|
||||
|
||||
if (effect_enabled) {
|
||||
if (parameter.state == LightLimiterInfo::ParameterState::Updating) {
|
||||
UpdateLightLimiterEffectParameter(parameter, *state_);
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "audio_core/renderer/effect/effect_light_limiter_info.h"
|
||||
#include "audio_core/renderer/effect/light_limiter.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
@@ -239,7 +239,7 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine&
|
||||
* Impl. Apply a Reverb according to the current state, on the input mix buffers,
|
||||
* saving the results to the output mix buffers.
|
||||
*
|
||||
* @tparam Channels - Number of channels to process. 1-6.
|
||||
* @tparam NumChannels - Number of channels to process. 1-6.
|
||||
Inputs/outputs should have this many buffers.
|
||||
* @param params - Input parameters to update the state.
|
||||
* @param state - State to use, must be initialized (see InitializeReverbEffect).
|
||||
@@ -247,7 +247,7 @@ static Common::FixedPoint<50, 14> Axfx2AllPassTick(ReverbInfo::ReverbDelayLine&
|
||||
* @param outputs - Output mix buffers to receive the reverbed samples.
|
||||
* @param sample_count - Number of samples to process.
|
||||
*/
|
||||
template <size_t Channels>
|
||||
template <size_t NumChannels>
|
||||
static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, ReverbInfo::State& state,
|
||||
std::vector<std::span<const s32>>& inputs,
|
||||
std::vector<std::span<s32>>& outputs, const u32 sample_count) {
|
||||
@@ -261,38 +261,38 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever
|
||||
0, 0, 1, 1, 0, 1, 2, 2, 3, 3,
|
||||
};
|
||||
constexpr std::array<u8, ReverbInfo::MaxDelayTaps> OutTapIndexes6Ch{
|
||||
0, 0, 1, 1, 4, 4, 2, 2, 3, 3,
|
||||
0, 0, 1, 1, 2, 2, 4, 4, 5, 5,
|
||||
};
|
||||
|
||||
std::span<const u8> tap_indexes{};
|
||||
if constexpr (Channels == 1) {
|
||||
if constexpr (NumChannels == 1) {
|
||||
tap_indexes = OutTapIndexes1Ch;
|
||||
} else if constexpr (Channels == 2) {
|
||||
} else if constexpr (NumChannels == 2) {
|
||||
tap_indexes = OutTapIndexes2Ch;
|
||||
} else if constexpr (Channels == 4) {
|
||||
} else if constexpr (NumChannels == 4) {
|
||||
tap_indexes = OutTapIndexes4Ch;
|
||||
} else if constexpr (Channels == 6) {
|
||||
} else if constexpr (NumChannels == 6) {
|
||||
tap_indexes = OutTapIndexes6Ch;
|
||||
}
|
||||
|
||||
for (u32 sample_index = 0; sample_index < sample_count; sample_index++) {
|
||||
std::array<Common::FixedPoint<50, 14>, Channels> output_samples{};
|
||||
std::array<Common::FixedPoint<50, 14>, NumChannels> output_samples{};
|
||||
|
||||
for (u32 early_tap = 0; early_tap < ReverbInfo::MaxDelayTaps; early_tap++) {
|
||||
const auto sample{state.pre_delay_line.TapOut(state.early_delay_times[early_tap]) *
|
||||
state.early_gains[early_tap]};
|
||||
output_samples[tap_indexes[early_tap]] += sample;
|
||||
if constexpr (Channels == 6) {
|
||||
output_samples[5] += sample;
|
||||
if constexpr (NumChannels == 6) {
|
||||
output_samples[static_cast<u32>(Channels::LFE)] += sample;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (Channels == 6) {
|
||||
output_samples[5] *= 0.2f;
|
||||
if constexpr (NumChannels == 6) {
|
||||
output_samples[static_cast<u32>(Channels::LFE)] *= 0.2f;
|
||||
}
|
||||
|
||||
Common::FixedPoint<50, 14> input_sample{};
|
||||
for (u32 channel = 0; channel < Channels; channel++) {
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
input_sample += inputs[channel][sample_index];
|
||||
}
|
||||
|
||||
@@ -316,34 +316,41 @@ static void ApplyReverbEffect(const ReverbInfo::ParameterVersion2& params, Rever
|
||||
state.prev_feedback_output[1] - state.prev_feedback_output[2] + pre_delay_sample,
|
||||
};
|
||||
|
||||
std::array<Common::FixedPoint<50, 14>, ReverbInfo::MaxDelayLines> out_line_samples{};
|
||||
std::array<Common::FixedPoint<50, 14>, ReverbInfo::MaxDelayLines> allpass_samples{};
|
||||
for (u32 i = 0; i < ReverbInfo::MaxDelayLines; i++) {
|
||||
out_line_samples[i] = Axfx2AllPassTick(state.decay_delay_lines[i],
|
||||
state.fdn_delay_lines[i], mix_matrix[i]);
|
||||
allpass_samples[i] = Axfx2AllPassTick(state.decay_delay_lines[i],
|
||||
state.fdn_delay_lines[i], mix_matrix[i]);
|
||||
}
|
||||
|
||||
const auto dry_gain{Common::FixedPoint<50, 14>::from_base(params.dry_gain)};
|
||||
const auto wet_gain{Common::FixedPoint<50, 14>::from_base(params.wet_gain)};
|
||||
|
||||
const auto out_channels{std::min(Channels, size_t(4))};
|
||||
for (u32 channel = 0; channel < out_channels; channel++) {
|
||||
auto in_sample{inputs[channel][channel] * dry_gain};
|
||||
auto out_sample{((output_samples[channel] + out_line_samples[channel]) * wet_gain) /
|
||||
64};
|
||||
outputs[channel][sample_index] = (in_sample + out_sample).to_int();
|
||||
}
|
||||
if constexpr (NumChannels == 6) {
|
||||
const std::array<Common::FixedPoint<50, 14>, MaxChannels> allpass_outputs{
|
||||
allpass_samples[0], allpass_samples[1], allpass_samples[2] - allpass_samples[3],
|
||||
allpass_samples[3], allpass_samples[2], allpass_samples[3],
|
||||
};
|
||||
|
||||
if constexpr (Channels == 6) {
|
||||
auto center{
|
||||
state.center_delay_line.Tick((out_line_samples[2] - out_line_samples[3]) * 0.5f)};
|
||||
auto in_sample{inputs[4][sample_index] * dry_gain};
|
||||
auto out_sample{((output_samples[4] + center) * wet_gain) / 64};
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
auto in_sample{inputs[channel][sample_index] * dry_gain};
|
||||
|
||||
outputs[4][sample_index] = (in_sample + out_sample).to_int();
|
||||
Common::FixedPoint<50, 14> allpass{};
|
||||
if (channel == static_cast<u32>(Channels::Center)) {
|
||||
allpass = state.center_delay_line.Tick(allpass_outputs[channel] * 0.5f);
|
||||
} else {
|
||||
allpass = allpass_outputs[channel];
|
||||
}
|
||||
|
||||
in_sample = inputs[5][sample_index] * dry_gain;
|
||||
out_sample = ((output_samples[5] + out_line_samples[3]) * wet_gain) / 64;
|
||||
|
||||
outputs[5][sample_index] = (in_sample + out_sample).to_int();
|
||||
auto out_sample{((output_samples[channel] + allpass) * wet_gain) / 64};
|
||||
outputs[channel][sample_index] = (in_sample + out_sample).to_int();
|
||||
}
|
||||
} else {
|
||||
for (u32 channel = 0; channel < NumChannels; channel++) {
|
||||
auto in_sample{inputs[channel][sample_index] * dry_gain};
|
||||
auto out_sample{((output_samples[channel] + allpass_samples[channel]) * wet_gain) /
|
||||
64};
|
||||
outputs[channel][sample_index] = (in_sample + out_sample).to_int();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/renderer/command/icommand.h"
|
||||
#include "audio_core/renderer/effect/effect_reverb_info.h"
|
||||
#include "audio_core/renderer/effect/reverb.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::AudioRenderer {
|
||||
|
@@ -42,6 +42,7 @@ enum class CommandId : u8 {
|
||||
/* 0x1B */ LightLimiterVersion2,
|
||||
/* 0x1C */ MultiTapBiquadFilter,
|
||||
/* 0x1D */ Capture,
|
||||
/* 0x1E */ Compressor,
|
||||
};
|
||||
|
||||
constexpr u32 CommandMagic{0xCAFEBABE};
|
||||
|
@@ -43,8 +43,9 @@ void DepopForMixBuffersCommand::Dump([[maybe_unused]] const ADSP::CommandListPro
|
||||
}
|
||||
|
||||
void DepopForMixBuffersCommand::Process(const ADSP::CommandListProcessor& processor) {
|
||||
std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), MaxMixBuffers};
|
||||
auto end_index{std::min(processor.buffer_count, input + count)};
|
||||
std::span<s32> depop_buff{reinterpret_cast<s32*>(depop_buffer), end_index};
|
||||
|
||||
for (u32 index = input; index < end_index; index++) {
|
||||
const auto depop_sample{depop_buff[index]};
|
||||
if (depop_sample != 0) {
|
||||
|
@@ -16,7 +16,7 @@ class CommandListProcessor;
|
||||
|
||||
/**
|
||||
* AudioRenderer command for downmixing 6 channels to 2.
|
||||
* Channels:
|
||||
* Channel layout (SMPTE):
|
||||
* 0 - front left
|
||||
* 1 - front right
|
||||
* 2 - center
|
||||
|
Reference in New Issue
Block a user