diff options
Diffstat (limited to 'src/audio_core')
-rw-r--r-- | src/audio_core/CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/audio_core/audio_renderer.cpp | 92 | ||||
-rw-r--r-- | src/audio_core/audio_renderer.h | 6 | ||||
-rw-r--r-- | src/audio_core/command_generator.cpp | 216 | ||||
-rw-r--r-- | src/audio_core/command_generator.h | 32 | ||||
-rw-r--r-- | src/audio_core/common.h | 2 | ||||
-rw-r--r-- | src/audio_core/info_updater.cpp | 3 | ||||
-rw-r--r-- | src/audio_core/sink_context.cpp | 15 | ||||
-rw-r--r-- | src/audio_core/sink_context.h | 2 | ||||
-rw-r--r-- | src/audio_core/voice_context.cpp | 88 | ||||
-rw-r--r-- | src/audio_core/voice_context.h | 13 |
11 files changed, 299 insertions, 173 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt index d25a1a645..090dd19b1 100644 --- a/src/audio_core/CMakeLists.txt +++ b/src/audio_core/CMakeLists.txt @@ -51,9 +51,6 @@ if (NOT MSVC) target_compile_options(audio_core PRIVATE -Werror=conversion -Werror=ignored-qualifiers - -Werror=implicit-fallthrough - -Werror=reorder - -Werror=sign-compare -Werror=shadow -Werror=unused-parameter -Werror=unused-variable diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 80ffddb10..7dba739b4 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp @@ -12,6 +12,7 @@ #include "audio_core/voice_context.h" #include "common/logging/log.h" #include "common/settings.h" +#include "core/core_timing.h" #include "core/memory.h" namespace { @@ -28,10 +29,9 @@ namespace { (static_cast<float>(r_channel) * r_mix_amount))); } -[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel, - s16 fc_channel, - [[maybe_unused]] s16 lf_channel, - s16 bl_channel, s16 br_channel) { +[[maybe_unused, nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2( + s16 fl_channel, s16 fr_channel, s16 fc_channel, [[maybe_unused]] s16 lf_channel, s16 bl_channel, + s16 br_channel) { // Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels // are mixed to be 36.94% @@ -56,11 +56,11 @@ namespace { const std::array<float_le, 4>& coeff) { const auto left = static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] + - static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[0]; + static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(bl_channel) * coeff[3]; const auto right = static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] + - static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[0]; + static_cast<float>(lf_channel) * coeff[2] + static_cast<float>(br_channel) * coeff[3]; return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))}; } @@ -68,7 +68,9 @@ namespace { } // namespace namespace AudioCore { -AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_, +constexpr s32 NUM_BUFFERS = 2; + +AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& memory_, AudioCommon::AudioRendererParameter params, Stream::ReleaseCallback&& release_callback, std::size_t instance_number) @@ -77,7 +79,8 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory sink_context(params.sink_count), splitter_context(), voices(params.voice_count), memory{memory_}, command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context, - memory) { + memory), + core_timing{core_timing_} { behavior_info.SetUserRevision(params.revision); splitter_context.Initialize(behavior_info, params.splitter_count, params.num_splitter_send_channels); @@ -86,16 +89,27 @@ AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory stream = audio_out->OpenStream( core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS, fmt::format("AudioRenderer-Instance{}", instance_number), std::move(release_callback)); - audio_out->StartStream(stream); - - QueueMixedBuffer(0); - QueueMixedBuffer(1); - QueueMixedBuffer(2); - QueueMixedBuffer(3); + process_event = Core::Timing::CreateEvent( + fmt::format("AudioRenderer-Instance{}-Process", instance_number), + [this](std::uintptr_t, std::chrono::nanoseconds) { ReleaseAndQueueBuffers(); }); + for (s32 i = 0; i < NUM_BUFFERS; ++i) { + QueueMixedBuffer(i); + } } AudioRenderer::~AudioRenderer() = default; +ResultCode AudioRenderer::Start() { + audio_out->StartStream(stream); + ReleaseAndQueueBuffers(); + return ResultSuccess; +} + +ResultCode AudioRenderer::Stop() { + audio_out->StopStream(stream); + return ResultSuccess; +} + u32 AudioRenderer::GetSampleRate() const { return worker_params.sample_rate; } @@ -114,7 +128,7 @@ Stream::State AudioRenderer::GetStreamState() const { ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params, std::vector<u8>& output_params) { - + std::scoped_lock lock{mutex}; InfoUpdater info_updater{input_params, output_params, behavior_info}; if (!info_updater.UpdateBehaviorInfo(behavior_info)) { @@ -194,9 +208,6 @@ ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_param LOG_ERROR(Audio, "Audio buffers were not consumed!"); return AudioCommon::Audren::ERR_INVALID_PARAMETERS; } - - ReleaseAndQueueBuffers(); - return ResultSuccess; } @@ -220,10 +231,8 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { command_generator.PostCommand(); // Base sample size std::size_t BUFFER_SIZE{worker_params.sample_count}; - // Samples - std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); - // Make sure to clear our samples - std::memset(buffer.data(), 0, buffer.size() * sizeof(s16)); + // Samples, making sure to clear + std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels(), 0); if (sink_context.InUse()) { const auto stream_channel_count = stream->GetNumChannels(); @@ -231,7 +240,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { const auto channel_count = buffer_offsets.size(); const auto& final_mix = mix_context.GetFinalMixInfo(); const auto& in_params = final_mix.GetInParams(); - std::vector<s32*> mix_buffers(channel_count); + std::vector<std::span<s32>> mix_buffers(channel_count); for (std::size_t i = 0; i < channel_count; i++) { mix_buffers[i] = command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[i]); @@ -284,18 +293,11 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample); } else if (stream_channel_count == 2) { // Mix all channels into 2 channels - if (sink_context.HasDownMixingCoefficients()) { - const auto [left, right] = Mix6To2WithCoefficients( - fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample, - sink_context.GetDownmixCoefficients()); - buffer[i * stream_channel_count + 0] = left; - buffer[i * stream_channel_count + 1] = right; - } else { - const auto [left, right] = Mix6To2(fl_sample, fr_sample, fc_sample, - lf_sample, bl_sample, br_sample); - buffer[i * stream_channel_count + 0] = left; - buffer[i * stream_channel_count + 1] = right; - } + const auto [left, right] = Mix6To2WithCoefficients( + fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample, + sink_context.GetDownmixCoefficients()); + buffer[i * stream_channel_count + 0] = left; + buffer[i * stream_channel_count + 1] = right; } else if (stream_channel_count == 6) { // Pass through buffer[i * stream_channel_count + 0] = fl_sample; @@ -315,10 +317,24 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { } void AudioRenderer::ReleaseAndQueueBuffers() { - const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)}; - for (const auto& tag : released_buffers) { - QueueMixedBuffer(tag); + if (!stream->IsPlaying()) { + return; } + + { + std::scoped_lock lock{mutex}; + const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream)}; + for (const auto& tag : released_buffers) { + QueueMixedBuffer(tag); + } + } + + const f32 sample_rate = static_cast<f32>(GetSampleRate()); + const f32 sample_count = static_cast<f32>(GetSampleCount()); + const f32 consume_rate = sample_rate / (sample_count * (sample_count / 240)); + const s32 ms = (1000 / static_cast<s32>(consume_rate)) - 1; + const std::chrono::milliseconds next_event_time(std::max(ms / NUM_BUFFERS, 1)); + core_timing.ScheduleEvent(next_event_time, process_event, {}); } } // 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 @@ #include <array> #include <memory> +#include <mutex> #include <vector> #include "audio_core/behavior_info.h" @@ -45,6 +46,8 @@ public: [[nodiscard]] ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params, std::vector<u8>& output_params); + [[nodiscard]] ResultCode Start(); + [[nodiscard]] ResultCode Stop(); void QueueMixedBuffer(Buffer::Tag tag); void ReleaseAndQueueBuffers(); [[nodiscard]] u32 GetSampleRate() const; @@ -68,6 +71,9 @@ private: Core::Memory::Memory& memory; CommandGenerator command_generator; std::size_t elapsed_frame_count{}; + Core::Timing::CoreTiming& core_timing; + std::shared_ptr<Core::Timing::EventType> process_event; + std::mutex mutex; }; } // namespace AudioCore diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp index 437cc5ccd..b99d0fc91 100644 --- a/src/audio_core/command_generator.cpp +++ b/src/audio_core/command_generator.cpp @@ -31,7 +31,7 @@ constexpr std::array<f32, AudioCommon::I3DL2REVERB_TAPS> EARLY_GAIN{ 0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f}; template <std::size_t N> -void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) { +void ApplyMix(std::span<s32> output, std::span<const s32> input, s32 gain, s32 sample_count) { for (std::size_t i = 0; i < static_cast<std::size_t>(sample_count); i += N) { for (std::size_t j = 0; j < N; j++) { output[i + j] += @@ -40,7 +40,8 @@ void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) { } } -s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sample_count) { +s32 ApplyMixRamp(std::span<s32> output, std::span<const s32> input, float gain, float delta, + s32 sample_count) { s32 x = 0; for (s32 i = 0; i < sample_count; i++) { x = static_cast<s32>(static_cast<float>(input[i]) * gain); @@ -50,20 +51,22 @@ s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sam return x; } -void ApplyGain(s32* output, const s32* input, s32 gain, s32 delta, s32 sample_count) { +void ApplyGain(std::span<s32> output, std::span<const s32> input, s32 gain, s32 delta, + s32 sample_count) { for (s32 i = 0; i < sample_count; i++) { output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15); gain += delta; } } -void ApplyGainWithoutDelta(s32* output, const s32* input, s32 gain, s32 sample_count) { +void ApplyGainWithoutDelta(std::span<s32> output, std::span<const s32> input, s32 gain, + s32 sample_count) { for (s32 i = 0; i < sample_count; i++) { output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15); } } -s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) { +s32 ApplyMixDepop(std::span<s32> output, s32 first_sample, s32 delta, s32 sample_count) { const bool positive = first_sample > 0; auto final_sample = std::abs(first_sample); for (s32 i = 0; i < sample_count; i++) { @@ -128,10 +131,10 @@ constexpr std::array<std::size_t, 20> REVERB_TAP_INDEX_6CH{4, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 3, 3, 3}; template <std::size_t CHANNEL_COUNT> -void ApplyReverbGeneric(I3dl2ReverbState& state, - const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input, - const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output, - s32 sample_count) { +void ApplyReverbGeneric( + I3dl2ReverbState& state, + const std::array<std::span<const s32>, AudioCommon::MAX_CHANNEL_COUNT>& input, + const std::array<std::span<s32>, AudioCommon::MAX_CHANNEL_COUNT>& output, s32 sample_count) { auto GetTapLookup = []() { if constexpr (CHANNEL_COUNT == 1) { @@ -400,7 +403,10 @@ void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, Vo } } else { switch (in_params.sample_format) { + case SampleFormat::Pcm8: case SampleFormat::Pcm16: + case SampleFormat::Pcm32: + case SampleFormat::PcmFloat: DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(channel), dsp_state, channel, worker_params.sample_rate, worker_params.sample_count, in_params.node_id); @@ -454,8 +460,8 @@ void CommandGenerator::GenerateBiquadFilterCommand([[maybe_unused]] s32 mix_buff "input_mix_buffer={}, output_mix_buffer={}", node_id, input_offset, output_offset); } - const auto* input = GetMixBuffer(input_offset); - auto* output = GetMixBuffer(output_offset); + std::span<const s32> input = GetMixBuffer(input_offset); + std::span<s32> output = GetMixBuffer(output_offset); // Biquad filter parameters const auto [n0, n1, n2] = params.numerator; @@ -548,8 +554,8 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E return; } - std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{}; - std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{}; + std::array<std::span<const s32>, AudioCommon::MAX_CHANNEL_COUNT> input{}; + std::array<std::span<s32>, AudioCommon::MAX_CHANNEL_COUNT> output{}; const auto status = params.status; for (s32 i = 0; i < channel_count; i++) { @@ -584,7 +590,8 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E for (s32 i = 0; i < channel_count; i++) { // Only copy if the buffer input and output do not match! if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) { - std::memcpy(output[i], input[i], worker_params.sample_count * sizeof(s32)); + std::memcpy(output[i].data(), input[i].data(), + worker_params.sample_count * sizeof(s32)); } } } @@ -600,8 +607,8 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, for (s32 i = 0; i < channel_count; i++) { // TODO(ogniK): Actually implement biquad filter if (params.input[i] != params.output[i]) { - const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]); - auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]); + std::span<const s32> input = GetMixBuffer(mix_buffer_offset + params.input[i]); + std::span<s32> output = GetMixBuffer(mix_buffer_offset + params.output[i]); ApplyMix<1>(output, input, 32768, worker_params.sample_count); } } @@ -640,14 +647,15 @@ void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* inf if (samples_read != static_cast<int>(worker_params.sample_count) && samples_read <= params.sample_count) { - std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read); + std::memset(GetMixBuffer(output_index).data(), 0, + params.sample_count - samples_read); } } else { AuxInfoDSP empty{}; memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP)); memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP)); if (output_index != input_index) { - std::memcpy(GetMixBuffer(output_index), GetMixBuffer(input_index), + std::memcpy(GetMixBuffer(output_index).data(), GetMixBuffer(input_index).data(), worker_params.sample_count * sizeof(s32)); } } @@ -665,7 +673,7 @@ ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter } s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, - const s32* data, u32 sample_count, u32 write_offset, + std::span<const s32> data, u32 sample_count, u32 write_offset, u32 write_count) { if (max_samples == 0) { return 0; @@ -675,14 +683,14 @@ s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u3 return 0; } - std::size_t data_offset{}; + s32 data_offset{}; u32 remaining = sample_count; while (remaining > 0) { // Get position in buffer const auto base = send_buffer + (offset * sizeof(u32)); const auto samples_to_grab = std::min(max_samples - offset, remaining); // Write to output - memory.WriteBlock(base, (data + data_offset), samples_to_grab * sizeof(u32)); + memory.WriteBlock(base, (data.data() + data_offset), samples_to_grab * sizeof(u32)); offset = (offset + samples_to_grab) % max_samples; remaining -= samples_to_grab; data_offset += samples_to_grab; @@ -695,7 +703,7 @@ s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u3 } s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, - s32* out_data, u32 sample_count, u32 read_offset, + std::span<s32> out_data, u32 sample_count, u32 read_offset, u32 read_count) { if (max_samples == 0) { return 0; @@ -707,15 +715,16 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3 } u32 remaining = sample_count; + s32 data_offset{}; while (remaining > 0) { const auto base = recv_buffer + (offset * sizeof(u32)); const auto samples_to_grab = std::min(max_samples - offset, remaining); std::vector<s32> buffer(samples_to_grab); memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32)); - std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32)); - out_data += samples_to_grab; + std::memcpy(out_data.data() + data_offset, buffer.data(), buffer.size() * sizeof(u32)); offset = (offset + samples_to_grab) % max_samples; remaining -= samples_to_grab; + data_offset += samples_to_grab; } if (read_count != 0) { @@ -795,7 +804,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta state.lowpass_1 = 0.0f; } else { const auto a = 1.0f - hf_gain; - const auto b = 2.0f * (1.0f - hf_gain * CosD(256.0f * info.hf_reference / + const auto b = 2.0f * (2.0f - hf_gain * CosD(256.0f * info.hf_reference / static_cast<f32>(info.sample_rate))); const auto c = std::sqrt(b * b - 4.0f * a * a); @@ -843,7 +852,7 @@ void CommandGenerator::UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbSta } const auto max_early_delay = state.early_delay_line.GetMaxDelay(); - const auto reflection_time = 1000.0f * (0.0098f * info.reverb_delay + 0.02f); + const auto reflection_time = 1000.0f * (0.9998f * info.reverb_delay + 0.02f); for (std::size_t tap = 0; tap < AudioCommon::I3DL2REVERB_TAPS; tap++) { const auto length = AudioCommon::CalculateDelaySamples( sample_rate, 1000.0f * info.reflection_delay + reflection_time * EARLY_TAP_TIMES[tap]); @@ -962,8 +971,8 @@ void CommandGenerator::GenerateMixCommand(std::size_t output_offset, std::size_t node_id, input_offset, output_offset, volume); } - auto* output = GetMixBuffer(output_offset); - const auto* input = GetMixBuffer(input_offset); + std::span<s32> output = GetMixBuffer(output_offset); + std::span<const s32> input = GetMixBuffer(input_offset); const s32 gain = static_cast<s32>(volume * 32768.0f); // Mix with loop unrolling @@ -1003,8 +1012,10 @@ void CommandGenerator::GenerateFinalMixCommand() { } } -s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, - s32 sample_count, s32 channel, std::size_t mix_offset) { +template <typename T> +s32 CommandGenerator::DecodePcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, + s32 sample_start_offset, s32 sample_end_offset, s32 sample_count, + s32 channel, std::size_t mix_offset) { const auto& in_params = voice_info.GetInParams(); const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; if (wave_buffer.buffer_address == 0) { @@ -1013,39 +1024,50 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s if (wave_buffer.buffer_size == 0) { return 0; } - if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { + if (sample_end_offset < sample_start_offset) { return 0; } - const auto samples_remaining = - (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; + const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; const auto start_offset = - ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) * - sizeof(s16); + ((dsp_state.offset + sample_start_offset) * in_params.channel_count) * sizeof(T); const auto buffer_pos = wave_buffer.buffer_address + start_offset; const auto samples_processed = std::min(sample_count, samples_remaining); - if (in_params.channel_count == 1) { - std::vector<s16> buffer(samples_processed); - memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16)); - for (std::size_t i = 0; i < buffer.size(); i++) { - sample_buffer[mix_offset + i] = buffer[i]; - } - } else { - const auto channel_count = in_params.channel_count; - std::vector<s16> buffer(samples_processed * channel_count); - memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16)); + const auto channel_count = in_params.channel_count; + std::vector<T> buffer(samples_processed * channel_count); + memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(T)); + if constexpr (std::is_floating_point_v<T>) { + for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) { + sample_buffer[mix_offset + i] = static_cast<s32>(buffer[i * channel_count + channel] * + std::numeric_limits<s16>::max()); + } + } else if constexpr (sizeof(T) == 1) { + for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) { + sample_buffer[mix_offset + i] = + static_cast<s32>(static_cast<f32>(buffer[i * channel_count + channel] / + std::numeric_limits<s8>::max()) * + std::numeric_limits<s16>::max()); + } + } else if constexpr (sizeof(T) == 2) { for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) { sample_buffer[mix_offset + i] = buffer[i * channel_count + channel]; } + } else { + for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) { + sample_buffer[mix_offset + i] = + static_cast<s32>(static_cast<f32>(buffer[i * channel_count + channel] / + std::numeric_limits<s32>::max()) * + std::numeric_limits<s16>::max()); + } } return samples_processed; } s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, - s32 sample_count, [[maybe_unused]] s32 channel, - std::size_t mix_offset) { + s32 sample_start_offset, s32 sample_end_offset, s32 sample_count, + [[maybe_unused]] s32 channel, std::size_t mix_offset) { const auto& in_params = voice_info.GetInParams(); const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; if (wave_buffer.buffer_address == 0) { @@ -1054,7 +1076,7 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s if (wave_buffer.buffer_size == 0) { return 0; } - if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) { + if (sample_end_offset < sample_start_offset) { return 0; } @@ -1079,10 +1101,9 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s s32 coef1 = coeffs[idx * 2]; s32 coef2 = coeffs[idx * 2 + 1]; - const auto samples_remaining = - (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset; + const auto samples_remaining = (sample_end_offset - sample_start_offset) - dsp_state.offset; const auto samples_processed = std::min(sample_count, samples_remaining); - const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset; + const auto sample_pos = dsp_state.offset + sample_start_offset; const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME; auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) + @@ -1157,12 +1178,14 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s return samples_processed; } -s32* CommandGenerator::GetMixBuffer(std::size_t index) { - return mix_buffer.data() + (index * worker_params.sample_count); +std::span<s32> CommandGenerator::GetMixBuffer(std::size_t index) { + return std::span<s32>(mix_buffer.data() + (index * worker_params.sample_count), + worker_params.sample_count); } -const s32* CommandGenerator::GetMixBuffer(std::size_t index) const { - return mix_buffer.data() + (index * worker_params.sample_count); +std::span<const s32> CommandGenerator::GetMixBuffer(std::size_t index) const { + return std::span<const s32>(mix_buffer.data() + (index * worker_params.sample_count), + worker_params.sample_count); } std::size_t CommandGenerator::GetMixChannelBufferOffset(s32 channel) const { @@ -1173,15 +1196,15 @@ std::size_t CommandGenerator::GetTotalMixBufferCount() const { return worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT; } -s32* CommandGenerator::GetChannelMixBuffer(s32 channel) { +std::span<s32> CommandGenerator::GetChannelMixBuffer(s32 channel) { return GetMixBuffer(worker_params.mix_buffer_count + channel); } -const s32* CommandGenerator::GetChannelMixBuffer(s32 channel) const { +std::span<const s32> CommandGenerator::GetChannelMixBuffer(s32 channel) const { return GetMixBuffer(worker_params.mix_buffer_count + channel); } -void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, +void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::span<s32> output, VoiceState& dsp_state, s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id) { @@ -1193,7 +1216,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate, in_params.mix_id, in_params.splitter_info_id); } - ASSERT_OR_EXECUTE(output != nullptr, { return; }); + ASSERT_OR_EXECUTE(output.data() != nullptr, { return; }); const auto resample_rate = static_cast<s32>( static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) * @@ -1210,9 +1233,9 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o } std::size_t temp_mix_offset{}; - bool is_buffer_completed{false}; + s32 samples_output{}; auto samples_remaining = sample_count; - while (samples_remaining > 0 && !is_buffer_completed) { + while (samples_remaining > 0) { const auto samples_to_output = std::min(samples_remaining, min_required_samples); const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15; @@ -1229,24 +1252,53 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index]; // No more data can be read if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) { - is_buffer_completed = true; break; } if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 && wave_buffer.context_address != 0 && wave_buffer.context_size != 0) { - // TODO(ogniK): ADPCM loop context + memory.ReadBlock(wave_buffer.context_address, &dsp_state.context, + sizeof(ADPCMContext)); + } + + s32 samples_offset_start; + s32 samples_offset_end; + if (dsp_state.loop_count > 0 && wave_buffer.loop_start_sample != 0 && + wave_buffer.loop_end_sample != 0 && + wave_buffer.loop_start_sample <= wave_buffer.loop_end_sample) { + samples_offset_start = wave_buffer.loop_start_sample; + samples_offset_end = wave_buffer.loop_end_sample; + } else { + samples_offset_start = wave_buffer.start_sample_offset; + samples_offset_end = wave_buffer.end_sample_offset; } s32 samples_decoded{0}; switch (in_params.sample_format) { + case SampleFormat::Pcm8: + samples_decoded = + DecodePcm<s8>(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); + break; case SampleFormat::Pcm16: - samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read, - channel, temp_mix_offset); + samples_decoded = + DecodePcm<s16>(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); + break; + case SampleFormat::Pcm32: + samples_decoded = + DecodePcm<s32>(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); + break; + case SampleFormat::PcmFloat: + samples_decoded = + DecodePcm<f32>(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); break; case SampleFormat::Adpcm: - samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read, - channel, temp_mix_offset); + samples_decoded = + DecodeAdpcm(voice_info, dsp_state, samples_offset_start, samples_offset_end, + samples_to_read - samples_read, channel, temp_mix_offset); break; default: UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format); @@ -1257,15 +1309,19 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o dsp_state.offset += samples_decoded; dsp_state.played_sample_count += samples_decoded; - if (dsp_state.offset >= - (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) || + if (dsp_state.offset >= (samples_offset_end - samples_offset_start) || samples_decoded == 0) { // Reset our sample offset dsp_state.offset = 0; if (wave_buffer.is_looping) { - if (samples_decoded == 0) { + dsp_state.loop_count++; + if (wave_buffer.loop_count > 0 && + (dsp_state.loop_count > wave_buffer.loop_count || samples_decoded == 0)) { // End of our buffer - is_buffer_completed = true; + voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); + } + + if (samples_decoded == 0) { break; } @@ -1273,35 +1329,29 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o dsp_state.played_sample_count = 0; } } else { - // Update our wave buffer states - dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false; - dsp_state.wave_buffer_consumed++; - dsp_state.wave_buffer_index = - (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS; - if (wave_buffer.end_of_stream) { - dsp_state.played_sample_count = 0; - } + voice_info.SetWaveBufferCompleted(dsp_state, wave_buffer); } } } if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) { // No need to resample - std::memcpy(output, sample_buffer.data(), samples_read * sizeof(s32)); + std::memcpy(output.data() + samples_output, sample_buffer.data(), + samples_read * sizeof(s32)); } else { std::fill(sample_buffer.begin() + temp_mix_offset, sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read), 0); - AudioCore::Resample(output, sample_buffer.data(), resample_rate, dsp_state.fraction, - samples_to_output); + AudioCore::Resample(output.data() + samples_output, sample_buffer.data(), resample_rate, + dsp_state.fraction, samples_to_output); // Resample for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) { dsp_state.sample_history[i] = sample_buffer[samples_to_read + i]; } } - output += samples_to_output; samples_remaining -= samples_to_output; + samples_output += samples_to_output; } } diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h index 2ebb755b0..59a33ba76 100644 --- a/src/audio_core/command_generator.h +++ b/src/audio_core/command_generator.h @@ -5,6 +5,7 @@ #pragma once #include <array> +#include <span> #include "audio_core/common.h" #include "audio_core/voice_context.h" #include "common/common_types.h" @@ -41,10 +42,10 @@ public: void PreCommand(); void PostCommand(); - [[nodiscard]] s32* GetChannelMixBuffer(s32 channel); - [[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const; - [[nodiscard]] s32* GetMixBuffer(std::size_t index); - [[nodiscard]] const s32* GetMixBuffer(std::size_t index) const; + [[nodiscard]] std::span<s32> GetChannelMixBuffer(s32 channel); + [[nodiscard]] std::span<const s32> GetChannelMixBuffer(s32 channel) const; + [[nodiscard]] std::span<s32> GetMixBuffer(std::size_t index); + [[nodiscard]] std::span<const s32> GetMixBuffer(std::size_t index) const; [[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const; [[nodiscard]] std::size_t GetTotalMixBufferCount() const; @@ -77,21 +78,24 @@ private: void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled); [[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index); - s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data, - u32 sample_count, u32 write_offset, u32 write_count); - s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data, - u32 sample_count, u32 read_offset, u32 read_count); + s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, + std::span<const s32> data, u32 sample_count, u32 write_offset, + u32 write_count); + s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, + std::span<s32> out_data, u32 sample_count, u32 read_offset, u32 read_count); void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, std::vector<u8>& work_buffer); void UpdateI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state, bool should_clear); // DSP Code - s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, - s32 channel, std::size_t mix_offset); - s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count, - s32 channel, std::size_t mix_offset); - void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state, - s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id); + template <typename T> + s32 DecodePcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset, + s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset); + s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_start_offset, + s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset); + void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::span<s32> output, + VoiceState& dsp_state, s32 channel, s32 target_sample_rate, + s32 sample_count, s32 node_id); AudioCommon::AudioRendererParameter& worker_params; VoiceContext& voice_context; diff --git a/src/audio_core/common.h b/src/audio_core/common.h index fe546c55d..1ab537588 100644 --- a/src/audio_core/common.h +++ b/src/audio_core/common.h @@ -15,7 +15,7 @@ constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41}; constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43}; } // namespace Audren -constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8'); +constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '9'); constexpr std::size_t MAX_MIX_BUFFERS = 24; constexpr std::size_t MAX_BIQUAD_FILTERS = 2; constexpr std::size_t MAX_CHANNEL_COUNT = 6; 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, if (voice_in_params.is_new) { // Default our values for our voice voice_info.Initialize(); - if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) { - continue; - } // Zero out our voice states for (std::size_t channel = 0; channel < channel_count; channel++) { diff --git a/src/audio_core/sink_context.cpp b/src/audio_core/sink_context.cpp index a69543696..cc55b290c 100644 --- a/src/audio_core/sink_context.cpp +++ b/src/audio_core/sink_context.cpp @@ -15,10 +15,17 @@ std::size_t SinkContext::GetCount() const { void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) { ASSERT(in.type == SinkTypes::Device); - has_downmix_coefs = in.device.down_matrix_enabled; - if (has_downmix_coefs) { + if (in.device.down_matrix_enabled) { downmix_coefficients = in.device.down_matrix_coef; + } else { + downmix_coefficients = { + 1.0f, // front + 0.707f, // center + 0.0f, // lfe + 0.707f, // back + }; } + in_use = in.in_use; use_count = in.device.input_count; buffers = in.device.input; @@ -34,10 +41,6 @@ std::vector<u8> SinkContext::OutputBuffers() const { return buffer_ret; } -bool SinkContext::HasDownMixingCoefficients() const { - return has_downmix_coefs; -} - const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const { return downmix_coefficients; } diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h index 9e2b69785..254961fe2 100644 --- a/src/audio_core/sink_context.h +++ b/src/audio_core/sink_context.h @@ -84,7 +84,6 @@ public: [[nodiscard]] bool InUse() const; [[nodiscard]] std::vector<u8> OutputBuffers() const; - [[nodiscard]] bool HasDownMixingCoefficients() const; [[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const; private: @@ -92,7 +91,6 @@ private: s32 use_count{}; std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{}; std::size_t sink_count{}; - bool has_downmix_coefs{false}; DownmixCoefficients downmix_coefficients{}; }; } // namespace AudioCore 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() { in_params.last_volume = 0.0f; in_params.biquad_filter.fill({}); in_params.wave_buffer_count = 0; - in_params.wave_bufffer_head = 0; + in_params.wave_buffer_head = 0; in_params.mix_id = AudioCommon::NO_MIX; in_params.splitter_info_id = AudioCommon::NO_SPLITTER; in_params.additional_params_address = 0; @@ -75,7 +75,7 @@ void ServerVoiceInfo::Initialize() { out_params.played_sample_count = 0; out_params.wave_buffer_consumed = 0; in_params.voice_drop_flag = false; - in_params.buffer_mapped = false; + in_params.buffer_mapped = true; in_params.wave_buffer_flush_request_count = 0; in_params.was_biquad_filter_enabled.fill(false); @@ -126,7 +126,7 @@ void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in, in_params.volume = voice_in.volume; in_params.biquad_filter = voice_in.biquad_filter; in_params.wave_buffer_count = voice_in.wave_buffer_count; - in_params.wave_bufffer_head = voice_in.wave_buffer_head; + in_params.wave_buffer_head = voice_in.wave_buffer_head; if (behavior_info.IsFlushVoiceWaveBuffersSupported()) { const auto in_request_count = in_params.wave_buffer_flush_request_count; const auto voice_request_count = voice_in.wave_buffer_flush_request_count; @@ -185,14 +185,16 @@ void ServerVoiceInfo::UpdateWaveBuffers( wave_buffer.buffer_size = 0; wave_buffer.context_address = 0; wave_buffer.context_size = 0; + wave_buffer.loop_start_sample = 0; + wave_buffer.loop_end_sample = 0; wave_buffer.sent_to_dsp = true; } // Mark all our wave buffers as invalid for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count); channel++) { - for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) { - is_valid = false; + for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; ++i) { + voice_states[channel]->is_wave_buffer_valid[i] = false; } } } @@ -211,7 +213,7 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, const WaveBuffer& in_wave_buffer, SampleFormat sample_format, bool is_buffer_valid, [[maybe_unused]] BehaviorInfo& behavior_info) { - if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) { + if (!is_buffer_valid && out_wavebuffer.sent_to_dsp && out_wavebuffer.buffer_address != 0) { out_wavebuffer.buffer_address = 0; out_wavebuffer.buffer_size = 0; } @@ -219,11 +221,40 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) { // Validate sample offset sizings if (sample_format == SampleFormat::Pcm16) { - const auto buffer_size = in_wave_buffer.buffer_size; - if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 || - (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) || - (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) { + const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size); + const s64 start = sizeof(s16) * in_wave_buffer.start_sample_offset; + const s64 end = sizeof(s16) * in_wave_buffer.end_sample_offset; + if (0 > start || start > buffer_size || 0 > end || end > buffer_size) { // TODO(ogniK): Write error info + LOG_ERROR(Audio, + "PCM16 wavebuffer has an invalid size. Buffer has size 0x{:08X}, but " + "offsets were " + "{:08X} - 0x{:08X}", + buffer_size, sizeof(s16) * in_wave_buffer.start_sample_offset, + sizeof(s16) * in_wave_buffer.end_sample_offset); + return; + } + } else if (sample_format == SampleFormat::Adpcm) { + const s64 buffer_size = static_cast<s64>(in_wave_buffer.buffer_size); + const s64 start_frames = in_wave_buffer.start_sample_offset / 14; + const s64 start_extra = in_wave_buffer.start_sample_offset % 14 == 0 + ? 0 + : (in_wave_buffer.start_sample_offset % 14) / 2 + 1 + + (in_wave_buffer.start_sample_offset % 2); + const s64 start = start_frames * 8 + start_extra; + const s64 end_frames = in_wave_buffer.end_sample_offset / 14; + const s64 end_extra = in_wave_buffer.end_sample_offset % 14 == 0 + ? 0 + : (in_wave_buffer.end_sample_offset % 14) / 2 + 1 + + (in_wave_buffer.end_sample_offset % 2); + const s64 end = end_frames * 8 + end_extra; + if (in_wave_buffer.start_sample_offset < 0 || start > buffer_size || + in_wave_buffer.end_sample_offset < 0 || end > buffer_size) { + LOG_ERROR(Audio, + "ADPMC wavebuffer has an invalid size. Buffer has size 0x{:08X}, but " + "offsets were " + "{:08X} - 0x{:08X}", + in_wave_buffer.buffer_size, start, end); return; } } @@ -239,29 +270,34 @@ void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, out_wavebuffer.buffer_size = in_wave_buffer.buffer_size; out_wavebuffer.context_address = in_wave_buffer.context_address; out_wavebuffer.context_size = in_wave_buffer.context_size; + out_wavebuffer.loop_start_sample = in_wave_buffer.loop_start_sample; + out_wavebuffer.loop_end_sample = in_wave_buffer.loop_end_sample; in_params.buffer_mapped = in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0; // TODO(ogniK): Pool mapper attachment // TODO(ogniK): IsAdpcmLoopContextBugFixed + if (sample_format == SampleFormat::Adpcm && in_wave_buffer.context_address != 0 && + in_wave_buffer.context_size != 0 && behavior_info.IsAdpcmLoopContextBugFixed()) { + } else { + out_wavebuffer.context_address = 0; + out_wavebuffer.context_size = 0; + } } } void ServerVoiceInfo::WriteOutStatus( VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) { - if (voice_in.is_new) { + if (voice_in.is_new || in_params.is_new) { in_params.is_new = true; voice_out.wave_buffer_consumed = 0; voice_out.played_sample_count = 0; voice_out.voice_dropped = false; - } else if (!in_params.is_new) { - voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed; - voice_out.played_sample_count = voice_states[0]->played_sample_count; - voice_out.voice_dropped = in_params.voice_drop_flag; } else { - voice_out.wave_buffer_consumed = 0; - voice_out.played_sample_count = 0; - voice_out.voice_dropped = false; + const auto& state = voice_states[0]; + voice_out.wave_buffer_consumed = state->wave_buffer_consumed; + voice_out.played_sample_count = state->played_sample_count; + voice_out.voice_dropped = state->voice_dropped; } } @@ -283,7 +319,8 @@ ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() { bool ServerVoiceInfo::ShouldSkip() const { // TODO(ogniK): Handle unmapped wave buffers or parameters - return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag; + return !in_params.in_use || in_params.wave_buffer_count == 0 || !in_params.buffer_mapped || + in_params.voice_drop_flag; } bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) { @@ -381,7 +418,7 @@ bool ServerVoiceInfo::UpdateParametersForCommandGeneration( void ServerVoiceInfo::FlushWaveBuffers( u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, s32 channel_count) { - auto wave_head = in_params.wave_bufffer_head; + auto wave_head = in_params.wave_buffer_head; for (u8 i = 0; i < flush_count; i++) { in_params.wave_buffer[wave_head].sent_to_dsp = true; @@ -401,6 +438,17 @@ bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const { return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end(); } +void ServerVoiceInfo::SetWaveBufferCompleted(VoiceState& dsp_state, + const ServerWaveBuffer& wave_buffer) { + dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false; + dsp_state.wave_buffer_consumed++; + dsp_state.wave_buffer_index = (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS; + dsp_state.loop_count = 0; + if (wave_buffer.end_of_stream) { + dsp_state.played_sample_count = 0; + } +} + VoiceContext::VoiceContext(std::size_t voice_count_) : voice_count{voice_count_} { for (std::size_t i = 0; i < voice_count; i++) { 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 { u8 is_looping{}; u8 end_of_stream{}; u8 sent_to_server{}; - INSERT_PADDING_BYTES(5); + INSERT_PADDING_BYTES(1); + s32 loop_count{}; u64 context_address{}; u64 context_size{}; - INSERT_PADDING_BYTES(8); + u32 loop_start_sample{}; + u32 loop_end_sample{}; }; static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size"); @@ -76,6 +78,9 @@ struct ServerWaveBuffer { bool end_of_stream{}; VAddr context_address{}; std::size_t context_size{}; + s32 loop_count{}; + u32 loop_start_sample{}; + u32 loop_end_sample{}; bool sent_to_dsp{true}; }; @@ -108,6 +113,7 @@ struct VoiceState { u32 external_context_size; bool is_external_context_used; bool voice_dropped; + s32 loop_count; }; class VoiceChannelResource { @@ -206,7 +212,7 @@ public: float last_volume{}; std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{}; s32 wave_buffer_count{}; - s16 wave_bufffer_head{}; + s16 wave_buffer_head{}; INSERT_PADDING_BYTES(2); BehaviorFlags behavior_flags{}; VAddr additional_params_address{}; @@ -252,6 +258,7 @@ public: void FlushWaveBuffers(u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states, s32 channel_count); + void SetWaveBufferCompleted(VoiceState& dsp_state, const ServerWaveBuffer& wave_buffer); private: std::vector<s16> stored_samples; |