diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp index a11d73cac6..0f6f78253c 100644 --- a/src/audio_core/renderer/command/data_source/decode.cpp +++ b/src/audio_core/renderer/command/data_source/decode.cpp @@ -18,20 +18,15 @@ namespace AudioCore::Renderer { constexpr u32 TempBufferSize = 0x3F00; -constexpr std::array PitchBySrcQuality = {4, 8, 4}; - -/** - * Decode PCM data. Only s16 or f32 is supported. - * - * @tparam T - Type to decode. Only s16 and f32 are supported. - * @param memory - Core memory for reading samples. - * @param out_buffer - Output mix buffer to receive the samples. - * @param req - Information for how to decode. - * @return Number of samples decoded. - */ + +/// @brief Decode PCM data. Only s16 or f32 is supported. +/// @tparam T - Type to decode. Only s16 and f32 are supported. +/// @param memory - Core memory for reading samples. +/// @param out_buffer - Output mix buffer to receive the samples. +/// @param req - Information for how to decode. +/// @return Number of samples decoded. template -static u32 DecodePcm(Core::Memory::Memory& memory, std::span out_buffer, - const DecodeArg& req) { +static u32 DecodePcm(Core::Memory::Memory& memory, std::span out_buffer, const DecodeArg& req) { constexpr s32 min{(std::numeric_limits::min)()}; constexpr s32 max{(std::numeric_limits::max)()}; @@ -94,16 +89,12 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span out_buffer, return samples_to_decode; } -/** - * Decode ADPCM data. - * - * @param memory - Core memory for reading samples. - * @param out_buffer - Output mix buffer to receive the samples. - * @param req - Information for how to decode. - * @return Number of samples decoded. - */ -static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span out_buffer, - const DecodeArg& req) { +/// @brief Decode ADPCM data. +/// @param memory - Core memory for reading samples. +/// @param out_buffer - Output mix buffer to receive the samples. +/// @param req - Information for how to decode. +/// @return Number of samples decoded. +static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span out_buffer, const DecodeArg& req) { constexpr u32 SamplesPerFrame{14}; constexpr u32 NibblesPerFrame{16}; @@ -115,8 +106,7 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span out_buffer, return 0; } - auto end{(req.end_offset % SamplesPerFrame) + - NibblesPerFrame * (req.end_offset / SamplesPerFrame)}; + auto end{(req.end_offset % SamplesPerFrame) + NibblesPerFrame * (req.end_offset / SamplesPerFrame)}; if (req.end_offset % SamplesPerFrame) { end += 3; } else { @@ -133,52 +123,49 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span out_buffer, return 0; } - auto samples_to_read{samples_to_process}; - auto samples_remaining_in_frame{start_pos % SamplesPerFrame}; - auto position_in_frame{(start_pos / SamplesPerFrame) * NibblesPerFrame + - samples_remaining_in_frame}; - + auto samples_to_read = samples_to_process; + auto samples_remaining_in_frame = start_pos % SamplesPerFrame; + auto position_in_frame = (start_pos / SamplesPerFrame) * NibblesPerFrame + samples_remaining_in_frame; if (samples_remaining_in_frame) { position_in_frame += 2; } const auto size{(std::max)((samples_to_process / 8U) * SamplesPerFrame, 8U)}; - Core::Memory::CpuGuestMemory wavebuffer( - memory, req.buffer + position_in_frame / 2, size); - - auto context{req.adpcm_context}; - auto header{context->header}; - u8 coeff_index{static_cast((header >> 4U) & 0xFU)}; - u8 scale{static_cast(header & 0xFU)}; - s32 coeff0{req.coefficients[coeff_index * 2 + 0]}; - s32 coeff1{req.coefficients[coeff_index * 2 + 1]}; - - auto yn0{context->yn0}; - auto yn1{context->yn1}; - - static constexpr std::array Steps{ - 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1, + Core::Memory::CpuGuestMemory wavebuffer(memory, req.buffer + position_in_frame / 2, size); + + auto context = req.adpcm_context; + auto header = context->header; + u8 scale = u8(header & 0xfU); + u8 coeff_index = u8((header >> 4U) & 0x7u); + s32 coeff0 = req.coefficients[coeff_index * 2 + 0]; + s32 coeff1 = req.coefficients[coeff_index * 2 + 1]; + + auto yn0 = context->yn0; + auto yn1 = context->yn1; + auto const get_step = [](u32 index) { + // Emulates the following table + // 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1, + constexpr u64 steps_table = 0x1234567876543210ull; + constexpr u64 steps_sign = 0b1111111100000000ull; + auto const r = s32((steps_table >> (index * 4)) & 0xf); + return ((steps_sign >> index) & 1) == 0 ? -r : r; }; - - const auto decode_sample = [&](const s32 code) -> s16 { + auto const decode_sample = [&](const s32 code) -> s16 { const auto xn = code * (1 << scale); const auto prediction = coeff0 * yn0 + coeff1 * yn1; const auto sample = ((xn << 11) + 0x400 + prediction) >> 11; const auto saturated = std::clamp(sample, -0x8000, 0x7FFF); yn1 = yn0; - yn0 = static_cast(saturated); - return yn0; + return yn0 = s16(saturated); }; - u32 read_index{0}; - u32 write_index{0}; - - while (samples_to_read > 0) { + u32 read_index = 0; + for (u32 write_index = 0; samples_to_read > 0 && write_index < out_buffer.size(); ) { // Are we at a new frame? if ((position_in_frame % NibblesPerFrame) == 0) { header = wavebuffer[read_index++]; - coeff_index = (header >> 4) & 0xF; - scale = header & 0xF; + scale = header & 0xFu; + coeff_index = (header >> 4) & 0x7u; coeff0 = req.coefficients[coeff_index * 2 + 0]; coeff1 = req.coefficients[coeff_index * 2 + 1]; position_in_frame += 2; @@ -187,14 +174,12 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span out_buffer, if (samples_to_read >= SamplesPerFrame) { // Can grab all samples until the next header for (u32 i = 0; i < SamplesPerFrame / 2; i++) { - auto code0{Steps[(wavebuffer[read_index] >> 4) & 0xF]}; - auto code1{Steps[wavebuffer[read_index] & 0xF]}; - read_index++; - + auto code0 = get_step((wavebuffer[read_index + i] >> 4) & 0xF); + auto code1 = get_step(wavebuffer[read_index + i] & 0xF); out_buffer[write_index++] = decode_sample(code0); out_buffer[write_index++] = decode_sample(code1); } - + read_index += SamplesPerFrame / 2; position_in_frame += SamplesPerFrame; samples_to_read -= SamplesPerFrame; continue; @@ -202,15 +187,14 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span out_buffer, } // Decode a single sample - auto code{wavebuffer[read_index]}; + auto code = wavebuffer[read_index]; if (position_in_frame & 1) { code &= 0xF; read_index++; } else { code >>= 4; } - - out_buffer[write_index++] = decode_sample(Steps[code]); + out_buffer[write_index++] = decode_sample(get_step(code)); position_in_frame++; samples_to_read--; @@ -219,27 +203,21 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span out_buffer, context->header = header; context->yn0 = yn0; context->yn1 = yn1; - return samples_to_process; } -/** - * Decode implementation. - * Decode wavebuffers according to the given args. - * - * @param memory - Core memory to read data from. - * @param args - The wavebuffer data, and information for how to decode it. - */ +/// @brief Decode implementation. +/// Decode wavebuffers according to the given args. +/// +/// @param memory - Core memory to read data from. +/// @param args - The wavebuffer data, and information for how to decode it. void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args) { - static constexpr auto EndWaveBuffer = [](auto& voice_state, auto& wavebuffer, auto& index, - auto& played_samples, auto& consumed) -> void { + constexpr auto EndWaveBuffer = [](auto& voice_state, auto& wavebuffer, auto& index, auto& played_samples, auto& consumed) -> void { voice_state.wave_buffer_valid[index] = false; voice_state.loop_count = 0; - if (wavebuffer.stream_ended) { played_samples = 0; } - index = (index + 1) % MaxWaveBuffers; consumed++; }; @@ -255,8 +233,9 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf return; } - auto pitch{PitchBySrcQuality[static_cast(args.src_quality)]}; - if (static_cast(pitch + size_required.to_int_floor()) > TempBufferSize) { + // 0 -> 4, 1 -> 8, 2 -> 4 + auto pitch = u32((0x040804ul >> (u32(args.src_quality) * 8)) & 0xfful); + if (u32(pitch + size_required.to_int_floor()) > TempBufferSize) { return; } @@ -272,7 +251,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf bool is_buffer_starved{false}; u32 offset{voice_state.offset}; - auto output_buffer{args.output}; + auto output_buffer = args.output; std::array temp_buffer{}; while (remaining_sample_count > 0) { @@ -294,8 +273,8 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf if (wavebuffer_index >= MaxWaveBuffers) { LOG_ERROR(Service_Audio, "Invalid wavebuffer index! {}", wavebuffer_index); wavebuffer_index = 0; - voice_state.wave_buffer_valid.fill(false); wavebuffers_consumed = MaxWaveBuffers; + voice_state.wave_buffer_valid.fill(false); } if (!voice_state.wave_buffer_valid[wavebuffer_index]) { @@ -303,12 +282,9 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf break; } - auto& wavebuffer{args.wave_buffers[wavebuffer_index]}; - - if (offset == 0 && args.sample_format == SampleFormat::Adpcm && - wavebuffer.context != 0) { - memory.ReadBlockUnsafe(wavebuffer.context, &voice_state.adpcm_context, - wavebuffer.context_size); + auto& wavebuffer = args.wave_buffers[wavebuffer_index]; + if (offset == 0 && args.sample_format == SampleFormat::Adpcm && wavebuffer.context != 0) { + memory.ReadBlockUnsafe(wavebuffer.context, &voice_state.adpcm_context, wavebuffer.context_size); } auto start_offset{wavebuffer.start_offset}; @@ -351,9 +327,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf case SampleFormat::Adpcm: { decode_arg.adpcm_context = &voice_state.adpcm_context; memory.ReadBlockUnsafe(args.data_address, &decode_arg.coefficients, args.data_size); - samples_decoded = DecodeAdpcm( - memory, {&temp_buffer[temp_buffer_pos], TempBufferSize - temp_buffer_pos}, - decode_arg); + samples_decoded = DecodeAdpcm( memory, {&temp_buffer[temp_buffer_pos], TempBufferSize - temp_buffer_pos}, decode_arg); } break; default: diff --git a/src/audio_core/renderer/command/resample/resample.cpp b/src/audio_core/renderer/command/resample/resample.cpp index 51f4ba39eb..c20b5aa4a8 100644 --- a/src/audio_core/renderer/command/resample/resample.cpp +++ b/src/audio_core/renderer/command/resample/resample.cpp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -5,21 +8,13 @@ namespace AudioCore::Renderer { -static void ResampleLowQuality(std::span output, std::span input, - const Common::FixedPoint<49, 15>& sample_rate_ratio, - Common::FixedPoint<49, 15>& fraction, const u32 samples_to_write) { - if (sample_rate_ratio == 1.0f) { - for (u32 i = 0; i < samples_to_write; i++) { - output[i] = input[i]; - } - } else { - u32 read_index{0}; - for (u32 i = 0; i < samples_to_write; i++) { - output[i] = input[read_index + (fraction >= 0.5f)]; - fraction += sample_rate_ratio; - read_index += static_cast(fraction.to_int_floor()); - fraction.clear_int(); - } +static void ResampleLowQuality(std::span output, std::span input, const Common::FixedPoint<49, 15>& sample_rate_ratio, Common::FixedPoint<49, 15>& fraction, const u32 samples_to_write) { + u32 read_index{0}; + for (u32 i = 0; i < samples_to_write; i++) { + output[i] = input[read_index + (fraction >= 0.5f)]; + fraction += sample_rate_ratio; + read_index += u32(fraction.to_int_floor()); + fraction.clear_int(); } } @@ -295,7 +290,7 @@ static void ResampleNormalQuality(std::span output, std::span in auto lut{get_lut()}; u32 read_index{0}; for (u32 i = 0; i < samples_to_write; i++) { - const auto lut_index{(fraction.get_frac() >> 8) * 4}; + const auto lut_index = ((fraction.get_frac() >> 8) << 2) & 511; const Common::FixedPoint<56, 8> sample0{input[read_index + 0] * lut[lut_index + 0]}; const Common::FixedPoint<56, 8> sample1{input[read_index + 1] * lut[lut_index + 1]}; const Common::FixedPoint<56, 8> sample2{input[read_index + 2] * lut[lut_index + 2]}; @@ -845,7 +840,7 @@ static void ResampleHighQuality(std::span output, std::span inpu auto lut{get_lut()}; u32 read_index{0}; for (u32 i = 0; i < samples_to_write; i++) { - const auto lut_index{(fraction.get_frac() >> 8) * 8}; + const auto lut_index = ((fraction.get_frac() >> 8) << 3) & 1023; const Common::FixedPoint<56, 8> sample0{input[read_index + 0] * lut[lut_index + 0]}; const Common::FixedPoint<56, 8> sample1{input[read_index + 1] * lut[lut_index + 1]}; const Common::FixedPoint<56, 8> sample2{input[read_index + 2] * lut[lut_index + 2]};