[audio] correct biquad filter v2 parameters (#3142)
We had the same struct for v1 and v2 - this was tested only with MP4, should output correct sounds now and boot it. Co-authored-by: MaranBr <maranbr@outlook.com> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3142 Reviewed-by: MaranBr <maranbr@eden-emu.dev> Reviewed-by: crueter <crueter@eden-emu.dev> Co-authored-by: Maufeat <sahyno1996@gmail.com> Co-committed-by: Maufeat <sahyno1996@gmail.com>
This commit is contained in:
@@ -21,6 +21,15 @@
|
||||
|
||||
namespace AudioCore::Renderer {
|
||||
|
||||
namespace {
|
||||
constexpr f32 BiquadParameterFixedScaleQ14 = 16384.0f; // 1 << 14
|
||||
|
||||
[[nodiscard]] inline s16 ToQ14Clamped(f32 v) {
|
||||
const f32 scaled = std::clamp(v * BiquadParameterFixedScaleQ14, -32768.0f, 32767.0f);
|
||||
return static_cast<s16>(scaled);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename T, CommandId Id>
|
||||
T& CommandBuffer::GenerateStart(const s32 node_id) {
|
||||
if (size + sizeof(T) >= command_list.size_bytes()) {
|
||||
@@ -259,18 +268,42 @@ void CommandBuffer::GenerateBiquadFilterCommand(const s32 node_id, EffectInfoBas
|
||||
const bool use_float_processing) {
|
||||
auto& cmd{GenerateStart<BiquadFilterCommand, CommandId::BiquadFilter>(node_id)};
|
||||
|
||||
const auto& parameter{
|
||||
*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];
|
||||
cmd.output = buffer_offset + parameter.outputs[channel];
|
||||
if (behavior->IsEffectInfoVersion2Supported()) {
|
||||
const auto& p{
|
||||
*reinterpret_cast<BiquadFilterInfo::ParameterVersion2*>(effect_info.GetParameter())};
|
||||
|
||||
cmd.biquad.b = parameter.b;
|
||||
cmd.biquad.a = parameter.a;
|
||||
if (!IsChannelCountValid(p.channel_count) || channel < 0 || channel >= p.channel_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Effects use legacy fixed-point format
|
||||
cmd.input = buffer_offset + p.inputs[channel];
|
||||
cmd.output = buffer_offset + p.outputs[channel];
|
||||
|
||||
// Convert float coefficients to Q2.14 fixed-point as expected by the legacy DSP path.
|
||||
cmd.biquad.b[0] = ToQ14Clamped(p.b[0]);
|
||||
cmd.biquad.b[1] = ToQ14Clamped(p.b[1]);
|
||||
cmd.biquad.b[2] = ToQ14Clamped(p.b[2]);
|
||||
cmd.biquad.a[0] = ToQ14Clamped(p.a[0]);
|
||||
cmd.biquad.a[1] = ToQ14Clamped(p.a[1]);
|
||||
} else {
|
||||
const auto& p{
|
||||
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
|
||||
|
||||
if (!IsChannelCountValid(p.channel_count) || channel < 0 || channel >= p.channel_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd.input = buffer_offset + p.inputs[channel];
|
||||
cmd.output = buffer_offset + p.outputs[channel];
|
||||
|
||||
cmd.biquad.b = p.b;
|
||||
cmd.biquad.a = p.a;
|
||||
}
|
||||
|
||||
// Effects always use the fixed-point coefficient path on the DSP.
|
||||
cmd.use_float_coefficients = false;
|
||||
|
||||
cmd.state = memory_pool->Translate(CpuAddr(state),
|
||||
@@ -595,6 +628,15 @@ void CommandBuffer::GenerateCopyMixBufferCommand(const s32 node_id, EffectInfoBa
|
||||
const s16 buffer_offset, const s8 channel) {
|
||||
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{
|
||||
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
|
||||
cmd.input_index = buffer_offset + parameter.inputs[channel];
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -361,12 +364,37 @@ void CommandGenerator::GenerateAuxCommand(const s16 buffer_offset, EffectInfoBas
|
||||
void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset,
|
||||
EffectInfoBase& effect_info,
|
||||
const s32 node_id) {
|
||||
const auto& parameter{
|
||||
*reinterpret_cast<BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter())};
|
||||
EffectInfoBase::ParameterState param_state{};
|
||||
s8 channel_count = 0;
|
||||
|
||||
if (render_context.behavior->IsEffectInfoVersion2Supported()) {
|
||||
const auto* parameter =
|
||||
reinterpret_cast<const BiquadFilterInfo::ParameterVersion2*>(effect_info.GetParameter());
|
||||
if (!parameter) {
|
||||
LOG_ERROR(Service_Audio, "Biquad filter parameter is null");
|
||||
return;
|
||||
}
|
||||
param_state = parameter->state;
|
||||
channel_count = parameter->channel_count;
|
||||
} else {
|
||||
const auto* parameter =
|
||||
reinterpret_cast<const BiquadFilterInfo::ParameterVersion1*>(effect_info.GetParameter());
|
||||
if (!parameter) {
|
||||
LOG_ERROR(Service_Audio, "Biquad filter parameter is null");
|
||||
return;
|
||||
}
|
||||
param_state = parameter->state;
|
||||
channel_count = parameter->channel_count;
|
||||
}
|
||||
|
||||
if (channel_count <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (effect_info.IsEnabled()) {
|
||||
bool needs_init{false};
|
||||
|
||||
switch (parameter.state) {
|
||||
switch (param_state) {
|
||||
case EffectInfoBase::ParameterState::Initialized:
|
||||
needs_init = true;
|
||||
break;
|
||||
@@ -375,22 +403,26 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(const s16 buffer_offset
|
||||
if (render_context.behavior->IsBiquadFilterEffectStateClearBugFixed()) {
|
||||
needs_init = false;
|
||||
} else {
|
||||
needs_init = parameter.state == EffectInfoBase::ParameterState::Updating;
|
||||
needs_init = param_state == EffectInfoBase::ParameterState::Updating;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR(Service_Audio, "Invalid biquad parameter state {}",
|
||||
static_cast<u32>(parameter.state));
|
||||
LOG_ERROR(Service_Audio,
|
||||
"Invalid biquad parameter state {}, treating as uninitialized",
|
||||
static_cast<u32>(param_state));
|
||||
needs_init = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (s8 channel = 0; channel < parameter.channel_count; channel++) {
|
||||
command_buffer.GenerateBiquadFilterCommand(
|
||||
node_id, effect_info, buffer_offset, channel, needs_init,
|
||||
render_context.behavior->UseBiquadFilterFloatProcessing());
|
||||
const bool use_float_processing =
|
||||
render_context.behavior->UseBiquadFilterFloatProcessing();
|
||||
|
||||
for (s8 channel = 0; channel < channel_count; channel++) {
|
||||
command_buffer.GenerateBiquadFilterCommand(node_id, effect_info, buffer_offset, channel,
|
||||
needs_init, use_float_processing);
|
||||
}
|
||||
} else {
|
||||
for (s8 channel = 0; channel < parameter.channel_count; channel++) {
|
||||
for (s8 channel = 0; channel < channel_count; channel++) {
|
||||
command_buffer.GenerateCopyMixBufferCommand(node_id, effect_info, buffer_offset,
|
||||
channel);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -34,14 +37,22 @@ void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
|
||||
}
|
||||
|
||||
void BiquadFilterInfo::UpdateForCommandGeneration() {
|
||||
if (enabled) {
|
||||
usage_state = UsageState::Enabled;
|
||||
} else {
|
||||
usage_state = UsageState::Disabled;
|
||||
}
|
||||
usage_state = enabled ? UsageState::Enabled : UsageState::Disabled;
|
||||
|
||||
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
|
||||
params->state = ParameterState::Updated;
|
||||
auto* params_v1 = reinterpret_cast<ParameterVersion1*>(parameter.data());
|
||||
auto* params_v2 = reinterpret_cast<ParameterVersion2*>(parameter.data());
|
||||
|
||||
const auto raw_state_v1 = static_cast<u8>(params_v1->state);
|
||||
const auto raw_state_v2 = static_cast<u8>(params_v2->state);
|
||||
|
||||
if (raw_state_v1 <= static_cast<u8>(ParameterState::Updated)) {
|
||||
params_v1->state = ParameterState::Updated;
|
||||
} else if (raw_state_v2 <= static_cast<u8>(ParameterState::Updated)) {
|
||||
params_v2->state = ParameterState::Updated;
|
||||
} else {
|
||||
params_v1->state = ParameterState::Updated;
|
||||
params_v2->state = ParameterState::Updated;
|
||||
}
|
||||
}
|
||||
|
||||
void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -27,10 +30,12 @@ public:
|
||||
struct ParameterVersion2 {
|
||||
/* 0x00 */ std::array<s8, MaxChannels> inputs;
|
||||
/* 0x06 */ std::array<s8, MaxChannels> outputs;
|
||||
/* 0x0C */ std::array<s16, 3> b;
|
||||
/* 0x12 */ std::array<s16, 2> a;
|
||||
/* 0x16 */ s8 channel_count;
|
||||
/* 0x17 */ ParameterState state;
|
||||
/* 0x0C */ u32 padding;
|
||||
/* 0x10 */ std::array<f32, 3> b;
|
||||
/* 0x1C */ std::array<f32, 2> a;
|
||||
/* 0x24 */ s8 channel_count;
|
||||
/* 0x25 */ ParameterState state;
|
||||
/* 0x26 */ u16 reserved;
|
||||
};
|
||||
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
|
||||
"BiquadFilterInfo::ParameterVersion2 has the wrong size!");
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
@@ -304,7 +307,7 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
case Tegra::Texture::WrapMode::Border:
|
||||
return GL_CLAMP_TO_BORDER;
|
||||
case Tegra::Texture::WrapMode::Clamp:
|
||||
return GL_CLAMP;
|
||||
return GL_CLAMP_TO_EDGE;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
|
||||
return GL_MIRROR_CLAMP_TO_EDGE;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceBorder:
|
||||
@@ -319,9 +322,10 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
} else {
|
||||
return GL_MIRROR_CLAMP_TO_EDGE;
|
||||
}
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", wrap_mode);
|
||||
return GL_REPEAT;
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unimplemented texture wrap mode={}", wrap_mode);
|
||||
return GL_REPEAT;
|
||||
}
|
||||
|
||||
inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
|
||||
|
||||
@@ -50,7 +50,8 @@ VkSamplerMipmapMode MipmapMode(Tegra::Texture::TextureMipmapFilter mipmap_filter
|
||||
return {};
|
||||
}
|
||||
|
||||
VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wrap_mode,
|
||||
VkSamplerAddressMode WrapMode(const Device& device,
|
||||
Tegra::Texture::WrapMode wrap_mode,
|
||||
Tegra::Texture::TextureFilter filter) {
|
||||
switch (wrap_mode) {
|
||||
case Tegra::Texture::WrapMode::Wrap:
|
||||
@@ -62,12 +63,6 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra
|
||||
case Tegra::Texture::WrapMode::Border:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
|
||||
case Tegra::Texture::WrapMode::Clamp:
|
||||
if (device.GetDriverID() == VK_DRIVER_ID_NVIDIA_PROPRIETARY) {
|
||||
// Nvidia's Vulkan driver defaults to GL_CLAMP on invalid enumerations, we can hack this
|
||||
// by sending an invalid enumeration.
|
||||
return static_cast<VkSamplerAddressMode>(0xcafe);
|
||||
}
|
||||
// TODO(Rodrigo): Emulate GL_CLAMP properly on other vendors
|
||||
switch (filter) {
|
||||
case Tegra::Texture::TextureFilter::Nearest:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
@@ -81,6 +76,8 @@ VkSamplerAddressMode WrapMode(const Device& device, Tegra::Texture::WrapMode wra
|
||||
case Tegra::Texture::WrapMode::MirrorOnceBorder:
|
||||
UNIMPLEMENTED();
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampOGL:
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", wrap_mode);
|
||||
return {};
|
||||
|
||||
@@ -240,13 +240,10 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) {
|
||||
UpdateDynamicStates();
|
||||
|
||||
HandleTransformFeedback();
|
||||
query_cache.NotifySegment(true);
|
||||
query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64,
|
||||
maxwell3d->regs.zpass_pixel_count_enable);
|
||||
|
||||
draw_func();
|
||||
|
||||
query_cache.CounterEnable(VideoCommon::QueryType::StreamingByteCount, false);
|
||||
}
|
||||
|
||||
void RasterizerVulkan::Draw(bool is_indexed, u32 instance_count) {
|
||||
|
||||
Reference in New Issue
Block a user