early-access version 2830

This commit is contained in:
pineappleEA
2022-07-13 16:55:33 +02:00
parent 3df4ab9726
commit 18b71d75ce
52 changed files with 3561 additions and 195 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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()));

View File

@@ -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.
*

View File

@@ -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{};

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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 {

View 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

View 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

View File

@@ -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;
}
}
}

View File

@@ -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

View File

@@ -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));
}
}
}
}

View File

@@ -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 {

View File

@@ -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_);

View File

@@ -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 {

View File

@@ -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();
}
}
}
}

View File

@@ -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 {

View File

@@ -42,6 +42,7 @@ enum class CommandId : u8 {
/* 0x1B */ LightLimiterVersion2,
/* 0x1C */ MultiTapBiquadFilter,
/* 0x1D */ Capture,
/* 0x1E */ Compressor,
};
constexpr u32 CommandMagic{0xCAFEBABE};

View File

@@ -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) {

View File

@@ -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