| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -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);
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |