mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-04-10 21:28:48 -04:00
feat(audio): add ParameterVersion2 support to biquad filter commands
- Add v2 path in GenerateBiquadFilterCommand with float coefficients - Add v2 path in GenerateCopyMixBufferCommand - Add v2 path in GenerateBiquadFilterEffectCommand - Use parameter-level enable field for v2 - Add buffer index validation for v2 commands Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
@@ -255,15 +255,89 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas
|
|||||||
const s16 buffer_offset, const s8 channel,
|
const s16 buffer_offset, const s8 channel,
|
||||||
const bool needs_init,
|
const bool needs_init,
|
||||||
const bool use_float_processing) {
|
const bool use_float_processing) {
|
||||||
auto& cmd{GenerateStart<BiquadFilterCommand, CommandId::BiquadFilter>(node_id)};
|
if (behavior->IsEffectInfoVersion2Supported()) {
|
||||||
|
const auto& parameter_v2{
|
||||||
|
*reinterpret_cast<BiquadFilterInfo::ParameterVersion2*>(effect_info.GetParameter())};
|
||||||
|
|
||||||
|
// Validate channel and channel count
|
||||||
|
if (!IsChannelCountValid(static_cast<u16>(parameter_v2.channel_count)) || channel < 0 ||
|
||||||
|
channel >= parameter_v2.channel_count) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParameterVersion2 has per-parameter enable field - if disabled, copy input to output
|
||||||
|
if (!parameter_v2.enable) {
|
||||||
|
GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset, channel);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate buffer indices before generating command
|
||||||
|
const s16 input_index = buffer_offset + parameter_v2.inputs[channel];
|
||||||
|
const s16 output_index = buffer_offset + parameter_v2.outputs[channel];
|
||||||
|
|
||||||
|
if (input_index < 0) {
|
||||||
|
LOG_WARNING(Service_Audio,
|
||||||
|
"BiquadFilterCommand: Skipping v2 command generation - invalid input index ({})",
|
||||||
|
input_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s16 effective_output = output_index;
|
||||||
|
if (output_index < 0) {
|
||||||
|
LOG_WARNING(Service_Audio,
|
||||||
|
"BiquadFilterCommand: Invalid v2 output index ({}), using input ({}) for in-place processing",
|
||||||
|
output_index, input_index);
|
||||||
|
effective_output = input_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& cmd{GenerateStart<BiquadFilterCommand, CommandId::BiquadFilter>(node_id)};
|
||||||
|
|
||||||
|
// ParameterVersion2 uses native float coefficients
|
||||||
|
const auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(
|
||||||
|
effect_info.GetStateBuffer() + channel * sizeof(VoiceState::BiquadFilterState))};
|
||||||
|
|
||||||
|
cmd.input = input_index;
|
||||||
|
cmd.output = effective_output;
|
||||||
|
cmd.biquad_float.numerator = parameter_v2.b;
|
||||||
|
cmd.biquad_float.denominator = parameter_v2.a;
|
||||||
|
cmd.use_float_coefficients = true;
|
||||||
|
cmd.state = memory_pool->Translate(CpuAddr(state), sizeof(VoiceState::BiquadFilterState));
|
||||||
|
cmd.needs_init = needs_init;
|
||||||
|
cmd.use_float_processing = use_float_processing;
|
||||||
|
|
||||||
|
GenerateEnd<BiquadFilterCommand>(cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto& parameter{
|
const auto& parameter{
|
||||||
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
|
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
|
||||||
const auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(
|
|
||||||
effect_info.GetStateBuffer() + channel * sizeof(VoiceState::BiquadFilterState))};
|
|
||||||
|
|
||||||
cmd.input = buffer_offset + parameter.inputs[channel];
|
const s16 input_index = buffer_offset + parameter.inputs[channel];
|
||||||
cmd.output = buffer_offset + parameter.outputs[channel];
|
const s16 output_index = buffer_offset + parameter.outputs[channel];
|
||||||
|
|
||||||
|
// Validate buffer indices before generating command
|
||||||
|
// If output is invalid, use input as output (in-place processing)
|
||||||
|
// This matches the fix in Process() and Verify()
|
||||||
|
if (input_index < 0) {
|
||||||
|
LOG_WARNING(Service_Audio,
|
||||||
|
"BiquadFilterCommand: Skipping command generation - invalid input index ({})",
|
||||||
|
input_index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s16 effective_output = output_index;
|
||||||
|
if (output_index < 0) {
|
||||||
|
LOG_WARNING(Service_Audio,
|
||||||
|
"BiquadFilterCommand: Invalid output index ({}), using input ({}) for in-place "
|
||||||
|
"processing",
|
||||||
|
output_index, input_index);
|
||||||
|
effective_output = input_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& cmd{GenerateStart<BiquadFilterCommand, CommandId::BiquadFilter>(node_id)};
|
||||||
|
|
||||||
|
cmd.input = input_index;
|
||||||
|
cmd.output = effective_output;
|
||||||
|
|
||||||
cmd.biquad.b = parameter.b;
|
cmd.biquad.b = parameter.b;
|
||||||
cmd.biquad.a = parameter.a;
|
cmd.biquad.a = parameter.a;
|
||||||
@@ -271,6 +345,8 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas
|
|||||||
// Effects use legacy fixed-point format
|
// Effects use legacy fixed-point format
|
||||||
cmd.use_float_coefficients = false;
|
cmd.use_float_coefficients = false;
|
||||||
|
|
||||||
|
const auto state{reinterpret_cast<VoiceState::BiquadFilterState*>(
|
||||||
|
effect_info.GetStateBuffer() + channel * sizeof(VoiceState::BiquadFilterState))};
|
||||||
cmd.state = memory_pool->Translate(CpuAddr(state),
|
cmd.state = memory_pool->Translate(CpuAddr(state),
|
||||||
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
|
MaxBiquadFilters * sizeof(VoiceState::BiquadFilterState));
|
||||||
|
|
||||||
@@ -593,6 +669,15 @@ void CommandBuffer::GenerateCopyMixBufferCommand(const s32 node_id, EffectInfoBa
|
|||||||
const s16 buffer_offset, const s8 channel) {
|
const s16 buffer_offset, const s8 channel) {
|
||||||
auto& cmd{GenerateStart<CopyMixBufferCommand, CommandId::CopyMixBuffer>(node_id)};
|
auto& cmd{GenerateStart<CopyMixBufferCommand, CommandId::CopyMixBuffer>(node_id)};
|
||||||
|
|
||||||
|
if (behavior->IsEffectInfoVersion2Supported()) {
|
||||||
|
const auto& parameter_v2{
|
||||||
|
*reinterpret_cast<BiquadFilterInfo::ParameterVersion2*>(effect_info.GetParameter())};
|
||||||
|
cmd.input_index = buffer_offset + parameter_v2.inputs[channel];
|
||||||
|
cmd.output_index = buffer_offset + parameter_v2.outputs[channel];
|
||||||
|
GenerateEnd<CopyMixBufferCommand>(cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto& parameter{
|
const auto& parameter{
|
||||||
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
|
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
|
||||||
cmd.input_index = buffer_offset + parameter.inputs[channel];
|
cmd.input_index = buffer_offset + parameter.inputs[channel];
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "audio_core/common/audio_renderer_parameter.h"
|
#include "audio_core/common/audio_renderer_parameter.h"
|
||||||
#include "audio_core/renderer/behavior/behavior_info.h"
|
#include "audio_core/renderer/behavior/behavior_info.h"
|
||||||
#include "audio_core/renderer/command/command_buffer.h"
|
#include "audio_core/renderer/command/command_buffer.h"
|
||||||
@@ -361,9 +363,61 @@ void CommandGenerator::GenerateAuxCommand(const s16 buffer_offset, EffectInfoBas
|
|||||||
void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset,
|
void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset,
|
||||||
EffectInfoBase& effect_info,
|
EffectInfoBase& effect_info,
|
||||||
const s32 node_id) {
|
const s32 node_id) {
|
||||||
const auto& parameter{
|
if (render_context.behavior->IsEffectInfoVersion2Supported()) {
|
||||||
|
const auto& parameter_v2{
|
||||||
|
*reinterpret_cast<BiquadFilterInfo::ParameterVersion2*>(effect_info.GetParameter())};
|
||||||
|
|
||||||
|
// ParameterVersion2 doesn't use state transitions, always needs_init = false
|
||||||
|
const bool needs_init = false;
|
||||||
|
const bool use_float_processing = render_context.behavior->UseBiquadFilterFloatProcessing();
|
||||||
|
|
||||||
|
// Validate channel count - use IsChannelCountValid for proper validation
|
||||||
|
if (!IsChannelCountValid(static_cast<u16>(parameter_v2.channel_count))) {
|
||||||
|
LOG_WARNING(Service_Audio, "BiquadFilterEffectCommand: Invalid v2 channel_count {}, skipping",
|
||||||
|
parameter_v2.channel_count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const s8 channels = parameter_v2.channel_count;
|
||||||
|
|
||||||
|
if (effect_info.IsEnabled()) {
|
||||||
|
for (s8 channel = 0; channel < channels; channel++) {
|
||||||
|
command_buffer.GenerateBiquadFilterCommand(
|
||||||
|
node_id, effect_info, buffer_offset, channel, needs_init, use_float_processing);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Effect disabled - generate copy commands for all channels
|
||||||
|
for (s8 channel = 0; channel < channels; channel++) {
|
||||||
|
command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset,
|
||||||
|
channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& parameter{
|
||||||
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
|
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
|
||||||
if (effect_info.IsEnabled()) {
|
|
||||||
|
// If effect is disabled (e.g., due to corrupted parameters), skip command generation
|
||||||
|
if (!effect_info.IsEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate parameters - if corrupted, skip command generation to prevent audio issues
|
||||||
|
if (parameter.channel_count < 0 || parameter.channel_count > MaxChannels) {
|
||||||
|
LOG_WARNING(Service_Audio, "BiquadFilterEffectCommand: Invalid channel_count {}, skipping command generation",
|
||||||
|
parameter.channel_count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static_cast<u8>(parameter.state) > static_cast<u8>(EffectInfoBase::ParameterState::Updated)) {
|
||||||
|
LOG_WARNING(Service_Audio, "BiquadFilterEffectCommand: Invalid parameter state {}, skipping command generation",
|
||||||
|
static_cast<u8>(parameter.state));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Effect is enabled and parameters are valid - generate commands
|
||||||
|
{
|
||||||
bool needs_init{false};
|
bool needs_init{false};
|
||||||
|
|
||||||
switch (parameter.state) {
|
switch (parameter.state) {
|
||||||
@@ -379,8 +433,10 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_ERROR(Service_Audio, "Invalid biquad parameter state {}",
|
// Should not reach here after validation, but handle gracefully
|
||||||
static_cast<u32>(parameter.state));
|
LOG_WARNING(Service_Audio, "BiquadFilterEffectCommand: Unexpected state {}, treating as Updated",
|
||||||
|
static_cast<u8>(parameter.state));
|
||||||
|
needs_init = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,11 +445,6 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset
|
|||||||
node_id, effect_info, buffer_offset, channel, needs_init,
|
node_id, effect_info, buffer_offset, channel, needs_init,
|
||||||
render_context.behavior->UseBiquadFilterFloatProcessing());
|
render_context.behavior->UseBiquadFilterFloatProcessing());
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
for (s8 channel = 0; channel < parameter.channel_count; channel++) {
|
|
||||||
command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset,
|
|
||||||
channel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user