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