summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Chloe Marcec2021-02-11 18:46:20 +1100
committerGravatar bunnei2021-02-12 18:48:10 -0800
commit4a7fd91857a95dd9ba7c838384671b2a83e46e7d (patch)
tree77dc77e963c2ce5e386d747b76ba19bec89a92b6
parentMerge pull request #5877 from ameerj/res-limit-usage (diff)
downloadyuzu-4a7fd91857a95dd9ba7c838384671b2a83e46e7d.tar.gz
yuzu-4a7fd91857a95dd9ba7c838384671b2a83e46e7d.tar.xz
yuzu-4a7fd91857a95dd9ba7c838384671b2a83e46e7d.zip
audren: Implement I3dl2Reverb
Most notable fix is the voices in Fire Emblem Three Houses
-rw-r--r--src/audio_core/CMakeLists.txt2
-rw-r--r--src/audio_core/command_generator.cpp355
-rw-r--r--src/audio_core/command_generator.h5
-rw-r--r--src/audio_core/common.h23
-rw-r--r--src/audio_core/delay_line.cpp103
-rw-r--r--src/audio_core/delay_line.h46
-rw-r--r--src/audio_core/effect_context.cpp22
-rw-r--r--src/audio_core/effect_context.h31
8 files changed, 569 insertions, 18 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index d1d177b51..a0ae07752 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -15,6 +15,8 @@ add_library(audio_core STATIC
15 command_generator.cpp 15 command_generator.cpp
16 command_generator.h 16 command_generator.h
17 common.h 17 common.h
18 delay_line.cpp
19 delay_line.h
18 effect_context.cpp 20 effect_context.cpp
19 effect_context.h 21 effect_context.h
20 info_updater.cpp 22 info_updater.cpp
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
index 5b1065520..90d8f90d3 100644
--- a/src/audio_core/command_generator.cpp
+++ b/src/audio_core/command_generator.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <numbers>
5#include "audio_core/algorithm/interpolate.h" 6#include "audio_core/algorithm/interpolate.h"
6#include "audio_core/command_generator.h" 7#include "audio_core/command_generator.h"
7#include "audio_core/effect_context.h" 8#include "audio_core/effect_context.h"
@@ -13,6 +14,20 @@ namespace AudioCore {
13namespace { 14namespace {
14constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00; 15constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
15constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL; 16constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
17using DelayLineTimes = std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>;
18
19constexpr DelayLineTimes FDN_MIN_DELAY_LINE_TIMES{5.0f, 6.0f, 13.0f, 14.0f};
20constexpr DelayLineTimes FDN_MAX_DELAY_LINE_TIMES{45.704f, 82.782f, 149.94f, 271.58f};
21constexpr DelayLineTimes DECAY0_MAX_DELAY_LINE_TIMES{17.0f, 13.0f, 9.0f, 7.0f};
22constexpr DelayLineTimes DECAY1_MAX_DELAY_LINE_TIMES{19.0f, 11.0f, 10.0f, 6.0f};
23constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_TAP_TIMES{
24 0.017136f, 0.059154f, 0.161733f, 0.390186f, 0.425262f, 0.455411f, 0.689737f,
25 0.745910f, 0.833844f, 0.859502f, 0.000000f, 0.075024f, 0.168788f, 0.299901f,
26 0.337443f, 0.371903f, 0.599011f, 0.716741f, 0.817859f, 0.851664f};
27constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_GAIN{
28 0.67096f, 0.61027f, 1.0f, 0.35680f, 0.68361f, 0.65978f, 0.51939f,
29 0.24712f, 0.45945f, 0.45021f, 0.64196f, 0.54879f, 0.92925f, 0.38270f,
30 0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f};
16 31
17template <std::size_t N> 32template <std::size_t N>
18void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) { 33void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
@@ -65,6 +80,154 @@ s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
65 } 80 }
66} 81}
67 82
83float Pow10(float x) {
84 if (x >= 0.0f) {
85 return 1.0f;
86 } else if (x <= -5.3f) {
87 return 0.0f;
88 }
89 return std::pow(10.0f, x);
90}
91
92float SinD(float degrees) {
93 return std::sinf(degrees * static_cast<float>(std::numbers::pi) / 180.0f);
94}
95
96float CosD(float degrees) {
97 return std::cosf(degrees * static_cast<float>(std::numbers::pi) / 180.0f);
98}
99
100float ToFloat(s32 sample) {
101 return static_cast<float>(sample) / 65536.f;
102}
103
104s32 ToS32(float sample) {
105 constexpr auto min = -8388608.0f;
106 constexpr auto max = 8388607.f;
107 float rescaled_sample = sample * 65536.0f;
108 if (rescaled_sample < min) {
109 rescaled_sample = min;
110 }
111 if (rescaled_sample > max) {
112 rescaled_sample = max;
113 }
114 return static_cast<s32>(rescaled_sample);
115}
116
117constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_1CH{0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
118 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
119
120constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_2CH{0, 0, 0, 1, 1, 1, 1, 0, 0, 0,
121 1, 1, 1, 0, 0, 0, 0, 1, 1, 1};
122
123constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_4CH{0, 0, 0, 1, 1, 1, 1, 2, 2, 2,
124 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
125
126constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 2, 2, 2,
127 1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
128
129template <std::size_t CHANNEL_COUNT>
130void ApplyReverbGeneric(const I3dl2ReverbParams& info, I3dl2ReverbState& state,
131 const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input,
132 const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output,
133 s32 sample_count) {
134
135 auto GetTapLookup = []() {
136 if constexpr (CHANNEL_COUNT == 1) {
137 return REVERB_TAP_INDEX_1CH;
138 } else if constexpr (CHANNEL_COUNT == 2) {
139 return REVERB_TAP_INDEX_2CH;
140 } else if constexpr (CHANNEL_COUNT == 4) {
141 return REVERB_TAP_INDEX_4CH;
142 } else if constexpr (CHANNEL_COUNT == 6) {
143 return REVERB_TAP_INDEX_6CH;
144 }
145 };
146
147 const auto& tap_index_lut = GetTapLookup();
148 for (s32 sample = 0; sample < sample_count; sample++) {
149 std::array<f32, CHANNEL_COUNT> out_samples{};
150 std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fsamp{};
151 std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> mixed{};
152 std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> osamp{};
153
154 // Mix everything into a single sample
155 s32 temp_mixed_sample = 0;
156 for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
157 temp_mixed_sample += input[i][sample];
158 }
159 const auto current_sample = ToFloat(temp_mixed_sample);
160 const auto early_tap = state.early_delay_line.TapOut(state.early_to_late_taps);
161
162 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_TAPS; i++) {
163 const auto tapped_samp =
164 state.early_delay_line.TapOut(state.early_tap_steps[i]) * EARLY_GAIN[i];
165 out_samples[tap_index_lut[i]] += tapped_samp;
166
167 if constexpr (CHANNEL_COUNT == 6) {
168 // handle lfe
169 out_samples[5] += tapped_samp;
170 }
171 }
172
173 state.lowpass_0 = current_sample * state.lowpass_2 + state.lowpass_0 * state.lowpass_1;
174 state.early_delay_line.Tick(state.lowpass_0);
175
176 for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
177 out_samples[i] *= state.early_gain;
178 }
179
180 // Two channel seems to apply a latet gain, we require to save this
181 f32 filter{};
182 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
183 filter = state.fdn_delay_line[i].GetOutputSample();
184 const auto computed = filter * state.lpf_coefficients[0][i] + state.shelf_filter[i];
185 state.shelf_filter[i] =
186 filter * state.lpf_coefficients[1][i] + computed * state.lpf_coefficients[2][i];
187 fsamp[i] = computed;
188 }
189
190 // Mixing matrix
191 mixed[0] = fsamp[1] + fsamp[2];
192 mixed[1] = -fsamp[0] - fsamp[3];
193 mixed[2] = fsamp[0] - fsamp[3];
194 mixed[3] = fsamp[1] - fsamp[2];
195
196 if constexpr (CHANNEL_COUNT == 2) {
197 for (auto& mix : mixed) {
198 mix *= (filter * state.late_gain);
199 }
200 }
201
202 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
203 const auto late = early_tap * state.late_gain;
204 osamp[i] = state.decay_delay_line0[i].Tick(late + mixed[i]);
205 osamp[i] = state.decay_delay_line1[i].Tick(osamp[i]);
206 state.fdn_delay_line[i].Tick(osamp[i]);
207 }
208
209 if constexpr (CHANNEL_COUNT == 1) {
210 output[0][sample] = ToS32(state.dry_gain * ToFloat(input[0][sample]) +
211 (out_samples[0] + osamp[0] + osamp[1]));
212 } else if constexpr (CHANNEL_COUNT == 2 || CHANNEL_COUNT == 4) {
213 for (std::size_t i = 0; i < CHANNEL_COUNT; i++) {
214 output[i][sample] =
215 ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
216 }
217 } else if constexpr (CHANNEL_COUNT == 6) {
218 const auto temp_center = state.center_delay_line.Tick(0.5f * (osamp[2] - osamp[3]));
219 for (std::size_t i = 0; i < 4; i++) {
220 output[i][sample] =
221 ToS32(state.dry_gain * ToFloat(input[i][sample]) + (out_samples[i] + osamp[i]));
222 }
223 output[4][sample] =
224 ToS32(state.dry_gain * ToFloat(input[4][sample]) + (out_samples[4] + temp_center));
225 output[5][sample] =
226 ToS32(state.dry_gain * ToFloat(input[5][sample]) + (out_samples[5] + osamp[3]));
227 }
228 }
229}
230
68} // namespace 231} // namespace
69 232
70CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_, 233CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params_,
@@ -271,11 +434,10 @@ void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voic
271 } 434 }
272 435
273 // Generate biquad filter 436 // Generate biquad filter
274 // GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter, 437 // GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
275 // dsp_state.biquad_filter_state, 438 // dsp_state.biquad_filter_state,
276 // mix_buffer_count + channel, mix_buffer_count + 439 // mix_buffer_count + channel, mix_buffer_count + channel,
277 // channel, worker_params.sample_count, 440 // worker_params.sample_count, voice_info.GetInParams().node_id);
278 // voice_info.GetInParams().node_id);
279 } 441 }
280} 442}
281 443
@@ -376,21 +538,54 @@ void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
376 538
377void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, 539void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
378 bool enabled) { 540 bool enabled) {
379 if (!enabled) { 541 auto* reverb = dynamic_cast<EffectI3dl2Reverb*>(info);
542 const auto& params = reverb->GetParams();
543 auto& state = reverb->GetState();
544 const auto channel_count = params.channel_count;
545
546 if (channel_count != 1 && channel_count != 2 && channel_count != 4 && channel_count != 6) {
380 return; 547 return;
381 } 548 }
382 const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams(); 549
383 const auto channel_count = params.channel_count; 550 std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{};
551 std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{};
552
553 const auto status = params.status;
384 for (s32 i = 0; i < channel_count; i++) { 554 for (s32 i = 0; i < channel_count; i++) {
385 // TODO(ogniK): Actually implement reverb 555 input[i] = GetMixBuffer(mix_buffer_offset + params.input[i]);
386 /* 556 output[i] = GetMixBuffer(mix_buffer_offset + params.output[i]);
387 if (params.input[i] != params.output[i]) { 557 }
388 const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]); 558
389 auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); 559 if (enabled) {
390 ApplyMix<1>(output, input, 32768, worker_params.sample_count); 560 if (status == ParameterStatus::Initialized) {
391 }*/ 561 InitializeI3dl2Reverb(reverb->GetParams(), state, info->GetWorkBuffer());
392 auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); 562 } else if (status == ParameterStatus::Updating) {
393 std::memset(output, 0, worker_params.sample_count * sizeof(s32)); 563 UpdateI3dl2Reverb(reverb->GetParams(), state, false);
564 }
565 }
566
567 if (enabled) {
568 switch (channel_count) {
569 case 1:
570 ApplyReverbGeneric<1>(params, state, input, output, worker_params.sample_count);
571 break;
572 case 2:
573 ApplyReverbGeneric<2>(params, state, input, output, worker_params.sample_count);
574 break;
575 case 4:
576 ApplyReverbGeneric<4>(params, state, input, output, worker_params.sample_count);
577 break;
578 case 6:
579 ApplyReverbGeneric<6>(params, state, input, output, worker_params.sample_count);
580 break;
581 }
582 } else {
583 for (s32 i = 0; i < channel_count; i++) {
584 // Only copy if the buffer input and output do not match!
585 if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) {
586 std::memcpy(output[i], input[i], worker_params.sample_count * sizeof(s32));
587 }
588 }
394 } 589 }
395} 590}
396 591
@@ -528,6 +723,132 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3
528 return sample_count; 723 return sample_count;
529} 724}
530 725
726void CommandGenerator::InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
727 std::vector<u8>& work_buffer) {
728 // Reset state
729 state.lowpass_0 = 0.0f;
730 state.lowpass_1 = 0.0f;
731 state.lowpass_2 = 0.0f;
732
733 state.early_delay_line.Reset();
734 state.early_tap_steps.fill(0);
735 state.early_gain = 0.0f;
736 state.late_gain = 0.0f;
737 state.early_to_late_taps = 0;
738 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
739 state.fdn_delay_line[i].Reset();
740 state.decay_delay_line0[i].Reset();
741 state.decay_delay_line1[i].Reset();
742 }
743 state.last_reverb_echo = 0.0f;
744 state.center_delay_line.Reset();
745 for (auto& coef : state.lpf_coefficients) {
746 coef.fill(0.0f);
747 }
748 state.shelf_filter.fill(0.0f);
749 state.dry_gain = 0.0f;
750
751 const auto sample_rate = info.sample_rate / 1000;
752 f32* work_buffer_ptr = reinterpret_cast<f32*>(work_buffer.data());
753
754 s32 delay_samples{};
755 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
756 delay_samples =
757 AudioCommon::CalculateDelaySamples(sample_rate, FDN_MAX_DELAY_LINE_TIMES[i]);
758 state.fdn_delay_line[i].Initialize(delay_samples, work_buffer_ptr);
759 work_buffer_ptr += delay_samples + 1;
760
761 delay_samples =
762 AudioCommon::CalculateDelaySamples(sample_rate, DECAY0_MAX_DELAY_LINE_TIMES[i]);
763 state.decay_delay_line0[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
764 work_buffer_ptr += delay_samples + 1;
765
766 delay_samples =
767 AudioCommon::CalculateDelaySamples(sample_rate, DECAY1_MAX_DELAY_LINE_TIMES[i]);
768 state.decay_delay_line1[i].Initialize(delay_samples, 0.0f, work_buffer_ptr);
769 work_buffer_ptr += delay_samples + 1;
770 }
771 delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 5.0f);
772 state.center_delay_line.Initialize(delay_samples, work_buffer_ptr);
773 work_buffer_ptr += delay_samples + 1;
774
775 delay_samples = AudioCommon::CalculateDelaySamples(sample_rate, 400.0f);
776 state.early_delay_line.Initialize(delay_samples, work_buffer_ptr);
777
778 UpdateI3dl2Reverb(info, state, true);
779}
780
781void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
782 bool should_clear) {
783
784 state.dry_gain = info.dry_gain;
785 state.shelf_filter.fill(0.0f);
786 state.lowpass_0 = 0.0f;
787 state.early_gain = Pow10(std::min(info.room + info.reflection, 5000.0f) / 2000.0f);
788 state.late_gain = Pow10(std::min(info.room + info.reverb, 5000.0f) / 2000.0f);
789
790 const auto sample_rate = info.sample_rate / 1000;
791 const f32 hf_gain = Pow10(info.room_hf / 2000.0f);
792 if (hf_gain >= 1.0f) {
793 state.lowpass_2 = 1.0f;
794 state.lowpass_1 = 0.0f;
795 } else {
796 const auto a = 1.0f - hf_gain;
797 const auto b =
798 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference / info.sample_rate));
799 const auto c = std::sqrt(b * b - 4.0f * a * a);
800
801 state.lowpass_1 = (b - c) / (2.0f * a);
802 state.lowpass_2 = 1.0f - state.lowpass_1;
803 }
804 state.early_to_late_taps = AudioCommon::CalculateDelaySamples(
805 sample_rate, 1000.0f * (info.reflection_delay + info.reverb_delay));
806
807 state.last_reverb_echo = 0.6f * info.diffusion * 0.01f;
808 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
809 const auto length =
810 FDN_MIN_DELAY_LINE_TIMES[i] +
811 (info.density / 100.0f) * (FDN_MAX_DELAY_LINE_TIMES[i] - FDN_MIN_DELAY_LINE_TIMES[i]);
812 state.fdn_delay_line[i].SetDelay(AudioCommon::CalculateDelaySamples(sample_rate, length));
813
814 const auto delay_sample_counts = state.fdn_delay_line[i].GetDelay() +
815 state.decay_delay_line0[i].GetDelay() +
816 state.decay_delay_line1[i].GetDelay();
817
818 float a = (-60.0f * delay_sample_counts) / (info.decay_time * info.sample_rate);
819 float b = a / info.hf_decay_ratio;
820 float c = CosD(128.0f * 0.5f * info.hf_reference / info.sample_rate) /
821 SinD(128.0f * 0.5f * info.hf_reference / info.sample_rate);
822 float d = Pow10((b - a) / 40.0f);
823 float e = Pow10((b + a) / 40.0f) * 0.7071f;
824
825 state.lpf_coefficients[0][i] = e * ((d * c) + 1.0f) / (c + d);
826 state.lpf_coefficients[1][i] = e * (1.0f - (d * c)) / (c + d);
827 state.lpf_coefficients[2][i] = (c - d) / (c + d);
828
829 state.decay_delay_line0[i].SetCoefficient(state.last_reverb_echo);
830 state.decay_delay_line1[i].SetCoefficient(-0.9f * state.last_reverb_echo);
831 }
832
833 if (should_clear) {
834 for (std::size_t i = 0; i < AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT; i++) {
835 state.fdn_delay_line[i].Clear();
836 state.decay_delay_line0[i].Clear();
837 state.decay_delay_line1[i].Clear();
838 }
839 state.early_delay_line.Clear();
840 state.center_delay_line.Clear();
841 }
842
843 const auto max_early_delay = state.early_delay_line.GetMaxDelay();
844 const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f);
845 for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) {
846 const auto length = AudioCommon::CalculateDelaySamples(
847 sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]);
848 state.early_tap_steps[tap] = std::min(length, max_early_delay);
849 }
850}
851
531void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume, 852void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
532 s32 channel, s32 node_id) { 853 s32 channel, s32 node_id) {
533 const auto last = static_cast<s32>(last_volume * 32768.0f); 854 const auto last = static_cast<s32>(last_volume * 32768.0f);
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h
index b937350b1..2ebb755b0 100644
--- a/src/audio_core/command_generator.h
+++ b/src/audio_core/command_generator.h
@@ -21,6 +21,8 @@ class ServerMixInfo;
21class EffectContext; 21class EffectContext;
22class EffectBase; 22class EffectBase;
23struct AuxInfoDSP; 23struct AuxInfoDSP;
24struct I3dl2ReverbParams;
25struct I3dl2ReverbState;
24using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>; 26using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
25 27
26class CommandGenerator { 28class CommandGenerator {
@@ -80,6 +82,9 @@ private:
80 s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data, 82 s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
81 u32 sample_count, u32 read_offset, u32 read_count); 83 u32 sample_count, u32 read_offset, u32 read_count);
82 84
85 void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
86 std::vector<u8>& work_buffer);
87 void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear);
83 // DSP Code 88 // DSP Code
84 s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, 89 s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
85 s32 channel, std::size_t mix_offset); 90 s32 channel, std::size_t mix_offset);
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index ec59a3ba9..fe546c55d 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -33,6 +33,29 @@ constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this
33// and our const ends up being 0x3f04, the 4 bytes are most 33// and our const ends up being 0x3f04, the 4 bytes are most
34// likely the sample history 34// likely the sample history
35constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY; 35constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
36constexpr f32 I3DL2REVERB_MAX_LEVEL = 5000.0f;
37constexpr f32 I3DL2REVERB_MIN_REFLECTION_DURATION = 0.02f;
38constexpr std::size_t I3DL2REVERB_TAPS = 20;
39constexpr std::size_t I3DL2REVERB_DELAY_LINE_COUNT = 4;
40using Fractional = s32;
41
42template <typename T>
43constexpr Fractional ToFractional(T x) {
44 return static_cast<Fractional>(x * static_cast<T>(0x4000));
45}
46
47constexpr Fractional MultiplyFractional(Fractional lhs, Fractional rhs) {
48 return static_cast<Fractional>(static_cast<s64>(lhs) * rhs >> 14);
49}
50
51constexpr s32 FractionalToFixed(Fractional x) {
52 const auto s = x & (1 << 13);
53 return static_cast<s32>(x >> 14) + s;
54}
55
56constexpr s32 CalculateDelaySamples(s32 sample_rate_khz, float time) {
57 return FractionalToFixed(MultiplyFractional(ToFractional(sample_rate_khz), ToFractional(time)));
58}
36 59
37static constexpr u32 VersionFromRevision(u32_le rev) { 60static constexpr u32 VersionFromRevision(u32_le rev) {
38 // "REV7" -> 7 61 // "REV7" -> 7
diff --git a/src/audio_core/delay_line.cpp b/src/audio_core/delay_line.cpp
new file mode 100644
index 000000000..c8bc6e23e
--- /dev/null
+++ b/src/audio_core/delay_line.cpp
@@ -0,0 +1,103 @@
1#include "audio_core/delay_line.h"
2
3namespace AudioCore {
4DelayLineBase::DelayLineBase() = default;
5DelayLineBase::~DelayLineBase() = default;
6
7void DelayLineBase::Initialize(s32 _max_delay, float* src_buffer) {
8 buffer = src_buffer;
9 buffer_end = buffer + _max_delay;
10 max_delay = _max_delay;
11 output = buffer;
12 SetDelay(_max_delay);
13 Clear();
14}
15
16void DelayLineBase::SetDelay(s32 new_delay) {
17 if (max_delay < new_delay) {
18 return;
19 }
20 delay = new_delay;
21 input = (buffer + ((output - buffer) + new_delay) % (max_delay + 1));
22}
23
24s32 DelayLineBase::GetDelay() const {
25 return delay;
26}
27
28s32 DelayLineBase::GetMaxDelay() const {
29 return max_delay;
30}
31
32f32 DelayLineBase::TapOut(s32 last_sample) {
33 float* ptr = input - (last_sample + 1);
34 if (ptr < buffer) {
35 ptr += (max_delay + 1);
36 }
37
38 return *ptr;
39}
40
41f32 DelayLineBase::Tick(f32 sample) {
42 *(input++) = sample;
43 const auto out_sample = *(output++);
44
45 if (buffer_end < input) {
46 input = buffer;
47 }
48
49 if (buffer_end < output) {
50 output = buffer;
51 }
52
53 return out_sample;
54}
55
56float* DelayLineBase::GetInput() {
57 return input;
58}
59
60const float* DelayLineBase::GetInput() const {
61 return input;
62}
63
64f32 DelayLineBase::GetOutputSample() const {
65 return *output;
66}
67
68void DelayLineBase::Clear() {
69 std::memset(buffer, 0, sizeof(float) * max_delay);
70}
71
72void DelayLineBase::Reset() {
73 buffer = nullptr;
74 buffer_end = nullptr;
75 max_delay = 0;
76 input = nullptr;
77 output = nullptr;
78 delay = 0;
79}
80
81DelayLineAllPass::DelayLineAllPass() = default;
82DelayLineAllPass::~DelayLineAllPass() = default;
83
84void DelayLineAllPass::Initialize(u32 delay, float _coeffcient, f32* src_buffer) {
85 DelayLineBase::Initialize(delay, src_buffer);
86 SetCoefficient(_coeffcient);
87}
88
89void DelayLineAllPass::SetCoefficient(float _coeffcient) {
90 coefficient = _coeffcient;
91}
92
93f32 DelayLineAllPass::Tick(f32 sample) {
94 const auto temp = sample - coefficient * *output;
95 return coefficient * temp + DelayLineBase::Tick(temp);
96}
97
98void DelayLineAllPass::Reset() {
99 coefficient = 0.0f;
100 DelayLineBase::Reset();
101}
102
103} // namespace AudioCore
diff --git a/src/audio_core/delay_line.h b/src/audio_core/delay_line.h
new file mode 100644
index 000000000..b6a6e0b12
--- /dev/null
+++ b/src/audio_core/delay_line.h
@@ -0,0 +1,46 @@
1#pragma once
2
3#include "common/common_types.h"
4
5namespace AudioCore {
6
7class DelayLineBase {
8public:
9 DelayLineBase();
10 ~DelayLineBase();
11
12 void Initialize(s32 _max_delay, float* src_buffer);
13 void SetDelay(s32 new_delay);
14 s32 GetDelay() const;
15 s32 GetMaxDelay() const;
16 f32 TapOut(s32 last_sample);
17 f32 Tick(f32 sample);
18 float* GetInput();
19 const float* GetInput() const;
20 f32 GetOutputSample() const;
21 void Clear();
22 void Reset();
23
24protected:
25 float* buffer{nullptr};
26 float* buffer_end{nullptr};
27 s32 max_delay{};
28 float* input{nullptr};
29 float* output{nullptr};
30 s32 delay{};
31};
32
33class DelayLineAllPass final : public DelayLineBase {
34public:
35 DelayLineAllPass();
36 ~DelayLineAllPass();
37
38 void Initialize(u32 delay, float _coeffcient, f32* src_buffer);
39 void SetCoefficient(float _coeffcient);
40 f32 Tick(f32 sample);
41 void Reset();
42
43private:
44 float coefficient{};
45};
46} // namespace AudioCore
diff --git a/src/audio_core/effect_context.cpp b/src/audio_core/effect_context.cpp
index f770b9608..eeee8e325 100644
--- a/src/audio_core/effect_context.cpp
+++ b/src/audio_core/effect_context.cpp
@@ -90,6 +90,14 @@ s32 EffectBase::GetProcessingOrder() const {
90 return processing_order; 90 return processing_order;
91} 91}
92 92
93std::vector<u8>& EffectBase::GetWorkBuffer() {
94 return work_buffer;
95}
96
97const std::vector<u8>& EffectBase::GetWorkBuffer() const {
98 return work_buffer;
99}
100
93EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {} 101EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric(EffectType::I3dl2Reverb) {}
94EffectI3dl2Reverb::~EffectI3dl2Reverb() = default; 102EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
95 103
@@ -117,6 +125,12 @@ void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
117 usage = UsageState::Initialized; 125 usage = UsageState::Initialized;
118 params.status = ParameterStatus::Initialized; 126 params.status = ParameterStatus::Initialized;
119 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0; 127 skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
128 if (!skipped) {
129 auto& work_buffer = GetWorkBuffer();
130 // Has two buffers internally
131 work_buffer.resize(in_params.buffer_size * 2);
132 std::fill(work_buffer.begin(), work_buffer.end(), 0);
133 }
120 } 134 }
121} 135}
122 136
@@ -129,6 +143,14 @@ void EffectI3dl2Reverb::UpdateForCommandGeneration() {
129 GetParams().status = ParameterStatus::Updated; 143 GetParams().status = ParameterStatus::Updated;
130} 144}
131 145
146I3dl2ReverbState& EffectI3dl2Reverb::GetState() {
147 return state;
148}
149
150const I3dl2ReverbState& EffectI3dl2Reverb::GetState() const {
151 return state;
152}
153
132EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {} 154EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric(EffectType::BiquadFilter) {}
133EffectBiquadFilter::~EffectBiquadFilter() = default; 155EffectBiquadFilter::~EffectBiquadFilter() = default;
134 156
diff --git a/src/audio_core/effect_context.h b/src/audio_core/effect_context.h
index c5e0b398c..5e0655dd7 100644
--- a/src/audio_core/effect_context.h
+++ b/src/audio_core/effect_context.h
@@ -8,6 +8,7 @@
8#include <memory> 8#include <memory>
9#include <vector> 9#include <vector>
10#include "audio_core/common.h" 10#include "audio_core/common.h"
11#include "audio_core/delay_line.h"
11#include "common/common_funcs.h" 12#include "common/common_funcs.h"
12#include "common/common_types.h" 13#include "common/common_types.h"
13#include "common/swap.h" 14#include "common/swap.h"
@@ -194,6 +195,8 @@ public:
194 [[nodiscard]] bool IsEnabled() const; 195 [[nodiscard]] bool IsEnabled() const;
195 [[nodiscard]] s32 GetMixID() const; 196 [[nodiscard]] s32 GetMixID() const;
196 [[nodiscard]] s32 GetProcessingOrder() const; 197 [[nodiscard]] s32 GetProcessingOrder() const;
198 [[nodiscard]] std::vector<u8>& GetWorkBuffer();
199 [[nodiscard]] const std::vector<u8>& GetWorkBuffer() const;
197 200
198protected: 201protected:
199 UsageState usage{UsageState::Invalid}; 202 UsageState usage{UsageState::Invalid};
@@ -201,6 +204,7 @@ protected:
201 s32 mix_id{}; 204 s32 mix_id{};
202 s32 processing_order{}; 205 s32 processing_order{};
203 bool enabled = false; 206 bool enabled = false;
207 std::vector<u8> work_buffer{};
204}; 208};
205 209
206template <typename T> 210template <typename T>
@@ -212,7 +216,7 @@ public:
212 return internal_params; 216 return internal_params;
213 } 217 }
214 218
215 const I3dl2ReverbParams& GetParams() const { 219 const T& GetParams() const {
216 return internal_params; 220 return internal_params;
217 } 221 }
218 222
@@ -229,6 +233,27 @@ public:
229 void UpdateForCommandGeneration() override; 233 void UpdateForCommandGeneration() override;
230}; 234};
231 235
236struct I3dl2ReverbState {
237 f32 lowpass_0{};
238 f32 lowpass_1{};
239 f32 lowpass_2{};
240
241 DelayLineBase early_delay_line{};
242 std::array<u32, AudioCommon::I3DL2REVERB_TAPS> early_tap_steps{};
243 f32 early_gain{};
244 f32 late_gain{};
245
246 u32 early_to_late_taps{};
247 std::array<DelayLineBase, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> fdn_delay_line{};
248 std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line0{};
249 std::array<DelayLineAllPass, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> decay_delay_line1{};
250 f32 last_reverb_echo{};
251 DelayLineBase center_delay_line{};
252 std::array<std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT>, 3> lpf_coefficients{};
253 std::array<f32, AudioCommon::I3DL2REVERB_DELAY_LINE_COUNT> shelf_filter{};
254 f32 dry_gain{};
255};
256
232class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> { 257class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
233public: 258public:
234 explicit EffectI3dl2Reverb(); 259 explicit EffectI3dl2Reverb();
@@ -237,8 +262,12 @@ public:
237 void Update(EffectInfo::InParams& in_params) override; 262 void Update(EffectInfo::InParams& in_params) override;
238 void UpdateForCommandGeneration() override; 263 void UpdateForCommandGeneration() override;
239 264
265 I3dl2ReverbState& GetState();
266 const I3dl2ReverbState& GetState() const;
267
240private: 268private:
241 bool skipped = false; 269 bool skipped = false;
270 I3dl2ReverbState state{};
242}; 271};
243 272
244class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> { 273class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {