mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Preliminary effects
This commit is contained in:
		
							parent
							
								
									1f1c3bddc0
								
							
						
					
					
						commit
						80ac1331b5
					
				@ -27,12 +27,13 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory
 | 
			
		||||
      voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
 | 
			
		||||
      sink_context(params.sink_count), splitter_context(),
 | 
			
		||||
      voices(params.voice_count), memory{memory_},
 | 
			
		||||
      command_generator(worker_params, voice_context, mix_context, splitter_context, memory),
 | 
			
		||||
      command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
 | 
			
		||||
                        memory),
 | 
			
		||||
      temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) {
 | 
			
		||||
    behavior_info.SetUserRevision(params.revision);
 | 
			
		||||
    splitter_context.Initialize(behavior_info, params.splitter_count,
 | 
			
		||||
                                params.num_splitter_send_channels);
 | 
			
		||||
    mix_context.Initialize(behavior_info, params.submix_count + 1);
 | 
			
		||||
    mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
 | 
			
		||||
    audio_out = std::make_unique<AudioCore::AudioOut>();
 | 
			
		||||
    stream =
 | 
			
		||||
        audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
 | 
			
		||||
@ -106,8 +107,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto mix_result =
 | 
			
		||||
        info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, splitter_context);
 | 
			
		||||
    auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
 | 
			
		||||
                                               splitter_context, effect_context);
 | 
			
		||||
 | 
			
		||||
    if (mix_result.IsError()) {
 | 
			
		||||
        LOG_ERROR(Audio, "Failed to update mix parameters");
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@
 | 
			
		||||
 | 
			
		||||
#include "audio_core/algorithm/interpolate.h"
 | 
			
		||||
#include "audio_core/command_generator.h"
 | 
			
		||||
#include "audio_core/effect_context.h"
 | 
			
		||||
#include "audio_core/mix_context.h"
 | 
			
		||||
#include "audio_core/voice_context.h"
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
@ -68,9 +69,10 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
 | 
			
		||||
 | 
			
		||||
CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
 | 
			
		||||
                                   VoiceContext& voice_context, MixContext& mix_context,
 | 
			
		||||
                                   SplitterContext& splitter_context, Core::Memory::Memory& memory)
 | 
			
		||||
                                   SplitterContext& splitter_context, EffectContext& effect_context,
 | 
			
		||||
                                   Core::Memory::Memory& memory)
 | 
			
		||||
    : worker_params(worker_params), voice_context(voice_context), mix_context(mix_context),
 | 
			
		||||
      splitter_context(splitter_context), memory(memory),
 | 
			
		||||
      splitter_context(splitter_context), effect_context(effect_context), memory(memory),
 | 
			
		||||
      mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
 | 
			
		||||
                 worker_params.sample_count),
 | 
			
		||||
      sample_buffer(MIX_BUFFER_SIZE),
 | 
			
		||||
@ -338,6 +340,120 @@ void CommandGenerator::GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
 | 
			
		||||
    const std::size_t effect_count = effect_context.GetCount();
 | 
			
		||||
    const auto buffer_offset = mix_info.GetInParams().buffer_offset;
 | 
			
		||||
    for (std::size_t i = 0; i < effect_count; i++) {
 | 
			
		||||
        const auto index = mix_info.GetEffectOrder(i);
 | 
			
		||||
        if (index == AudioCommon::NO_EFFECT_ORDER) {
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        auto* info = effect_context.GetInfo(index);
 | 
			
		||||
        const auto type = info->GetType();
 | 
			
		||||
 | 
			
		||||
        // TODO(ogniK): Finish remaining effects
 | 
			
		||||
        switch (type) {
 | 
			
		||||
        case EffectType::Aux:
 | 
			
		||||
            GenerateAuxCommand(buffer_offset, info, info->IsEnabled());
 | 
			
		||||
            break;
 | 
			
		||||
        case EffectType::I3dl2Reverb:
 | 
			
		||||
            GenerateI3dl2ReverbEffectCommand(buffer_offset, info, info->IsEnabled());
 | 
			
		||||
            break;
 | 
			
		||||
        case EffectType::BiquadFilter:
 | 
			
		||||
            GenerateBiquadFilterEffectCommand(buffer_offset, info, info->IsEnabled());
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        info->UpdateForCommandGeneration();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
 | 
			
		||||
                                                        bool enabled) {
 | 
			
		||||
    if (!enabled) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams();
 | 
			
		||||
    const auto channel_count = params.channel_count;
 | 
			
		||||
    for (s32 i = 0; i < channel_count; i++) {
 | 
			
		||||
        // TODO(ogniK): Actually implement reverb
 | 
			
		||||
        if (params.input[i] != params.output[i]) {
 | 
			
		||||
            const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
 | 
			
		||||
            auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
 | 
			
		||||
            ApplyMix<1>(output, input, 32768, worker_params.sample_count);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info,
 | 
			
		||||
                                                         bool enabled) {
 | 
			
		||||
    if (!enabled) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const auto& params = dynamic_cast<EffectBiquadFilter*>(info)->GetParams();
 | 
			
		||||
    const auto channel_count = params.channel_count;
 | 
			
		||||
    for (s32 i = 0; i < channel_count; i++) {
 | 
			
		||||
        // TODO(ogniK): Actually implement biquad filter
 | 
			
		||||
        if (params.input[i] != params.output[i]) {
 | 
			
		||||
            const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
 | 
			
		||||
            auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
 | 
			
		||||
            ApplyMix<1>(output, input, 32768, worker_params.sample_count);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) {
 | 
			
		||||
    auto aux = dynamic_cast<EffectAuxInfo*>(info);
 | 
			
		||||
    const auto& params = aux->GetParams();
 | 
			
		||||
    if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) {
 | 
			
		||||
        const auto max_channels = params.count;
 | 
			
		||||
        u32 offset{};
 | 
			
		||||
        for (u32 channel = 0; channel < max_channels; channel++) {
 | 
			
		||||
            u32 write_count = 0;
 | 
			
		||||
            if (channel == (max_channels - 1)) {
 | 
			
		||||
                write_count = offset + worker_params.sample_count;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const auto input_index = params.input_mix_buffers[channel] + mix_buffer_offset;
 | 
			
		||||
            const auto output_index = params.output_mix_buffers[channel] + mix_buffer_offset;
 | 
			
		||||
 | 
			
		||||
            if (enabled) {
 | 
			
		||||
                AuxInfoDSP send_info{};
 | 
			
		||||
                AuxInfoDSP recv_info{};
 | 
			
		||||
                memory.ReadBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
 | 
			
		||||
                memory.ReadBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
 | 
			
		||||
 | 
			
		||||
                WriteAuxBuffer(send_info, aux->GetSendBuffer(), params.sample_count,
 | 
			
		||||
                               GetMixBuffer(input_index), worker_params.sample_count, offset,
 | 
			
		||||
                               write_count);
 | 
			
		||||
                memory.WriteBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
 | 
			
		||||
 | 
			
		||||
                const auto samples_read = ReadAuxBuffer(
 | 
			
		||||
                    recv_info, aux->GetRecvBuffer(), params.sample_count,
 | 
			
		||||
                    GetMixBuffer(output_index), worker_params.sample_count, offset, write_count);
 | 
			
		||||
                memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
 | 
			
		||||
 | 
			
		||||
                if (samples_read != worker_params.sample_count &&
 | 
			
		||||
                    samples_read <= params.sample_count) {
 | 
			
		||||
                    std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                AuxInfoDSP empty{};
 | 
			
		||||
                memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP));
 | 
			
		||||
                memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP));
 | 
			
		||||
                if (output_index != input_index) {
 | 
			
		||||
                    std::memcpy(GetMixBuffer(output_index), GetMixBuffer(input_index),
 | 
			
		||||
                                worker_params.sample_count * sizeof(s32));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            offset += worker_params.sample_count;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) {
 | 
			
		||||
    if (splitter_id == AudioCommon::NO_SPLITTER) {
 | 
			
		||||
        return nullptr;
 | 
			
		||||
@ -345,6 +461,66 @@ ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter
 | 
			
		||||
    return splitter_context.GetDestinationData(splitter_id, index);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
 | 
			
		||||
                                     const s32* data, u32 sample_count, u32 write_offset,
 | 
			
		||||
                                     u32 write_count) {
 | 
			
		||||
    if (max_samples == 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
    u32 offset = dsp_info.write_offset + write_offset;
 | 
			
		||||
    if (send_buffer == 0 || offset > max_samples) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::size_t data_offset{};
 | 
			
		||||
    u32 remaining = sample_count;
 | 
			
		||||
    while (remaining > 0) {
 | 
			
		||||
        // Get position in buffer
 | 
			
		||||
        const auto base = send_buffer + (offset * sizeof(u32));
 | 
			
		||||
        const auto samples_to_grab = std::min(max_samples - offset, remaining);
 | 
			
		||||
        // Write to output
 | 
			
		||||
        memory.WriteBlock(base, (data + data_offset), samples_to_grab * sizeof(u32));
 | 
			
		||||
        offset = (offset + samples_to_grab) % max_samples;
 | 
			
		||||
        remaining -= samples_to_grab;
 | 
			
		||||
        data_offset += samples_to_grab;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (write_count != 0) {
 | 
			
		||||
        dsp_info.write_offset = (dsp_info.write_offset + write_count) % max_samples;
 | 
			
		||||
    }
 | 
			
		||||
    return sample_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples,
 | 
			
		||||
                                    s32* out_data, u32 sample_count, u32 read_offset,
 | 
			
		||||
                                    u32 read_count) {
 | 
			
		||||
    if (max_samples == 0) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u32 offset = recv_info.read_offset + read_offset;
 | 
			
		||||
    if (recv_buffer == 0 || offset > max_samples) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u32 remaining = sample_count;
 | 
			
		||||
    while (remaining > 0) {
 | 
			
		||||
        const auto base = recv_buffer + (offset * sizeof(u32));
 | 
			
		||||
        const auto samples_to_grab = std::min(max_samples - offset, remaining);
 | 
			
		||||
        std::vector<s32> buffer(samples_to_grab);
 | 
			
		||||
        memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32));
 | 
			
		||||
        std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32));
 | 
			
		||||
        out_data += samples_to_grab;
 | 
			
		||||
        offset = (offset + samples_to_grab) % max_samples;
 | 
			
		||||
        remaining -= samples_to_grab;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (read_count != 0) {
 | 
			
		||||
        recv_info.read_offset = (recv_info.read_offset + read_count) % max_samples;
 | 
			
		||||
    }
 | 
			
		||||
    return sample_count;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
 | 
			
		||||
                                                 s32 channel, s32 node_id) {
 | 
			
		||||
    const auto last = static_cast<s32>(last_volume * 32768.0f);
 | 
			
		||||
@ -398,7 +574,9 @@ void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) {
 | 
			
		||||
    auto& in_params = mix_info.GetInParams();
 | 
			
		||||
    GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
 | 
			
		||||
                                      in_params.sample_rate);
 | 
			
		||||
    // TODO(ogniK): Effects
 | 
			
		||||
 | 
			
		||||
    GenerateEffectCommand(mix_info);
 | 
			
		||||
 | 
			
		||||
    GenerateMixCommands(mix_info);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -476,7 +654,8 @@ void CommandGenerator::GenerateFinalMixCommand() {
 | 
			
		||||
 | 
			
		||||
    GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
 | 
			
		||||
                                      in_params.sample_rate);
 | 
			
		||||
    // TODO(ogniK): Effects
 | 
			
		||||
 | 
			
		||||
    GenerateEffectCommand(mix_info);
 | 
			
		||||
 | 
			
		||||
    for (s32 i = 0; i < in_params.buffer_count; i++) {
 | 
			
		||||
        const s32 gain = static_cast<s32>(in_params.volume * 32768.0f);
 | 
			
		||||
 | 
			
		||||
@ -19,14 +19,17 @@ class MixContext;
 | 
			
		||||
class SplitterContext;
 | 
			
		||||
class ServerSplitterDestinationData;
 | 
			
		||||
class ServerMixInfo;
 | 
			
		||||
 | 
			
		||||
class EffectContext;
 | 
			
		||||
class EffectBase;
 | 
			
		||||
struct AuxInfoDSP;
 | 
			
		||||
using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
 | 
			
		||||
 | 
			
		||||
class CommandGenerator {
 | 
			
		||||
public:
 | 
			
		||||
    explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
 | 
			
		||||
                              VoiceContext& voice_context, MixContext& mix_context,
 | 
			
		||||
                              SplitterContext& splitter_context, Core::Memory::Memory& memory);
 | 
			
		||||
                              SplitterContext& splitter_context, EffectContext& effect_context,
 | 
			
		||||
                              Core::Memory::Memory& memory);
 | 
			
		||||
    ~CommandGenerator();
 | 
			
		||||
 | 
			
		||||
    void ClearMixBuffers();
 | 
			
		||||
@ -67,8 +70,17 @@ private:
 | 
			
		||||
                                     std::size_t mix_buffer_offset);
 | 
			
		||||
    void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
 | 
			
		||||
                                           std::size_t mix_buffer_offset, s32 sample_rate);
 | 
			
		||||
    void GenerateEffectCommand(ServerMixInfo& mix_info);
 | 
			
		||||
    void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
 | 
			
		||||
    void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
 | 
			
		||||
    void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
 | 
			
		||||
    ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
 | 
			
		||||
 | 
			
		||||
    s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
 | 
			
		||||
                       u32 sample_count, u32 write_offset, u32 write_count);
 | 
			
		||||
    s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
 | 
			
		||||
                      u32 sample_count, u32 read_offset, u32 read_count);
 | 
			
		||||
 | 
			
		||||
    // DSP Code
 | 
			
		||||
    s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
 | 
			
		||||
                    s32 channel, std::size_t mix_offset);
 | 
			
		||||
@ -81,6 +93,7 @@ private:
 | 
			
		||||
    VoiceContext& voice_context;
 | 
			
		||||
    MixContext& mix_context;
 | 
			
		||||
    SplitterContext& splitter_context;
 | 
			
		||||
    EffectContext& effect_context;
 | 
			
		||||
    Core::Memory::Memory& memory;
 | 
			
		||||
    std::vector<s32> mix_buffer{};
 | 
			
		||||
    std::vector<s32> sample_buffer{};
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ constexpr s32 NO_SPLITTER = -1;
 | 
			
		||||
constexpr s32 NO_MIX = 0x7fffffff;
 | 
			
		||||
constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
 | 
			
		||||
constexpr s32 FINAL_MIX = 0;
 | 
			
		||||
constexpr s32 NO_EFFECT_ORDER = -1;
 | 
			
		||||
constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this constant
 | 
			
		||||
// Any size checks seem to take the sample history into account
 | 
			
		||||
// and our const ends up being 0x3f04, the 4 bytes are most
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,12 @@
 | 
			
		||||
#include "audio_core/effect_context.h"
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
namespace {
 | 
			
		||||
bool ValidChannelCountForEffect(s32 channel_count) {
 | 
			
		||||
    return channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6;
 | 
			
		||||
}
 | 
			
		||||
} // namespace
 | 
			
		||||
 | 
			
		||||
EffectContext::EffectContext(std::size_t effect_count) : effect_count(effect_count) {
 | 
			
		||||
    effects.reserve(effect_count);
 | 
			
		||||
    std::generate_n(std::back_inserter(effects), effect_count,
 | 
			
		||||
@ -21,24 +27,273 @@ EffectBase* EffectContext::GetInfo(std::size_t i) {
 | 
			
		||||
    return effects.at(i).get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
 | 
			
		||||
    switch (effect) {
 | 
			
		||||
    case EffectType::Invalid:
 | 
			
		||||
        effects[i] = std::make_unique<EffectStubbed>();
 | 
			
		||||
        break;
 | 
			
		||||
    case EffectType::BufferMixer:
 | 
			
		||||
        effects[i] = std::make_unique<EffectBufferMixer>();
 | 
			
		||||
        break;
 | 
			
		||||
    case EffectType::Aux:
 | 
			
		||||
        effects[i] = std::make_unique<EffectAuxInfo>();
 | 
			
		||||
        break;
 | 
			
		||||
    case EffectType::Delay:
 | 
			
		||||
        effects[i] = std::make_unique<EffectDelay>();
 | 
			
		||||
        break;
 | 
			
		||||
    case EffectType::Reverb:
 | 
			
		||||
        effects[i] = std::make_unique<EffectReverb>();
 | 
			
		||||
        break;
 | 
			
		||||
    case EffectType::I3dl2Reverb:
 | 
			
		||||
        effects[i] = std::make_unique<EffectI3dl2Reverb>();
 | 
			
		||||
        break;
 | 
			
		||||
    case EffectType::BiquadFilter:
 | 
			
		||||
        effects[i] = std::make_unique<EffectBiquadFilter>();
 | 
			
		||||
        break;
 | 
			
		||||
    default:
 | 
			
		||||
        UNREACHABLE_MSG("Unimplemented effect {}", effect);
 | 
			
		||||
        effects[i] = std::make_unique<EffectStubbed>();
 | 
			
		||||
    }
 | 
			
		||||
    return GetInfo(i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const EffectBase* EffectContext::GetInfo(std::size_t i) const {
 | 
			
		||||
    return effects.at(i).get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EffectStubbed::EffectStubbed() : EffectBase::EffectBase() {}
 | 
			
		||||
EffectStubbed::EffectStubbed() : EffectBase::EffectBase(EffectType::Invalid) {}
 | 
			
		||||
EffectStubbed::~EffectStubbed() = default;
 | 
			
		||||
 | 
			
		||||
void EffectStubbed::Update(EffectInfo::InParams& in_params) {
 | 
			
		||||
    if (in_params.is_new) {
 | 
			
		||||
        usage = UsageStatus::New;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
void EffectStubbed::Update(EffectInfo::InParams& in_params) {}
 | 
			
		||||
void EffectStubbed::UpdateForCommandGeneration() {}
 | 
			
		||||
 | 
			
		||||
EffectBase::EffectBase() = default;
 | 
			
		||||
EffectBase::EffectBase(EffectType effect_type) : effect_type(effect_type) {}
 | 
			
		||||
EffectBase::~EffectBase() = default;
 | 
			
		||||
 | 
			
		||||
UsageStatus EffectBase::GetUsage() const {
 | 
			
		||||
UsageState EffectBase::GetUsage() const {
 | 
			
		||||
    return usage;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EffectType EffectBase::GetType() const {
 | 
			
		||||
    return effect_type;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool EffectBase::IsEnabled() const {
 | 
			
		||||
    return enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
s32 EffectBase::GetMixID() const {
 | 
			
		||||
    return mix_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
s32 EffectBase::GetProcessingOrder() const {
 | 
			
		||||
    return processing_order;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric::EffectGeneric(EffectType::I3dl2Reverb) {}
 | 
			
		||||
EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
 | 
			
		||||
 | 
			
		||||
void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
 | 
			
		||||
    auto& internal_params = GetParams();
 | 
			
		||||
    const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
 | 
			
		||||
    if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
 | 
			
		||||
        UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto last_status = internal_params.status;
 | 
			
		||||
    mix_id = in_params.mix_id;
 | 
			
		||||
    processing_order = in_params.processing_order;
 | 
			
		||||
    internal_params = *reverb_params;
 | 
			
		||||
    if (!ValidChannelCountForEffect(reverb_params->channel_count)) {
 | 
			
		||||
        internal_params.channel_count = internal_params.max_channels;
 | 
			
		||||
    }
 | 
			
		||||
    enabled = in_params.is_enabled;
 | 
			
		||||
    if (last_status != ParameterStatus::Updated) {
 | 
			
		||||
        internal_params.status = last_status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (in_params.is_new || skipped) {
 | 
			
		||||
        usage = UsageState::Initialized;
 | 
			
		||||
        internal_params.status = ParameterStatus::Initialized;
 | 
			
		||||
        skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EffectI3dl2Reverb::UpdateForCommandGeneration() {
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
        usage = UsageState::Running;
 | 
			
		||||
    } else {
 | 
			
		||||
        usage = UsageState::Stopped;
 | 
			
		||||
    }
 | 
			
		||||
    GetParams().status = ParameterStatus::Updated;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric::EffectGeneric(EffectType::BiquadFilter) {}
 | 
			
		||||
EffectBiquadFilter::~EffectBiquadFilter() = default;
 | 
			
		||||
 | 
			
		||||
void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) {
 | 
			
		||||
    auto& internal_params = GetParams();
 | 
			
		||||
    const auto* biquad_params = reinterpret_cast<BiquadFilterParams*>(in_params.raw.data());
 | 
			
		||||
    mix_id = in_params.mix_id;
 | 
			
		||||
    processing_order = in_params.processing_order;
 | 
			
		||||
    internal_params = *biquad_params;
 | 
			
		||||
    enabled = in_params.is_enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EffectBiquadFilter::UpdateForCommandGeneration() {
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
        usage = UsageState::Running;
 | 
			
		||||
    } else {
 | 
			
		||||
        usage = UsageState::Stopped;
 | 
			
		||||
    }
 | 
			
		||||
    GetParams().status = ParameterStatus::Updated;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EffectAuxInfo::EffectAuxInfo() : EffectGeneric::EffectGeneric(EffectType::Aux) {}
 | 
			
		||||
EffectAuxInfo::~EffectAuxInfo() = default;
 | 
			
		||||
 | 
			
		||||
void EffectAuxInfo::Update(EffectInfo::InParams& in_params) {
 | 
			
		||||
    const auto* aux_params = reinterpret_cast<AuxInfo*>(in_params.raw.data());
 | 
			
		||||
    mix_id = in_params.mix_id;
 | 
			
		||||
    processing_order = in_params.processing_order;
 | 
			
		||||
    GetParams() = *aux_params;
 | 
			
		||||
    enabled = in_params.is_enabled;
 | 
			
		||||
 | 
			
		||||
    if (in_params.is_new || skipped) {
 | 
			
		||||
        skipped = aux_params->send_buffer_info == 0 || aux_params->return_buffer_info == 0;
 | 
			
		||||
        if (skipped) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // There's two AuxInfos which are an identical size, the first one is managed by the cpu,
 | 
			
		||||
        // the second is managed by the dsp. All we care about is managing the DSP one
 | 
			
		||||
        send_info = aux_params->send_buffer_info + sizeof(AuxInfoDSP);
 | 
			
		||||
        send_buffer = aux_params->send_buffer_info + (sizeof(AuxInfoDSP) * 2);
 | 
			
		||||
 | 
			
		||||
        recv_info = aux_params->return_buffer_info + sizeof(AuxInfoDSP);
 | 
			
		||||
        recv_buffer = aux_params->return_buffer_info + (sizeof(AuxInfoDSP) * 2);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EffectAuxInfo::UpdateForCommandGeneration() {
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
        usage = UsageState::Running;
 | 
			
		||||
    } else {
 | 
			
		||||
        usage = UsageState::Stopped;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const VAddr EffectAuxInfo::GetSendInfo() const {
 | 
			
		||||
    return send_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const VAddr EffectAuxInfo::GetSendBuffer() const {
 | 
			
		||||
    return send_buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const VAddr EffectAuxInfo::GetRecvInfo() const {
 | 
			
		||||
    return recv_info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const VAddr EffectAuxInfo::GetRecvBuffer() const {
 | 
			
		||||
    return recv_buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EffectDelay::EffectDelay() : EffectGeneric::EffectGeneric(EffectType::Delay) {}
 | 
			
		||||
EffectDelay::~EffectDelay() = default;
 | 
			
		||||
 | 
			
		||||
void EffectDelay::Update(EffectInfo::InParams& in_params) {
 | 
			
		||||
    const auto* delay_params = reinterpret_cast<DelayParams*>(in_params.raw.data());
 | 
			
		||||
    auto& internal_params = GetParams();
 | 
			
		||||
    if (!ValidChannelCountForEffect(delay_params->max_channels)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto last_status = internal_params.status;
 | 
			
		||||
    mix_id = in_params.mix_id;
 | 
			
		||||
    processing_order = in_params.processing_order;
 | 
			
		||||
    internal_params = *delay_params;
 | 
			
		||||
    if (!ValidChannelCountForEffect(delay_params->channels)) {
 | 
			
		||||
        internal_params.channels = internal_params.max_channels;
 | 
			
		||||
    }
 | 
			
		||||
    enabled = in_params.is_enabled;
 | 
			
		||||
 | 
			
		||||
    if (last_status != ParameterStatus::Updated) {
 | 
			
		||||
        internal_params.status = last_status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (in_params.is_new || skipped) {
 | 
			
		||||
        usage = UsageState::Initialized;
 | 
			
		||||
        internal_params.status = ParameterStatus::Initialized;
 | 
			
		||||
        skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EffectDelay::UpdateForCommandGeneration() {
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
        usage = UsageState::Running;
 | 
			
		||||
    } else {
 | 
			
		||||
        usage = UsageState::Stopped;
 | 
			
		||||
    }
 | 
			
		||||
    GetParams().status = ParameterStatus::Updated;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EffectBufferMixer::EffectBufferMixer() : EffectGeneric::EffectGeneric(EffectType::BufferMixer) {}
 | 
			
		||||
EffectBufferMixer::~EffectBufferMixer() = default;
 | 
			
		||||
 | 
			
		||||
void EffectBufferMixer::Update(EffectInfo::InParams& in_params) {
 | 
			
		||||
    mix_id = in_params.mix_id;
 | 
			
		||||
    processing_order = in_params.processing_order;
 | 
			
		||||
    GetParams() = *reinterpret_cast<BufferMixerParams*>(in_params.raw.data());
 | 
			
		||||
    enabled = in_params.is_enabled;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EffectBufferMixer::UpdateForCommandGeneration() {
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
        usage = UsageState::Running;
 | 
			
		||||
    } else {
 | 
			
		||||
        usage = UsageState::Stopped;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EffectReverb::EffectReverb() : EffectGeneric::EffectGeneric(EffectType::Reverb) {}
 | 
			
		||||
EffectReverb::~EffectReverb() = default;
 | 
			
		||||
 | 
			
		||||
void EffectReverb::Update(EffectInfo::InParams& in_params) {
 | 
			
		||||
    const auto* reverb_params = reinterpret_cast<ReverbParams*>(in_params.raw.data());
 | 
			
		||||
    auto& internal_params = GetParams();
 | 
			
		||||
    if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto last_status = internal_params.status;
 | 
			
		||||
    mix_id = in_params.mix_id;
 | 
			
		||||
    processing_order = in_params.processing_order;
 | 
			
		||||
    internal_params = *reverb_params;
 | 
			
		||||
    if (!ValidChannelCountForEffect(reverb_params->channels)) {
 | 
			
		||||
        internal_params.channels = internal_params.max_channels;
 | 
			
		||||
    }
 | 
			
		||||
    enabled = in_params.is_enabled;
 | 
			
		||||
 | 
			
		||||
    if (last_status != ParameterStatus::Updated) {
 | 
			
		||||
        internal_params.status = last_status;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (in_params.is_new || skipped) {
 | 
			
		||||
        usage = UsageState::Initialized;
 | 
			
		||||
        internal_params.status = ParameterStatus::Initialized;
 | 
			
		||||
        skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EffectReverb::UpdateForCommandGeneration() {
 | 
			
		||||
    if (enabled) {
 | 
			
		||||
        usage = UsageState::Running;
 | 
			
		||||
    } else {
 | 
			
		||||
        usage = UsageState::Stopped;
 | 
			
		||||
    }
 | 
			
		||||
    GetParams().status = ParameterStatus::Updated;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace AudioCore
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,19 @@ enum class UsageStatus : u8 {
 | 
			
		||||
    Removed = 4,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class UsageState {
 | 
			
		||||
    Invalid = 0,
 | 
			
		||||
    Initialized = 1,
 | 
			
		||||
    Running = 2,
 | 
			
		||||
    Stopped = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class ParameterStatus : u8 {
 | 
			
		||||
    Initialized = 0,
 | 
			
		||||
    Updating = 1,
 | 
			
		||||
    Updated = 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct BufferMixerParams {
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input{};
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output{};
 | 
			
		||||
@ -39,6 +52,14 @@ struct BufferMixerParams {
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(BufferMixerParams) == 0x94, "BufferMixerParams is an invalid size");
 | 
			
		||||
 | 
			
		||||
struct AuxInfoDSP {
 | 
			
		||||
    u32_le read_offset{};
 | 
			
		||||
    u32_le write_offset{};
 | 
			
		||||
    u32_le remaining{};
 | 
			
		||||
    INSERT_PADDING_WORDS(13);
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(AuxInfoDSP) == 0x40, "AuxInfoDSP is an invalid size");
 | 
			
		||||
 | 
			
		||||
struct AuxInfo {
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input_mix_buffers{};
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output_mix_buffers{};
 | 
			
		||||
@ -54,6 +75,81 @@ struct AuxInfo {
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
 | 
			
		||||
 | 
			
		||||
struct I3dl2ReverbParams {
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
 | 
			
		||||
    u16_le max_channels{};
 | 
			
		||||
    u16_le channel_count{};
 | 
			
		||||
    INSERT_PADDING_BYTES(1);
 | 
			
		||||
    u32_le sample_rate{};
 | 
			
		||||
    f32 room_hf{};
 | 
			
		||||
    f32 hf_reference{};
 | 
			
		||||
    f32 decay_time{};
 | 
			
		||||
    f32 hf_decay_ratio{};
 | 
			
		||||
    f32 room{};
 | 
			
		||||
    f32 reflection{};
 | 
			
		||||
    f32 reverb{};
 | 
			
		||||
    f32 diffusion{};
 | 
			
		||||
    f32 reflection_delay{};
 | 
			
		||||
    f32 reverb_delay{};
 | 
			
		||||
    f32 density{};
 | 
			
		||||
    f32 dry_gain{};
 | 
			
		||||
    ParameterStatus status{};
 | 
			
		||||
    INSERT_PADDING_BYTES(3);
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(I3dl2ReverbParams) == 0x4c, "I3dl2ReverbParams is an invalid size");
 | 
			
		||||
 | 
			
		||||
struct BiquadFilterParams {
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
 | 
			
		||||
    std::array<s16_le, 3> numerator;
 | 
			
		||||
    std::array<s16_le, 2> denominator;
 | 
			
		||||
    s8 channel_count{};
 | 
			
		||||
    ParameterStatus status{};
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(BiquadFilterParams) == 0x18, "BiquadFilterParams is an invalid size");
 | 
			
		||||
 | 
			
		||||
struct DelayParams {
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
 | 
			
		||||
    u16_le max_channels{};
 | 
			
		||||
    u16_le channels{};
 | 
			
		||||
    s32_le max_delay{};
 | 
			
		||||
    s32_le delay{};
 | 
			
		||||
    s32_le sample_rate{};
 | 
			
		||||
    s32_le gain{};
 | 
			
		||||
    s32_le feedback_gain{};
 | 
			
		||||
    s32_le out_gain{};
 | 
			
		||||
    s32_le dry_gain{};
 | 
			
		||||
    s32_le channel_spread{};
 | 
			
		||||
    s32_le low_pass{};
 | 
			
		||||
    ParameterStatus status{};
 | 
			
		||||
    INSERT_PADDING_BYTES(3);
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(DelayParams) == 0x38, "DelayParams is an invalid size");
 | 
			
		||||
 | 
			
		||||
struct ReverbParams {
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
 | 
			
		||||
    std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
 | 
			
		||||
    u16_le max_channels{};
 | 
			
		||||
    u16_le channels{};
 | 
			
		||||
    s32_le sample_rate{};
 | 
			
		||||
    s32_le mode0{};
 | 
			
		||||
    s32_le mode0_gain{};
 | 
			
		||||
    s32_le pre_delay{};
 | 
			
		||||
    s32_le mode1{};
 | 
			
		||||
    s32_le mode1_gain{};
 | 
			
		||||
    s32_le decay{};
 | 
			
		||||
    s32_le hf_decay_ratio{};
 | 
			
		||||
    s32_le coloration{};
 | 
			
		||||
    s32_le reverb_gain{};
 | 
			
		||||
    s32_le out_gain{};
 | 
			
		||||
    s32_le dry_gain{};
 | 
			
		||||
    ParameterStatus status{};
 | 
			
		||||
    INSERT_PADDING_BYTES(3);
 | 
			
		||||
};
 | 
			
		||||
static_assert(sizeof(ReverbParams) == 0x44, "ReverbParams is an invalid size");
 | 
			
		||||
 | 
			
		||||
class EffectInfo {
 | 
			
		||||
public:
 | 
			
		||||
    struct InParams {
 | 
			
		||||
@ -64,7 +160,7 @@ public:
 | 
			
		||||
        s32_le mix_id{};
 | 
			
		||||
        u64_le buffer_address{};
 | 
			
		||||
        u64_le buffer_size{};
 | 
			
		||||
        s32_le priority{};
 | 
			
		||||
        s32_le processing_order{};
 | 
			
		||||
        INSERT_PADDING_BYTES(4);
 | 
			
		||||
        union {
 | 
			
		||||
            std::array<u8, 0xa0> raw;
 | 
			
		||||
@ -79,16 +175,50 @@ public:
 | 
			
		||||
    static_assert(sizeof(EffectInfo::OutParams) == 0x10, "OutParams is an invalid size");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct AuxAddress {
 | 
			
		||||
    VAddr send_dsp_info{};
 | 
			
		||||
    VAddr send_buffer_base{};
 | 
			
		||||
    VAddr return_dsp_info{};
 | 
			
		||||
    VAddr return_buffer_base{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EffectBase {
 | 
			
		||||
public:
 | 
			
		||||
    EffectBase();
 | 
			
		||||
    EffectBase(EffectType effect_type);
 | 
			
		||||
    ~EffectBase();
 | 
			
		||||
 | 
			
		||||
    virtual void Update(EffectInfo::InParams& in_params) = 0;
 | 
			
		||||
    UsageStatus GetUsage() const;
 | 
			
		||||
    virtual void UpdateForCommandGeneration() = 0;
 | 
			
		||||
    UsageState GetUsage() const;
 | 
			
		||||
    EffectType GetType() const;
 | 
			
		||||
    bool IsEnabled() const;
 | 
			
		||||
    s32 GetMixID() const;
 | 
			
		||||
    s32 GetProcessingOrder() const;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    UsageStatus usage{UsageStatus::Invalid};
 | 
			
		||||
    UsageState usage{UsageState::Invalid};
 | 
			
		||||
    EffectType effect_type{};
 | 
			
		||||
    s32 mix_id{};
 | 
			
		||||
    s32 processing_order{};
 | 
			
		||||
    bool enabled = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
class EffectGeneric : public EffectBase {
 | 
			
		||||
public:
 | 
			
		||||
    EffectGeneric(EffectType effect_type) : EffectBase::EffectBase(effect_type) {}
 | 
			
		||||
    ~EffectGeneric() = default;
 | 
			
		||||
 | 
			
		||||
    T& GetParams() {
 | 
			
		||||
        return internal_params;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const I3dl2ReverbParams& GetParams() const {
 | 
			
		||||
        return internal_params;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    T internal_params{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EffectStubbed : public EffectBase {
 | 
			
		||||
@ -97,6 +227,82 @@ public:
 | 
			
		||||
    ~EffectStubbed();
 | 
			
		||||
 | 
			
		||||
    void Update(EffectInfo::InParams& in_params) override;
 | 
			
		||||
    void UpdateForCommandGeneration() override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
 | 
			
		||||
public:
 | 
			
		||||
    explicit EffectI3dl2Reverb();
 | 
			
		||||
    ~EffectI3dl2Reverb();
 | 
			
		||||
 | 
			
		||||
    void Update(EffectInfo::InParams& in_params) override;
 | 
			
		||||
    void UpdateForCommandGeneration() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    bool skipped = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
 | 
			
		||||
public:
 | 
			
		||||
    explicit EffectBiquadFilter();
 | 
			
		||||
    ~EffectBiquadFilter();
 | 
			
		||||
 | 
			
		||||
    void Update(EffectInfo::InParams& in_params) override;
 | 
			
		||||
    void UpdateForCommandGeneration() override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EffectAuxInfo : public EffectGeneric<AuxInfo> {
 | 
			
		||||
public:
 | 
			
		||||
    explicit EffectAuxInfo();
 | 
			
		||||
    ~EffectAuxInfo();
 | 
			
		||||
 | 
			
		||||
    void Update(EffectInfo::InParams& in_params) override;
 | 
			
		||||
    void UpdateForCommandGeneration() override;
 | 
			
		||||
    const VAddr GetSendInfo() const;
 | 
			
		||||
    const VAddr GetSendBuffer() const;
 | 
			
		||||
    const VAddr GetRecvInfo() const;
 | 
			
		||||
    const VAddr GetRecvBuffer() const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    VAddr send_info{};
 | 
			
		||||
    VAddr send_buffer{};
 | 
			
		||||
    VAddr recv_info{};
 | 
			
		||||
    VAddr recv_buffer{};
 | 
			
		||||
    bool skipped = false;
 | 
			
		||||
    AuxAddress addresses{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EffectDelay : public EffectGeneric<DelayParams> {
 | 
			
		||||
public:
 | 
			
		||||
    explicit EffectDelay();
 | 
			
		||||
    ~EffectDelay();
 | 
			
		||||
 | 
			
		||||
    void Update(EffectInfo::InParams& in_params) override;
 | 
			
		||||
    void UpdateForCommandGeneration() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    bool skipped = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EffectBufferMixer : public EffectGeneric<BufferMixerParams> {
 | 
			
		||||
public:
 | 
			
		||||
    explicit EffectBufferMixer();
 | 
			
		||||
    ~EffectBufferMixer();
 | 
			
		||||
 | 
			
		||||
    void Update(EffectInfo::InParams& in_params) override;
 | 
			
		||||
    void UpdateForCommandGeneration() override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EffectReverb : public EffectGeneric<ReverbParams> {
 | 
			
		||||
public:
 | 
			
		||||
    explicit EffectReverb();
 | 
			
		||||
    ~EffectReverb();
 | 
			
		||||
 | 
			
		||||
    void Update(EffectInfo::InParams& in_params) override;
 | 
			
		||||
    void UpdateForCommandGeneration() override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    bool skipped = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class EffectContext {
 | 
			
		||||
@ -106,6 +312,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    std::size_t GetCount() const;
 | 
			
		||||
    EffectBase* GetInfo(std::size_t i);
 | 
			
		||||
    EffectBase* RetargetEffect(std::size_t i, EffectType effect);
 | 
			
		||||
    const EffectBase* GetInfo(std::size_t i) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
@ -244,14 +244,15 @@ bool InfoUpdater::UpdateEffects(EffectContext& effect_context, bool is_active) {
 | 
			
		||||
    // Update effects
 | 
			
		||||
    for (std::size_t i = 0; i < effect_count; i++) {
 | 
			
		||||
        auto* info = effect_context.GetInfo(i);
 | 
			
		||||
        if (effect_in[i].type != info->GetType()) {
 | 
			
		||||
            info = effect_context.RetargetEffect(i, effect_in[i].type);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        info->Update(effect_in[i]);
 | 
			
		||||
 | 
			
		||||
        // TODO(ogniK): Update individual effects
 | 
			
		||||
        if ((!is_active && info->GetUsage() != UsageStatus::New) ||
 | 
			
		||||
            info->GetUsage() == UsageStatus::Removed) {
 | 
			
		||||
        if ((!is_active && info->GetUsage() != UsageState::Initialized) ||
 | 
			
		||||
            info->GetUsage() == UsageState::Stopped) {
 | 
			
		||||
            effect_out[i].status = UsageStatus::Removed;
 | 
			
		||||
        } else if (info->GetUsage() == UsageStatus::New) {
 | 
			
		||||
            effect_out[i].status = UsageStatus::New;
 | 
			
		||||
        } else {
 | 
			
		||||
            effect_out[i].status = UsageStatus::Used;
 | 
			
		||||
        }
 | 
			
		||||
@ -290,7 +291,8 @@ bool InfoUpdater::UpdateSplitterInfo(SplitterContext& splitter_context) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
 | 
			
		||||
                                    SplitterContext& splitter_context) {
 | 
			
		||||
                                    SplitterContext& splitter_context,
 | 
			
		||||
                                    EffectContext& effect_context) {
 | 
			
		||||
    std::vector<MixInfo::InParams> mix_in_params;
 | 
			
		||||
 | 
			
		||||
    if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
 | 
			
		||||
@ -387,13 +389,13 @@ ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buf
 | 
			
		||||
        auto& mix_info_params = mix_info.GetInParams();
 | 
			
		||||
        if (mix_info_params.in_use != mix_in.in_use) {
 | 
			
		||||
            mix_info_params.in_use = mix_in.in_use;
 | 
			
		||||
            // TODO(ogniK): Update effect processing order
 | 
			
		||||
            mix_info.ResetEffectProcessingOrder();
 | 
			
		||||
            should_sort = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (mix_in.in_use) {
 | 
			
		||||
            should_sort |= mix_info.Update(mix_context.GetEdgeMatrix(), mix_in, behavior_info,
 | 
			
		||||
                                           splitter_context);
 | 
			
		||||
                                           splitter_context, effect_context);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ public:
 | 
			
		||||
    bool UpdateEffects(EffectContext& effect_context, bool is_active);
 | 
			
		||||
    bool UpdateSplitterInfo(SplitterContext& splitter_context);
 | 
			
		||||
    ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
 | 
			
		||||
                           SplitterContext& splitter_context);
 | 
			
		||||
                           SplitterContext& splitter_context, EffectContext& effect_context);
 | 
			
		||||
    bool UpdateSinks(SinkContext& sink_context);
 | 
			
		||||
    bool UpdatePerformanceBuffer();
 | 
			
		||||
    bool UpdateErrorInfo(BehaviorInfo& in_behavior_info);
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@
 | 
			
		||||
 | 
			
		||||
#include "audio_core/behavior_info.h"
 | 
			
		||||
#include "audio_core/common.h"
 | 
			
		||||
#include "audio_core/effect_context.h"
 | 
			
		||||
#include "audio_core/mix_context.h"
 | 
			
		||||
#include "audio_core/splitter_context.h"
 | 
			
		||||
 | 
			
		||||
@ -11,7 +12,8 @@ namespace AudioCore {
 | 
			
		||||
MixContext::MixContext() = default;
 | 
			
		||||
MixContext::~MixContext() = default;
 | 
			
		||||
 | 
			
		||||
void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count) {
 | 
			
		||||
void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
 | 
			
		||||
                            std::size_t effect_count) {
 | 
			
		||||
    info_count = mix_count;
 | 
			
		||||
    infos.resize(info_count);
 | 
			
		||||
    auto& final_mix = GetInfo(AudioCommon::FINAL_MIX);
 | 
			
		||||
@ -21,6 +23,10 @@ void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_c
 | 
			
		||||
        sorted_info.push_back(&info);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (auto& info : infos) {
 | 
			
		||||
        info.SetEffectCount(effect_count);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Only initialize our edge matrix and node states if splitters are supported
 | 
			
		||||
    if (behavior_info.IsSplitterSupported()) {
 | 
			
		||||
        node_states.Initialize(mix_count);
 | 
			
		||||
@ -185,7 +191,8 @@ ServerMixInfo::InParams& ServerMixInfo::GetInParams() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
 | 
			
		||||
                           BehaviorInfo& behavior_info, SplitterContext& splitter_context) {
 | 
			
		||||
                           BehaviorInfo& behavior_info, SplitterContext& splitter_context,
 | 
			
		||||
                           EffectContext& effect_context) {
 | 
			
		||||
    in_params.volume = mix_in.volume;
 | 
			
		||||
    in_params.sample_rate = mix_in.sample_rate;
 | 
			
		||||
    in_params.buffer_count = mix_in.buffer_count;
 | 
			
		||||
@ -206,6 +213,15 @@ bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix
 | 
			
		||||
        in_params.splitter_id = AudioCommon::NO_SPLITTER;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ResetEffectProcessingOrder();
 | 
			
		||||
    const auto effect_count = effect_context.GetCount();
 | 
			
		||||
    for (std::size_t i = 0; i < effect_count; i++) {
 | 
			
		||||
        auto* effect_info = effect_context.GetInfo(i);
 | 
			
		||||
        if (effect_info->GetMixID() == in_params.mix_id) {
 | 
			
		||||
            effect_processing_order[effect_info->GetProcessingOrder()] = static_cast<s32>(i);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(ogniK): Update effect processing order
 | 
			
		||||
    return require_sort;
 | 
			
		||||
}
 | 
			
		||||
@ -228,6 +244,21 @@ void ServerMixInfo::Cleanup() {
 | 
			
		||||
    std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ServerMixInfo::SetEffectCount(std::size_t count) {
 | 
			
		||||
    effect_processing_order.resize(count);
 | 
			
		||||
    ResetEffectProcessingOrder();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ServerMixInfo::ResetEffectProcessingOrder() {
 | 
			
		||||
    for (auto& order : effect_processing_order) {
 | 
			
		||||
        order = AudioCommon::NO_EFFECT_ORDER;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
s32 ServerMixInfo::GetEffectOrder(std::size_t i) const {
 | 
			
		||||
    return effect_processing_order.at(i);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
 | 
			
		||||
                                     SplitterContext& splitter_context) {
 | 
			
		||||
    // Mixes are identical
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@
 | 
			
		||||
 | 
			
		||||
namespace AudioCore {
 | 
			
		||||
class BehaviorInfo;
 | 
			
		||||
class EffectContext;
 | 
			
		||||
 | 
			
		||||
class MixInfo {
 | 
			
		||||
public:
 | 
			
		||||
@ -65,11 +66,16 @@ public:
 | 
			
		||||
    ServerMixInfo::InParams& GetInParams();
 | 
			
		||||
 | 
			
		||||
    bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
 | 
			
		||||
                BehaviorInfo& behavior_info, SplitterContext& splitter_context);
 | 
			
		||||
                BehaviorInfo& behavior_info, SplitterContext& splitter_context,
 | 
			
		||||
                EffectContext& effect_context);
 | 
			
		||||
    bool HasAnyConnection() const;
 | 
			
		||||
    void Cleanup();
 | 
			
		||||
    void SetEffectCount(std::size_t count);
 | 
			
		||||
    void ResetEffectProcessingOrder();
 | 
			
		||||
    s32 GetEffectOrder(std::size_t i) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::vector<s32> effect_processing_order;
 | 
			
		||||
    InParams in_params{};
 | 
			
		||||
    bool UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
 | 
			
		||||
                          SplitterContext& splitter_context);
 | 
			
		||||
@ -80,7 +86,8 @@ public:
 | 
			
		||||
    MixContext();
 | 
			
		||||
    ~MixContext();
 | 
			
		||||
 | 
			
		||||
    void Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count);
 | 
			
		||||
    void Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
 | 
			
		||||
                    std::size_t effect_count);
 | 
			
		||||
    void SortInfo();
 | 
			
		||||
    bool TsortInfo(SplitterContext& splitter_context);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user