diff options
| author | 2020-05-13 22:46:41 -0400 | |
|---|---|---|
| committer | 2020-05-13 22:46:41 -0400 | |
| commit | 0e2ded049d6d5a39d18cf904e54c053355482e31 (patch) | |
| tree | 0cc136ecc5d107308fb47907feb235af51394f68 | |
| parent | Merge pull request #3909 from bunnei/timezone (diff) | |
| parent | fix logic error & scale sample volume based on voice volume (diff) | |
| download | yuzu-0e2ded049d6d5a39d18cf904e54c053355482e31.tar.gz yuzu-0e2ded049d6d5a39d18cf904e54c053355482e31.tar.xz yuzu-0e2ded049d6d5a39d18cf904e54c053355482e31.zip | |
Merge pull request #3757 from ogniK5377/better-voice-mixing
audio_renderer: Better voice mixing and 6 channel downmixing
Diffstat (limited to '')
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 99 | ||||
| -rw-r--r-- | src/audio_core/audio_renderer.h | 10 | ||||
| -rw-r--r-- | src/audio_core/common.h | 1 |
3 files changed, 98 insertions, 12 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index d18ef6940..50846a854 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -17,7 +17,7 @@ namespace AudioCore { | |||
| 17 | 17 | ||
| 18 | constexpr u32 STREAM_SAMPLE_RATE{48000}; | 18 | constexpr u32 STREAM_SAMPLE_RATE{48000}; |
| 19 | constexpr u32 STREAM_NUM_CHANNELS{2}; | 19 | constexpr u32 STREAM_NUM_CHANNELS{2}; |
| 20 | 20 | using VoiceChannelHolder = std::array<VoiceResourceInformation*, 6>; | |
| 21 | class AudioRenderer::VoiceState { | 21 | class AudioRenderer::VoiceState { |
| 22 | public: | 22 | public: |
| 23 | bool IsPlaying() const { | 23 | bool IsPlaying() const { |
| @@ -37,9 +37,10 @@ public: | |||
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | void SetWaveIndex(std::size_t index); | 39 | void SetWaveIndex(std::size_t index); |
| 40 | std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory); | 40 | std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory, |
| 41 | const VoiceChannelHolder& voice_resources); | ||
| 41 | void UpdateState(); | 42 | void UpdateState(); |
| 42 | void RefreshBuffer(Core::Memory::Memory& memory); | 43 | void RefreshBuffer(Core::Memory::Memory& memory, const VoiceChannelHolder& voice_resources); |
| 43 | 44 | ||
| 44 | private: | 45 | private: |
| 45 | bool is_in_use{}; | 46 | bool is_in_use{}; |
| @@ -79,7 +80,7 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory | |||
| 79 | std::shared_ptr<Kernel::WritableEvent> buffer_event, | 80 | std::shared_ptr<Kernel::WritableEvent> buffer_event, |
| 80 | std::size_t instance_number) | 81 | std::size_t instance_number) |
| 81 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), | 82 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), |
| 82 | effects(params.effect_count), memory{memory_} { | 83 | voice_resources(params.voice_count), effects(params.effect_count), memory{memory_} { |
| 83 | behavior_info.SetUserRevision(params.revision); | 84 | behavior_info.SetUserRevision(params.revision); |
| 84 | audio_out = std::make_unique<AudioCore::AudioOut>(); | 85 | audio_out = std::make_unique<AudioCore::AudioOut>(); |
| 85 | stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, | 86 | stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, |
| @@ -127,6 +128,12 @@ ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector< | |||
| 127 | input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size, | 128 | input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size, |
| 128 | memory_pool_count * sizeof(MemoryPoolInfo)); | 129 | memory_pool_count * sizeof(MemoryPoolInfo)); |
| 129 | 130 | ||
| 131 | // Copy voice resources | ||
| 132 | const std::size_t voice_resource_offset{sizeof(UpdateDataHeader) + config.behavior_size + | ||
| 133 | config.memory_pools_size}; | ||
| 134 | std::memcpy(voice_resources.data(), input_params.data() + voice_resource_offset, | ||
| 135 | sizeof(VoiceResourceInformation) * voice_resources.size()); | ||
| 136 | |||
| 130 | // Copy VoiceInfo structs | 137 | // Copy VoiceInfo structs |
| 131 | std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size + | 138 | std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size + |
| 132 | config.memory_pools_size + config.voice_resource_size}; | 139 | config.memory_pools_size + config.voice_resource_size}; |
| @@ -220,14 +227,15 @@ void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { | |||
| 220 | is_refresh_pending = true; | 227 | is_refresh_pending = true; |
| 221 | } | 228 | } |
| 222 | 229 | ||
| 223 | std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count, | 230 | std::vector<s16> AudioRenderer::VoiceState::DequeueSamples( |
| 224 | Core::Memory::Memory& memory) { | 231 | std::size_t sample_count, Core::Memory::Memory& memory, |
| 232 | const VoiceChannelHolder& voice_resources) { | ||
| 225 | if (!IsPlaying()) { | 233 | if (!IsPlaying()) { |
| 226 | return {}; | 234 | return {}; |
| 227 | } | 235 | } |
| 228 | 236 | ||
| 229 | if (is_refresh_pending) { | 237 | if (is_refresh_pending) { |
| 230 | RefreshBuffer(memory); | 238 | RefreshBuffer(memory, voice_resources); |
| 231 | } | 239 | } |
| 232 | 240 | ||
| 233 | const std::size_t max_size{samples.size() - offset}; | 241 | const std::size_t max_size{samples.size() - offset}; |
| @@ -271,7 +279,8 @@ void AudioRenderer::VoiceState::UpdateState() { | |||
| 271 | is_in_use = info.is_in_use; | 279 | is_in_use = info.is_in_use; |
| 272 | } | 280 | } |
| 273 | 281 | ||
| 274 | void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) { | 282 | void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory, |
| 283 | const VoiceChannelHolder& voice_resources) { | ||
| 275 | const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; | 284 | const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; |
| 276 | const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; | 285 | const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; |
| 277 | std::vector<s16> new_samples(wave_buffer_size / sizeof(s16)); | 286 | std::vector<s16> new_samples(wave_buffer_size / sizeof(s16)); |
| @@ -296,17 +305,77 @@ void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory) { | |||
| 296 | } | 305 | } |
| 297 | 306 | ||
| 298 | switch (info.channel_count) { | 307 | switch (info.channel_count) { |
| 299 | case 1: | 308 | case 1: { |
| 300 | // 1 channel is upsampled to 2 channel | 309 | // 1 channel is upsampled to 2 channel |
| 301 | samples.resize(new_samples.size() * 2); | 310 | samples.resize(new_samples.size() * 2); |
| 311 | |||
| 302 | for (std::size_t index = 0; index < new_samples.size(); ++index) { | 312 | for (std::size_t index = 0; index < new_samples.size(); ++index) { |
| 303 | samples[index * 2] = new_samples[index]; | 313 | auto sample = static_cast<float>(new_samples[index]); |
| 304 | samples[index * 2 + 1] = new_samples[index]; | 314 | if (voice_resources[0]->in_use) { |
| 315 | sample *= voice_resources[0]->mix_volumes[0]; | ||
| 316 | } | ||
| 317 | |||
| 318 | samples[index * 2] = static_cast<s16>(sample * info.volume); | ||
| 319 | samples[index * 2 + 1] = static_cast<s16>(sample * info.volume); | ||
| 305 | } | 320 | } |
| 306 | break; | 321 | break; |
| 322 | } | ||
| 307 | case 2: { | 323 | case 2: { |
| 308 | // 2 channel is played as is | 324 | // 2 channel is played as is |
| 309 | samples = std::move(new_samples); | 325 | samples = std::move(new_samples); |
| 326 | const std::size_t sample_count = (samples.size() / 2); | ||
| 327 | for (std::size_t index = 0; index < sample_count; ++index) { | ||
| 328 | const std::size_t index_l = index * 2; | ||
| 329 | const std::size_t index_r = index * 2 + 1; | ||
| 330 | |||
| 331 | auto sample_l = static_cast<float>(samples[index_l]); | ||
| 332 | auto sample_r = static_cast<float>(samples[index_r]); | ||
| 333 | |||
| 334 | if (voice_resources[0]->in_use) { | ||
| 335 | sample_l *= voice_resources[0]->mix_volumes[0]; | ||
| 336 | } | ||
| 337 | |||
| 338 | if (voice_resources[1]->in_use) { | ||
| 339 | sample_r *= voice_resources[1]->mix_volumes[1]; | ||
| 340 | } | ||
| 341 | |||
| 342 | samples[index_l] = static_cast<s16>(sample_l * info.volume); | ||
| 343 | samples[index_r] = static_cast<s16>(sample_r * info.volume); | ||
| 344 | } | ||
| 345 | break; | ||
| 346 | } | ||
| 347 | case 6: { | ||
| 348 | samples.resize((new_samples.size() / 6) * 2); | ||
| 349 | const std::size_t sample_count = samples.size() / 2; | ||
| 350 | |||
| 351 | for (std::size_t index = 0; index < sample_count; ++index) { | ||
| 352 | auto FL = static_cast<float>(new_samples[index * 6]); | ||
| 353 | auto FR = static_cast<float>(new_samples[index * 6 + 1]); | ||
| 354 | auto FC = static_cast<float>(new_samples[index * 6 + 2]); | ||
| 355 | auto BL = static_cast<float>(new_samples[index * 6 + 4]); | ||
| 356 | auto BR = static_cast<float>(new_samples[index * 6 + 5]); | ||
| 357 | |||
| 358 | if (voice_resources[0]->in_use) { | ||
| 359 | FL *= voice_resources[0]->mix_volumes[0]; | ||
| 360 | } | ||
| 361 | if (voice_resources[1]->in_use) { | ||
| 362 | FR *= voice_resources[1]->mix_volumes[1]; | ||
| 363 | } | ||
| 364 | if (voice_resources[2]->in_use) { | ||
| 365 | FC *= voice_resources[2]->mix_volumes[2]; | ||
| 366 | } | ||
| 367 | if (voice_resources[4]->in_use) { | ||
| 368 | BL *= voice_resources[4]->mix_volumes[4]; | ||
| 369 | } | ||
| 370 | if (voice_resources[5]->in_use) { | ||
| 371 | BR *= voice_resources[5]->mix_volumes[5]; | ||
| 372 | } | ||
| 373 | |||
| 374 | samples[index * 2] = | ||
| 375 | static_cast<s16>((0.3694f * FL + 0.2612f * FC + 0.3694f * BL) * info.volume); | ||
| 376 | samples[index * 2 + 1] = | ||
| 377 | static_cast<s16>((0.3694f * FR + 0.2612f * FC + 0.3694f * BR) * info.volume); | ||
| 378 | } | ||
| 310 | break; | 379 | break; |
| 311 | } | 380 | } |
| 312 | default: | 381 | default: |
| @@ -352,11 +421,17 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 352 | if (!voice.IsPlaying()) { | 421 | if (!voice.IsPlaying()) { |
| 353 | continue; | 422 | continue; |
| 354 | } | 423 | } |
| 424 | VoiceChannelHolder resources{}; | ||
| 425 | for (u32 channel = 0; channel < voice.GetInfo().channel_count; channel++) { | ||
| 426 | const auto channel_resource_id = voice.GetInfo().voice_channel_resource_ids[channel]; | ||
| 427 | resources[channel] = &voice_resources[channel_resource_id]; | ||
| 428 | } | ||
| 355 | 429 | ||
| 356 | std::size_t offset{}; | 430 | std::size_t offset{}; |
| 357 | s64 samples_remaining{BUFFER_SIZE}; | 431 | s64 samples_remaining{BUFFER_SIZE}; |
| 358 | while (samples_remaining > 0) { | 432 | while (samples_remaining > 0) { |
| 359 | const std::vector<s16> samples{voice.DequeueSamples(samples_remaining, memory)}; | 433 | const std::vector<s16> samples{ |
| 434 | voice.DequeueSamples(samples_remaining, memory, resources)}; | ||
| 360 | 435 | ||
| 361 | if (samples.empty()) { | 436 | if (samples.empty()) { |
| 362 | break; | 437 | break; |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index b42770fae..1f9114c07 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | 10 | ||
| 11 | #include "audio_core/behavior_info.h" | 11 | #include "audio_core/behavior_info.h" |
| 12 | #include "audio_core/common.h" | ||
| 12 | #include "audio_core/stream.h" | 13 | #include "audio_core/stream.h" |
| 13 | #include "common/common_funcs.h" | 14 | #include "common/common_funcs.h" |
| 14 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| @@ -116,6 +117,14 @@ struct WaveBuffer { | |||
| 116 | }; | 117 | }; |
| 117 | static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); | 118 | static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size"); |
| 118 | 119 | ||
| 120 | struct VoiceResourceInformation { | ||
| 121 | s32_le id{}; | ||
| 122 | std::array<float_le, MAX_MIX_BUFFERS> mix_volumes{}; | ||
| 123 | bool in_use{}; | ||
| 124 | INSERT_PADDING_BYTES(11); | ||
| 125 | }; | ||
| 126 | static_assert(sizeof(VoiceResourceInformation) == 0x70, "VoiceResourceInformation has wrong size"); | ||
| 127 | |||
| 119 | struct VoiceInfo { | 128 | struct VoiceInfo { |
| 120 | u32_le id; | 129 | u32_le id; |
| 121 | u32_le node_id; | 130 | u32_le node_id; |
| @@ -244,6 +253,7 @@ private: | |||
| 244 | AudioRendererParameter worker_params; | 253 | AudioRendererParameter worker_params; |
| 245 | std::shared_ptr<Kernel::WritableEvent> buffer_event; | 254 | std::shared_ptr<Kernel::WritableEvent> buffer_event; |
| 246 | std::vector<VoiceState> voices; | 255 | std::vector<VoiceState> voices; |
| 256 | std::vector<VoiceResourceInformation> voice_resources; | ||
| 247 | std::vector<EffectState> effects; | 257 | std::vector<EffectState> effects; |
| 248 | std::unique_ptr<AudioOut> audio_out; | 258 | std::unique_ptr<AudioOut> audio_out; |
| 249 | StreamPtr stream; | 259 | StreamPtr stream; |
diff --git a/src/audio_core/common.h b/src/audio_core/common.h index 98478b66b..7bb145c53 100644 --- a/src/audio_core/common.h +++ b/src/audio_core/common.h | |||
| @@ -14,6 +14,7 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41}; | |||
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); | 16 | constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); |
| 17 | constexpr std::size_t MAX_MIX_BUFFERS = 24; | ||
| 17 | 18 | ||
| 18 | static constexpr u32 VersionFromRevision(u32_le rev) { | 19 | static constexpr u32 VersionFromRevision(u32_le rev) { |
| 19 | // "REV7" -> 7 | 20 | // "REV7" -> 7 |