diff options
| author | 2020-08-17 01:23:55 +1000 | |
|---|---|---|
| committer | 2020-08-17 01:23:55 +1000 | |
| commit | 80ac1331b545d993aa7c205dc24f8b20a4d6d44e (patch) | |
| tree | f0b1138935e239ff7c5766fc26bc259b375ae712 /src | |
| parent | Disable biquad filter (diff) | |
| download | yuzu-80ac1331b545d993aa7c205dc24f8b20a4d6d44e.tar.gz yuzu-80ac1331b545d993aa7c205dc24f8b20a4d6d44e.tar.xz yuzu-80ac1331b545d993aa7c205dc24f8b20a4d6d44e.zip | |
Preliminary effects
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 9 | ||||
| -rw-r--r-- | src/audio_core/command_generator.cpp | 187 | ||||
| -rw-r--r-- | src/audio_core/command_generator.h | 17 | ||||
| -rw-r--r-- | src/audio_core/common.h | 1 | ||||
| -rw-r--r-- | src/audio_core/effect_context.cpp | 271 | ||||
| -rw-r--r-- | src/audio_core/effect_context.h | 215 | ||||
| -rw-r--r-- | src/audio_core/info_updater.cpp | 18 | ||||
| -rw-r--r-- | src/audio_core/info_updater.h | 2 | ||||
| -rw-r--r-- | src/audio_core/mix_context.cpp | 35 | ||||
| -rw-r--r-- | src/audio_core/mix_context.h | 11 |
10 files changed, 731 insertions, 35 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index a3ff819e1..56dc892b1 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -27,12 +27,13 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory | |||
| 27 | voice_context(params.voice_count), effect_context(params.effect_count), mix_context(), | 27 | voice_context(params.voice_count), effect_context(params.effect_count), mix_context(), |
| 28 | sink_context(params.sink_count), splitter_context(), | 28 | sink_context(params.sink_count), splitter_context(), |
| 29 | voices(params.voice_count), memory{memory_}, | 29 | voices(params.voice_count), memory{memory_}, |
| 30 | command_generator(worker_params, voice_context, mix_context, splitter_context, memory), | 30 | command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context, |
| 31 | memory), | ||
| 31 | temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) { | 32 | temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) { |
| 32 | behavior_info.SetUserRevision(params.revision); | 33 | behavior_info.SetUserRevision(params.revision); |
| 33 | splitter_context.Initialize(behavior_info, params.splitter_count, | 34 | splitter_context.Initialize(behavior_info, params.splitter_count, |
| 34 | params.num_splitter_send_channels); | 35 | params.num_splitter_send_channels); |
| 35 | mix_context.Initialize(behavior_info, params.submix_count + 1); | 36 | mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count); |
| 36 | audio_out = std::make_unique<AudioCore::AudioOut>(); | 37 | audio_out = std::make_unique<AudioCore::AudioOut>(); |
| 37 | stream = | 38 | stream = |
| 38 | audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, | 39 | 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 | |||
| 106 | } | 107 | } |
| 107 | } | 108 | } |
| 108 | 109 | ||
| 109 | auto mix_result = | 110 | auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, |
| 110 | info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, splitter_context); | 111 | splitter_context, effect_context); |
| 111 | 112 | ||
| 112 | if (mix_result.IsError()) { | 113 | if (mix_result.IsError()) { |
| 113 | LOG_ERROR(Audio, "Failed to update mix parameters"); | 114 | LOG_ERROR(Audio, "Failed to update mix parameters"); |
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index 73608c9ed..84782cde6 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "audio_core/algorithm/interpolate.h" | 5 | #include "audio_core/algorithm/interpolate.h" |
| 6 | #include "audio_core/command_generator.h" | 6 | #include "audio_core/command_generator.h" |
| 7 | #include "audio_core/effect_context.h" | ||
| 7 | #include "audio_core/mix_context.h" | 8 | #include "audio_core/mix_context.h" |
| 8 | #include "audio_core/voice_context.h" | 9 | #include "audio_core/voice_context.h" |
| 9 | #include "core/memory.h" | 10 | #include "core/memory.h" |
| @@ -68,9 +69,10 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) { | |||
| 68 | 69 | ||
| 69 | CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params, | 70 | CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params, |
| 70 | VoiceContext& voice_context, MixContext& mix_context, | 71 | VoiceContext& voice_context, MixContext& mix_context, |
| 71 | SplitterContext& splitter_context, Core::Memory::Memory& memory) | 72 | SplitterContext& splitter_context, EffectContext& effect_context, |
| 73 | Core::Memory::Memory& memory) | ||
| 72 | : worker_params(worker_params), voice_context(voice_context), mix_context(mix_context), | 74 | : worker_params(worker_params), voice_context(voice_context), mix_context(mix_context), |
| 73 | splitter_context(splitter_context), memory(memory), | 75 | splitter_context(splitter_context), effect_context(effect_context), memory(memory), |
| 74 | mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) * | 76 | mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) * |
| 75 | worker_params.sample_count), | 77 | worker_params.sample_count), |
| 76 | sample_buffer(MIX_BUFFER_SIZE), | 78 | sample_buffer(MIX_BUFFER_SIZE), |
| @@ -338,6 +340,120 @@ void CommandGenerator::GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_ | |||
| 338 | } | 340 | } |
| 339 | } | 341 | } |
| 340 | 342 | ||
| 343 | void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) { | ||
| 344 | const std::size_t effect_count = effect_context.GetCount(); | ||
| 345 | const auto buffer_offset = mix_info.GetInParams().buffer_offset; | ||
| 346 | for (std::size_t i = 0; i < effect_count; i++) { | ||
| 347 | const auto index = mix_info.GetEffectOrder(i); | ||
| 348 | if (index == AudioCommon::NO_EFFECT_ORDER) { | ||
| 349 | break; | ||
| 350 | } | ||
| 351 | auto* info = effect_context.GetInfo(index); | ||
| 352 | const auto type = info->GetType(); | ||
| 353 | |||
| 354 | // TODO(ogniK): Finish remaining effects | ||
| 355 | switch (type) { | ||
| 356 | case EffectType::Aux: | ||
| 357 | GenerateAuxCommand(buffer_offset, info, info->IsEnabled()); | ||
| 358 | break; | ||
| 359 | case EffectType::I3dl2Reverb: | ||
| 360 | GenerateI3dl2ReverbEffectCommand(buffer_offset, info, info->IsEnabled()); | ||
| 361 | break; | ||
| 362 | case EffectType::BiquadFilter: | ||
| 363 | GenerateBiquadFilterEffectCommand(buffer_offset, info, info->IsEnabled()); | ||
| 364 | break; | ||
| 365 | default: | ||
| 366 | break; | ||
| 367 | } | ||
| 368 | |||
| 369 | info->UpdateForCommandGeneration(); | ||
| 370 | } | ||
| 371 | } | ||
| 372 | |||
| 373 | void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, | ||
| 374 | bool enabled) { | ||
| 375 | if (!enabled) { | ||
| 376 | return; | ||
| 377 | } | ||
| 378 | const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams(); | ||
| 379 | const auto channel_count = params.channel_count; | ||
| 380 | for (s32 i = 0; i < channel_count; i++) { | ||
| 381 | // TODO(ogniK): Actually implement reverb | ||
| 382 | if (params.input[i] != params.output[i]) { | ||
| 383 | const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]); | ||
| 384 | auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); | ||
| 385 | ApplyMix<1>(output, input, 32768, worker_params.sample_count); | ||
| 386 | } | ||
| 387 | } | ||
| 388 | } | ||
| 389 | |||
| 390 | void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, | ||
| 391 | bool enabled) { | ||
| 392 | if (!enabled) { | ||
| 393 | return; | ||
| 394 | } | ||
| 395 | const auto& params = dynamic_cast<EffectBiquadFilter*>(info)->GetParams(); | ||
| 396 | const auto channel_count = params.channel_count; | ||
| 397 | for (s32 i = 0; i < channel_count; i++) { | ||
| 398 | // TODO(ogniK): Actually implement biquad filter | ||
| 399 | if (params.input[i] != params.output[i]) { | ||
| 400 | const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]); | ||
| 401 | auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); | ||
| 402 | ApplyMix<1>(output, input, 32768, worker_params.sample_count); | ||
| 403 | } | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) { | ||
| 408 | auto aux = dynamic_cast<EffectAuxInfo*>(info); | ||
| 409 | const auto& params = aux->GetParams(); | ||
| 410 | if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) { | ||
| 411 | const auto max_channels = params.count; | ||
| 412 | u32 offset{}; | ||
| 413 | for (u32 channel = 0; channel < max_channels; channel++) { | ||
| 414 | u32 write_count = 0; | ||
| 415 | if (channel == (max_channels - 1)) { | ||
| 416 | write_count = offset + worker_params.sample_count; | ||
| 417 | } | ||
| 418 | |||
| 419 | const auto input_index = params.input_mix_buffers[channel] + mix_buffer_offset; | ||
| 420 | const auto output_index = params.output_mix_buffers[channel] + mix_buffer_offset; | ||
| 421 | |||
| 422 | if (enabled) { | ||
| 423 | AuxInfoDSP send_info{}; | ||
| 424 | AuxInfoDSP recv_info{}; | ||
| 425 | memory.ReadBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP)); | ||
| 426 | memory.ReadBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP)); | ||
| 427 | |||
| 428 | WriteAuxBuffer(send_info, aux->GetSendBuffer(), params.sample_count, | ||
| 429 | GetMixBuffer(input_index), worker_params.sample_count, offset, | ||
| 430 | write_count); | ||
| 431 | memory.WriteBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP)); | ||
| 432 | |||
| 433 | const auto samples_read = ReadAuxBuffer( | ||
| 434 | recv_info, aux->GetRecvBuffer(), params.sample_count, | ||
| 435 | GetMixBuffer(output_index), worker_params.sample_count, offset, write_count); | ||
| 436 | memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP)); | ||
| 437 | |||
| 438 | if (samples_read != worker_params.sample_count && | ||
| 439 | samples_read <= params.sample_count) { | ||
| 440 | std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read); | ||
| 441 | } | ||
| 442 | } else { | ||
| 443 | AuxInfoDSP empty{}; | ||
| 444 | memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP)); | ||
| 445 | memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP)); | ||
| 446 | if (output_index != input_index) { | ||
| 447 | std::memcpy(GetMixBuffer(output_index), GetMixBuffer(input_index), | ||
| 448 | worker_params.sample_count * sizeof(s32)); | ||
| 449 | } | ||
| 450 | } | ||
| 451 | |||
| 452 | offset += worker_params.sample_count; | ||
| 453 | } | ||
| 454 | } | ||
| 455 | } | ||
| 456 | |||
| 341 | ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) { | 457 | ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) { |
| 342 | if (splitter_id == AudioCommon::NO_SPLITTER) { | 458 | if (splitter_id == AudioCommon::NO_SPLITTER) { |
| 343 | return nullptr; | 459 | return nullptr; |
| @@ -345,6 +461,66 @@ ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter | |||
| 345 | return splitter_context.GetDestinationData(splitter_id, index); | 461 | return splitter_context.GetDestinationData(splitter_id, index); |
| 346 | } | 462 | } |
| 347 | 463 | ||
| 464 | s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, | ||
| 465 | const s32* data, u32 sample_count, u32 write_offset, | ||
| 466 | u32 write_count) { | ||
| 467 | if (max_samples == 0) { | ||
| 468 | return 0; | ||
| 469 | } | ||
| 470 | u32 offset = dsp_info.write_offset + write_offset; | ||
| 471 | if (send_buffer == 0 || offset > max_samples) { | ||
| 472 | return 0; | ||
| 473 | } | ||
| 474 | |||
| 475 | std::size_t data_offset{}; | ||
| 476 | u32 remaining = sample_count; | ||
| 477 | while (remaining > 0) { | ||
| 478 | // Get position in buffer | ||
| 479 | const auto base = send_buffer + (offset * sizeof(u32)); | ||
| 480 | const auto samples_to_grab = std::min(max_samples - offset, remaining); | ||
| 481 | // Write to output | ||
| 482 | memory.WriteBlock(base, (data + data_offset), samples_to_grab * sizeof(u32)); | ||
| 483 | offset = (offset + samples_to_grab) % max_samples; | ||
| 484 | remaining -= samples_to_grab; | ||
| 485 | data_offset += samples_to_grab; | ||
| 486 | } | ||
| 487 | |||
| 488 | if (write_count != 0) { | ||
| 489 | dsp_info.write_offset = (dsp_info.write_offset + write_count) % max_samples; | ||
| 490 | } | ||
| 491 | return sample_count; | ||
| 492 | } | ||
| 493 | |||
| 494 | s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, | ||
| 495 | s32* out_data, u32 sample_count, u32 read_offset, | ||
| 496 | u32 read_count) { | ||
| 497 | if (max_samples == 0) { | ||
| 498 | return 0; | ||
| 499 | } | ||
| 500 | |||
| 501 | u32 offset = recv_info.read_offset + read_offset; | ||
| 502 | if (recv_buffer == 0 || offset > max_samples) { | ||
| 503 | return 0; | ||
| 504 | } | ||
| 505 | |||
| 506 | u32 remaining = sample_count; | ||
| 507 | while (remaining > 0) { | ||
| 508 | const auto base = recv_buffer + (offset * sizeof(u32)); | ||
| 509 | const auto samples_to_grab = std::min(max_samples - offset, remaining); | ||
| 510 | std::vector<s32> buffer(samples_to_grab); | ||
| 511 | memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32)); | ||
| 512 | std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32)); | ||
| 513 | out_data += samples_to_grab; | ||
| 514 | offset = (offset + samples_to_grab) % max_samples; | ||
| 515 | remaining -= samples_to_grab; | ||
| 516 | } | ||
| 517 | |||
| 518 | if (read_count != 0) { | ||
| 519 | recv_info.read_offset = (recv_info.read_offset + read_count) % max_samples; | ||
| 520 | } | ||
| 521 | return sample_count; | ||
| 522 | } | ||
| 523 | |||
| 348 | void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume, | 524 | void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume, |
| 349 | s32 channel, s32 node_id) { | 525 | s32 channel, s32 node_id) { |
| 350 | const auto last = static_cast<s32>(last_volume * 32768.0f); | 526 | const auto last = static_cast<s32>(last_volume * 32768.0f); |
| @@ -398,7 +574,9 @@ void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) { | |||
| 398 | auto& in_params = mix_info.GetInParams(); | 574 | auto& in_params = mix_info.GetInParams(); |
| 399 | GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, | 575 | GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, |
| 400 | in_params.sample_rate); | 576 | in_params.sample_rate); |
| 401 | // TODO(ogniK): Effects | 577 | |
| 578 | GenerateEffectCommand(mix_info); | ||
| 579 | |||
| 402 | GenerateMixCommands(mix_info); | 580 | GenerateMixCommands(mix_info); |
| 403 | } | 581 | } |
| 404 | 582 | ||
| @@ -476,7 +654,8 @@ void CommandGenerator::GenerateFinalMixCommand() { | |||
| 476 | 654 | ||
| 477 | GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, | 655 | GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset, |
| 478 | in_params.sample_rate); | 656 | in_params.sample_rate); |
| 479 | // TODO(ogniK): Effects | 657 | |
| 658 | GenerateEffectCommand(mix_info); | ||
| 480 | 659 | ||
| 481 | for (s32 i = 0; i < in_params.buffer_count; i++) { | 660 | for (s32 i = 0; i < in_params.buffer_count; i++) { |
| 482 | const s32 gain = static_cast<s32>(in_params.volume * 32768.0f); | 661 | const s32 gain = static_cast<s32>(in_params.volume * 32768.0f); |
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h index 656ad8143..967d24078 100644 --- a/src/audio_core/command_generator.h +++ b/src/audio_core/command_generator.h | |||
| @@ -19,14 +19,17 @@ class MixContext; | |||
| 19 | class SplitterContext; | 19 | class SplitterContext; |
| 20 | class ServerSplitterDestinationData; | 20 | class ServerSplitterDestinationData; |
| 21 | class ServerMixInfo; | 21 | class ServerMixInfo; |
| 22 | 22 | class EffectContext; | |
| 23 | class EffectBase; | ||
| 24 | struct AuxInfoDSP; | ||
| 23 | using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>; | 25 | using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>; |
| 24 | 26 | ||
| 25 | class CommandGenerator { | 27 | class CommandGenerator { |
| 26 | public: | 28 | public: |
| 27 | explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params, | 29 | explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params, |
| 28 | VoiceContext& voice_context, MixContext& mix_context, | 30 | VoiceContext& voice_context, MixContext& mix_context, |
| 29 | SplitterContext& splitter_context, Core::Memory::Memory& memory); | 31 | SplitterContext& splitter_context, EffectContext& effect_context, |
| 32 | Core::Memory::Memory& memory); | ||
| 30 | ~CommandGenerator(); | 33 | ~CommandGenerator(); |
| 31 | 34 | ||
| 32 | void ClearMixBuffers(); | 35 | void ClearMixBuffers(); |
| @@ -67,8 +70,17 @@ private: | |||
| 67 | std::size_t mix_buffer_offset); | 70 | std::size_t mix_buffer_offset); |
| 68 | void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count, | 71 | void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count, |
| 69 | std::size_t mix_buffer_offset, s32 sample_rate); | 72 | std::size_t mix_buffer_offset, s32 sample_rate); |
| 73 | void GenerateEffectCommand(ServerMixInfo& mix_info); | ||
| 74 | void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); | ||
| 75 | void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); | ||
| 76 | void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); | ||
| 70 | ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index); | 77 | ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index); |
| 71 | 78 | ||
| 79 | s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data, | ||
| 80 | u32 sample_count, u32 write_offset, u32 write_count); | ||
| 81 | s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data, | ||
| 82 | u32 sample_count, u32 read_offset, u32 read_count); | ||
| 83 | |||
| 72 | // DSP Code | 84 | // DSP Code |
| 73 | s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, | 85 | s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, |
| 74 | s32 channel, std::size_t mix_offset); | 86 | s32 channel, std::size_t mix_offset); |
| @@ -81,6 +93,7 @@ private: | |||
| 81 | VoiceContext& voice_context; | 93 | VoiceContext& voice_context; |
| 82 | MixContext& mix_context; | 94 | MixContext& mix_context; |
| 83 | SplitterContext& splitter_context; | 95 | SplitterContext& splitter_context; |
| 96 | EffectContext& effect_context; | ||
| 84 | Core::Memory::Memory& memory; | 97 | Core::Memory::Memory& memory; |
| 85 | std::vector<s32> mix_buffer{}; | 98 | std::vector<s32> mix_buffer{}; |
| 86 | std::vector<s32> sample_buffer{}; | 99 | std::vector<s32> sample_buffer{}; |
diff --git a/src/audio_core/common.h b/src/audio_core/common.h index 0731d3eb3..72ebce221 100644 --- a/src/audio_core/common.h +++ b/src/audio_core/common.h | |||
| @@ -26,6 +26,7 @@ constexpr s32 NO_SPLITTER = -1; | |||
| 26 | constexpr s32 NO_MIX = 0x7fffffff; | 26 | constexpr s32 NO_MIX = 0x7fffffff; |
| 27 | constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min(); | 27 | constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min(); |
| 28 | constexpr s32 FINAL_MIX = 0; | 28 | constexpr s32 FINAL_MIX = 0; |
| 29 | constexpr s32 NO_EFFECT_ORDER = -1; | ||
| 29 | constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this constant | 30 | constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this constant |
| 30 | // Any size checks seem to take the sample history into account | 31 | // Any size checks seem to take the sample history into account |
| 31 | // and our const ends up being 0x3f04, the 4 bytes are most | 32 | // and our const ends up being 0x3f04, the 4 bytes are most |
diff --git a/src/audio_core/effect_context.cpp b/src/audio_core/effect_context.cpp index 2497d2f32..adfec3df5 100644 --- a/src/audio_core/effect_context.cpp +++ b/src/audio_core/effect_context.cpp | |||
| @@ -6,6 +6,12 @@ | |||
| 6 | #include "audio_core/effect_context.h" | 6 | #include "audio_core/effect_context.h" |
| 7 | 7 | ||
| 8 | namespace AudioCore { | 8 | namespace AudioCore { |
| 9 | namespace { | ||
| 10 | bool ValidChannelCountForEffect(s32 channel_count) { | ||
| 11 | return channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6; | ||
| 12 | } | ||
| 13 | } // namespace | ||
| 14 | |||
| 9 | EffectContext::EffectContext(std::size_t effect_count) : effect_count(effect_count) { | 15 | EffectContext::EffectContext(std::size_t effect_count) : effect_count(effect_count) { |
| 10 | effects.reserve(effect_count); | 16 | effects.reserve(effect_count); |
| 11 | std::generate_n(std::back_inserter(effects), effect_count, | 17 | std::generate_n(std::back_inserter(effects), effect_count, |
| @@ -21,24 +27,273 @@ EffectBase* EffectContext::GetInfo(std::size_t i) { | |||
| 21 | return effects.at(i).get(); | 27 | return effects.at(i).get(); |
| 22 | } | 28 | } |
| 23 | 29 | ||
| 30 | EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) { | ||
| 31 | switch (effect) { | ||
| 32 | case EffectType::Invalid: | ||
| 33 | effects[i] = std::make_unique<EffectStubbed>(); | ||
| 34 | break; | ||
| 35 | case EffectType::BufferMixer: | ||
| 36 | effects[i] = std::make_unique<EffectBufferMixer>(); | ||
| 37 | break; | ||
| 38 | case EffectType::Aux: | ||
| 39 | effects[i] = std::make_unique<EffectAuxInfo>(); | ||
| 40 | break; | ||
| 41 | case EffectType::Delay: | ||
| 42 | effects[i] = std::make_unique<EffectDelay>(); | ||
| 43 | break; | ||
| 44 | case EffectType::Reverb: | ||
| 45 | effects[i] = std::make_unique<EffectReverb>(); | ||
| 46 | break; | ||
| 47 | case EffectType::I3dl2Reverb: | ||
| 48 | effects[i] = std::make_unique<EffectI3dl2Reverb>(); | ||
| 49 | break; | ||
| 50 | case EffectType::BiquadFilter: | ||
| 51 | effects[i] = std::make_unique<EffectBiquadFilter>(); | ||
| 52 | break; | ||
| 53 | default: | ||
| 54 | UNREACHABLE_MSG("Unimplemented effect {}", effect); | ||
| 55 | effects[i] = std::make_unique<EffectStubbed>(); | ||
| 56 | } | ||
| 57 | return GetInfo(i); | ||
| 58 | } | ||
| 59 | |||
| 24 | const EffectBase* EffectContext::GetInfo(std::size_t i) const { | 60 | const EffectBase* EffectContext::GetInfo(std::size_t i) const { |
| 25 | return effects.at(i).get(); | 61 | return effects.at(i).get(); |
| 26 | } | 62 | } |
| 27 | 63 | ||
| 28 | EffectStubbed::EffectStubbed() : EffectBase::EffectBase() {} | 64 | EffectStubbed::EffectStubbed() : EffectBase::EffectBase(EffectType::Invalid) {} |
| 29 | EffectStubbed::~EffectStubbed() = default; | 65 | EffectStubbed::~EffectStubbed() = default; |
| 30 | 66 | ||
| 31 | void EffectStubbed::Update(EffectInfo::InParams& in_params) { | 67 | void EffectStubbed::Update(EffectInfo::InParams& in_params) {} |
| 32 | if (in_params.is_new) { | 68 | void EffectStubbed::UpdateForCommandGeneration() {} |
| 33 | usage = UsageStatus::New; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | 69 | ||
| 37 | EffectBase::EffectBase() = default; | 70 | EffectBase::EffectBase(EffectType effect_type) : effect_type(effect_type) {} |
| 38 | EffectBase::~EffectBase() = default; | 71 | EffectBase::~EffectBase() = default; |
| 39 | 72 | ||
| 40 | UsageStatus EffectBase::GetUsage() const { | 73 | UsageState EffectBase::GetUsage() const { |
| 41 | return usage; | 74 | return usage; |
| 42 | } | 75 | } |
| 43 | 76 | ||
| 77 | EffectType EffectBase::GetType() const { | ||
| 78 | return effect_type; | ||
| 79 | } | ||
| 80 | |||
| 81 | bool EffectBase::IsEnabled() const { | ||
| 82 | return enabled; | ||
| 83 | } | ||
| 84 | |||
| 85 | s32 EffectBase::GetMixID() const { | ||
| 86 | return mix_id; | ||
| 87 | } | ||
| 88 | |||
| 89 | s32 EffectBase::GetProcessingOrder() const { | ||
| 90 | return processing_order; | ||
| 91 | } | ||
| 92 | |||
| 93 | EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric::EffectGeneric(EffectType::I3dl2Reverb) {} | ||
| 94 | EffectI3dl2Reverb::~EffectI3dl2Reverb() = default; | ||
| 95 | |||
| 96 | void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) { | ||
| 97 | auto& internal_params = GetParams(); | ||
| 98 | const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data()); | ||
| 99 | if (!ValidChannelCountForEffect(reverb_params->max_channels)) { | ||
| 100 | UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels); | ||
| 101 | return; | ||
| 102 | } | ||
| 103 | |||
| 104 | const auto last_status = internal_params.status; | ||
| 105 | mix_id = in_params.mix_id; | ||
| 106 | processing_order = in_params.processing_order; | ||
| 107 | internal_params = *reverb_params; | ||
| 108 | if (!ValidChannelCountForEffect(reverb_params->channel_count)) { | ||
| 109 | internal_params.channel_count = internal_params.max_channels; | ||
| 110 | } | ||
| 111 | enabled = in_params.is_enabled; | ||
| 112 | if (last_status != ParameterStatus::Updated) { | ||
| 113 | internal_params.status = last_status; | ||
| 114 | } | ||
| 115 | |||
| 116 | if (in_params.is_new || skipped) { | ||
| 117 | usage = UsageState::Initialized; | ||
| 118 | internal_params.status = ParameterStatus::Initialized; | ||
| 119 | skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | void EffectI3dl2Reverb::UpdateForCommandGeneration() { | ||
| 124 | if (enabled) { | ||
| 125 | usage = UsageState::Running; | ||
| 126 | } else { | ||
| 127 | usage = UsageState::Stopped; | ||
| 128 | } | ||
| 129 | GetParams().status = ParameterStatus::Updated; | ||
| 130 | } | ||
| 131 | |||
| 132 | EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric::EffectGeneric(EffectType::BiquadFilter) {} | ||
| 133 | EffectBiquadFilter::~EffectBiquadFilter() = default; | ||
| 134 | |||
| 135 | void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) { | ||
| 136 | auto& internal_params = GetParams(); | ||
| 137 | const auto* biquad_params = reinterpret_cast<BiquadFilterParams*>(in_params.raw.data()); | ||
| 138 | mix_id = in_params.mix_id; | ||
| 139 | processing_order = in_params.processing_order; | ||
| 140 | internal_params = *biquad_params; | ||
| 141 | enabled = in_params.is_enabled; | ||
| 142 | } | ||
| 143 | |||
| 144 | void EffectBiquadFilter::UpdateForCommandGeneration() { | ||
| 145 | if (enabled) { | ||
| 146 | usage = UsageState::Running; | ||
| 147 | } else { | ||
| 148 | usage = UsageState::Stopped; | ||
| 149 | } | ||
| 150 | GetParams().status = ParameterStatus::Updated; | ||
| 151 | } | ||
| 152 | |||
| 153 | EffectAuxInfo::EffectAuxInfo() : EffectGeneric::EffectGeneric(EffectType::Aux) {} | ||
| 154 | EffectAuxInfo::~EffectAuxInfo() = default; | ||
| 155 | |||
| 156 | void EffectAuxInfo::Update(EffectInfo::InParams& in_params) { | ||
| 157 | const auto* aux_params = reinterpret_cast<AuxInfo*>(in_params.raw.data()); | ||
| 158 | mix_id = in_params.mix_id; | ||
| 159 | processing_order = in_params.processing_order; | ||
| 160 | GetParams() = *aux_params; | ||
| 161 | enabled = in_params.is_enabled; | ||
| 162 | |||
| 163 | if (in_params.is_new || skipped) { | ||
| 164 | skipped = aux_params->send_buffer_info == 0 || aux_params->return_buffer_info == 0; | ||
| 165 | if (skipped) { | ||
| 166 | return; | ||
| 167 | } | ||
| 168 | |||
| 169 | // There's two AuxInfos which are an identical size, the first one is managed by the cpu, | ||
| 170 | // the second is managed by the dsp. All we care about is managing the DSP one | ||
| 171 | send_info = aux_params->send_buffer_info + sizeof(AuxInfoDSP); | ||
| 172 | send_buffer = aux_params->send_buffer_info + (sizeof(AuxInfoDSP) * 2); | ||
| 173 | |||
| 174 | recv_info = aux_params->return_buffer_info + sizeof(AuxInfoDSP); | ||
| 175 | recv_buffer = aux_params->return_buffer_info + (sizeof(AuxInfoDSP) * 2); | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | void EffectAuxInfo::UpdateForCommandGeneration() { | ||
| 180 | if (enabled) { | ||
| 181 | usage = UsageState::Running; | ||
| 182 | } else { | ||
| 183 | usage = UsageState::Stopped; | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | const VAddr EffectAuxInfo::GetSendInfo() const { | ||
| 188 | return send_info; | ||
| 189 | } | ||
| 190 | |||
| 191 | const VAddr EffectAuxInfo::GetSendBuffer() const { | ||
| 192 | return send_buffer; | ||
| 193 | } | ||
| 194 | |||
| 195 | const VAddr EffectAuxInfo::GetRecvInfo() const { | ||
| 196 | return recv_info; | ||
| 197 | } | ||
| 198 | |||
| 199 | const VAddr EffectAuxInfo::GetRecvBuffer() const { | ||
| 200 | return recv_buffer; | ||
| 201 | } | ||
| 202 | |||
| 203 | EffectDelay::EffectDelay() : EffectGeneric::EffectGeneric(EffectType::Delay) {} | ||
| 204 | EffectDelay::~EffectDelay() = default; | ||
| 205 | |||
| 206 | void EffectDelay::Update(EffectInfo::InParams& in_params) { | ||
| 207 | const auto* delay_params = reinterpret_cast<DelayParams*>(in_params.raw.data()); | ||
| 208 | auto& internal_params = GetParams(); | ||
| 209 | if (!ValidChannelCountForEffect(delay_params->max_channels)) { | ||
| 210 | return; | ||
| 211 | } | ||
| 212 | |||
| 213 | const auto last_status = internal_params.status; | ||
| 214 | mix_id = in_params.mix_id; | ||
| 215 | processing_order = in_params.processing_order; | ||
| 216 | internal_params = *delay_params; | ||
| 217 | if (!ValidChannelCountForEffect(delay_params->channels)) { | ||
| 218 | internal_params.channels = internal_params.max_channels; | ||
| 219 | } | ||
| 220 | enabled = in_params.is_enabled; | ||
| 221 | |||
| 222 | if (last_status != ParameterStatus::Updated) { | ||
| 223 | internal_params.status = last_status; | ||
| 224 | } | ||
| 225 | |||
| 226 | if (in_params.is_new || skipped) { | ||
| 227 | usage = UsageState::Initialized; | ||
| 228 | internal_params.status = ParameterStatus::Initialized; | ||
| 229 | skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | |||
| 233 | void EffectDelay::UpdateForCommandGeneration() { | ||
| 234 | if (enabled) { | ||
| 235 | usage = UsageState::Running; | ||
| 236 | } else { | ||
| 237 | usage = UsageState::Stopped; | ||
| 238 | } | ||
| 239 | GetParams().status = ParameterStatus::Updated; | ||
| 240 | } | ||
| 241 | |||
| 242 | EffectBufferMixer::EffectBufferMixer() : EffectGeneric::EffectGeneric(EffectType::BufferMixer) {} | ||
| 243 | EffectBufferMixer::~EffectBufferMixer() = default; | ||
| 244 | |||
| 245 | void EffectBufferMixer::Update(EffectInfo::InParams& in_params) { | ||
| 246 | mix_id = in_params.mix_id; | ||
| 247 | processing_order = in_params.processing_order; | ||
| 248 | GetParams() = *reinterpret_cast<BufferMixerParams*>(in_params.raw.data()); | ||
| 249 | enabled = in_params.is_enabled; | ||
| 250 | } | ||
| 251 | |||
| 252 | void EffectBufferMixer::UpdateForCommandGeneration() { | ||
| 253 | if (enabled) { | ||
| 254 | usage = UsageState::Running; | ||
| 255 | } else { | ||
| 256 | usage = UsageState::Stopped; | ||
| 257 | } | ||
| 258 | } | ||
| 259 | |||
| 260 | EffectReverb::EffectReverb() : EffectGeneric::EffectGeneric(EffectType::Reverb) {} | ||
| 261 | EffectReverb::~EffectReverb() = default; | ||
| 262 | |||
| 263 | void EffectReverb::Update(EffectInfo::InParams& in_params) { | ||
| 264 | const auto* reverb_params = reinterpret_cast<ReverbParams*>(in_params.raw.data()); | ||
| 265 | auto& internal_params = GetParams(); | ||
| 266 | if (!ValidChannelCountForEffect(reverb_params->max_channels)) { | ||
| 267 | return; | ||
| 268 | } | ||
| 269 | |||
| 270 | const auto last_status = internal_params.status; | ||
| 271 | mix_id = in_params.mix_id; | ||
| 272 | processing_order = in_params.processing_order; | ||
| 273 | internal_params = *reverb_params; | ||
| 274 | if (!ValidChannelCountForEffect(reverb_params->channels)) { | ||
| 275 | internal_params.channels = internal_params.max_channels; | ||
| 276 | } | ||
| 277 | enabled = in_params.is_enabled; | ||
| 278 | |||
| 279 | if (last_status != ParameterStatus::Updated) { | ||
| 280 | internal_params.status = last_status; | ||
| 281 | } | ||
| 282 | |||
| 283 | if (in_params.is_new || skipped) { | ||
| 284 | usage = UsageState::Initialized; | ||
| 285 | internal_params.status = ParameterStatus::Initialized; | ||
| 286 | skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0; | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | void EffectReverb::UpdateForCommandGeneration() { | ||
| 291 | if (enabled) { | ||
| 292 | usage = UsageState::Running; | ||
| 293 | } else { | ||
| 294 | usage = UsageState::Stopped; | ||
| 295 | } | ||
| 296 | GetParams().status = ParameterStatus::Updated; | ||
| 297 | } | ||
| 298 | |||
| 44 | } // namespace AudioCore | 299 | } // namespace AudioCore |
diff --git a/src/audio_core/effect_context.h b/src/audio_core/effect_context.h index e3c367296..2f2da72dd 100644 --- a/src/audio_core/effect_context.h +++ b/src/audio_core/effect_context.h | |||
| @@ -31,6 +31,19 @@ enum class UsageStatus : u8 { | |||
| 31 | Removed = 4, | 31 | Removed = 4, |
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | enum class UsageState { | ||
| 35 | Invalid = 0, | ||
| 36 | Initialized = 1, | ||
| 37 | Running = 2, | ||
| 38 | Stopped = 3, | ||
| 39 | }; | ||
| 40 | |||
| 41 | enum class ParameterStatus : u8 { | ||
| 42 | Initialized = 0, | ||
| 43 | Updating = 1, | ||
| 44 | Updated = 2, | ||
| 45 | }; | ||
| 46 | |||
| 34 | struct BufferMixerParams { | 47 | struct BufferMixerParams { |
| 35 | std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input{}; | 48 | std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input{}; |
| 36 | std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output{}; | 49 | std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output{}; |
| @@ -39,6 +52,14 @@ struct BufferMixerParams { | |||
| 39 | }; | 52 | }; |
| 40 | static_assert(sizeof(BufferMixerParams) == 0x94, "BufferMixerParams is an invalid size"); | 53 | static_assert(sizeof(BufferMixerParams) == 0x94, "BufferMixerParams is an invalid size"); |
| 41 | 54 | ||
| 55 | struct AuxInfoDSP { | ||
| 56 | u32_le read_offset{}; | ||
| 57 | u32_le write_offset{}; | ||
| 58 | u32_le remaining{}; | ||
| 59 | INSERT_PADDING_WORDS(13); | ||
| 60 | }; | ||
| 61 | static_assert(sizeof(AuxInfoDSP) == 0x40, "AuxInfoDSP is an invalid size"); | ||
| 62 | |||
| 42 | struct AuxInfo { | 63 | struct AuxInfo { |
| 43 | std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input_mix_buffers{}; | 64 | std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input_mix_buffers{}; |
| 44 | std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output_mix_buffers{}; | 65 | std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output_mix_buffers{}; |
| @@ -54,6 +75,81 @@ struct AuxInfo { | |||
| 54 | }; | 75 | }; |
| 55 | static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size"); | 76 | static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size"); |
| 56 | 77 | ||
| 78 | struct I3dl2ReverbParams { | ||
| 79 | std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{}; | ||
| 80 | std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{}; | ||
| 81 | u16_le max_channels{}; | ||
| 82 | u16_le channel_count{}; | ||
| 83 | INSERT_PADDING_BYTES(1); | ||
| 84 | u32_le sample_rate{}; | ||
| 85 | f32 room_hf{}; | ||
| 86 | f32 hf_reference{}; | ||
| 87 | f32 decay_time{}; | ||
| 88 | f32 hf_decay_ratio{}; | ||
| 89 | f32 room{}; | ||
| 90 | f32 reflection{}; | ||
| 91 | f32 reverb{}; | ||
| 92 | f32 diffusion{}; | ||
| 93 | f32 reflection_delay{}; | ||
| 94 | f32 reverb_delay{}; | ||
| 95 | f32 density{}; | ||
| 96 | f32 dry_gain{}; | ||
| 97 | ParameterStatus status{}; | ||
| 98 | INSERT_PADDING_BYTES(3); | ||
| 99 | }; | ||
| 100 | static_assert(sizeof(I3dl2ReverbParams) == 0x4c, "I3dl2ReverbParams is an invalid size"); | ||
| 101 | |||
| 102 | struct BiquadFilterParams { | ||
| 103 | std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{}; | ||
| 104 | std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{}; | ||
| 105 | std::array<s16_le, 3> numerator; | ||
| 106 | std::array<s16_le, 2> denominator; | ||
| 107 | s8 channel_count{}; | ||
| 108 | ParameterStatus status{}; | ||
| 109 | }; | ||
| 110 | static_assert(sizeof(BiquadFilterParams) == 0x18, "BiquadFilterParams is an invalid size"); | ||
| 111 | |||
| 112 | struct DelayParams { | ||
| 113 | std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{}; | ||
| 114 | std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{}; | ||
| 115 | u16_le max_channels{}; | ||
| 116 | u16_le channels{}; | ||
| 117 | s32_le max_delay{}; | ||
| 118 | s32_le delay{}; | ||
| 119 | s32_le sample_rate{}; | ||
| 120 | s32_le gain{}; | ||
| 121 | s32_le feedback_gain{}; | ||
| 122 | s32_le out_gain{}; | ||
| 123 | s32_le dry_gain{}; | ||
| 124 | s32_le channel_spread{}; | ||
| 125 | s32_le low_pass{}; | ||
| 126 | ParameterStatus status{}; | ||
| 127 | INSERT_PADDING_BYTES(3); | ||
| 128 | }; | ||
| 129 | static_assert(sizeof(DelayParams) == 0x38, "DelayParams is an invalid size"); | ||
| 130 | |||
| 131 | struct ReverbParams { | ||
| 132 | std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{}; | ||
| 133 | std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{}; | ||
| 134 | u16_le max_channels{}; | ||
| 135 | u16_le channels{}; | ||
| 136 | s32_le sample_rate{}; | ||
| 137 | s32_le mode0{}; | ||
| 138 | s32_le mode0_gain{}; | ||
| 139 | s32_le pre_delay{}; | ||
| 140 | s32_le mode1{}; | ||
| 141 | s32_le mode1_gain{}; | ||
| 142 | s32_le decay{}; | ||
| 143 | s32_le hf_decay_ratio{}; | ||
| 144 | s32_le coloration{}; | ||
| 145 | s32_le reverb_gain{}; | ||
| 146 | s32_le out_gain{}; | ||
| 147 | s32_le dry_gain{}; | ||
| 148 | ParameterStatus status{}; | ||
| 149 | INSERT_PADDING_BYTES(3); | ||
| 150 | }; | ||
| 151 | static_assert(sizeof(ReverbParams) == 0x44, "ReverbParams is an invalid size"); | ||
| 152 | |||
| 57 | class EffectInfo { | 153 | class EffectInfo { |
| 58 | public: | 154 | public: |
| 59 | struct InParams { | 155 | struct InParams { |
| @@ -64,7 +160,7 @@ public: | |||
| 64 | s32_le mix_id{}; | 160 | s32_le mix_id{}; |
| 65 | u64_le buffer_address{}; | 161 | u64_le buffer_address{}; |
| 66 | u64_le buffer_size{}; | 162 | u64_le buffer_size{}; |
| 67 | s32_le priority{}; | 163 | s32_le processing_order{}; |
| 68 | INSERT_PADDING_BYTES(4); | 164 | INSERT_PADDING_BYTES(4); |
| 69 | union { | 165 | union { |
| 70 | std::array<u8, 0xa0> raw; | 166 | std::array<u8, 0xa0> raw; |
| @@ -79,16 +175,50 @@ public: | |||
| 79 | static_assert(sizeof(EffectInfo::OutParams) == 0x10, "OutParams is an invalid size"); | 175 | static_assert(sizeof(EffectInfo::OutParams) == 0x10, "OutParams is an invalid size"); |
| 80 | }; | 176 | }; |
| 81 | 177 | ||
| 178 | struct AuxAddress { | ||
| 179 | VAddr send_dsp_info{}; | ||
| 180 | VAddr send_buffer_base{}; | ||
| 181 | VAddr return_dsp_info{}; | ||
| 182 | VAddr return_buffer_base{}; | ||
| 183 | }; | ||
| 184 | |||
| 82 | class EffectBase { | 185 | class EffectBase { |
| 83 | public: | 186 | public: |
| 84 | EffectBase(); | 187 | EffectBase(EffectType effect_type); |
| 85 | ~EffectBase(); | 188 | ~EffectBase(); |
| 86 | 189 | ||
| 87 | virtual void Update(EffectInfo::InParams& in_params) = 0; | 190 | virtual void Update(EffectInfo::InParams& in_params) = 0; |
| 88 | UsageStatus GetUsage() const; | 191 | virtual void UpdateForCommandGeneration() = 0; |
| 192 | UsageState GetUsage() const; | ||
| 193 | EffectType GetType() const; | ||
| 194 | bool IsEnabled() const; | ||
| 195 | s32 GetMixID() const; | ||
| 196 | s32 GetProcessingOrder() const; | ||
| 89 | 197 | ||
| 90 | protected: | 198 | protected: |
| 91 | UsageStatus usage{UsageStatus::Invalid}; | 199 | UsageState usage{UsageState::Invalid}; |
| 200 | EffectType effect_type{}; | ||
| 201 | s32 mix_id{}; | ||
| 202 | s32 processing_order{}; | ||
| 203 | bool enabled = false; | ||
| 204 | }; | ||
| 205 | |||
| 206 | template <typename T> | ||
| 207 | class EffectGeneric : public EffectBase { | ||
| 208 | public: | ||
| 209 | EffectGeneric(EffectType effect_type) : EffectBase::EffectBase(effect_type) {} | ||
| 210 | ~EffectGeneric() = default; | ||
| 211 | |||
| 212 | T& GetParams() { | ||
| 213 | return internal_params; | ||
| 214 | } | ||
| 215 | |||
| 216 | const I3dl2ReverbParams& GetParams() const { | ||
| 217 | return internal_params; | ||
| 218 | } | ||
| 219 | |||
| 220 | private: | ||
| 221 | T internal_params{}; | ||
| 92 | }; | 222 | }; |
| 93 | 223 | ||
| 94 | class EffectStubbed : public EffectBase { | 224 | class EffectStubbed : public EffectBase { |
| @@ -97,6 +227,82 @@ public: | |||
| 97 | ~EffectStubbed(); | 227 | ~EffectStubbed(); |
| 98 | 228 | ||
| 99 | void Update(EffectInfo::InParams& in_params) override; | 229 | void Update(EffectInfo::InParams& in_params) override; |
| 230 | void UpdateForCommandGeneration() override; | ||
| 231 | }; | ||
| 232 | |||
| 233 | class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> { | ||
| 234 | public: | ||
| 235 | explicit EffectI3dl2Reverb(); | ||
| 236 | ~EffectI3dl2Reverb(); | ||
| 237 | |||
| 238 | void Update(EffectInfo::InParams& in_params) override; | ||
| 239 | void UpdateForCommandGeneration() override; | ||
| 240 | |||
| 241 | private: | ||
| 242 | bool skipped = false; | ||
| 243 | }; | ||
| 244 | |||
| 245 | class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> { | ||
| 246 | public: | ||
| 247 | explicit EffectBiquadFilter(); | ||
| 248 | ~EffectBiquadFilter(); | ||
| 249 | |||
| 250 | void Update(EffectInfo::InParams& in_params) override; | ||
| 251 | void UpdateForCommandGeneration() override; | ||
| 252 | }; | ||
| 253 | |||
| 254 | class EffectAuxInfo : public EffectGeneric<AuxInfo> { | ||
| 255 | public: | ||
| 256 | explicit EffectAuxInfo(); | ||
| 257 | ~EffectAuxInfo(); | ||
| 258 | |||
| 259 | void Update(EffectInfo::InParams& in_params) override; | ||
| 260 | void UpdateForCommandGeneration() override; | ||
| 261 | const VAddr GetSendInfo() const; | ||
| 262 | const VAddr GetSendBuffer() const; | ||
| 263 | const VAddr GetRecvInfo() const; | ||
| 264 | const VAddr GetRecvBuffer() const; | ||
| 265 | |||
| 266 | private: | ||
| 267 | VAddr send_info{}; | ||
| 268 | VAddr send_buffer{}; | ||
| 269 | VAddr recv_info{}; | ||
| 270 | VAddr recv_buffer{}; | ||
| 271 | bool skipped = false; | ||
| 272 | AuxAddress addresses{}; | ||
| 273 | }; | ||
| 274 | |||
| 275 | class EffectDelay : public EffectGeneric<DelayParams> { | ||
| 276 | public: | ||
| 277 | explicit EffectDelay(); | ||
| 278 | ~EffectDelay(); | ||
| 279 | |||
| 280 | void Update(EffectInfo::InParams& in_params) override; | ||
| 281 | void UpdateForCommandGeneration() override; | ||
| 282 | |||
| 283 | private: | ||
| 284 | bool skipped = false; | ||
| 285 | }; | ||
| 286 | |||
| 287 | class EffectBufferMixer : public EffectGeneric<BufferMixerParams> { | ||
| 288 | public: | ||
| 289 | explicit EffectBufferMixer(); | ||
| 290 | ~EffectBufferMixer(); | ||
| 291 | |||
| 292 | void Update(EffectInfo::InParams& in_params) override; | ||
| 293 | void UpdateForCommandGeneration() override; | ||
| 294 | }; | ||
| 295 | |||
| 296 | class EffectReverb : public EffectGeneric<ReverbParams> { | ||
| 297 | public: | ||
| 298 | explicit EffectReverb(); | ||
| 299 | ~EffectReverb(); | ||
| 300 | |||
| 301 | void Update(EffectInfo::InParams& in_params) override; | ||
| 302 | void UpdateForCommandGeneration() override; | ||
| 303 | |||
| 304 | private: | ||
| 305 | bool skipped = false; | ||
| 100 | }; | 306 | }; |
| 101 | 307 | ||
| 102 | class EffectContext { | 308 | class EffectContext { |
| @@ -106,6 +312,7 @@ public: | |||
| 106 | 312 | ||
| 107 | std::size_t GetCount() const; | 313 | std::size_t GetCount() const; |
| 108 | EffectBase* GetInfo(std::size_t i); | 314 | EffectBase* GetInfo(std::size_t i); |
| 315 | EffectBase* RetargetEffect(std::size_t i, EffectType effect); | ||
| 109 | const EffectBase* GetInfo(std::size_t i) const; | 316 | const EffectBase* GetInfo(std::size_t i) const; |
| 110 | 317 | ||
| 111 | private: | 318 | private: |
diff --git a/src/audio_core/info_updater.cpp b/src/audio_core/info_updater.cpp index 286aa0321..f53ce21a5 100644 --- a/src/audio_core/info_updater.cpp +++ b/src/audio_core/info_updater.cpp | |||
| @@ -244,14 +244,15 @@ bool InfoUpdater::UpdateEffects(EffectContext& effect_context, bool is_active) { | |||
| 244 | // Update effects | 244 | // Update effects |
| 245 | for (std::size_t i = 0; i < effect_count; i++) { | 245 | for (std::size_t i = 0; i < effect_count; i++) { |
| 246 | auto* info = effect_context.GetInfo(i); | 246 | auto* info = effect_context.GetInfo(i); |
| 247 | if (effect_in[i].type != info->GetType()) { | ||
| 248 | info = effect_context.RetargetEffect(i, effect_in[i].type); | ||
| 249 | } | ||
| 250 | |||
| 247 | info->Update(effect_in[i]); | 251 | info->Update(effect_in[i]); |
| 248 | 252 | ||
| 249 | // TODO(ogniK): Update individual effects | 253 | if ((!is_active && info->GetUsage() != UsageState::Initialized) || |
| 250 | if ((!is_active && info->GetUsage() != UsageStatus::New) || | 254 | info->GetUsage() == UsageState::Stopped) { |
| 251 | info->GetUsage() == UsageStatus::Removed) { | ||
| 252 | effect_out[i].status = UsageStatus::Removed; | 255 | effect_out[i].status = UsageStatus::Removed; |
| 253 | } else if (info->GetUsage() == UsageStatus::New) { | ||
| 254 | effect_out[i].status = UsageStatus::New; | ||
| 255 | } else { | 256 | } else { |
| 256 | effect_out[i].status = UsageStatus::Used; | 257 | effect_out[i].status = UsageStatus::Used; |
| 257 | } | 258 | } |
| @@ -290,7 +291,8 @@ bool InfoUpdater::UpdateSplitterInfo(SplitterContext& splitter_context) { | |||
| 290 | } | 291 | } |
| 291 | 292 | ||
| 292 | ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count, | 293 | ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count, |
| 293 | SplitterContext& splitter_context) { | 294 | SplitterContext& splitter_context, |
| 295 | EffectContext& effect_context) { | ||
| 294 | std::vector<MixInfo::InParams> mix_in_params; | 296 | std::vector<MixInfo::InParams> mix_in_params; |
| 295 | 297 | ||
| 296 | if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) { | 298 | if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) { |
| @@ -387,13 +389,13 @@ ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buf | |||
| 387 | auto& mix_info_params = mix_info.GetInParams(); | 389 | auto& mix_info_params = mix_info.GetInParams(); |
| 388 | if (mix_info_params.in_use != mix_in.in_use) { | 390 | if (mix_info_params.in_use != mix_in.in_use) { |
| 389 | mix_info_params.in_use = mix_in.in_use; | 391 | mix_info_params.in_use = mix_in.in_use; |
| 390 | // TODO(ogniK): Update effect processing order | 392 | mix_info.ResetEffectProcessingOrder(); |
| 391 | should_sort = true; | 393 | should_sort = true; |
| 392 | } | 394 | } |
| 393 | 395 | ||
| 394 | if (mix_in.in_use) { | 396 | if (mix_in.in_use) { |
| 395 | should_sort |= mix_info.Update(mix_context.GetEdgeMatrix(), mix_in, behavior_info, | 397 | should_sort |= mix_info.Update(mix_context.GetEdgeMatrix(), mix_in, behavior_info, |
| 396 | splitter_context); | 398 | splitter_context, effect_context); |
| 397 | } | 399 | } |
| 398 | } | 400 | } |
| 399 | 401 | ||
diff --git a/src/audio_core/info_updater.h b/src/audio_core/info_updater.h index 6969de67d..06f9d770f 100644 --- a/src/audio_core/info_updater.h +++ b/src/audio_core/info_updater.h | |||
| @@ -34,7 +34,7 @@ public: | |||
| 34 | bool UpdateEffects(EffectContext& effect_context, bool is_active); | 34 | bool UpdateEffects(EffectContext& effect_context, bool is_active); |
| 35 | bool UpdateSplitterInfo(SplitterContext& splitter_context); | 35 | bool UpdateSplitterInfo(SplitterContext& splitter_context); |
| 36 | ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count, | 36 | ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count, |
| 37 | SplitterContext& splitter_context); | 37 | SplitterContext& splitter_context, EffectContext& effect_context); |
| 38 | bool UpdateSinks(SinkContext& sink_context); | 38 | bool UpdateSinks(SinkContext& sink_context); |
| 39 | bool UpdatePerformanceBuffer(); | 39 | bool UpdatePerformanceBuffer(); |
| 40 | bool UpdateErrorInfo(BehaviorInfo& in_behavior_info); | 40 | bool UpdateErrorInfo(BehaviorInfo& in_behavior_info); |
diff --git a/src/audio_core/mix_context.cpp b/src/audio_core/mix_context.cpp index f6f119a11..042891490 100644 --- a/src/audio_core/mix_context.cpp +++ b/src/audio_core/mix_context.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "audio_core/behavior_info.h" | 5 | #include "audio_core/behavior_info.h" |
| 6 | #include "audio_core/common.h" | 6 | #include "audio_core/common.h" |
| 7 | #include "audio_core/effect_context.h" | ||
| 7 | #include "audio_core/mix_context.h" | 8 | #include "audio_core/mix_context.h" |
| 8 | #include "audio_core/splitter_context.h" | 9 | #include "audio_core/splitter_context.h" |
| 9 | 10 | ||
| @@ -11,7 +12,8 @@ namespace AudioCore { | |||
| 11 | MixContext::MixContext() = default; | 12 | MixContext::MixContext() = default; |
| 12 | MixContext::~MixContext() = default; | 13 | MixContext::~MixContext() = default; |
| 13 | 14 | ||
| 14 | void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count) { | 15 | void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count, |
| 16 | std::size_t effect_count) { | ||
| 15 | info_count = mix_count; | 17 | info_count = mix_count; |
| 16 | infos.resize(info_count); | 18 | infos.resize(info_count); |
| 17 | auto& final_mix = GetInfo(AudioCommon::FINAL_MIX); | 19 | auto& final_mix = GetInfo(AudioCommon::FINAL_MIX); |
| @@ -21,6 +23,10 @@ void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_c | |||
| 21 | sorted_info.push_back(&info); | 23 | sorted_info.push_back(&info); |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 26 | for (auto& info : infos) { | ||
| 27 | info.SetEffectCount(effect_count); | ||
| 28 | } | ||
| 29 | |||
| 24 | // Only initialize our edge matrix and node states if splitters are supported | 30 | // Only initialize our edge matrix and node states if splitters are supported |
| 25 | if (behavior_info.IsSplitterSupported()) { | 31 | if (behavior_info.IsSplitterSupported()) { |
| 26 | node_states.Initialize(mix_count); | 32 | node_states.Initialize(mix_count); |
| @@ -185,7 +191,8 @@ ServerMixInfo::InParams& ServerMixInfo::GetInParams() { | |||
| 185 | } | 191 | } |
| 186 | 192 | ||
| 187 | bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, | 193 | bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, |
| 188 | BehaviorInfo& behavior_info, SplitterContext& splitter_context) { | 194 | BehaviorInfo& behavior_info, SplitterContext& splitter_context, |
| 195 | EffectContext& effect_context) { | ||
| 189 | in_params.volume = mix_in.volume; | 196 | in_params.volume = mix_in.volume; |
| 190 | in_params.sample_rate = mix_in.sample_rate; | 197 | in_params.sample_rate = mix_in.sample_rate; |
| 191 | in_params.buffer_count = mix_in.buffer_count; | 198 | in_params.buffer_count = mix_in.buffer_count; |
| @@ -206,6 +213,15 @@ bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix | |||
| 206 | in_params.splitter_id = AudioCommon::NO_SPLITTER; | 213 | in_params.splitter_id = AudioCommon::NO_SPLITTER; |
| 207 | } | 214 | } |
| 208 | 215 | ||
| 216 | ResetEffectProcessingOrder(); | ||
| 217 | const auto effect_count = effect_context.GetCount(); | ||
| 218 | for (std::size_t i = 0; i < effect_count; i++) { | ||
| 219 | auto* effect_info = effect_context.GetInfo(i); | ||
| 220 | if (effect_info->GetMixID() == in_params.mix_id) { | ||
| 221 | effect_processing_order[effect_info->GetProcessingOrder()] = static_cast<s32>(i); | ||
| 222 | } | ||
| 223 | } | ||
| 224 | |||
| 209 | // TODO(ogniK): Update effect processing order | 225 | // TODO(ogniK): Update effect processing order |
| 210 | return require_sort; | 226 | return require_sort; |
| 211 | } | 227 | } |
| @@ -228,6 +244,21 @@ void ServerMixInfo::Cleanup() { | |||
| 228 | std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size()); | 244 | std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size()); |
| 229 | } | 245 | } |
| 230 | 246 | ||
| 247 | void ServerMixInfo::SetEffectCount(std::size_t count) { | ||
| 248 | effect_processing_order.resize(count); | ||
| 249 | ResetEffectProcessingOrder(); | ||
| 250 | } | ||
| 251 | |||
| 252 | void ServerMixInfo::ResetEffectProcessingOrder() { | ||
| 253 | for (auto& order : effect_processing_order) { | ||
| 254 | order = AudioCommon::NO_EFFECT_ORDER; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | s32 ServerMixInfo::GetEffectOrder(std::size_t i) const { | ||
| 259 | return effect_processing_order.at(i); | ||
| 260 | } | ||
| 261 | |||
| 231 | bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, | 262 | bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, |
| 232 | SplitterContext& splitter_context) { | 263 | SplitterContext& splitter_context) { |
| 233 | // Mixes are identical | 264 | // Mixes are identical |
diff --git a/src/audio_core/mix_context.h b/src/audio_core/mix_context.h index 381566699..6a588eeb4 100644 --- a/src/audio_core/mix_context.h +++ b/src/audio_core/mix_context.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | namespace AudioCore { | 14 | namespace AudioCore { |
| 15 | class BehaviorInfo; | 15 | class BehaviorInfo; |
| 16 | class EffectContext; | ||
| 16 | 17 | ||
| 17 | class MixInfo { | 18 | class MixInfo { |
| 18 | public: | 19 | public: |
| @@ -65,11 +66,16 @@ public: | |||
| 65 | ServerMixInfo::InParams& GetInParams(); | 66 | ServerMixInfo::InParams& GetInParams(); |
| 66 | 67 | ||
| 67 | bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, | 68 | bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, |
| 68 | BehaviorInfo& behavior_info, SplitterContext& splitter_context); | 69 | BehaviorInfo& behavior_info, SplitterContext& splitter_context, |
| 70 | EffectContext& effect_context); | ||
| 69 | bool HasAnyConnection() const; | 71 | bool HasAnyConnection() const; |
| 70 | void Cleanup(); | 72 | void Cleanup(); |
| 73 | void SetEffectCount(std::size_t count); | ||
| 74 | void ResetEffectProcessingOrder(); | ||
| 75 | s32 GetEffectOrder(std::size_t i) const; | ||
| 71 | 76 | ||
| 72 | private: | 77 | private: |
| 78 | std::vector<s32> effect_processing_order; | ||
| 73 | InParams in_params{}; | 79 | InParams in_params{}; |
| 74 | bool UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, | 80 | bool UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in, |
| 75 | SplitterContext& splitter_context); | 81 | SplitterContext& splitter_context); |
| @@ -80,7 +86,8 @@ public: | |||
| 80 | MixContext(); | 86 | MixContext(); |
| 81 | ~MixContext(); | 87 | ~MixContext(); |
| 82 | 88 | ||
| 83 | void Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count); | 89 | void Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count, |
| 90 | std::size_t effect_count); | ||
| 84 | void SortInfo(); | 91 | void SortInfo(); |
| 85 | bool TsortInfo(SplitterContext& splitter_context); | 92 | bool TsortInfo(SplitterContext& splitter_context); |
| 86 | 93 | ||