diff options
| author | 2021-07-03 00:24:33 -0700 | |
|---|---|---|
| committer | 2021-07-03 00:24:33 -0700 | |
| commit | 2fc0a760f09c4557d476204ef558743e6f42bd71 (patch) | |
| tree | 87be7a47b82b30d3a2805f47a77b72ef28805af6 /src | |
| parent | Merge pull request #6459 from lat9nq/ubuntu-fixes (diff) | |
| parent | Fix XC2/VOEZ crashing, add audio looping and a few misc fixes (diff) | |
| download | yuzu-2fc0a760f09c4557d476204ef558743e6f42bd71.tar.gz yuzu-2fc0a760f09c4557d476204ef558743e6f42bd71.tar.xz yuzu-2fc0a760f09c4557d476204ef558743e6f42bd71.zip | |
Merge pull request #6498 from Kelebek1/Audio
[audio_core] Decouple audio update and processing, and process at variable rate
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio_core/audio_renderer.cpp | 62 | ||||
| -rw-r--r-- | src/audio_core/audio_renderer.h | 6 | ||||
| -rw-r--r-- | src/audio_core/command_generator.cpp | 76 | ||||
| -rw-r--r-- | src/audio_core/command_generator.h | 8 | ||||
| -rw-r--r-- | src/audio_core/info_updater.cpp | 3 | ||||
| -rw-r--r-- | src/audio_core/voice_context.cpp | 88 | ||||
| -rw-r--r-- | src/audio_core/voice_context.h | 13 | ||||
| -rw-r--r-- | src/core/hle/service/audio/audren_u.cpp | 12 |
8 files changed, 180 insertions, 88 deletions
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 80ffddb10..ccd5ca6cc 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "audio_core/voice_context.h" | 12 | #include "audio_core/voice_context.h" |
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | #include "common/settings.h" | 14 | #include "common/settings.h" |
| 15 | #include "core/core_timing.h" | ||
| 15 | #include "core/memory.h" | 16 | #include "core/memory.h" |
| 16 | 17 | ||
| 17 | namespace { | 18 | namespace { |
| @@ -68,7 +69,9 @@ namespace { | |||
| 68 | } // namespace | 69 | } // namespace |
| 69 | 70 | ||
| 70 | namespace AudioCore { | 71 | namespace AudioCore { |
| 71 | AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, | 72 | constexpr s32 NUM_BUFFERS = 2; |
| 73 | |||
| 74 | AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_, | ||
| 72 | AudioCommon::AudioRendererParameter params, | 75 | AudioCommon::AudioRendererParameter params, |
| 73 | Stream::ReleaseCallback&& release_callback, | 76 | Stream::ReleaseCallback&& release_callback, |
| 74 | std::size_t instance_number) | 77 | std::size_t instance_number) |
| @@ -77,7 +80,8 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory | |||
| 77 | sink_context(params.sink_count), splitter_context(), | 80 | sink_context(params.sink_count), splitter_context(), |
| 78 | voices(params.voice_count), memory{memory_}, | 81 | voices(params.voice_count), memory{memory_}, |
| 79 | command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context, | 82 | command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context, |
| 80 | memory) { | 83 | memory), |
| 84 | core_timing{core_timing_} { | ||
| 81 | behavior_info.SetUserRevision(params.revision); | 85 | behavior_info.SetUserRevision(params.revision); |
| 82 | splitter_context.Initialize(behavior_info, params.splitter_count, | 86 | splitter_context.Initialize(behavior_info, params.splitter_count, |
| 83 | params.num_splitter_send_channels); | 87 | params.num_splitter_send_channels); |
| @@ -86,16 +90,27 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory | |||
| 86 | stream = audio_out->OpenStream( | 90 | stream = audio_out->OpenStream( |
| 87 | core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, | 91 | core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, |
| 88 | fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); | 92 | fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); |
| 89 | audio_out->StartStream(stream); | 93 | process_event = Core::Timing::CreateEvent( |
| 90 | 94 | fmt::format("AudioRenderer-Instance{}-Process", instance_number), | |
| 91 | QueueMixedBuffer(0); | 95 | [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); }); |
| 92 | QueueMixedBuffer(1); | 96 | for (s32 i = 0; i < NUM_BUFFERS; ++i) { |
| 93 | QueueMixedBuffer(2); | 97 | QueueMixedBuffer(i); |
| 94 | QueueMixedBuffer(3); | 98 | } |
| 95 | } | 99 | } |
| 96 | 100 | ||
| 97 | AudioRenderer::~AudioRenderer() = default; | 101 | AudioRenderer::~AudioRenderer() = default; |
| 98 | 102 | ||
| 103 | ResultCode AudioRenderer::Start() { | ||
| 104 | audio_out->StartStream(stream); | ||
| 105 | ReleaseAndQueueBuffers(); | ||
| 106 | return ResultSuccess; | ||
| 107 | } | ||
| 108 | |||
| 109 | ResultCode AudioRenderer::Stop() { | ||
| 110 | audio_out->StopStream(stream); | ||
| 111 | return ResultSuccess; | ||
| 112 | } | ||
| 113 | |||
| 99 | u32 AudioRenderer::GetSampleRate() const { | 114 | u32 AudioRenderer::GetSampleRate() const { |
| 100 | return worker_params.sample_rate; | 115 | return worker_params.sample_rate; |
| 101 | } | 116 | } |
| @@ -114,7 +129,7 @@ Stream::State AudioRenderer::GetStreamState() const { | |||
| 114 | 129 | ||
| 115 | ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, | 130 | ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, |
| 116 | std::vector<u8>& output_params) { | 131 | std::vector<u8>& output_params) { |
| 117 | 132 | std::scoped_lock lock{mutex}; | |
| 118 | InfoUpdater info_updater{input_params, output_params, behavior_info}; | 133 | InfoUpdater info_updater{input_params, output_params, behavior_info}; |
| 119 | 134 | ||
| 120 | if (!info_updater.UpdateBehaviorInfo(behavior_info)) { | 135 | if (!info_updater.UpdateBehaviorInfo(behavior_info)) { |
| @@ -194,9 +209,6 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param | |||
| 194 | LOG_ERROR(Audio, "Audio buffers were not consumed!"); | 209 | LOG_ERROR(Audio, "Audio buffers were not consumed!"); |
| 195 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; | 210 | return AudioCommon::Audren::ERR_INVALID_PARAMETERS; |
| 196 | } | 211 | } |
| 197 | |||
| 198 | ReleaseAndQueueBuffers(); | ||
| 199 | |||
| 200 | return ResultSuccess; | 212 | return ResultSuccess; |
| 201 | } | 213 | } |
| 202 | 214 | ||
| @@ -220,10 +232,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 220 | command_generator.PostCommand(); | 232 | command_generator.PostCommand(); |
| 221 | // Base sample size | 233 | // Base sample size |
| 222 | std::size_t BUFFER_SIZE{worker_params.sample_count}; | 234 | std::size_t BUFFER_SIZE{worker_params.sample_count}; |
| 223 | // Samples | 235 | // Samples, making sure to clear |
| 224 | std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); | 236 | std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels(), 0); |
| 225 | // Make sure to clear our samples | ||
| 226 | std::memset(buffer.data(), 0, buffer.size() * sizeof(s16)); | ||
| 227 | 237 | ||
| 228 | if (sink_context.InUse()) { | 238 | if (sink_context.InUse()) { |
| 229 | const auto stream_channel_count = stream->GetNumChannels(); | 239 | const auto stream_channel_count = stream->GetNumChannels(); |
| @@ -315,10 +325,24 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 315 | } | 325 | } |
| 316 | 326 | ||
| 317 | void AudioRenderer::ReleaseAndQueueBuffers() { | 327 | void AudioRenderer::ReleaseAndQueueBuffers() { |
| 318 | const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)}; | 328 | if (!stream->IsPlaying()) { |
| 319 | for (const auto& tag : released_buffers) { | 329 | return; |
| 320 | QueueMixedBuffer(tag); | ||
| 321 | } | 330 | } |
| 331 | |||
| 332 | { | ||
| 333 | std::scoped_lock lock{mutex}; | ||
| 334 | const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)}; | ||
| 335 | for (const auto& tag : released_buffers) { | ||
| 336 | QueueMixedBuffer(tag); | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | const f32 sample_rate = static_cast<f32>(GetSampleRate()); | ||
| 341 | const f32 sample_count = static_cast<f32>(GetSampleCount()); | ||
| 342 | const f32 consume_rate = sample_rate / (sample_count * (sample_count / 240)); | ||
| 343 | const s32 ms = (1000 / static_cast<s32>(consume_rate)) - 1; | ||
| 344 | const std::chrono::milliseconds next_event_time(std::max(ms / NUM_BUFFERS, 1)); | ||
| 345 | core_timing.ScheduleEvent(next_event_time, process_event, {}); | ||
| 322 | } | 346 | } |
| 323 | 347 | ||
| 324 | } // namespace AudioCore | 348 | } // namespace AudioCore |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 18567f618..88fdd13dd 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <mutex> | ||
| 9 | #include <vector> | 10 | #include <vector> |
| 10 | 11 | ||
| 11 | #include "audio_core/behavior_info.h" | 12 | #include "audio_core/behavior_info.h" |
| @@ -45,6 +46,8 @@ public: | |||
| 45 | 46 | ||
| 46 | [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, | 47 | [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, |
| 47 | std::vector<u8>& output_params); | 48 | std::vector<u8>& output_params); |
| 49 | [[nodiscard]] ResultCode Start(); | ||
| 50 | [[nodiscard]] ResultCode Stop(); | ||
| 48 | void QueueMixedBuffer(Buffer::Tag tag); | 51 | void QueueMixedBuffer(Buffer::Tag tag); |
| 49 | void ReleaseAndQueueBuffers(); | 52 | void ReleaseAndQueueBuffers(); |
| 50 | [[nodiscard]] u32 GetSampleRate() const; | 53 | [[nodiscard]] u32 GetSampleRate() const; |
| @@ -68,6 +71,9 @@ private: | |||
| 68 | Core::Memory::Memory& memory; | 71 | Core::Memory::Memory& memory; |
| 69 | CommandGenerator command_generator; | 72 | CommandGenerator command_generator; |
| 70 | std::size_t elapsed_frame_count{}; | 73 | std::size_t elapsed_frame_count{}; |
| 74 | Core::Timing::CoreTiming& core_timing; | ||
| 75 | std::shared_ptr<Core::Timing::EventType> process_event; | ||
| 76 | std::mutex mutex; | ||
| 71 | }; | 77 | }; |
| 72 | 78 | ||
| 73 | } // namespace AudioCore | 79 | } // namespace AudioCore |
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index 437cc5ccd..27437f1ea 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp | |||
| @@ -795,7 +795,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta | |||
| 795 | state.lowpass_1 = 0.0f; | 795 | state.lowpass_1 = 0.0f; |
| 796 | } else { | 796 | } else { |
| 797 | const auto a = 1.0f - hf_gain; | 797 | const auto a = 1.0f - hf_gain; |
| 798 | const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference / | 798 | const auto b = 2.0f * (2.0f - hf_gain * CosD(256.0f * info.hf_reference / |
| 799 | static_cast<f32>(info.sample_rate))); | 799 | static_cast<f32>(info.sample_rate))); |
| 800 | const auto c = std::sqrt(b * b - 4.0f * a * a); | 800 | const auto c = std::sqrt(b * b - 4.0f * a * a); |
| 801 | 801 | ||
| @@ -843,7 +843,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta | |||
| 843 | } | 843 | } |
| 844 | 844 | ||
| 845 | const auto max_early_delay = state.early_delay_line.GetMaxDelay(); | 845 | const auto max_early_delay = state.early_delay_line.GetMaxDelay(); |
| 846 | const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f); | 846 | const auto reflection_time = 1000.0f * (0.9998f * info.reverb_delay + 0.02f); |
| 847 | for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) { | 847 | for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) { |
| 848 | const auto length = AudioCommon::CalculateDelaySamples( | 848 | const auto length = AudioCommon::CalculateDelaySamples( |
| 849 | sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]); | 849 | sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]); |
| @@ -1004,7 +1004,8 @@ void CommandGenerator::GenerateFinalMixCommand() { | |||
| 1004 | } | 1004 | } |
| 1005 | 1005 | ||
| 1006 | s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | 1006 | s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, |
| 1007 | s32 sample_count, s32 channel, std::size_t mix_offset) { | 1007 | s32 sample_start_offset, s32 sample_end_offset, s32 sample_count, |
| 1008 | s32 channel, std::size_t mix_offset) { | ||
| 1008 | const auto& in_params = voice_info.GetInParams(); | 1009 | const auto& in_params = voice_info.GetInParams(); |
| 1009 | const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; | 1010 | const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; |
| 1010 | if (wave_buffer.buffer_address == 0) { | 1011 | if (wave_buffer.buffer_address == 0) { |
| @@ -1013,14 +1014,12 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s | |||
| 1013 | if (wave_buffer.buffer_size == 0) { | 1014 | if (wave_buffer.buffer_size == 0) { |
| 1014 | return 0; | 1015 | return 0; |
| 1015 | } | 1016 | } |
| 1016 | if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { | 1017 | if (sample_end_offset < sample_start_offset) { |
| 1017 | return 0; | 1018 | return 0; |
| 1018 | } | 1019 | } |
| 1019 | const auto samples_remaining = | 1020 | const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; |
| 1020 | (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; | ||
| 1021 | const auto start_offset = | 1021 | const auto start_offset = |
| 1022 | ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) * | 1022 | ((dsp_state.offset + sample_start_offset) * in_params.channel_count) * sizeof(s16); |
| 1023 | sizeof(s16); | ||
| 1024 | const auto buffer_pos = wave_buffer.buffer_address + start_offset; | 1023 | const auto buffer_pos = wave_buffer.buffer_address + start_offset; |
| 1025 | const auto samples_processed = std::min(sample_count, samples_remaining); | 1024 | const auto samples_processed = std::min(sample_count, samples_remaining); |
| 1026 | 1025 | ||
| @@ -1044,8 +1043,8 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s | |||
| 1044 | } | 1043 | } |
| 1045 | 1044 | ||
| 1046 | s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, | 1045 | s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, |
| 1047 | s32 sample_count, [[maybe_unused]] s32 channel, | 1046 | s32 sample_start_offset, s32 sample_end_offset, s32 sample_count, |
| 1048 | std::size_t mix_offset) { | 1047 | [[maybe_unused]] s32 channel, std::size_t mix_offset) { |
| 1049 | const auto& in_params = voice_info.GetInParams(); | 1048 | const auto& in_params = voice_info.GetInParams(); |
| 1050 | const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; | 1049 | const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; |
| 1051 | if (wave_buffer.buffer_address == 0) { | 1050 | if (wave_buffer.buffer_address == 0) { |
| @@ -1054,7 +1053,7 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s | |||
| 1054 | if (wave_buffer.buffer_size == 0) { | 1053 | if (wave_buffer.buffer_size == 0) { |
| 1055 | return 0; | 1054 | return 0; |
| 1056 | } | 1055 | } |
| 1057 | if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { | 1056 | if (sample_end_offset < sample_start_offset) { |
| 1058 | return 0; | 1057 | return 0; |
| 1059 | } | 1058 | } |
| 1060 | 1059 | ||
| @@ -1079,10 +1078,9 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s | |||
| 1079 | s32 coef1 = coeffs[idx * 2]; | 1078 | s32 coef1 = coeffs[idx * 2]; |
| 1080 | s32 coef2 = coeffs[idx * 2 + 1]; | 1079 | s32 coef2 = coeffs[idx * 2 + 1]; |
| 1081 | 1080 | ||
| 1082 | const auto samples_remaining = | 1081 | const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; |
| 1083 | (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; | ||
| 1084 | const auto samples_processed = std::min(sample_count, samples_remaining); | 1082 | const auto samples_processed = std::min(sample_count, samples_remaining); |
| 1085 | const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset; | 1083 | const auto sample_pos = dsp_state.offset + sample_start_offset; |
| 1086 | 1084 | ||
| 1087 | const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME; | 1085 | const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME; |
| 1088 | auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) + | 1086 | auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) + |
| @@ -1210,9 +1208,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o | |||
| 1210 | } | 1208 | } |
| 1211 | 1209 | ||
| 1212 | std::size_t temp_mix_offset{}; | 1210 | std::size_t temp_mix_offset{}; |
| 1213 | bool is_buffer_completed{false}; | ||
| 1214 | auto samples_remaining = sample_count; | 1211 | auto samples_remaining = sample_count; |
| 1215 | while (samples_remaining > 0 && !is_buffer_completed) { | 1212 | while (samples_remaining > 0) { |
| 1216 | const auto samples_to_output = std::min(samples_remaining, min_required_samples); | 1213 | const auto samples_to_output = std::min(samples_remaining, min_required_samples); |
| 1217 | const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15; | 1214 | const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15; |
| 1218 | 1215 | ||
| @@ -1229,24 +1226,38 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o | |||
| 1229 | const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; | 1226 | const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; |
| 1230 | // No more data can be read | 1227 | // No more data can be read |
| 1231 | if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) { | 1228 | if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) { |
| 1232 | is_buffer_completed = true; | ||
| 1233 | break; | 1229 | break; |
| 1234 | } | 1230 | } |
| 1235 | 1231 | ||
| 1236 | if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 && | 1232 | if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 && |
| 1237 | wave_buffer.context_address != 0 && wave_buffer.context_size != 0) { | 1233 | wave_buffer.context_address != 0 && wave_buffer.context_size != 0) { |
| 1238 | // TODO(ogniK): ADPCM loop context | 1234 | memory.ReadBlock(wave_buffer.context_address, &dsp_state.context, |
| 1235 | sizeof(ADPCMContext)); | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | s32 samples_offset_start; | ||
| 1239 | s32 samples_offset_end; | ||
| 1240 | if (dsp_state.loop_count > 0 && wave_buffer.loop_start_sample != 0 && | ||
| 1241 | wave_buffer.loop_end_sample != 0 && | ||
| 1242 | wave_buffer.loop_start_sample <= wave_buffer.loop_end_sample) { | ||
| 1243 | samples_offset_start = wave_buffer.loop_start_sample; | ||
| 1244 | samples_offset_end = wave_buffer.loop_end_sample; | ||
| 1245 | } else { | ||
| 1246 | samples_offset_start = wave_buffer.start_sample_offset; | ||
| 1247 | samples_offset_end = wave_buffer.end_sample_offset; | ||
| 1239 | } | 1248 | } |
| 1240 | 1249 | ||
| 1241 | s32 samples_decoded{0}; | 1250 | s32 samples_decoded{0}; |
| 1242 | switch (in_params.sample_format) { | 1251 | switch (in_params.sample_format) { |
| 1243 | case SampleFormat::Pcm16: | 1252 | case SampleFormat::Pcm16: |
| 1244 | samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read, | 1253 | samples_decoded = |
| 1245 | channel, temp_mix_offset); | 1254 | DecodePcm16(voice_info, dsp_state, samples_offset_start, samples_offset_end, |
| 1255 | samples_to_read - samples_read, channel, temp_mix_offset); | ||
| 1246 | break; | 1256 | break; |
| 1247 | case SampleFormat::Adpcm: | 1257 | case SampleFormat::Adpcm: |
| 1248 | samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read, | 1258 | samples_decoded = |
| 1249 | channel, temp_mix_offset); | 1259 | DecodeAdpcm(voice_info, dsp_state, samples_offset_start, samples_offset_end, |
| 1260 | samples_to_read - samples_read, channel, temp_mix_offset); | ||
| 1250 | break; | 1261 | break; |
| 1251 | default: | 1262 | default: |
| 1252 | UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); | 1263 | UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); |
| @@ -1257,15 +1268,19 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o | |||
| 1257 | dsp_state.offset += samples_decoded; | 1268 | dsp_state.offset += samples_decoded; |
| 1258 | dsp_state.played_sample_count += samples_decoded; | 1269 | dsp_state.played_sample_count += samples_decoded; |
| 1259 | 1270 | ||
| 1260 | if (dsp_state.offset >= | 1271 | if (dsp_state.offset >= (samples_offset_end - samples_offset_start) || |
| 1261 | (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) || | ||
| 1262 | samples_decoded == 0) { | 1272 | samples_decoded == 0) { |
| 1263 | // Reset our sample offset | 1273 | // Reset our sample offset |
| 1264 | dsp_state.offset = 0; | 1274 | dsp_state.offset = 0; |
| 1265 | if (wave_buffer.is_looping) { | 1275 | if (wave_buffer.is_looping) { |
| 1266 | if (samples_decoded == 0) { | 1276 | dsp_state.loop_count++; |
| 1277 | if (wave_buffer.loop_count > 0 && | ||
| 1278 | (dsp_state.loop_count > wave_buffer.loop_count || samples_decoded == 0)) { | ||
| 1267 | // End of our buffer | 1279 | // End of our buffer |
| 1268 | is_buffer_completed = true; | 1280 | voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); |
| 1281 | } | ||
| 1282 | |||
| 1283 | if (samples_decoded == 0) { | ||
| 1269 | break; | 1284 | break; |
| 1270 | } | 1285 | } |
| 1271 | 1286 | ||
| @@ -1273,15 +1288,8 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o | |||
| 1273 | dsp_state.played_sample_count = 0; | 1288 | dsp_state.played_sample_count = 0; |
| 1274 | } | 1289 | } |
| 1275 | } else { | 1290 | } else { |
| 1276 | |||
| 1277 | // Update our wave buffer states | 1291 | // Update our wave buffer states |
| 1278 | dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false; | 1292 | voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); |
| 1279 | dsp_state.wave_buffer_consumed++; | ||
| 1280 | dsp_state.wave_buffer_index = | ||
| 1281 | (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS; | ||
| 1282 | if (wave_buffer.end_of_stream) { | ||
| 1283 | dsp_state.played_sample_count = 0; | ||
| 1284 | } | ||
| 1285 | } | 1293 | } |
| 1286 | } | 1294 | } |
| 1287 | } | 1295 | } |
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h index 2ebb755b0..673e4fbef 100644 --- a/src/audio_core/command_generator.h +++ b/src/audio_core/command_generator.h | |||
| @@ -86,10 +86,10 @@ private: | |||
| 86 | std::vector<u8>& work_buffer); | 86 | std::vector<u8>& work_buffer); |
| 87 | void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear); | 87 | void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear); |
| 88 | // DSP Code | 88 | // DSP Code |
| 89 | s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, | 89 | s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset, |
| 90 | s32 channel, std::size_t mix_offset); | 90 | s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset); |
| 91 | s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, | 91 | s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset, |
| 92 | s32 channel, std::size_t mix_offset); | 92 | s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset); |
| 93 | void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state, | 93 | void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state, |
| 94 | s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id); | 94 | s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id); |
| 95 | 95 | ||
diff --git a/src/audio_core/info_updater.cpp b/src/audio_core/info_updater.cpp index 4a5b1b4ab..9b4ca1851 100644 --- a/src/audio_core/info_updater.cpp +++ b/src/audio_core/info_updater.cpp | |||
| @@ -189,9 +189,6 @@ bool InfoUpdater::UpdateVoices(VoiceContext& voice_context, | |||
| 189 | if (voice_in_params.is_new) { | 189 | if (voice_in_params.is_new) { |
| 190 | // Default our values for our voice | 190 | // Default our values for our voice |
| 191 | voice_info.Initialize(); | 191 | voice_info.Initialize(); |
| 192 | if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) { | ||
| 193 | continue; | ||
| 194 | } | ||
| 195 | 192 | ||
| 196 | // Zero out our voice states | 193 | // Zero out our voice states |
| 197 | for (std::size_t channel = 0; channel < channel_count; channel++) { | 194 | for (std::size_t channel = 0; channel < channel_count; channel++) { |
diff --git a/src/audio_core/voice_context.cpp b/src/audio_core/voice_context.cpp index 867b8fc6b..d8c954b60 100644 --- a/src/audio_core/voice_context.cpp +++ b/src/audio_core/voice_context.cpp | |||
| @@ -66,7 +66,7 @@ void ServerVoiceInfo::Initialize() { | |||
| 66 | in_params.last_volume = 0.0f; | 66 | in_params.last_volume = 0.0f; |
| 67 | in_params.biquad_filter.fill({}); | 67 | in_params.biquad_filter.fill({}); |
| 68 | in_params.wave_buffer_count = 0; | 68 | in_params.wave_buffer_count = 0; |
| 69 | in_params.wave_bufffer_head = 0; | 69 | in_params.wave_buffer_head = 0; |
| 70 | in_params.mix_id = AudioCommon::NO_MIX; | 70 | in_params.mix_id = AudioCommon::NO_MIX; |
| 71 | in_params.splitter_info_id = AudioCommon::NO_SPLITTER; | 71 | in_params.splitter_info_id = AudioCommon::NO_SPLITTER; |
| 72 | in_params.additional_params_address = 0; | 72 | in_params.additional_params_address = 0; |
| @@ -75,7 +75,7 @@ void ServerVoiceInfo::Initialize() { | |||
| 75 | out_params.played_sample_count = 0; | 75 | out_params.played_sample_count = 0; |
| 76 | out_params.wave_buffer_consumed = 0; | 76 | out_params.wave_buffer_consumed = 0; |
| 77 | in_params.voice_drop_flag = false; | 77 | in_params.voice_drop_flag = false; |
| 78 | in_params.buffer_mapped = false; | 78 | in_params.buffer_mapped = true; |
| 79 | in_params.wave_buffer_flush_request_count = 0; | 79 | in_params.wave_buffer_flush_request_count = 0; |
| 80 | in_params.was_biquad_filter_enabled.fill(false); | 80 | in_params.was_biquad_filter_enabled.fill(false); |
| 81 | 81 | ||
| @@ -126,7 +126,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in, | |||
| 126 | in_params.volume = voice_in.volume; | 126 | in_params.volume = voice_in.volume; |
| 127 | in_params.biquad_filter = voice_in.biquad_filter; | 127 | in_params.biquad_filter = voice_in.biquad_filter; |
| 128 | in_params.wave_buffer_count = voice_in.wave_buffer_count; | 128 | in_params.wave_buffer_count = voice_in.wave_buffer_count; |
| 129 | in_params.wave_bufffer_head = voice_in.wave_buffer_head; | 129 | in_params.wave_buffer_head = voice_in.wave_buffer_head; |
| 130 | if (behavior_info.IsFlushVoiceWaveBuffersSupported()) { | 130 | if (behavior_info.IsFlushVoiceWaveBuffersSupported()) { |
| 131 | const auto in_request_count = in_params.wave_buffer_flush_request_count; | 131 | const auto in_request_count = in_params.wave_buffer_flush_request_count; |
| 132 | const auto voice_request_count = voice_in.wave_buffer_flush_request_count; | 132 | const auto voice_request_count = voice_in.wave_buffer_flush_request_count; |
| @@ -185,14 +185,16 @@ void ServerVoiceInfo::UpdateWaveBuffers( | |||
| 185 | wave_buffer.buffer_size = 0; | 185 | wave_buffer.buffer_size = 0; |
| 186 | wave_buffer.context_address = 0; | 186 | wave_buffer.context_address = 0; |
| 187 | wave_buffer.context_size = 0; | 187 | wave_buffer.context_size = 0; |
| 188 | wave_buffer.loop_start_sample = 0; | ||
| 189 | wave_buffer.loop_end_sample = 0; | ||
| 188 | wave_buffer.sent_to_dsp = true; | 190 | wave_buffer.sent_to_dsp = true; |
| 189 | } | 191 | } |
| 190 | 192 | ||
| 191 | // Mark all our wave buffers as invalid | 193 | // Mark all our wave buffers as invalid |
| 192 | for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count); | 194 | for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count); |
| 193 | channel++) { | 195 | channel++) { |
| 194 | for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) { | 196 | for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; ++i) { |
| 195 | is_valid = false; | 197 | voice_states[channel]->is_wave_buffer_valid[i] = false; |
| 196 | } | 198 | } |
| 197 | } | 199 | } |
| 198 | } | 200 | } |
| @@ -211,7 +213,7 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, | |||
| 211 | const WaveBuffer& in_wave_buffer, SampleFormat sample_format, | 213 | const WaveBuffer& in_wave_buffer, SampleFormat sample_format, |
| 212 | bool is_buffer_valid, | 214 | bool is_buffer_valid, |
| 213 | [[maybe_unused]] BehaviorInfo& behavior_info) { | 215 | [[maybe_unused]] BehaviorInfo& behavior_info) { |
| 214 | if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) { | 216 | if (!is_buffer_valid && out_wavebuffer.sent_to_dsp && out_wavebuffer.buffer_address != 0) { |
| 215 | out_wavebuffer.buffer_address = 0; | 217 | out_wavebuffer.buffer_address = 0; |
| 216 | out_wavebuffer.buffer_size = 0; | 218 | out_wavebuffer.buffer_size = 0; |
| 217 | } | 219 | } |
| @@ -219,11 +221,40 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, | |||
| 219 | if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) { | 221 | if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) { |
| 220 | // Validate sample offset sizings | 222 | // Validate sample offset sizings |
| 221 | if (sample_format == SampleFormat::Pcm16) { | 223 | if (sample_format == SampleFormat::Pcm16) { |
| 222 | const auto buffer_size = in_wave_buffer.buffer_size; | 224 | const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size); |
| 223 | if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 || | 225 | const s64 start = sizeof(s16) * in_wave_buffer.start_sample_offset; |
| 224 | (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) || | 226 | const s64 end = sizeof(s16) * in_wave_buffer.end_sample_offset; |
| 225 | (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) { | 227 | if (0 > start || start > buffer_size || 0 > end || end > buffer_size) { |
| 226 | // TODO(ogniK): Write error info | 228 | // TODO(ogniK): Write error info |
| 229 | LOG_ERROR(Audio, | ||
| 230 | "PCM16 wavebuffer has an invalid size. Buffer has size 0x{:08X}, but " | ||
| 231 | "offsets were " | ||
| 232 | "{:08X} - 0x{:08X}", | ||
| 233 | buffer_size, sizeof(s16) * in_wave_buffer.start_sample_offset, | ||
| 234 | sizeof(s16) * in_wave_buffer.end_sample_offset); | ||
| 235 | return; | ||
| 236 | } | ||
| 237 | } else if (sample_format == SampleFormat::Adpcm) { | ||
| 238 | const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size); | ||
| 239 | const s64 start_frames = in_wave_buffer.start_sample_offset / 14; | ||
| 240 | const s64 start_extra = in_wave_buffer.start_sample_offset % 14 == 0 | ||
| 241 | ? 0 | ||
| 242 | : (in_wave_buffer.start_sample_offset % 14) / 2 + 1 + | ||
| 243 | (in_wave_buffer.start_sample_offset % 2); | ||
| 244 | const s64 start = start_frames * 8 + start_extra; | ||
| 245 | const s64 end_frames = in_wave_buffer.end_sample_offset / 14; | ||
| 246 | const s64 end_extra = in_wave_buffer.end_sample_offset % 14 == 0 | ||
| 247 | ? 0 | ||
| 248 | : (in_wave_buffer.end_sample_offset % 14) / 2 + 1 + | ||
| 249 | (in_wave_buffer.end_sample_offset % 2); | ||
| 250 | const s64 end = end_frames * 8 + end_extra; | ||
| 251 | if (in_wave_buffer.start_sample_offset < 0 || start > buffer_size || | ||
| 252 | in_wave_buffer.end_sample_offset < 0 || end > buffer_size) { | ||
| 253 | LOG_ERROR(Audio, | ||
| 254 | "ADPMC wavebuffer has an invalid size. Buffer has size 0x{:08X}, but " | ||
| 255 | "offsets were " | ||
| 256 | "{:08X} - 0x{:08X}", | ||
| 257 | in_wave_buffer.buffer_size, start, end); | ||
| 227 | return; | 258 | return; |
| 228 | } | 259 | } |
| 229 | } | 260 | } |
| @@ -239,29 +270,34 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, | |||
| 239 | out_wavebuffer.buffer_size = in_wave_buffer.buffer_size; | 270 | out_wavebuffer.buffer_size = in_wave_buffer.buffer_size; |
| 240 | out_wavebuffer.context_address = in_wave_buffer.context_address; | 271 | out_wavebuffer.context_address = in_wave_buffer.context_address; |
| 241 | out_wavebuffer.context_size = in_wave_buffer.context_size; | 272 | out_wavebuffer.context_size = in_wave_buffer.context_size; |
| 273 | out_wavebuffer.loop_start_sample = in_wave_buffer.loop_start_sample; | ||
| 274 | out_wavebuffer.loop_end_sample = in_wave_buffer.loop_end_sample; | ||
| 242 | in_params.buffer_mapped = | 275 | in_params.buffer_mapped = |
| 243 | in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0; | 276 | in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0; |
| 244 | // TODO(ogniK): Pool mapper attachment | 277 | // TODO(ogniK): Pool mapper attachment |
| 245 | // TODO(ogniK): IsAdpcmLoopContextBugFixed | 278 | // TODO(ogniK): IsAdpcmLoopContextBugFixed |
| 279 | if (sample_format == SampleFormat::Adpcm && in_wave_buffer.context_address != 0 && | ||
| 280 | in_wave_buffer.context_size != 0 && behavior_info.IsAdpcmLoopContextBugFixed()) { | ||
| 281 | } else { | ||
| 282 | out_wavebuffer.context_address = 0; | ||
| 283 | out_wavebuffer.context_size = 0; | ||
| 284 | } | ||
| 246 | } | 285 | } |
| 247 | } | 286 | } |
| 248 | 287 | ||
| 249 | void ServerVoiceInfo::WriteOutStatus( | 288 | void ServerVoiceInfo::WriteOutStatus( |
| 250 | VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in, | 289 | VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in, |
| 251 | std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) { | 290 | std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) { |
| 252 | if (voice_in.is_new) { | 291 | if (voice_in.is_new || in_params.is_new) { |
| 253 | in_params.is_new = true; | 292 | in_params.is_new = true; |
| 254 | voice_out.wave_buffer_consumed = 0; | 293 | voice_out.wave_buffer_consumed = 0; |
| 255 | voice_out.played_sample_count = 0; | 294 | voice_out.played_sample_count = 0; |
| 256 | voice_out.voice_dropped = false; | 295 | voice_out.voice_dropped = false; |
| 257 | } else if (!in_params.is_new) { | ||
| 258 | voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed; | ||
| 259 | voice_out.played_sample_count = voice_states[0]->played_sample_count; | ||
| 260 | voice_out.voice_dropped = in_params.voice_drop_flag; | ||
| 261 | } else { | 296 | } else { |
| 262 | voice_out.wave_buffer_consumed = 0; | 297 | const auto& state = voice_states[0]; |
| 263 | voice_out.played_sample_count = 0; | 298 | voice_out.wave_buffer_consumed = state->wave_buffer_consumed; |
| 264 | voice_out.voice_dropped = false; | 299 | voice_out.played_sample_count = state->played_sample_count; |
| 300 | voice_out.voice_dropped = state->voice_dropped; | ||
| 265 | } | 301 | } |
| 266 | } | 302 | } |
| 267 | 303 | ||
| @@ -283,7 +319,8 @@ ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() { | |||
| 283 | 319 | ||
| 284 | bool ServerVoiceInfo::ShouldSkip() const { | 320 | bool ServerVoiceInfo::ShouldSkip() const { |
| 285 | // TODO(ogniK): Handle unmapped wave buffers or parameters | 321 | // TODO(ogniK): Handle unmapped wave buffers or parameters |
| 286 | return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag; | 322 | return !in_params.in_use || in_params.wave_buffer_count == 0 || !in_params.buffer_mapped || |
| 323 | in_params.voice_drop_flag; | ||
| 287 | } | 324 | } |
| 288 | 325 | ||
| 289 | bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) { | 326 | bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) { |
| @@ -381,7 +418,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration( | |||
| 381 | void ServerVoiceInfo::FlushWaveBuffers( | 418 | void ServerVoiceInfo::FlushWaveBuffers( |
| 382 | u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, | 419 | u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, |
| 383 | s32 channel_count) { | 420 | s32 channel_count) { |
| 384 | auto wave_head = in_params.wave_bufffer_head; | 421 | auto wave_head = in_params.wave_buffer_head; |
| 385 | 422 | ||
| 386 | for (u8 i = 0; i < flush_count; i++) { | 423 | for (u8 i = 0; i < flush_count; i++) { |
| 387 | in_params.wave_buffer[wave_head].sent_to_dsp = true; | 424 | in_params.wave_buffer[wave_head].sent_to_dsp = true; |
| @@ -401,6 +438,17 @@ bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const { | |||
| 401 | return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end(); | 438 | return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end(); |
| 402 | } | 439 | } |
| 403 | 440 | ||
| 441 | void ServerVoiceInfo::SetWaveBufferCompleted(VoiceState& dsp_state, | ||
| 442 | const ServerWaveBuffer& wave_buffer) { | ||
| 443 | dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false; | ||
| 444 | dsp_state.wave_buffer_consumed++; | ||
| 445 | dsp_state.wave_buffer_index = (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS; | ||
| 446 | dsp_state.loop_count = 0; | ||
| 447 | if (wave_buffer.end_of_stream) { | ||
| 448 | dsp_state.played_sample_count = 0; | ||
| 449 | } | ||
| 450 | } | ||
| 451 | |||
| 404 | VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} { | 452 | VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} { |
| 405 | for (std::size_t i = 0; i < voice_count; i++) { | 453 | for (std::size_t i = 0; i < voice_count; i++) { |
| 406 | voice_channel_resources.emplace_back(static_cast<s32>(i)); | 454 | voice_channel_resources.emplace_back(static_cast<s32>(i)); |
diff --git a/src/audio_core/voice_context.h b/src/audio_core/voice_context.h index 70359cadb..e1050897b 100644 --- a/src/audio_core/voice_context.h +++ b/src/audio_core/voice_context.h | |||
| @@ -60,10 +60,12 @@ struct WaveBuffer { | |||
| 60 | u8 is_looping{}; | 60 | u8 is_looping{}; |
| 61 | u8 end_of_stream{}; | 61 | u8 end_of_stream{}; |
| 62 | u8 sent_to_server{}; | 62 | u8 sent_to_server{}; |
| 63 | INSERT_PADDING_BYTES(5); | 63 | INSERT_PADDING_BYTES(1); |
| 64 | s32 loop_count{}; | ||
| 64 | u64 context_address{}; | 65 | u64 context_address{}; |
| 65 | u64 context_size{}; | 66 | u64 context_size{}; |
| 66 | INSERT_PADDING_BYTES(8); | 67 | u32 loop_start_sample{}; |
| 68 | u32 loop_end_sample{}; | ||
| 67 | }; | 69 | }; |
| 68 | static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size"); | 70 | static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size"); |
| 69 | 71 | ||
| @@ -76,6 +78,9 @@ struct ServerWaveBuffer { | |||
| 76 | bool end_of_stream{}; | 78 | bool end_of_stream{}; |
| 77 | VAddr context_address{}; | 79 | VAddr context_address{}; |
| 78 | std::size_t context_size{}; | 80 | std::size_t context_size{}; |
| 81 | s32 loop_count{}; | ||
| 82 | u32 loop_start_sample{}; | ||
| 83 | u32 loop_end_sample{}; | ||
| 79 | bool sent_to_dsp{true}; | 84 | bool sent_to_dsp{true}; |
| 80 | }; | 85 | }; |
| 81 | 86 | ||
| @@ -108,6 +113,7 @@ struct VoiceState { | |||
| 108 | u32 external_context_size; | 113 | u32 external_context_size; |
| 109 | bool is_external_context_used; | 114 | bool is_external_context_used; |
| 110 | bool voice_dropped; | 115 | bool voice_dropped; |
| 116 | s32 loop_count; | ||
| 111 | }; | 117 | }; |
| 112 | 118 | ||
| 113 | class VoiceChannelResource { | 119 | class VoiceChannelResource { |
| @@ -206,7 +212,7 @@ public: | |||
| 206 | float last_volume{}; | 212 | float last_volume{}; |
| 207 | std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{}; | 213 | std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{}; |
| 208 | s32 wave_buffer_count{}; | 214 | s32 wave_buffer_count{}; |
| 209 | s16 wave_bufffer_head{}; | 215 | s16 wave_buffer_head{}; |
| 210 | INSERT_PADDING_BYTES(2); | 216 | INSERT_PADDING_BYTES(2); |
| 211 | BehaviorFlags behavior_flags{}; | 217 | BehaviorFlags behavior_flags{}; |
| 212 | VAddr additional_params_address{}; | 218 | VAddr additional_params_address{}; |
| @@ -252,6 +258,7 @@ public: | |||
| 252 | void FlushWaveBuffers(u8 flush_count, | 258 | void FlushWaveBuffers(u8 flush_count, |
| 253 | std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, | 259 | std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, |
| 254 | s32 channel_count); | 260 | s32 channel_count); |
| 261 | void SetWaveBufferCompleted(VoiceState& dsp_state, const ServerWaveBuffer& wave_buffer); | ||
| 255 | 262 | ||
| 256 | private: | 263 | private: |
| 257 | std::vector<s16> stored_samples; | 264 | std::vector<s16> stored_samples; |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index 800feba6e..7583d68b2 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -96,7 +96,7 @@ private: | |||
| 96 | void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { | 96 | void RequestUpdateImpl(Kernel::HLERequestContext& ctx) { |
| 97 | LOG_DEBUG(Service_Audio, "(STUBBED) called"); | 97 | LOG_DEBUG(Service_Audio, "(STUBBED) called"); |
| 98 | 98 | ||
| 99 | std::vector<u8> output_params(ctx.GetWriteBufferSize()); | 99 | std::vector<u8> output_params(ctx.GetWriteBufferSize(), 0); |
| 100 | auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); | 100 | auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params); |
| 101 | 101 | ||
| 102 | if (result.IsSuccess()) { | 102 | if (result.IsSuccess()) { |
| @@ -110,17 +110,19 @@ private: | |||
| 110 | void Start(Kernel::HLERequestContext& ctx) { | 110 | void Start(Kernel::HLERequestContext& ctx) { |
| 111 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 111 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 112 | 112 | ||
| 113 | IPC::ResponseBuilder rb{ctx, 2}; | 113 | const auto result = renderer->Start(); |
| 114 | 114 | ||
| 115 | rb.Push(ResultSuccess); | 115 | IPC::ResponseBuilder rb{ctx, 2}; |
| 116 | rb.Push(result); | ||
| 116 | } | 117 | } |
| 117 | 118 | ||
| 118 | void Stop(Kernel::HLERequestContext& ctx) { | 119 | void Stop(Kernel::HLERequestContext& ctx) { |
| 119 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 120 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 120 | 121 | ||
| 121 | IPC::ResponseBuilder rb{ctx, 2}; | 122 | const auto result = renderer->Stop(); |
| 122 | 123 | ||
| 123 | rb.Push(ResultSuccess); | 124 | IPC::ResponseBuilder rb{ctx, 2}; |
| 125 | rb.Push(result); | ||
| 124 | } | 126 | } |
| 125 | 127 | ||
| 126 | void QuerySystemEvent(Kernel::HLERequestContext& ctx) { | 128 | void QuerySystemEvent(Kernel::HLERequestContext& ctx) { |