diff options
Diffstat (limited to 'src/audio_core/audio_renderer.cpp')
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 541 |
1 files changed, 169 insertions, 372 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index d64452617..fbd87d5bf 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -2,90 +2,42 @@ | |||
| 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 <vector> | ||
| 5 | #include "audio_core/algorithm/interpolate.h" | 6 | #include "audio_core/algorithm/interpolate.h" |
| 6 | #include "audio_core/audio_out.h" | 7 | #include "audio_core/audio_out.h" |
| 7 | #include "audio_core/audio_renderer.h" | 8 | #include "audio_core/audio_renderer.h" |
| 8 | #include "audio_core/codec.h" | 9 | #include "audio_core/codec.h" |
| 9 | #include "audio_core/common.h" | 10 | #include "audio_core/common.h" |
| 11 | #include "audio_core/info_updater.h" | ||
| 12 | #include "audio_core/voice_context.h" | ||
| 10 | #include "common/assert.h" | 13 | #include "common/assert.h" |
| 11 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 12 | #include "core/core.h" | 15 | #include "core/core.h" |
| 13 | #include "core/hle/kernel/writable_event.h" | 16 | #include "core/hle/kernel/writable_event.h" |
| 14 | #include "core/memory.h" | 17 | #include "core/memory.h" |
| 18 | #include "core/settings.h" | ||
| 15 | 19 | ||
| 16 | namespace AudioCore { | 20 | namespace AudioCore { |
| 17 | |||
| 18 | constexpr u32 STREAM_SAMPLE_RATE{48000}; | ||
| 19 | constexpr u32 STREAM_NUM_CHANNELS{2}; | ||
| 20 | using VoiceChannelHolder = std::array<VoiceResourceInformation*, 6>; | ||
| 21 | class AudioRenderer::VoiceState { | ||
| 22 | public: | ||
| 23 | bool IsPlaying() const { | ||
| 24 | return is_in_use && info.play_state == PlayState::Started; | ||
| 25 | } | ||
| 26 | |||
| 27 | const VoiceOutStatus& GetOutStatus() const { | ||
| 28 | return out_status; | ||
| 29 | } | ||
| 30 | |||
| 31 | const VoiceInfo& GetInfo() const { | ||
| 32 | return info; | ||
| 33 | } | ||
| 34 | |||
| 35 | VoiceInfo& GetInfo() { | ||
| 36 | return info; | ||
| 37 | } | ||
| 38 | |||
| 39 | void SetWaveIndex(std::size_t index); | ||
| 40 | std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory, | ||
| 41 | const VoiceChannelHolder& voice_resources); | ||
| 42 | void UpdateState(); | ||
| 43 | void RefreshBuffer(Core::Memory::Memory& memory, const VoiceChannelHolder& voice_resources); | ||
| 44 | |||
| 45 | private: | ||
| 46 | bool is_in_use{}; | ||
| 47 | bool is_refresh_pending{}; | ||
| 48 | std::size_t wave_index{}; | ||
| 49 | std::size_t offset{}; | ||
| 50 | Codec::ADPCMState adpcm_state{}; | ||
| 51 | InterpolationState interp_state{}; | ||
| 52 | std::vector<s16> samples; | ||
| 53 | VoiceOutStatus out_status{}; | ||
| 54 | VoiceInfo info{}; | ||
| 55 | }; | ||
| 56 | |||
| 57 | class AudioRenderer::EffectState { | ||
| 58 | public: | ||
| 59 | const EffectOutStatus& GetOutStatus() const { | ||
| 60 | return out_status; | ||
| 61 | } | ||
| 62 | |||
| 63 | const EffectInStatus& GetInfo() const { | ||
| 64 | return info; | ||
| 65 | } | ||
| 66 | |||
| 67 | EffectInStatus& GetInfo() { | ||
| 68 | return info; | ||
| 69 | } | ||
| 70 | |||
| 71 | void UpdateState(Core::Memory::Memory& memory); | ||
| 72 | |||
| 73 | private: | ||
| 74 | EffectOutStatus out_status{}; | ||
| 75 | EffectInStatus info{}; | ||
| 76 | }; | ||
| 77 | |||
| 78 | AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, | 21 | AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, |
| 79 | AudioRendererParameter params, | 22 | AudioCommon::AudioRendererParameter params, |
| 80 | std::shared_ptr<Kernel::WritableEvent> buffer_event, | 23 | std::shared_ptr<Kernel::WritableEvent> buffer_event, |
| 81 | std::size_t instance_number) | 24 | std::size_t instance_number) |
| 82 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count), | 25 | : worker_params{params}, buffer_event{buffer_event}, |
| 83 | voice_resources(params.voice_count), effects(params.effect_count), memory{memory_} { | 26 | memory_pool_info(params.effect_count + params.voice_count * 4), |
| 27 | voice_context(params.voice_count), effect_context(params.effect_count), mix_context(), | ||
| 28 | sink_context(params.sink_count), splitter_context(), | ||
| 29 | voices(params.voice_count), memory{memory_}, | ||
| 30 | command_generator(worker_params, voice_context, mix_context, splitter_context, memory), | ||
| 31 | temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) { | ||
| 84 | behavior_info.SetUserRevision(params.revision); | 32 | behavior_info.SetUserRevision(params.revision); |
| 33 | splitter_context.Initialize(behavior_info, params.splitter_count, | ||
| 34 | params.num_splitter_send_channels); | ||
| 35 | mix_context.Initialize(behavior_info, params.submix_count + 1); | ||
| 85 | audio_out = std::make_unique<AudioCore::AudioOut>(); | 36 | audio_out = std::make_unique<AudioCore::AudioOut>(); |
| 86 | stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, | 37 | stream = |
| 87 | fmt::format("AudioRenderer-Instance{}", instance_number), | 38 | audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, |
| 88 | [=]() { buffer_event->Signal(); }); | 39 | fmt::format("AudioRenderer-Instance{}", instance_number), |
| 40 | [=]() { buffer_event->Signal(); }); | ||
| 89 | audio_out->StartStream(stream); | 41 | audio_out->StartStream(stream); |
| 90 | 42 | ||
| 91 | QueueMixedBuffer(0); | 43 | QueueMixedBuffer(0); |
| @@ -111,355 +63,200 @@ Stream::State AudioRenderer::GetStreamState() const { | |||
| 111 | return stream->GetState(); | 63 | return stream->GetState(); |
| 112 | } | 64 | } |
| 113 | 65 | ||
| 114 | ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { | 66 | static constexpr s16 ClampToS16(s32 value) { |
| 115 | // Copy UpdateDataHeader struct | 67 | return static_cast<s16>(std::clamp(value, -32768, 32767)); |
| 116 | UpdateDataHeader config{}; | 68 | } |
| 117 | std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader)); | ||
| 118 | u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4); | ||
| 119 | |||
| 120 | if (!behavior_info.UpdateInput(input_params, sizeof(UpdateDataHeader))) { | ||
| 121 | LOG_ERROR(Audio, "Failed to update behavior info input parameters"); | ||
| 122 | return Audren::ERR_INVALID_PARAMETERS; | ||
| 123 | } | ||
| 124 | |||
| 125 | // Copy MemoryPoolInfo structs | ||
| 126 | std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count); | ||
| 127 | std::memcpy(mem_pool_info.data(), | ||
| 128 | input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size, | ||
| 129 | memory_pool_count * sizeof(MemoryPoolInfo)); | ||
| 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 | |||
| 137 | // Copy VoiceInfo structs | ||
| 138 | std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size + | ||
| 139 | config.memory_pools_size + config.voice_resource_size}; | ||
| 140 | for (auto& voice : voices) { | ||
| 141 | std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo)); | ||
| 142 | voice_offset += sizeof(VoiceInfo); | ||
| 143 | } | ||
| 144 | |||
| 145 | std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size + | ||
| 146 | config.memory_pools_size + config.voice_resource_size + | ||
| 147 | config.voices_size}; | ||
| 148 | for (auto& effect : effects) { | ||
| 149 | std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus)); | ||
| 150 | effect_offset += sizeof(EffectInStatus); | ||
| 151 | } | ||
| 152 | |||
| 153 | // Update memory pool state | ||
| 154 | std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); | ||
| 155 | for (std::size_t index = 0; index < memory_pool.size(); ++index) { | ||
| 156 | if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { | ||
| 157 | memory_pool[index].state = MemoryPoolStates::Attached; | ||
| 158 | } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { | ||
| 159 | memory_pool[index].state = MemoryPoolStates::Detached; | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | // Update voices | ||
| 164 | for (auto& voice : voices) { | ||
| 165 | voice.UpdateState(); | ||
| 166 | if (!voice.GetInfo().is_in_use) { | ||
| 167 | continue; | ||
| 168 | } | ||
| 169 | if (voice.GetInfo().is_new) { | ||
| 170 | voice.SetWaveIndex(voice.GetInfo().wave_buffer_head); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | for (auto& effect : effects) { | ||
| 175 | effect.UpdateState(memory); | ||
| 176 | } | ||
| 177 | |||
| 178 | // Release previous buffers and queue next ones for playback | ||
| 179 | ReleaseAndQueueBuffers(); | ||
| 180 | |||
| 181 | // Copy output header | ||
| 182 | UpdateDataHeader response_data{worker_params}; | ||
| 183 | if (behavior_info.IsElapsedFrameCountSupported()) { | ||
| 184 | response_data.render_info = sizeof(RendererInfo); | ||
| 185 | response_data.total_size += sizeof(RendererInfo); | ||
| 186 | } | ||
| 187 | |||
| 188 | std::vector<u8> output_params(response_data.total_size); | ||
| 189 | std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); | ||
| 190 | |||
| 191 | // Copy output memory pool entries | ||
| 192 | std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(), | ||
| 193 | response_data.memory_pools_size); | ||
| 194 | |||
| 195 | // Copy output voice status | ||
| 196 | std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size}; | ||
| 197 | for (const auto& voice : voices) { | ||
| 198 | std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(), | ||
| 199 | sizeof(VoiceOutStatus)); | ||
| 200 | voice_out_status_offset += sizeof(VoiceOutStatus); | ||
| 201 | } | ||
| 202 | 69 | ||
| 203 | std::size_t effect_out_status_offset{ | 70 | ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, |
| 204 | sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + | 71 | std::vector<u8>& output_params) { |
| 205 | response_data.voice_resource_size}; | ||
| 206 | for (const auto& effect : effects) { | ||
| 207 | std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(), | ||
| 208 | sizeof(EffectOutStatus)); | ||
| 209 | effect_out_status_offset += sizeof(EffectOutStatus); | ||
| 210 | } | ||
| 211 | 72 | ||
| 212 | // Update behavior info output | 73 | InfoUpdater info_updater{input_params, output_params, behavior_info}; |
| 213 | const std::size_t behavior_out_status_offset{ | ||
| 214 | sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + | ||
| 215 | response_data.effects_size + response_data.sinks_size + | ||
| 216 | response_data.performance_manager_size}; | ||
| 217 | 74 | ||
| 218 | if (!behavior_info.UpdateOutput(output_params, behavior_out_status_offset)) { | 75 | if (!info_updater.UpdateBehaviorInfo(behavior_info)) { |
| 219 | LOG_ERROR(Audio, "Failed to update behavior info output parameters"); | 76 | LOG_ERROR(Audio, "Failed to update behavior info input parameters"); |
| 220 | return Audren::ERR_INVALID_PARAMETERS; | 77 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 221 | } | 78 | } |
| 222 | 79 | ||
| 223 | if (behavior_info.IsElapsedFrameCountSupported()) { | 80 | if (!info_updater.UpdateMemoryPools(memory_pool_info)) { |
| 224 | const std::size_t renderer_info_offset{ | 81 | LOG_ERROR(Audio, "Failed to update memory pool parameters"); |
| 225 | sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size + | 82 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 226 | response_data.effects_size + response_data.sinks_size + | ||
| 227 | response_data.performance_manager_size + response_data.behavior_size}; | ||
| 228 | RendererInfo renderer_info{}; | ||
| 229 | renderer_info.elasped_frame_count = elapsed_frame_count; | ||
| 230 | std::memcpy(output_params.data() + renderer_info_offset, &renderer_info, | ||
| 231 | sizeof(RendererInfo)); | ||
| 232 | } | 83 | } |
| 233 | 84 | ||
| 234 | return MakeResult(output_params); | 85 | if (!info_updater.UpdateVoiceChannelResources(voice_context)) { |
| 235 | } | 86 | LOG_ERROR(Audio, "Failed to update voice channel resource parameters"); |
| 236 | 87 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | |
| 237 | void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { | ||
| 238 | wave_index = index & 3; | ||
| 239 | is_refresh_pending = true; | ||
| 240 | } | ||
| 241 | |||
| 242 | std::vector<s16> AudioRenderer::VoiceState::DequeueSamples( | ||
| 243 | std::size_t sample_count, Core::Memory::Memory& memory, | ||
| 244 | const VoiceChannelHolder& voice_resources) { | ||
| 245 | if (!IsPlaying()) { | ||
| 246 | return {}; | ||
| 247 | } | 88 | } |
| 248 | 89 | ||
| 249 | if (is_refresh_pending) { | 90 | if (!info_updater.UpdateVoices(voice_context, memory_pool_info, 0)) { |
| 250 | RefreshBuffer(memory, voice_resources); | 91 | LOG_ERROR(Audio, "Failed to update voice parameters"); |
| 92 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | ||
| 251 | } | 93 | } |
| 252 | 94 | ||
| 253 | const std::size_t max_size{samples.size() - offset}; | 95 | // TODO(ogniK): Deal with stopped audio renderer but updates still taking place |
| 254 | const std::size_t dequeue_offset{offset}; | 96 | if (!info_updater.UpdateEffects(effect_context, true)) { |
| 255 | std::size_t size{sample_count * STREAM_NUM_CHANNELS}; | 97 | LOG_ERROR(Audio, "Failed to update effect parameters"); |
| 256 | if (size > max_size) { | 98 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 257 | size = max_size; | ||
| 258 | } | 99 | } |
| 259 | 100 | ||
| 260 | out_status.played_sample_count += size / STREAM_NUM_CHANNELS; | 101 | if (behavior_info.IsSplitterSupported()) { |
| 261 | offset += size; | 102 | if (!info_updater.UpdateSplitterInfo(splitter_context)) { |
| 262 | 103 | LOG_ERROR(Audio, "Failed to update splitter parameters"); | |
| 263 | const auto& wave_buffer{info.wave_buffer[wave_index]}; | 104 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 264 | if (offset == samples.size()) { | ||
| 265 | offset = 0; | ||
| 266 | |||
| 267 | if (!wave_buffer.is_looping && wave_buffer.buffer_sz) { | ||
| 268 | SetWaveIndex(wave_index + 1); | ||
| 269 | } | ||
| 270 | |||
| 271 | if (wave_buffer.buffer_sz) { | ||
| 272 | out_status.wave_buffer_consumed++; | ||
| 273 | } | ||
| 274 | |||
| 275 | if (wave_buffer.end_of_stream || wave_buffer.buffer_sz == 0) { | ||
| 276 | info.play_state = PlayState::Paused; | ||
| 277 | } | 105 | } |
| 278 | } | 106 | } |
| 279 | 107 | ||
| 280 | return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size}; | 108 | auto mix_result = |
| 281 | } | 109 | info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count, splitter_context); |
| 282 | 110 | ||
| 283 | void AudioRenderer::VoiceState::UpdateState() { | 111 | if (mix_result.IsError()) { |
| 284 | if (is_in_use && !info.is_in_use) { | 112 | LOG_ERROR(Audio, "Failed to update mix parameters"); |
| 285 | // No longer in use, reset state | 113 | return mix_result; |
| 286 | is_refresh_pending = true; | ||
| 287 | wave_index = 0; | ||
| 288 | offset = 0; | ||
| 289 | out_status = {}; | ||
| 290 | } | 114 | } |
| 291 | is_in_use = info.is_in_use; | ||
| 292 | } | ||
| 293 | 115 | ||
| 294 | void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory, | 116 | // TODO(ogniK): Sinks |
| 295 | const VoiceChannelHolder& voice_resources) { | 117 | if (!info_updater.UpdateSinks(sink_context)) { |
| 296 | const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr; | 118 | LOG_ERROR(Audio, "Failed to update sink parameters"); |
| 297 | const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz; | 119 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 298 | std::vector<s16> new_samples(wave_buffer_size / sizeof(s16)); | ||
| 299 | memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size); | ||
| 300 | |||
| 301 | switch (static_cast<Codec::PcmFormat>(info.sample_format)) { | ||
| 302 | case Codec::PcmFormat::Int16: { | ||
| 303 | // PCM16 is played as-is | ||
| 304 | break; | ||
| 305 | } | ||
| 306 | case Codec::PcmFormat::Adpcm: { | ||
| 307 | // Decode ADPCM to PCM16 | ||
| 308 | Codec::ADPCM_Coeff coeffs; | ||
| 309 | memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff)); | ||
| 310 | new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()), | ||
| 311 | new_samples.size() * sizeof(s16), coeffs, adpcm_state); | ||
| 312 | break; | ||
| 313 | } | 120 | } |
| 314 | default: | ||
| 315 | UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format); | ||
| 316 | break; | ||
| 317 | } | ||
| 318 | |||
| 319 | switch (info.channel_count) { | ||
| 320 | case 1: { | ||
| 321 | // 1 channel is upsampled to 2 channel | ||
| 322 | samples.resize(new_samples.size() * 2); | ||
| 323 | 121 | ||
| 324 | for (std::size_t index = 0; index < new_samples.size(); ++index) { | 122 | // TODO(ogniK): Performance buffer |
| 325 | auto sample = static_cast<float>(new_samples[index]); | 123 | if (!info_updater.UpdatePerformanceBuffer()) { |
| 326 | if (voice_resources[0]->in_use) { | 124 | LOG_ERROR(Audio, "Failed to update performance buffer parameters"); |
| 327 | sample *= voice_resources[0]->mix_volumes[0]; | 125 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 328 | } | ||
| 329 | |||
| 330 | samples[index * 2] = static_cast<s16>(sample * info.volume); | ||
| 331 | samples[index * 2 + 1] = static_cast<s16>(sample * info.volume); | ||
| 332 | } | ||
| 333 | break; | ||
| 334 | } | 126 | } |
| 335 | case 2: { | ||
| 336 | // 2 channel is played as is | ||
| 337 | samples = std::move(new_samples); | ||
| 338 | const std::size_t sample_count = (samples.size() / 2); | ||
| 339 | for (std::size_t index = 0; index < sample_count; ++index) { | ||
| 340 | const std::size_t index_l = index * 2; | ||
| 341 | const std::size_t index_r = index * 2 + 1; | ||
| 342 | |||
| 343 | auto sample_l = static_cast<float>(samples[index_l]); | ||
| 344 | auto sample_r = static_cast<float>(samples[index_r]); | ||
| 345 | |||
| 346 | if (voice_resources[0]->in_use) { | ||
| 347 | sample_l *= voice_resources[0]->mix_volumes[0]; | ||
| 348 | } | ||
| 349 | |||
| 350 | if (voice_resources[1]->in_use) { | ||
| 351 | sample_r *= voice_resources[1]->mix_volumes[1]; | ||
| 352 | } | ||
| 353 | 127 | ||
| 354 | samples[index_l] = static_cast<s16>(sample_l * info.volume); | 128 | if (!info_updater.UpdateErrorInfo(behavior_info)) { |
| 355 | samples[index_r] = static_cast<s16>(sample_r * info.volume); | 129 | LOG_ERROR(Audio, "Failed to update error info"); |
| 356 | } | 130 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 357 | break; | ||
| 358 | } | 131 | } |
| 359 | case 6: { | ||
| 360 | samples.resize((new_samples.size() / 6) * 2); | ||
| 361 | const std::size_t sample_count = samples.size() / 2; | ||
| 362 | |||
| 363 | for (std::size_t index = 0; index < sample_count; ++index) { | ||
| 364 | auto FL = static_cast<float>(new_samples[index * 6]); | ||
| 365 | auto FR = static_cast<float>(new_samples[index * 6 + 1]); | ||
| 366 | auto FC = static_cast<float>(new_samples[index * 6 + 2]); | ||
| 367 | auto BL = static_cast<float>(new_samples[index * 6 + 4]); | ||
| 368 | auto BR = static_cast<float>(new_samples[index * 6 + 5]); | ||
| 369 | |||
| 370 | if (voice_resources[0]->in_use) { | ||
| 371 | FL *= voice_resources[0]->mix_volumes[0]; | ||
| 372 | } | ||
| 373 | if (voice_resources[1]->in_use) { | ||
| 374 | FR *= voice_resources[1]->mix_volumes[1]; | ||
| 375 | } | ||
| 376 | if (voice_resources[2]->in_use) { | ||
| 377 | FC *= voice_resources[2]->mix_volumes[2]; | ||
| 378 | } | ||
| 379 | if (voice_resources[4]->in_use) { | ||
| 380 | BL *= voice_resources[4]->mix_volumes[4]; | ||
| 381 | } | ||
| 382 | if (voice_resources[5]->in_use) { | ||
| 383 | BR *= voice_resources[5]->mix_volumes[5]; | ||
| 384 | } | ||
| 385 | 132 | ||
| 386 | samples[index * 2] = | 133 | if (behavior_info.IsElapsedFrameCountSupported()) { |
| 387 | static_cast<s16>((0.3694f * FL + 0.2612f * FC + 0.3694f * BL) * info.volume); | 134 | if (!info_updater.UpdateRendererInfo(elapsed_frame_count)) { |
| 388 | samples[index * 2 + 1] = | 135 | LOG_ERROR(Audio, "Failed to update renderer info"); |
| 389 | static_cast<s16>((0.3694f * FR + 0.2612f * FC + 0.3694f * BR) * info.volume); | 136 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 390 | } | 137 | } |
| 391 | break; | ||
| 392 | } | ||
| 393 | default: | ||
| 394 | UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count); | ||
| 395 | break; | ||
| 396 | } | 138 | } |
| 139 | // TODO(ogniK): Statistics | ||
| 397 | 140 | ||
| 398 | // Only interpolate when necessary, expensive. | 141 | if (!info_updater.WriteOutputHeader()) { |
| 399 | if (GetInfo().sample_rate != STREAM_SAMPLE_RATE) { | 142 | LOG_ERROR(Audio, "Failed to write output header"); |
| 400 | samples = Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, | 143 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 401 | STREAM_SAMPLE_RATE); | ||
| 402 | } | 144 | } |
| 403 | 145 | ||
| 404 | is_refresh_pending = false; | 146 | // TODO(ogniK): Check when all sections are implemented |
| 405 | } | ||
| 406 | 147 | ||
| 407 | void AudioRenderer::EffectState::UpdateState(Core::Memory::Memory& memory) { | 148 | if (!info_updater.CheckConsumedSize()) { |
| 408 | if (info.is_new) { | 149 | LOG_ERROR(Audio, "Audio buffers were not consumed!"); |
| 409 | out_status.state = EffectStatus::New; | 150 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 410 | } else { | ||
| 411 | if (info.type == Effect::Aux) { | ||
| 412 | ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0, | ||
| 413 | "Aux buffers tried to update"); | ||
| 414 | ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0, | ||
| 415 | "Aux buffers tried to update"); | ||
| 416 | ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0, | ||
| 417 | "Aux buffers tried to update"); | ||
| 418 | ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0, | ||
| 419 | "Aux buffers tried to update"); | ||
| 420 | } | ||
| 421 | } | 151 | } |
| 422 | } | ||
| 423 | 152 | ||
| 424 | static constexpr s16 ClampToS16(s32 value) { | 153 | ReleaseAndQueueBuffers(); |
| 425 | return static_cast<s16>(std::clamp(value, -32768, 32767)); | 154 | |
| 155 | return RESULT_SUCCESS; | ||
| 426 | } | 156 | } |
| 427 | 157 | ||
| 428 | void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | 158 | void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { |
| 429 | constexpr std::size_t BUFFER_SIZE{512}; | 159 | command_generator.PreCommand(); |
| 160 | // Clear mix buffers before our next operation | ||
| 161 | command_generator.ClearMixBuffers(); | ||
| 162 | |||
| 163 | // If the splitter is not in use, sort our mixes | ||
| 164 | if (!splitter_context.UsingSplitter()) { | ||
| 165 | mix_context.SortInfo(); | ||
| 166 | } | ||
| 167 | // Sort our voices | ||
| 168 | voice_context.SortInfo(); | ||
| 169 | |||
| 170 | // Handle samples | ||
| 171 | command_generator.GenerateVoiceCommands(); | ||
| 172 | command_generator.GenerateSubMixCommands(); | ||
| 173 | command_generator.GenerateFinalMixCommands(); | ||
| 174 | |||
| 175 | command_generator.PostCommand(); | ||
| 176 | // Base sample size | ||
| 177 | std::size_t BUFFER_SIZE{worker_params.sample_count}; | ||
| 178 | // Samples | ||
| 430 | std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); | 179 | std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); |
| 431 | 180 | // Make sure to clear our samples | |
| 432 | for (auto& voice : voices) { | 181 | std::memset(buffer.data(), 0, buffer.size() * sizeof(s16)); |
| 433 | if (!voice.IsPlaying()) { | 182 | |
| 434 | continue; | 183 | if (sink_context.InUse()) { |
| 435 | } | 184 | const auto stream_channel_count = stream->GetNumChannels(); |
| 436 | VoiceChannelHolder resources{}; | 185 | const auto buffer_offsets = sink_context.OutputBuffers(); |
| 437 | for (u32 channel = 0; channel < voice.GetInfo().channel_count; channel++) { | 186 | const auto channel_count = buffer_offsets.size(); |
| 438 | const auto channel_resource_id = voice.GetInfo().voice_channel_resource_ids[channel]; | 187 | const auto& final_mix = mix_context.GetFinalMixInfo(); |
| 439 | resources[channel] = &voice_resources[channel_resource_id]; | 188 | const auto& in_params = final_mix.GetInParams(); |
| 189 | std::vector<s32*> mix_buffers(channel_count); | ||
| 190 | for (std::size_t i = 0; i < channel_count; i++) { | ||
| 191 | mix_buffers[i] = | ||
| 192 | command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[i]); | ||
| 440 | } | 193 | } |
| 441 | 194 | ||
| 442 | std::size_t offset{}; | 195 | for (std::size_t i = 0; i < BUFFER_SIZE; i++) { |
| 443 | s64 samples_remaining{BUFFER_SIZE}; | 196 | if (channel_count == 1) { |
| 444 | while (samples_remaining > 0) { | 197 | const auto sample = ClampToS16(mix_buffers[0][i]); |
| 445 | const std::vector<s16> samples{ | 198 | buffer[i * stream_channel_count + 0] = sample; |
| 446 | voice.DequeueSamples(samples_remaining, memory, resources)}; | 199 | if (stream_channel_count > 1) { |
| 447 | 200 | buffer[i * stream_channel_count + 1] = sample; | |
| 448 | if (samples.empty()) { | 201 | } |
| 449 | break; | 202 | if (stream_channel_count == 6) { |
| 450 | } | 203 | buffer[i * stream_channel_count + 2] = sample; |
| 451 | 204 | buffer[i * stream_channel_count + 4] = sample; | |
| 452 | samples_remaining -= samples.size() / stream->GetNumChannels(); | 205 | buffer[i * stream_channel_count + 5] = sample; |
| 453 | 206 | } | |
| 454 | for (const auto& sample : samples) { | 207 | } else if (channel_count == 2) { |
| 455 | const s32 buffer_sample{buffer[offset]}; | 208 | const auto l_sample = ClampToS16(mix_buffers[0][i]); |
| 456 | buffer[offset++] = | 209 | const auto r_sample = ClampToS16(mix_buffers[1][i]); |
| 457 | ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume)); | 210 | if (stream_channel_count == 0) { |
| 211 | buffer[i * stream_channel_count + 0] = l_sample; | ||
| 212 | } else if (stream_channel_count == 2) { | ||
| 213 | buffer[i * stream_channel_count + 0] = l_sample; | ||
| 214 | buffer[i * stream_channel_count + 1] = r_sample; | ||
| 215 | } else if (stream_channel_count == 6) { | ||
| 216 | buffer[i * stream_channel_count + 0] = l_sample; | ||
| 217 | buffer[i * stream_channel_count + 1] = r_sample; | ||
| 218 | |||
| 219 | buffer[i * stream_channel_count + 2] = | ||
| 220 | ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2); | ||
| 221 | |||
| 222 | buffer[i * stream_channel_count + 4] = l_sample; | ||
| 223 | buffer[i * stream_channel_count + 5] = r_sample; | ||
| 224 | } | ||
| 225 | |||
| 226 | } else if (channel_count == 6) { | ||
| 227 | const auto fl_sample = ClampToS16(mix_buffers[0][i]); | ||
| 228 | const auto fr_sample = ClampToS16(mix_buffers[1][i]); | ||
| 229 | const auto fc_sample = ClampToS16(mix_buffers[2][i]); | ||
| 230 | const auto lf_sample = ClampToS16(mix_buffers[3][i]); | ||
| 231 | const auto bl_sample = ClampToS16(mix_buffers[4][i]); | ||
| 232 | const auto br_sample = ClampToS16(mix_buffers[5][i]); | ||
| 233 | |||
| 234 | if (stream_channel_count == 1) { | ||
| 235 | buffer[i * stream_channel_count + 0] = fc_sample; | ||
| 236 | } else if (stream_channel_count == 2) { | ||
| 237 | buffer[i * stream_channel_count + 0] = | ||
| 238 | static_cast<s16>(0.3694f * static_cast<float>(fl_sample) + | ||
| 239 | 0.2612f * static_cast<float>(fc_sample) + | ||
| 240 | 0.3694f * static_cast<float>(bl_sample)); | ||
| 241 | buffer[i * stream_channel_count + 1] = | ||
| 242 | static_cast<s16>(0.3694f * static_cast<float>(fr_sample) + | ||
| 243 | 0.2612f * static_cast<float>(fc_sample) + | ||
| 244 | 0.3694f * static_cast<float>(br_sample)); | ||
| 245 | } else if (stream_channel_count == 6) { | ||
| 246 | buffer[i * stream_channel_count + 0] = fl_sample; | ||
| 247 | buffer[i * stream_channel_count + 1] = fr_sample; | ||
| 248 | buffer[i * stream_channel_count + 2] = fc_sample; | ||
| 249 | buffer[i * stream_channel_count + 3] = lf_sample; | ||
| 250 | buffer[i * stream_channel_count + 4] = bl_sample; | ||
| 251 | buffer[i * stream_channel_count + 5] = br_sample; | ||
| 252 | } | ||
| 458 | } | 253 | } |
| 459 | } | 254 | } |
| 460 | } | 255 | } |
| 256 | |||
| 461 | audio_out->QueueBuffer(stream, tag, std::move(buffer)); | 257 | audio_out->QueueBuffer(stream, tag, std::move(buffer)); |
| 462 | elapsed_frame_count++; | 258 | elapsed_frame_count++; |
| 259 | voice_context.UpdateStateByDspShared(); | ||
| 463 | } | 260 | } |
| 464 | 261 | ||
| 465 | void AudioRenderer::ReleaseAndQueueBuffers() { | 262 | void AudioRenderer::ReleaseAndQueueBuffers() { |