early-access version 2830

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

View File

@ -1,7 +1,7 @@
yuzu emulator early access
=============
This is the source code for early-access 2829.
This is the source code for early-access 2830.
## Legal Notice

View File

@ -57,6 +57,8 @@ add_library(audio_core STATIC
renderer/command/effect/biquad_filter.h
renderer/command/effect/capture.cpp
renderer/command/effect/capture.h
renderer/command/effect/compressor.cpp
renderer/command/effect/compressor.h
renderer/command/effect/delay.cpp
renderer/command/effect/delay.h
renderer/command/effect/i3dl2_reverb.cpp
@ -106,27 +108,29 @@ add_library(audio_core STATIC
renderer/command/command_processing_time_estimator.h
renderer/command/commands.h
renderer/command/icommand.h
renderer/effect/effect_aux_info.cpp
renderer/effect/effect_aux_info.h
renderer/effect/effect_biquad_filter_info.cpp
renderer/effect/effect_biquad_filter_info.h
renderer/effect/effect_buffer_mixer_info.cpp
renderer/effect/effect_buffer_mixer_info.h
renderer/effect/effect_capture_info.cpp
renderer/effect/effect_capture_info.h
renderer/effect/aux_.cpp
renderer/effect/aux_.h
renderer/effect/biquad_filter.cpp
renderer/effect/biquad_filter.h
renderer/effect/buffer_mixer.cpp
renderer/effect/buffer_mixer.h
renderer/effect/capture.cpp
renderer/effect/capture.h
renderer/effect/compressor.cpp
renderer/effect/compressor.h
renderer/effect/delay.cpp
renderer/effect/delay.h
renderer/effect/effect_context.cpp
renderer/effect/effect_context.h
renderer/effect/effect_delay_info.cpp
renderer/effect/effect_delay_info.h
renderer/effect/effect_i3dl2_info.cpp
renderer/effect/effect_i3dl2_info.h
renderer/effect/effect_reset.h
renderer/effect/effect_info_base.h
renderer/effect/effect_light_limiter_info.cpp
renderer/effect/effect_light_limiter_info.h
renderer/effect/effect_reset.h
renderer/effect/effect_result_state.h
renderer/effect/effect_reverb_info.h
renderer/effect/effect_reverb_info.cpp
renderer/effect/i3dl2.cpp
renderer/effect/i3dl2.h
renderer/effect/light_limiter.cpp
renderer/effect/light_limiter.h
renderer/effect/reverb.h
renderer/effect/reverb.cpp
renderer/mix/mix_context.cpp
renderer/mix/mix_context.h
renderer/mix/mix_info.cpp

View File

@ -4,6 +4,7 @@
#pragma once
#include <numeric>
#include <span>
#include "common/assert.h"
#include "common/common_funcs.h"
@ -40,6 +41,25 @@ enum class SessionTypes {
FinalOutputRecorder,
};
enum class Channels : u32 {
FrontLeft,
FrontRight,
Center,
LFE,
BackLeft,
BackRight,
};
// These are used by Delay, Reverb and I3dl2Reverb prior to Revision 11.
enum class OldChannels : u32 {
FrontLeft,
FrontRight,
BackLeft,
BackRight,
Center,
LFE,
};
constexpr u32 BufferCount = 32;
constexpr u32 MaxRendererSessions = 2;
@ -66,6 +86,27 @@ constexpr bool IsChannelCountValid(u16 channel_count) {
(channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6);
}
constexpr void UseOldChannelMapping(std::span<s16> inputs, std::span<s16> outputs) {
constexpr auto old_center{static_cast<u32>(OldChannels::Center)};
constexpr auto new_center{static_cast<u32>(Channels::Center)};
constexpr auto old_lfe{static_cast<u32>(OldChannels::LFE)};
constexpr auto new_lfe{static_cast<u32>(Channels::LFE)};
auto center{inputs[old_center]};
auto lfe{inputs[old_lfe]};
inputs[old_center] = inputs[new_center];
inputs[old_lfe] = inputs[new_lfe];
inputs[new_center] = center;
inputs[new_lfe] = lfe;
center = outputs[old_center];
lfe = outputs[old_lfe];
outputs[old_center] = outputs[new_center];
outputs[old_lfe] = outputs[new_lfe];
outputs[new_center] = center;
outputs[new_lfe] = lfe;
}
constexpr u32 GetSplitterInParamHeaderMagic() {
return Common::MakeMagic('S', 'N', 'D', 'H');
}

View File

@ -12,7 +12,7 @@
#include "common/common_types.h"
namespace AudioCore {
constexpr u32 CurrentRevision = 10;
constexpr u32 CurrentRevision = 11;
enum class SupportTags {
CommandProcessingTimeEstimatorVersion4,
@ -40,6 +40,9 @@ enum class SupportTags {
LongSizePreDelay,
AudioUsbDeviceOutput,
DeviceApiVersion2,
DelayChannelMappingChange,
ReverbChannelMappingChange,
I3dl2ReverbChannelMappingChange,
// Not a real tag, just here to get the count.
Size
@ -80,6 +83,9 @@ constexpr bool CheckFeatureSupported(SupportTags tag, u32 user_revision) {
{SupportTags::EffectInfoVer2, 9},
{SupportTags::CommandProcessingTimeEstimatorVersion4, 10},
{SupportTags::MultiTapBiquadFilterProcessing, 10},
{SupportTags::DelayChannelMappingChange, 11},
{SupportTags::ReverbChannelMappingChange, 11},
{SupportTags::I3dl2ReverbChannelMappingChange, 11},
}};
const auto& feature =

View File

@ -104,17 +104,22 @@ bool BehaviorInfo::IsCommandProcessingTimeEstimatorVersion4Supported() const {
user_revision);
}
bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const {
bool BehaviorInfo::IsCommandProcessingTimeEstimatorVersion5Supported() const {
return CheckFeatureSupported(SupportTags::CommandProcessingTimeEstimatorVersion4,
user_revision);
}
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit70PercentSupported() const {
return CheckFeatureSupported(SupportTags::AudioRendererProcessingTimeLimit70Percent,
user_revision);
}
bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const {
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit75PercentSupported() const {
return CheckFeatureSupported(SupportTags::AudioRendererProcessingTimeLimit75Percent,
user_revision);
}
bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const {
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit80PercentSupported() const {
return CheckFeatureSupported(SupportTags::AudioRendererProcessingTimeLimit80Percent,
user_revision);
}
@ -147,7 +152,7 @@ bool BehaviorInfo::IsVoicePlayedSampleCountResetAtLoopPointSupported() const {
user_revision);
}
bool BehaviorInfo::IsBiquadFilterEffectStateClaerBugFixed() const {
bool BehaviorInfo::IsBiquadFilterEffectStateClearBugFixed() const {
return CheckFeatureSupported(SupportTags::BiquadFilterEffectStateClearBugFix, user_revision);
}
@ -171,4 +176,16 @@ bool BehaviorInfo::IsDeviceApiVersion2Supported() const {
return CheckFeatureSupported(SupportTags::DeviceApiVersion2, user_revision);
}
bool BehaviorInfo::IsDelayChannelMappingChanged() const {
return CheckFeatureSupported(SupportTags::DelayChannelMappingChange, user_revision);
}
bool BehaviorInfo::IsReverbChannelMappingChanged() const {
return CheckFeatureSupported(SupportTags::ReverbChannelMappingChange, user_revision);
}
bool BehaviorInfo::IsI3dl2ReverbChannelMappingChanged() const {
return CheckFeatureSupported(SupportTags::I3dl2ReverbChannelMappingChange, user_revision);
}
} // namespace AudioCore::AudioRenderer

View File

@ -199,28 +199,32 @@ public:
bool IsCommandProcessingTimeEstimatorVersion4Supported() const;
/**
* Check if the AudioRenderer can use up to 70% of the allocated processing timeslice.
* Note: Name is correct, Nintendo have the typo here
* Check if the command time estimator version 5 is supported.
*
* @return True if supported, otherwise false.
*/
bool IsAudioRenererProcessingTimeLimit70PercentSupported() const;
bool IsCommandProcessingTimeEstimatorVersion5Supported() const;
/**
* Check if the AudioRenderer can use up to 70% of the allocated processing timeslice.
*
* @return True if supported, otherwise false.
*/
bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
/**
* Check if the AudioRenderer can use up to 75% of the allocated processing timeslice.
* Note: Name is correct, Nintendo have the typo here
*
* @return True if supported, otherwise false.
*/
bool IsAudioRenererProcessingTimeLimit75PercentSupported() const;
bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
/**
* Check if the AudioRenderer can use up to 80% of the allocated processing timeslice.
* Note: Name is correct, Nintendo have the typo here
*
* @return True if supported, otherwise false.
*/
bool IsAudioRenererProcessingTimeLimit80PercentSupported() const;
bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
/**
* Check if voice flushing is supported
@ -279,11 +283,10 @@ public:
* Check if the clear state bug for biquad filters is fixed.
* The biquad state was not marked as needing re-initialisation when the effect was updated, it
* was only initialized once with a new effect.
* Note: Name is correct, Nintendo have the typo here
*
* @return True if fixed, otherwise false.
*/
bool IsBiquadFilterEffectStateClaerBugFixed() const;
bool IsBiquadFilterEffectStateClearBugFixed() const;
/**
* Check if Q23 precision is supported for fixed point.
@ -322,6 +325,42 @@ public:
*/
bool IsDeviceApiVersion2Supported() const;
/**
* Check if new channel mappings are used for Delay commands.
* Older commands used:
* front left/front right/back left/back right/center/lfe
* Whereas everywhere else in the code uses:
* front left/front right/center/lfe/back left/back right
* This corrects that and makes everything standardised.
*
* @return True if supported, otherwise false.
*/
bool IsDelayChannelMappingChanged() const;
/**
* Check if new channel mappings are used for Reverb commands.
* Older commands used:
* front left/front right/back left/back right/center/lfe
* Whereas everywhere else in the code uses:
* front left/front right/center/lfe/back left/back right
* This corrects that and makes everything standardised.
*
* @return True if supported, otherwise false.
*/
bool IsReverbChannelMappingChanged() const;
/**
* Check if new channel mappings are used for I3dl2Reverb commands.
* Older commands used:
* front left/front right/back left/back right/center/lfe
* Whereas everywhere else in the code uses:
* front left/front right/center/lfe/back left/back right
* This corrects that and makes everything standardised.
*
* @return True if supported, otherwise false.
*/
bool IsI3dl2ReverbChannelMappingChanged() const;
/// Host version
u32 process_revision;
/// User version

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

View File

@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/aux_.h"
namespace AudioCore::AudioRenderer {
void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
const bool send_unmapped{!pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_specific->send_buffer_info_address,
sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))};
const bool return_unmapped{!pool_mapper.TryAttachBuffer(
error_info, workbuffers[1], in_specific->return_buffer_info_address,
sizeof(AuxBufferInfo) + in_specific->count_max * sizeof(s32))};
buffer_unmapped = send_unmapped || return_unmapped;
if (!buffer_unmapped) {
auto send{workbuffers[0].GetReference(false)};
send_buffer_info = send + sizeof(AuxInfoDsp);
send_buffer = send + sizeof(AuxBufferInfo);
auto ret{workbuffers[1].GetReference(false)};
return_buffer_info = ret + sizeof(AuxInfoDsp);
return_buffer = ret + sizeof(AuxBufferInfo);
}
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void AuxInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
const bool send_unmapped{!pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], params->send_buffer_info_address,
sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))};
const bool return_unmapped{!pool_mapper.TryAttachBuffer(
error_info, workbuffers[1], params->return_buffer_info_address,
sizeof(AuxBufferInfo) + params->count_max * sizeof(s32))};
buffer_unmapped = send_unmapped || return_unmapped;
if (!buffer_unmapped) {
auto send{workbuffers[0].GetReference(false)};
send_buffer_info = send + sizeof(AuxInfoDsp);
send_buffer = send + sizeof(AuxBufferInfo);
auto ret{workbuffers[1].GetReference(false)};
return_buffer_info = ret + sizeof(AuxInfoDsp);
return_buffer = ret + sizeof(AuxBufferInfo);
}
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void AuxInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
}
void AuxInfo::InitializeResultState(EffectResultState& result_state) {}
void AuxInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {}
CpuAddr AuxInfo::GetWorkbuffer(s32 index) {
return workbuffers[index].GetReference(true);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
/**
* Auxiliary Buffer used for Aux commands.
* Send and return buffers are available (names from the game's perspective).
* Send is read by the host, containing a buffer of samples to be used for whatever purpose.
* Return is written by the host, writing a mix buffer back to the game.
* This allows the game to use pre-processed samples skipping the other render processing,
* and to examine or modify what the audio renderer has generated.
*/
class AuxInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxMixBuffers> inputs;
/* 0x18 */ std::array<s8, MaxMixBuffers> outputs;
/* 0x30 */ u32 mix_buffer_count;
/* 0x34 */ u32 sample_rate;
/* 0x38 */ u32 count_max;
/* 0x3C */ u32 mix_buffer_count_max;
/* 0x40 */ CpuAddr send_buffer_info_address;
/* 0x48 */ CpuAddr send_buffer_address;
/* 0x50 */ CpuAddr return_buffer_info_address;
/* 0x58 */ CpuAddr return_buffer_address;
/* 0x60 */ u32 mix_buffer_sample_size;
/* 0x64 */ u32 sample_count;
/* 0x68 */ u32 mix_buffer_sample_count;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"AuxInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxMixBuffers> inputs;
/* 0x18 */ std::array<s8, MaxMixBuffers> outputs;
/* 0x30 */ u32 mix_buffer_count;
/* 0x34 */ u32 sample_rate;
/* 0x38 */ u32 count_max;
/* 0x3C */ u32 mix_buffer_count_max;
/* 0x40 */ CpuAddr send_buffer_info_address;
/* 0x48 */ CpuAddr send_buffer_address;
/* 0x50 */ CpuAddr return_buffer_info_address;
/* 0x58 */ CpuAddr return_buffer_address;
/* 0x60 */ u32 mix_buffer_sample_size;
/* 0x64 */ u32 sample_count;
/* 0x68 */ u32 mix_buffer_sample_count;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"AuxInfo::ParameterVersion2 has the wrong size!");
struct AuxInfoDsp {
/* 0x00 */ u32 read_offset;
/* 0x04 */ u32 write_offset;
/* 0x08 */ u32 lost_sample_count;
/* 0x0C */ u32 total_sample_count;
/* 0x10 */ char unk10[0x30];
};
static_assert(sizeof(AuxInfoDsp) == 0x40, "AuxInfo::AuxInfoDsp has the wrong size!");
struct AuxBufferInfo {
/* 0x00 */ AuxInfoDsp cpu_info;
/* 0x40 */ AuxInfoDsp dsp_info;
};
static_assert(sizeof(AuxBufferInfo) == 0x80, "AuxInfo::AuxBufferInfo has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/biquad_filter.h"
namespace AudioCore::AudioRenderer {
void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void BiquadFilterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void BiquadFilterInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
void BiquadFilterInfo::InitializeResultState(EffectResultState& result_state) {}
void BiquadFilterInfo::UpdateResultState(EffectResultState& cpu_state,
EffectResultState& dsp_state) {}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
class BiquadFilterInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 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;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"BiquadFilterInfo::ParameterVersion1 has the wrong size!");
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;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"BiquadFilterInfo::ParameterVersion2 has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/buffer_mixer.h"
namespace AudioCore::AudioRenderer {
void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void BufferMixerInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void BufferMixerInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
}
void BufferMixerInfo::InitializeResultState(EffectResultState& result_state) {}
void BufferMixerInfo::UpdateResultState(EffectResultState& cpu_state,
EffectResultState& dsp_state) {}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
class BufferMixerInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxMixBuffers> inputs;
/* 0x18 */ std::array<s8, MaxMixBuffers> outputs;
/* 0x30 */ std::array<f32, MaxMixBuffers> volumes;
/* 0x90 */ u32 mix_count;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"BufferMixerInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxMixBuffers> inputs;
/* 0x18 */ std::array<s8, MaxMixBuffers> outputs;
/* 0x30 */ std::array<f32, MaxMixBuffers> volumes;
/* 0x90 */ u32 mix_count;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"BufferMixerInfo::ParameterVersion2 has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,82 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/aux_.h"
#include "audio_core/renderer/effect/capture.h"
namespace AudioCore::AudioRenderer {
void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{
reinterpret_cast<const AuxInfo::ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<AuxInfo::ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_specific->send_buffer_info_address,
in_specific->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo));
if (!buffer_unmapped) {
const auto send_address{workbuffers[0].GetReference(false)};
send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp);
send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo);
return_buffer_info = 0;
return_buffer = 0;
}
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void CaptureInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{
reinterpret_cast<const AuxInfo::ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<AuxInfo::ParameterVersion2*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(AuxInfo::ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], params->send_buffer_info_address,
params->count_max * sizeof(s32) + sizeof(AuxInfo::AuxBufferInfo));
if (!buffer_unmapped) {
const auto send_address{workbuffers[0].GetReference(false)};
send_buffer_info = send_address + sizeof(AuxInfo::AuxInfoDsp);
send_buffer = send_address + sizeof(AuxInfo::AuxBufferInfo);
return_buffer_info = 0;
return_buffer = 0;
}
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void CaptureInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
}
void CaptureInfo::InitializeResultState(EffectResultState& result_state) {}
void CaptureInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {}
CpuAddr CaptureInfo::GetWorkbuffer(s32 index) {
return workbuffers[index].GetReference(true);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
class CaptureInfo : public EffectInfoBase {
public:
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,40 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/compressor.h"
namespace AudioCore::AudioRenderer {
void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {}
void CompressorInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void CompressorInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
CpuAddr CompressorInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,106 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class CompressorInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ s16 channel_count_max;
/* 0x0E */ s16 channel_count;
/* 0x10 */ s32 sample_rate;
/* 0x14 */ f32 threshold;
/* 0x18 */ f32 compressor_ratio;
/* 0x1C */ s32 attack_time;
/* 0x20 */ s32 release_time;
/* 0x24 */ f32 unk_24;
/* 0x28 */ f32 unk_28;
/* 0x2C */ f32 unk_2C;
/* 0x30 */ f32 out_gain;
/* 0x34 */ ParameterState state;
/* 0x35 */ bool makeup_gain_enabled;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"CompressorInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ s16 channel_count_max;
/* 0x0E */ s16 channel_count;
/* 0x10 */ s32 sample_rate;
/* 0x14 */ f32 threshold;
/* 0x18 */ f32 compressor_ratio;
/* 0x1C */ s32 attack_time;
/* 0x20 */ s32 release_time;
/* 0x24 */ f32 unk_24;
/* 0x28 */ f32 unk_28;
/* 0x2C */ f32 unk_2C;
/* 0x30 */ f32 out_gain;
/* 0x34 */ ParameterState state;
/* 0x35 */ bool makeup_gain_enabled;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"CompressorInfo::ParameterVersion2 has the wrong size!");
struct State {
f32 unk_00;
f32 unk_04;
f32 unk_08;
f32 unk_0C;
f32 unk_10;
f32 unk_14;
f32 unk_18;
f32 makeup_gain;
f32 unk_20;
char unk_24[0x1C];
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"CompressorInfo::State has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/delay.h"
namespace AudioCore::AudioRenderer {
void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void DelayInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void DelayInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
void DelayInfo::InitializeResultState(EffectResultState& result_state) {}
void DelayInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {}
CpuAddr DelayInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,135 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class DelayInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ u32 delay_time_max;
/* 0x14 */ u32 delay_time;
/* 0x18 */ Common::FixedPoint<18, 14> sample_rate;
/* 0x1C */ Common::FixedPoint<18, 14> in_gain;
/* 0x20 */ Common::FixedPoint<18, 14> feedback_gain;
/* 0x24 */ Common::FixedPoint<18, 14> wet_gain;
/* 0x28 */ Common::FixedPoint<18, 14> dry_gain;
/* 0x2C */ Common::FixedPoint<18, 14> channel_spread;
/* 0x30 */ Common::FixedPoint<18, 14> lowpass_amount;
/* 0x34 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"DelayInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ s16 channel_count_max;
/* 0x0E */ s16 channel_count;
/* 0x10 */ s32 delay_time_max;
/* 0x14 */ s32 delay_time;
/* 0x18 */ s32 sample_rate;
/* 0x1C */ s32 in_gain;
/* 0x20 */ s32 feedback_gain;
/* 0x24 */ s32 wet_gain;
/* 0x28 */ s32 dry_gain;
/* 0x2C */ s32 channel_spread;
/* 0x30 */ s32 lowpass_amount;
/* 0x34 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"DelayInfo::ParameterVersion2 has the wrong size!");
struct DelayLine {
Common::FixedPoint<50, 14> Read() const {
return buffer[buffer_pos];
}
void Write(const Common::FixedPoint<50, 14> value) {
buffer[buffer_pos] = value;
buffer_pos = static_cast<u32>((buffer_pos + 1) % buffer.size());
}
s32 sample_count_max{};
s32 sample_count{};
std::vector<Common::FixedPoint<50, 14>> buffer{};
u32 buffer_pos{};
Common::FixedPoint<18, 14> decay_rate{};
};
struct State {
/* 0x000 */ std::array<s32, 8> unk_000;
/* 0x020 */ std::array<DelayLine, MaxChannels> delay_lines;
/* 0x0B0 */ Common::FixedPoint<18, 14> feedback_gain;
/* 0x0B4 */ Common::FixedPoint<18, 14> delay_feedback_gain;
/* 0x0B8 */ Common::FixedPoint<18, 14> delay_feedback_cross_gain;
/* 0x0BC */ Common::FixedPoint<18, 14> lowpass_gain;
/* 0x0C0 */ Common::FixedPoint<18, 14> lowpass_feedback_gain;
/* 0x0C4 */ std::array<Common::FixedPoint<50, 14>, MaxChannels> lowpass_z;
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"DelayInfo::State has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -29,6 +29,7 @@ public:
BiquadFilter,
LightLimiter,
Capture,
Compressor,
};
enum class UsageState {

View File

@ -3,14 +3,15 @@
#pragma once
#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/effect_delay_info.h"
#include "audio_core/renderer/effect/effect_i3dl2_info.h"
#include "audio_core/renderer/effect/effect_light_limiter_info.h"
#include "audio_core/renderer/effect/effect_reverb_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/compressor.h"
#include "audio_core/renderer/effect/delay.h"
#include "audio_core/renderer/effect/i3dl2.h"
#include "audio_core/renderer/effect/light_limiter.h"
#include "audio_core/renderer/effect/reverb.h"
#include "common/common_types.h"
namespace AudioCore::AudioRenderer {
@ -60,6 +61,10 @@ static void ResetEffect(EffectInfoBase* effect, const EffectInfoBase::Type type)
std::construct_at<CaptureInfo>(reinterpret_cast<CaptureInfo*>(effect));
effect->SetType(EffectInfoBase::Type::Capture);
break;
case EffectInfoBase::Type::Compressor:
std::construct_at<CompressorInfo>(reinterpret_cast<CompressorInfo*>(effect));
effect->SetType(EffectInfoBase::Type::Compressor);
break;
}
}

View File

@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/i3dl2.h"
namespace AudioCore::AudioRenderer {
void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void I3dl2ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void I3dl2ReverbInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
void I3dl2ReverbInfo::InitializeResultState(EffectResultState& result_state) {}
void I3dl2ReverbInfo::UpdateResultState(EffectResultState& cpu_state,
EffectResultState& dsp_state) {}
CpuAddr I3dl2ReverbInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,200 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class I3dl2ReverbInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ char unk10[0x4];
/* 0x14 */ u32 sample_rate;
/* 0x18 */ f32 room_HF_gain;
/* 0x1C */ f32 reference_HF;
/* 0x20 */ f32 late_reverb_decay_time;
/* 0x24 */ f32 late_reverb_HF_decay_ratio;
/* 0x28 */ f32 room_gain;
/* 0x2C */ f32 reflection_gain;
/* 0x30 */ f32 reverb_gain;
/* 0x34 */ f32 late_reverb_diffusion;
/* 0x38 */ f32 reflection_delay;
/* 0x3C */ f32 late_reverb_delay_time;
/* 0x40 */ f32 late_reverb_density;
/* 0x44 */ f32 dry_gain;
/* 0x48 */ ParameterState state;
/* 0x49 */ char unk49[0x3];
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"I3dl2ReverbInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ char unk10[0x4];
/* 0x14 */ u32 sample_rate;
/* 0x18 */ f32 room_HF_gain;
/* 0x1C */ f32 reference_HF;
/* 0x20 */ f32 late_reverb_decay_time;
/* 0x24 */ f32 late_reverb_HF_decay_ratio;
/* 0x28 */ f32 room_gain;
/* 0x2C */ f32 reflection_gain;
/* 0x30 */ f32 reverb_gain;
/* 0x34 */ f32 late_reverb_diffusion;
/* 0x38 */ f32 reflection_delay;
/* 0x3C */ f32 late_reverb_delay_time;
/* 0x40 */ f32 late_reverb_density;
/* 0x44 */ f32 dry_gain;
/* 0x48 */ ParameterState state;
/* 0x49 */ char unk49[0x3];
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"I3dl2ReverbInfo::ParameterVersion2 has the wrong size!");
static constexpr u32 MaxDelayLines = 4;
static constexpr u32 MaxDelayTaps = 20;
struct I3dl2DelayLine {
void Initialize(const s32 delay_time) {
max_delay = delay_time;
buffer.resize(delay_time + 1, 0);
buffer_end = &buffer[delay_time];
output = &buffer[0];
SetDelay(delay_time);
wet_gain = 0.0f;
}
void SetDelay(const s32 delay_time) {
if (max_delay < delay_time) {
return;
}
delay = delay_time;
input = &buffer[(output - buffer.data() + delay) % (max_delay + 1)];
}
Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) {
Write(sample);
auto out_sample{Read()};
output++;
if (output >= buffer_end) {
output = buffer.data();
}
return out_sample;
}
Common::FixedPoint<50, 14> Read() {
return *output;
}
void Write(const Common::FixedPoint<50, 14> sample) {
*(input++) = sample;
if (input >= buffer_end) {
input = buffer.data();
}
}
Common::FixedPoint<50, 14> TapOut(const s32 index) {
auto out{input - (index + 1)};
if (out < buffer.data()) {
out += max_delay + 1;
}
return *out;
}
std::vector<Common::FixedPoint<50, 14>> buffer{};
Common::FixedPoint<50, 14>* buffer_end{};
s32 max_delay{};
Common::FixedPoint<50, 14>* input{};
Common::FixedPoint<50, 14>* output{};
s32 delay{};
f32 wet_gain{};
};
struct State {
f32 lowpass_0;
f32 lowpass_1;
f32 lowpass_2;
I3dl2DelayLine early_delay_line;
std::array<s32, MaxDelayTaps> early_tap_steps;
f32 early_gain;
f32 late_gain;
s32 early_to_late_taps;
std::array<I3dl2DelayLine, MaxDelayLines> fdn_delay_lines;
std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines0;
std::array<I3dl2DelayLine, MaxDelayLines> decay_delay_lines1;
f32 last_reverb_echo;
I3dl2DelayLine center_delay_line;
std::array<std::array<f32, 3>, MaxDelayLines> lowpass_coeff;
std::array<f32, MaxDelayLines> shelf_filter;
f32 dry_gain;
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"I3dl2ReverbInfo::State is too large!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,81 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/light_limiter.h"
namespace AudioCore::AudioRenderer {
void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion1& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void LightLimiterInfo::Update(BehaviorInfo::ErrorInfo& error_info,
const InParameterVersion2& in_params, const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
} else {
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
}
void LightLimiterInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
params->statistics_reset_required = false;
}
void LightLimiterInfo::InitializeResultState(EffectResultState& result_state) {
auto result_state_{reinterpret_cast<StatisticsInternal*>(result_state.state.data())};
result_state_->channel_max_sample.fill(0);
result_state_->channel_compression_gain_min.fill(1.0f);
}
void LightLimiterInfo::UpdateResultState(EffectResultState& cpu_state,
EffectResultState& dsp_state) {
auto cpu_statistics{reinterpret_cast<StatisticsInternal*>(cpu_state.state.data())};
auto dsp_statistics{reinterpret_cast<StatisticsInternal*>(dsp_state.state.data())};
*cpu_statistics = *dsp_statistics;
}
CpuAddr LightLimiterInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,138 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class LightLimiterInfo : public EffectInfoBase {
public:
enum class ProcessingMode {
Mode0,
Mode1,
};
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x0C */ u32 sample_rate;
/* 0x14 */ s32 look_ahead_time_max;
/* 0x18 */ s32 attack_time;
/* 0x1C */ s32 release_time;
/* 0x20 */ s32 look_ahead_time;
/* 0x24 */ f32 attack_coeff;
/* 0x28 */ f32 release_coeff;
/* 0x2C */ f32 threshold;
/* 0x30 */ f32 input_gain;
/* 0x34 */ f32 output_gain;
/* 0x38 */ s32 look_ahead_samples_min;
/* 0x3C */ s32 look_ahead_samples_max;
/* 0x40 */ ParameterState state;
/* 0x41 */ bool statistics_enabled;
/* 0x42 */ bool statistics_reset_required;
/* 0x43 */ ProcessingMode processing_mode;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"LightLimiterInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x0C */ u32 sample_rate;
/* 0x14 */ s32 look_ahead_time_max;
/* 0x18 */ s32 attack_time;
/* 0x1C */ s32 release_time;
/* 0x20 */ s32 look_ahead_time;
/* 0x24 */ f32 attack_coeff;
/* 0x28 */ f32 release_coeff;
/* 0x2C */ f32 threshold;
/* 0x30 */ f32 input_gain;
/* 0x34 */ f32 output_gain;
/* 0x38 */ s32 look_ahead_samples_min;
/* 0x3C */ s32 look_ahead_samples_max;
/* 0x40 */ ParameterState state;
/* 0x41 */ bool statistics_enabled;
/* 0x42 */ bool statistics_reset_required;
/* 0x43 */ ProcessingMode processing_mode;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"LightLimiterInfo::ParameterVersion2 has the wrong size!");
struct State {
std::array<Common::FixedPoint<49, 15>, MaxChannels> samples_average;
std::array<Common::FixedPoint<49, 15>, MaxChannels> compression_gain;
std::array<s32, MaxChannels> look_ahead_sample_offsets;
std::array<std::vector<Common::FixedPoint<49, 15>>, MaxChannels> look_ahead_sample_buffers;
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"LightLimiterInfo::State has the wrong size!");
struct StatisticsInternal {
/* 0x00 */ std::array<f32, MaxChannels> channel_max_sample;
/* 0x18 */ std::array<f32, MaxChannels> channel_compression_gain_min;
};
static_assert(sizeof(StatisticsInternal) == 0x30,
"LightLimiterInfo::StatisticsInternal has the wrong size!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new limiter statistics result state. Version 2 only.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side limiter statistics with the ADSP-side one. Version 2 only.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,93 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "audio_core/renderer/effect/reverb.h"
namespace AudioCore::AudioRenderer {
void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion1*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion1));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void ReverbInfo::Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) {
auto in_specific{reinterpret_cast<const ParameterVersion2*>(in_params.specific.data())};
auto params{reinterpret_cast<ParameterVersion2*>(parameter.data())};
if (IsChannelCountValid(in_specific->channel_count_max)) {
const auto old_state{params->state};
std::memcpy(params, in_specific, sizeof(ParameterVersion2));
mix_id = in_params.mix_id;
process_order = in_params.process_order;
enabled = in_params.enabled;
if (!IsChannelCountValid(in_specific->channel_count)) {
params->channel_count = params->channel_count_max;
}
if (!IsChannelCountValid(in_specific->channel_count) ||
old_state != ParameterState::Updated) {
params->state = old_state;
}
if (buffer_unmapped || in_params.is_new) {
usage_state = UsageState::New;
params->state = ParameterState::Initialized;
buffer_unmapped = !pool_mapper.TryAttachBuffer(
error_info, workbuffers[0], in_params.workbuffer, in_params.workbuffer_size);
return;
}
}
error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0);
}
void ReverbInfo::UpdateForCommandGeneration() {
if (enabled) {
usage_state = UsageState::Enabled;
} else {
usage_state = UsageState::Disabled;
}
auto params{reinterpret_cast<ParameterVersion1*>(parameter.data())};
params->state = ParameterState::Updated;
}
void ReverbInfo::InitializeResultState(EffectResultState& result_state) {}
void ReverbInfo::UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) {}
CpuAddr ReverbInfo::GetWorkbuffer(s32 index) {
return GetSingleBuffer(index);
}
} // namespace AudioCore::AudioRenderer

View File

@ -0,0 +1,190 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <vector>
#include "audio_core/common/common.h"
#include "audio_core/renderer/effect/effect_info_base.h"
#include "common/common_types.h"
#include "common/fixed_point.h"
namespace AudioCore::AudioRenderer {
class ReverbInfo : public EffectInfoBase {
public:
struct ParameterVersion1 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ u32 sample_rate;
/* 0x14 */ u32 early_mode;
/* 0x18 */ s32 early_gain;
/* 0x1C */ s32 pre_delay;
/* 0x20 */ s32 late_mode;
/* 0x24 */ s32 late_gain;
/* 0x28 */ s32 decay_time;
/* 0x2C */ s32 high_freq_Decay_ratio;
/* 0x30 */ s32 colouration;
/* 0x34 */ s32 base_gain;
/* 0x38 */ s32 wet_gain;
/* 0x3C */ s32 dry_gain;
/* 0x40 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion1) <= sizeof(EffectInfoBase::InParameterVersion1),
"ReverbInfo::ParameterVersion1 has the wrong size!");
struct ParameterVersion2 {
/* 0x00 */ std::array<s8, MaxChannels> inputs;
/* 0x06 */ std::array<s8, MaxChannels> outputs;
/* 0x0C */ u16 channel_count_max;
/* 0x0E */ u16 channel_count;
/* 0x10 */ u32 sample_rate;
/* 0x14 */ u32 early_mode;
/* 0x18 */ s32 early_gain;
/* 0x1C */ s32 pre_delay;
/* 0x20 */ s32 late_mode;
/* 0x24 */ s32 late_gain;
/* 0x28 */ s32 decay_time;
/* 0x2C */ s32 high_freq_decay_ratio;
/* 0x30 */ s32 colouration;
/* 0x34 */ s32 base_gain;
/* 0x38 */ s32 wet_gain;
/* 0x3C */ s32 dry_gain;
/* 0x40 */ ParameterState state;
};
static_assert(sizeof(ParameterVersion2) <= sizeof(EffectInfoBase::InParameterVersion2),
"ReverbInfo::ParameterVersion2 has the wrong size!");
static constexpr u32 MaxDelayLines = 4;
static constexpr u32 MaxDelayTaps = 10;
static constexpr u32 NumEarlyModes = 5;
static constexpr u32 NumLateModes = 5;
struct ReverbDelayLine {
void Initialize(const s32 delay_time, const f32 decay_rate) {
buffer.resize(delay_time + 1, 0);
buffer_end = &buffer[delay_time];
output = &buffer[0];
decay = decay_rate;
sample_count_max = delay_time;
SetDelay(delay_time);
}
void SetDelay(const s32 delay_time) {
if (sample_count_max < delay_time) {
return;
}
sample_count = delay_time;
input = &buffer[(output - buffer.data() + sample_count) % (sample_count_max + 1)];
}
Common::FixedPoint<50, 14> Tick(const Common::FixedPoint<50, 14> sample) {
Write(sample);
auto out_sample{Read()};
output++;
if (output >= buffer_end) {
output = buffer.data();
}
return out_sample;
}
Common::FixedPoint<50, 14> Read() {
return *output;
}
void Write(const Common::FixedPoint<50, 14> sample) {
*(input++) = sample;
if (input >= buffer_end) {
input = buffer.data();
}
}
Common::FixedPoint<50, 14> TapOut(const s32 index) {
auto out{input - (index + 1)};
if (out < buffer.data()) {
out += sample_count;
}
return *out;
}
s32 sample_count{};
s32 sample_count_max{};
std::vector<Common::FixedPoint<50, 14>> buffer{};
Common::FixedPoint<50, 14>* buffer_end;
Common::FixedPoint<50, 14>* input{};
Common::FixedPoint<50, 14>* output{};
Common::FixedPoint<50, 14> decay{};
};
struct State {
ReverbDelayLine pre_delay_line;
ReverbDelayLine center_delay_line;
std::array<s32, MaxDelayTaps> early_delay_times;
std::array<Common::FixedPoint<50, 14>, MaxDelayTaps> early_gains;
s32 pre_delay_time;
std::array<ReverbDelayLine, MaxDelayLines> decay_delay_lines;
std::array<ReverbDelayLine, MaxDelayLines> fdn_delay_lines;
std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_gain;
std::array<Common::FixedPoint<50, 14>, MaxDelayLines> hf_decay_prev_gain;
std::array<Common::FixedPoint<50, 14>, MaxDelayLines> prev_feedback_output;
};
static_assert(sizeof(State) <= sizeof(EffectInfoBase::State),
"ReverbInfo::State is too large!");
/**
* Update the info with new parameters, version 1.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion1& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info with new parameters, version 2.
*
* @param error_info - Used to write call result code.
* @param in_params - New parameters to update the info with.
* @param pool_mapper - Pool for mapping buffers.
*/
void Update(BehaviorInfo::ErrorInfo& error_info, const InParameterVersion2& in_params,
const PoolMapper& pool_mapper) override;
/**
* Update the info after command generation. Usually only changes its state.
*/
void UpdateForCommandGeneration() override;
/**
* Initialize a new result state. Version 2 only, unused.
*
* @param result_state - Result state to initialize.
*/
void InitializeResultState(EffectResultState& result_state) override;
/**
* Update the host-side state with the ADSP-side state. Version 2 only, unused.
*
* @param cpu_state - Host-side result state to update.
* @param dsp_state - AudioRenderer-side result state to update from.
*/
void UpdateResultState(EffectResultState& cpu_state, EffectResultState& dsp_state) override;
/**
* Get a workbuffer assigned to this effect with the given index.
*
* @param index - Workbuffer index.
* @return Address of the buffer.
*/
CpuAddr GetWorkbuffer(s32 index) override;
};
} // namespace AudioCore::AudioRenderer

View File

@ -9,19 +9,20 @@
namespace AudioCore::AudioRenderer {
enum class PerformanceDetailType : u8 {
/* 0 */ Invalid,
/* 1 */ Unk1,
/* 2 */ Unk2,
/* 3 */ Unk3,
/* 4 */ Unk4,
/* 5 */ Unk5,
/* 6 */ Unk6,
/* 7 */ Unk7,
/* 8 */ Unk8,
/* 9 */ Unk9,
/* 10 */ Unk10,
/* 11 */ Unk11,
/* 12 */ Unk12,
Invalid,
Unk1,
Unk2,
Unk3,
Unk4,
Unk5,
Unk6,
Unk7,
Unk8,
Unk9,
Unk10,
Unk11,
Unk12,
Unk13,
};
struct PerformanceDetailVersion1 {

View File

@ -367,7 +367,11 @@ Result System::Initialize(const AudioRendererParameterInternal& params,
// nn::audio::dsp::FlushDataCache(transferMemory, transferMemorySize);
if (behavior.IsCommandProcessingTimeEstimatorVersion4Supported()) {
if (behavior.IsCommandProcessingTimeEstimatorVersion5Supported()) {
command_processing_time_estimator =
std::make_unique<CommandProcessingTimeEstimatorVersion5>(sample_count,
mix_buffer_count);
} else if (behavior.IsCommandProcessingTimeEstimatorVersion4Supported()) {
command_processing_time_estimator =
std::make_unique<CommandProcessingTimeEstimatorVersion4>(sample_count,
mix_buffer_count);
@ -595,13 +599,13 @@ void System::SendCommandToDsp() {
memory_pool_info.Translate(CpuAddr(command_workbuffer.data()), command_size)};
auto time_limit_percent{70.0f};
if (behavior.IsAudioRenererProcessingTimeLimit80PercentSupported()) {
if (behavior.IsAudioRendererProcessingTimeLimit80PercentSupported()) {
time_limit_percent = 80.0f;
} else if (behavior.IsAudioRenererProcessingTimeLimit75PercentSupported()) {
} else if (behavior.IsAudioRendererProcessingTimeLimit75PercentSupported()) {
time_limit_percent = 75.0f;
} else {
// result ignored and 70 is used anyway
behavior.IsAudioRenererProcessingTimeLimit70PercentSupported();
behavior.IsAudioRendererProcessingTimeLimit70PercentSupported();
time_limit_percent = 70.0f;
}
@ -698,13 +702,14 @@ u64 System::GenerateCommand(std::span<u8> in_command_buffer,
if (drop_voice) {
f32 time_limit_percent{70.0f};
if (render_context.behavior->IsAudioRenererProcessingTimeLimit80PercentSupported()) {
if (render_context.behavior->IsAudioRendererProcessingTimeLimit80PercentSupported()) {
time_limit_percent = 80.0f;
} else if (render_context.behavior->IsAudioRenererProcessingTimeLimit75PercentSupported()) {
} else if (render_context.behavior
->IsAudioRendererProcessingTimeLimit75PercentSupported()) {
time_limit_percent = 75.0f;
} else {
// result is ignored
render_context.behavior->IsAudioRenererProcessingTimeLimit70PercentSupported();
render_context.behavior->IsAudioRendererProcessingTimeLimit70PercentSupported();
time_limit_percent = 70.0f;
}
const auto time_limit{static_cast<u32>(

View File

@ -107,7 +107,7 @@ private:
}
void RequestUpdate(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
LOG_TRACE(Service_Audio, "called");
std::vector<u8> input{ctx.ReadBuffer(0)};

View File

@ -298,7 +298,7 @@ void HwOpus::OpenHardwareOpusDecoderEx(Kernel::HLERequestContext& ctx) {
const auto sample_rate = rp.Pop<u32>();
const auto channel_count = rp.Pop<u32>();
LOG_CRITICAL(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count);
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,