diff options
Diffstat (limited to 'src/audio_core/audio_renderer.cpp')
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 131 |
1 files changed, 92 insertions, 39 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 56dc892b1..d2ce8c814 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -2,43 +2,90 @@ | |||
| 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 <limits> | ||
| 5 | #include <vector> | 6 | #include <vector> |
| 6 | #include "audio_core/algorithm/interpolate.h" | 7 | |
| 7 | #include "audio_core/audio_out.h" | 8 | #include "audio_core/audio_out.h" |
| 8 | #include "audio_core/audio_renderer.h" | 9 | #include "audio_core/audio_renderer.h" |
| 9 | #include "audio_core/codec.h" | ||
| 10 | #include "audio_core/common.h" | 10 | #include "audio_core/common.h" |
| 11 | #include "audio_core/info_updater.h" | 11 | #include "audio_core/info_updater.h" |
| 12 | #include "audio_core/voice_context.h" | 12 | #include "audio_core/voice_context.h" |
| 13 | #include "common/assert.h" | ||
| 14 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 15 | #include "core/core.h" | ||
| 16 | #include "core/hle/kernel/writable_event.h" | ||
| 17 | #include "core/memory.h" | 14 | #include "core/memory.h" |
| 18 | #include "core/settings.h" | 15 | #include "core/settings.h" |
| 19 | 16 | ||
| 17 | namespace { | ||
| 18 | [[nodiscard]] static constexpr s16 ClampToS16(s32 value) { | ||
| 19 | return static_cast<s16>(std::clamp(value, s32{std::numeric_limits<s16>::min()}, | ||
| 20 | s32{std::numeric_limits<s16>::max()})); | ||
| 21 | } | ||
| 22 | |||
| 23 | [[nodiscard]] static constexpr s16 Mix2To1(s16 l_channel, s16 r_channel) { | ||
| 24 | // Mix 50% from left and 50% from right channel | ||
| 25 | constexpr float l_mix_amount = 50.0f / 100.0f; | ||
| 26 | constexpr float r_mix_amount = 50.0f / 100.0f; | ||
| 27 | return ClampToS16(static_cast<s32>((static_cast<float>(l_channel) * l_mix_amount) + | ||
| 28 | (static_cast<float>(r_channel) * r_mix_amount))); | ||
| 29 | } | ||
| 30 | |||
| 31 | [[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel, | ||
| 32 | s16 fc_channel, | ||
| 33 | [[maybe_unused]] s16 lf_channel, | ||
| 34 | s16 bl_channel, s16 br_channel) { | ||
| 35 | // Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels | ||
| 36 | // are mixed to be 36.94% | ||
| 37 | |||
| 38 | constexpr float front_mix_amount = 36.94f / 100.0f; | ||
| 39 | constexpr float center_mix_amount = 26.12f / 100.0f; | ||
| 40 | constexpr float back_mix_amount = 36.94f / 100.0f; | ||
| 41 | |||
| 42 | // Mix 50% from left and 50% from right channel | ||
| 43 | const auto left = front_mix_amount * static_cast<float>(fl_channel) + | ||
| 44 | center_mix_amount * static_cast<float>(fc_channel) + | ||
| 45 | back_mix_amount * static_cast<float>(bl_channel); | ||
| 46 | |||
| 47 | const auto right = front_mix_amount * static_cast<float>(fr_channel) + | ||
| 48 | center_mix_amount * static_cast<float>(fc_channel) + | ||
| 49 | back_mix_amount * static_cast<float>(br_channel); | ||
| 50 | |||
| 51 | return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))}; | ||
| 52 | } | ||
| 53 | |||
| 54 | [[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2WithCoefficients( | ||
| 55 | s16 fl_channel, s16 fr_channel, s16 fc_channel, s16 lf_channel, s16 bl_channel, s16 br_channel, | ||
| 56 | const std::array<float_le, 4>& coeff) { | ||
| 57 | const auto left = | ||
| 58 | static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] + | ||
| 59 | static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[0]; | ||
| 60 | |||
| 61 | const auto right = | ||
| 62 | static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] + | ||
| 63 | static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[0]; | ||
| 64 | |||
| 65 | return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))}; | ||
| 66 | } | ||
| 67 | |||
| 68 | } // namespace | ||
| 69 | |||
| 20 | namespace AudioCore { | 70 | namespace AudioCore { |
| 21 | AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, | 71 | AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, |
| 22 | AudioCommon::AudioRendererParameter params, | 72 | AudioCommon::AudioRendererParameter params, |
| 23 | std::shared_ptr<Kernel::WritableEvent> buffer_event, | 73 | Stream::ReleaseCallback&& release_callback, |
| 24 | std::size_t instance_number) | 74 | std::size_t instance_number) |
| 25 | : worker_params{params}, buffer_event{buffer_event}, | 75 | : worker_params{params}, memory_pool_info(params.effect_count + params.voice_count * 4), |
| 26 | memory_pool_info(params.effect_count + params.voice_count * 4), | ||
| 27 | voice_context(params.voice_count), effect_context(params.effect_count), mix_context(), | 76 | voice_context(params.voice_count), effect_context(params.effect_count), mix_context(), |
| 28 | sink_context(params.sink_count), splitter_context(), | 77 | sink_context(params.sink_count), splitter_context(), |
| 29 | voices(params.voice_count), memory{memory_}, | 78 | voices(params.voice_count), memory{memory_}, |
| 30 | command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context, | 79 | command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context, |
| 31 | memory), | 80 | memory) { |
| 32 | temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) { | ||
| 33 | behavior_info.SetUserRevision(params.revision); | 81 | behavior_info.SetUserRevision(params.revision); |
| 34 | splitter_context.Initialize(behavior_info, params.splitter_count, | 82 | splitter_context.Initialize(behavior_info, params.splitter_count, |
| 35 | params.num_splitter_send_channels); | 83 | params.num_splitter_send_channels); |
| 36 | mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count); | 84 | mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count); |
| 37 | audio_out = std::make_unique<AudioCore::AudioOut>(); | 85 | audio_out = std::make_unique<AudioCore::AudioOut>(); |
| 38 | stream = | 86 | stream = audio_out->OpenStream( |
| 39 | audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, | 87 | core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, |
| 40 | fmt::format("AudioRenderer-Instance{}", instance_number), | 88 | fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); |
| 41 | [=]() { buffer_event->Signal(); }); | ||
| 42 | audio_out->StartStream(stream); | 89 | audio_out->StartStream(stream); |
| 43 | 90 | ||
| 44 | QueueMixedBuffer(0); | 91 | QueueMixedBuffer(0); |
| @@ -65,10 +112,6 @@ Stream::State AudioRenderer::GetStreamState() const { | |||
| 65 | return stream->GetState(); | 112 | return stream->GetState(); |
| 66 | } | 113 | } |
| 67 | 114 | ||
| 68 | static constexpr s16 ClampToS16(s32 value) { | ||
| 69 | return static_cast<s16>(std::clamp(value, -32768, 32767)); | ||
| 70 | } | ||
| 71 | |||
| 72 | ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, | 115 | ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, |
| 73 | std::vector<u8>& output_params) { | 116 | std::vector<u8>& output_params) { |
| 74 | 117 | ||
| @@ -107,8 +150,8 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param | |||
| 107 | } | 150 | } |
| 108 | } | 151 | } |
| 109 | 152 | ||
| 110 | auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, | 153 | const auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, |
| 111 | splitter_context, effect_context); | 154 | splitter_context, effect_context); |
| 112 | 155 | ||
| 113 | if (mix_result.IsError()) { | 156 | if (mix_result.IsError()) { |
| 114 | LOG_ERROR(Audio, "Failed to update mix parameters"); | 157 | LOG_ERROR(Audio, "Failed to update mix parameters"); |
| @@ -197,20 +240,22 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 197 | for (std::size_t i = 0; i < BUFFER_SIZE; i++) { | 240 | for (std::size_t i = 0; i < BUFFER_SIZE; i++) { |
| 198 | if (channel_count == 1) { | 241 | if (channel_count == 1) { |
| 199 | const auto sample = ClampToS16(mix_buffers[0][i]); | 242 | const auto sample = ClampToS16(mix_buffers[0][i]); |
| 200 | buffer[i * stream_channel_count + 0] = sample; | 243 | |
| 201 | if (stream_channel_count > 1) { | 244 | // Place sample in all channels |
| 202 | buffer[i * stream_channel_count + 1] = sample; | 245 | for (u32 channel = 0; channel < stream_channel_count; channel++) { |
| 246 | buffer[i * stream_channel_count + channel] = sample; | ||
| 203 | } | 247 | } |
| 248 | |||
| 204 | if (stream_channel_count == 6) { | 249 | if (stream_channel_count == 6) { |
| 205 | buffer[i * stream_channel_count + 2] = sample; | 250 | // Output stream has a LF channel, mute it! |
| 206 | buffer[i * stream_channel_count + 4] = sample; | 251 | buffer[i * stream_channel_count + 3] = 0; |
| 207 | buffer[i * stream_channel_count + 5] = sample; | ||
| 208 | } | 252 | } |
| 253 | |||
| 209 | } else if (channel_count == 2) { | 254 | } else if (channel_count == 2) { |
| 210 | const auto l_sample = ClampToS16(mix_buffers[0][i]); | 255 | const auto l_sample = ClampToS16(mix_buffers[0][i]); |
| 211 | const auto r_sample = ClampToS16(mix_buffers[1][i]); | 256 | const auto r_sample = ClampToS16(mix_buffers[1][i]); |
| 212 | if (stream_channel_count == 1) { | 257 | if (stream_channel_count == 1) { |
| 213 | buffer[i * stream_channel_count + 0] = l_sample; | 258 | buffer[i * stream_channel_count + 0] = Mix2To1(l_sample, r_sample); |
| 214 | } else if (stream_channel_count == 2) { | 259 | } else if (stream_channel_count == 2) { |
| 215 | buffer[i * stream_channel_count + 0] = l_sample; | 260 | buffer[i * stream_channel_count + 0] = l_sample; |
| 216 | buffer[i * stream_channel_count + 1] = r_sample; | 261 | buffer[i * stream_channel_count + 1] = r_sample; |
| @@ -218,8 +263,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 218 | buffer[i * stream_channel_count + 0] = l_sample; | 263 | buffer[i * stream_channel_count + 0] = l_sample; |
| 219 | buffer[i * stream_channel_count + 1] = r_sample; | 264 | buffer[i * stream_channel_count + 1] = r_sample; |
| 220 | 265 | ||
| 221 | buffer[i * stream_channel_count + 2] = | 266 | // Combine both left and right channels to the center channel |
| 222 | ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2); | 267 | buffer[i * stream_channel_count + 2] = Mix2To1(l_sample, r_sample); |
| 223 | 268 | ||
| 224 | buffer[i * stream_channel_count + 4] = l_sample; | 269 | buffer[i * stream_channel_count + 4] = l_sample; |
| 225 | buffer[i * stream_channel_count + 5] = r_sample; | 270 | buffer[i * stream_channel_count + 5] = r_sample; |
| @@ -234,17 +279,25 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 234 | const auto br_sample = ClampToS16(mix_buffers[5][i]); | 279 | const auto br_sample = ClampToS16(mix_buffers[5][i]); |
| 235 | 280 | ||
| 236 | if (stream_channel_count == 1) { | 281 | if (stream_channel_count == 1) { |
| 237 | buffer[i * stream_channel_count + 0] = fc_sample; | 282 | // Games seem to ignore the center channel half the time, we use the front left |
| 283 | // and right channel for mixing as that's where majority of the audio goes | ||
| 284 | buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample); | ||
| 238 | } else if (stream_channel_count == 2) { | 285 | } else if (stream_channel_count == 2) { |
| 239 | buffer[i * stream_channel_count + 0] = | 286 | // Mix all channels into 2 channels |
| 240 | static_cast<s16>(0.3694f * static_cast<float>(fl_sample) + | 287 | if (sink_context.HasDownMixingCoefficients()) { |
| 241 | 0.2612f * static_cast<float>(fc_sample) + | 288 | const auto [left, right] = Mix6To2WithCoefficients( |
| 242 | 0.3694f * static_cast<float>(bl_sample)); | 289 | fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample, |
| 243 | buffer[i * stream_channel_count + 1] = | 290 | sink_context.GetDownmixCoefficients()); |
| 244 | static_cast<s16>(0.3694f * static_cast<float>(fr_sample) + | 291 | buffer[i * stream_channel_count + 0] = left; |
| 245 | 0.2612f * static_cast<float>(fc_sample) + | 292 | buffer[i * stream_channel_count + 1] = right; |
| 246 | 0.3694f * static_cast<float>(br_sample)); | 293 | } else { |
| 294 | const auto [left, right] = Mix6To2(fl_sample, fr_sample, fc_sample, | ||
| 295 | lf_sample, bl_sample, br_sample); | ||
| 296 | buffer[i * stream_channel_count + 0] = left; | ||
| 297 | buffer[i * stream_channel_count + 1] = right; | ||
| 298 | } | ||
| 247 | } else if (stream_channel_count == 6) { | 299 | } else if (stream_channel_count == 6) { |
| 300 | // Pass through | ||
| 248 | buffer[i * stream_channel_count + 0] = fl_sample; | 301 | buffer[i * stream_channel_count + 0] = fl_sample; |
| 249 | buffer[i * stream_channel_count + 1] = fr_sample; | 302 | buffer[i * stream_channel_count + 1] = fr_sample; |
| 250 | buffer[i * stream_channel_count + 2] = fc_sample; | 303 | buffer[i * stream_channel_count + 2] = fc_sample; |
| @@ -262,7 +315,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 262 | } | 315 | } |
| 263 | 316 | ||
| 264 | void AudioRenderer::ReleaseAndQueueBuffers() { | 317 | void AudioRenderer::ReleaseAndQueueBuffers() { |
| 265 | const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)}; | 318 | const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)}; |
| 266 | for (const auto& tag : released_buffers) { | 319 | for (const auto& tag : released_buffers) { |
| 267 | QueueMixedBuffer(tag); | 320 | QueueMixedBuffer(tag); |
| 268 | } | 321 | } |