Compare commits

...

4 Commits

Author SHA1 Message Date
lizzie eded728f91 got it ok 2026-08-05 17:36:17 +00:00
lizzie c8799e42c6 fix2 2026-08-05 17:19:42 +00:00
lizzie d87ffba82a A 2026-08-05 16:53:47 +00:00
lizzie 09c6b57c99 [audio_core] Fix jamboree crash due to OOB access
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-08-05 16:52:40 +00:00
2 changed files with 71 additions and 102 deletions
@@ -18,20 +18,15 @@
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
constexpr u32 TempBufferSize = 0x3F00; constexpr u32 TempBufferSize = 0x3F00;
constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4};
/** /// @brief Decode PCM data. Only s16 or f32 is supported.
* 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.
* @tparam T - Type to decode. Only s16 and f32 are supported. /// @param out_buffer - Output mix buffer to receive the samples.
* @param memory - Core memory for reading samples. /// @param req - Information for how to decode.
* @param out_buffer - Output mix buffer to receive the samples. /// @return Number of samples decoded.
* @param req - Information for how to decode.
* @return Number of samples decoded.
*/
template <typename T> template <typename T>
static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, const DecodeArg& req) {
const DecodeArg& req) {
constexpr s32 min{(std::numeric_limits<s16>::min)()}; constexpr s32 min{(std::numeric_limits<s16>::min)()};
constexpr s32 max{(std::numeric_limits<s16>::max)()}; constexpr s32 max{(std::numeric_limits<s16>::max)()};
@@ -94,16 +89,12 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
return samples_to_decode; return samples_to_decode;
} }
/** /// @brief Decode ADPCM data.
* Decode ADPCM data. /// @param memory - Core memory for reading samples.
* /// @param out_buffer - Output mix buffer to receive the samples.
* @param memory - Core memory for reading samples. /// @param req - Information for how to decode.
* @param out_buffer - Output mix buffer to receive the samples. /// @return Number of samples decoded.
* @param req - Information for how to decode. static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, const DecodeArg& req) {
* @return Number of samples decoded.
*/
static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
const DecodeArg& req) {
constexpr u32 SamplesPerFrame{14}; constexpr u32 SamplesPerFrame{14};
constexpr u32 NibblesPerFrame{16}; constexpr u32 NibblesPerFrame{16};
@@ -115,8 +106,7 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
return 0; return 0;
} }
auto end{(req.end_offset % SamplesPerFrame) + auto end{(req.end_offset % SamplesPerFrame) + NibblesPerFrame * (req.end_offset / SamplesPerFrame)};
NibblesPerFrame * (req.end_offset / SamplesPerFrame)};
if (req.end_offset % SamplesPerFrame) { if (req.end_offset % SamplesPerFrame) {
end += 3; end += 3;
} else { } else {
@@ -133,52 +123,49 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
return 0; return 0;
} }
auto samples_to_read{samples_to_process}; auto samples_to_read = samples_to_process;
auto samples_remaining_in_frame{start_pos % SamplesPerFrame}; auto samples_remaining_in_frame = start_pos % SamplesPerFrame;
auto position_in_frame{(start_pos / SamplesPerFrame) * NibblesPerFrame + auto position_in_frame = (start_pos / SamplesPerFrame) * NibblesPerFrame + samples_remaining_in_frame;
samples_remaining_in_frame};
if (samples_remaining_in_frame) { if (samples_remaining_in_frame) {
position_in_frame += 2; position_in_frame += 2;
} }
const auto size{(std::max)((samples_to_process / 8U) * SamplesPerFrame, 8U)}; const auto size{(std::max)((samples_to_process / 8U) * SamplesPerFrame, 8U)};
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> wavebuffer( Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> wavebuffer(memory, req.buffer + position_in_frame / 2, size);
memory, req.buffer + position_in_frame / 2, size);
auto context{req.adpcm_context}; auto context = req.adpcm_context;
auto header{context->header}; auto header = context->header;
u8 coeff_index{static_cast<u8>((header >> 4U) & 0xFU)}; u8 scale = u8(header & 0xfU);
u8 scale{static_cast<u8>(header & 0xFU)}; u8 coeff_index = u8((header >> 4U) & 0x7u);
s32 coeff0{req.coefficients[coeff_index * 2 + 0]}; s32 coeff0 = req.coefficients[coeff_index * 2 + 0];
s32 coeff1{req.coefficients[coeff_index * 2 + 1]}; s32 coeff1 = req.coefficients[coeff_index * 2 + 1];
auto yn0{context->yn0}; auto yn0 = context->yn0;
auto yn1{context->yn1}; auto yn1 = context->yn1;
auto const get_step = [](u32 index) {
static constexpr std::array<s32, 16> Steps{ // Emulates the following table
0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1, // 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;
}; };
auto const decode_sample = [&](const s32 code) -> s16 {
const auto decode_sample = [&](const s32 code) -> s16 {
const auto xn = code * (1 << scale); const auto xn = code * (1 << scale);
const auto prediction = coeff0 * yn0 + coeff1 * yn1; const auto prediction = coeff0 * yn0 + coeff1 * yn1;
const auto sample = ((xn << 11) + 0x400 + prediction) >> 11; const auto sample = ((xn << 11) + 0x400 + prediction) >> 11;
const auto saturated = std::clamp<s32>(sample, -0x8000, 0x7FFF); const auto saturated = std::clamp<s32>(sample, -0x8000, 0x7FFF);
yn1 = yn0; yn1 = yn0;
yn0 = static_cast<s16>(saturated); return yn0 = s16(saturated);
return yn0;
}; };
u32 read_index{0}; u32 read_index = 0;
u32 write_index{0}; for (u32 write_index = 0; samples_to_read > 0 && write_index < out_buffer.size(); ) {
while (samples_to_read > 0) {
// Are we at a new frame? // Are we at a new frame?
if ((position_in_frame % NibblesPerFrame) == 0) { if ((position_in_frame % NibblesPerFrame) == 0) {
header = wavebuffer[read_index++]; header = wavebuffer[read_index++];
coeff_index = (header >> 4) & 0xF; scale = header & 0xFu;
scale = header & 0xF; coeff_index = (header >> 4) & 0x7u;
coeff0 = req.coefficients[coeff_index * 2 + 0]; coeff0 = req.coefficients[coeff_index * 2 + 0];
coeff1 = req.coefficients[coeff_index * 2 + 1]; coeff1 = req.coefficients[coeff_index * 2 + 1];
position_in_frame += 2; position_in_frame += 2;
@@ -187,14 +174,12 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
if (samples_to_read >= SamplesPerFrame) { if (samples_to_read >= SamplesPerFrame) {
// Can grab all samples until the next header // Can grab all samples until the next header
for (u32 i = 0; i < SamplesPerFrame / 2; i++) { for (u32 i = 0; i < SamplesPerFrame / 2; i++) {
auto code0{Steps[(wavebuffer[read_index] >> 4) & 0xF]}; auto code0 = get_step((wavebuffer[read_index + i] >> 4) & 0xF);
auto code1{Steps[wavebuffer[read_index] & 0xF]}; auto code1 = get_step(wavebuffer[read_index + i] & 0xF);
read_index++;
out_buffer[write_index++] = decode_sample(code0); out_buffer[write_index++] = decode_sample(code0);
out_buffer[write_index++] = decode_sample(code1); out_buffer[write_index++] = decode_sample(code1);
} }
read_index += SamplesPerFrame / 2;
position_in_frame += SamplesPerFrame; position_in_frame += SamplesPerFrame;
samples_to_read -= SamplesPerFrame; samples_to_read -= SamplesPerFrame;
continue; continue;
@@ -202,15 +187,14 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
} }
// Decode a single sample // Decode a single sample
auto code{wavebuffer[read_index]}; auto code = wavebuffer[read_index];
if (position_in_frame & 1) { if (position_in_frame & 1) {
code &= 0xF; code &= 0xF;
read_index++; read_index++;
} else { } else {
code >>= 4; code >>= 4;
} }
out_buffer[write_index++] = decode_sample(get_step(code));
out_buffer[write_index++] = decode_sample(Steps[code]);
position_in_frame++; position_in_frame++;
samples_to_read--; samples_to_read--;
@@ -219,27 +203,21 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer,
context->header = header; context->header = header;
context->yn0 = yn0; context->yn0 = yn0;
context->yn1 = yn1; context->yn1 = yn1;
return samples_to_process; return samples_to_process;
} }
/** /// @brief Decode implementation.
* Decode implementation. /// Decode wavebuffers according to the given args.
* Decode wavebuffers according to the given args. ///
* /// @param memory - Core memory to read data from.
* @param memory - Core memory to read data from. /// @param args - The wavebuffer data, and information for how to decode it.
* @param args - The wavebuffer data, and information for how to decode it.
*/
void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args) { void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuffersArgs& args) {
static constexpr auto EndWaveBuffer = [](auto& voice_state, auto& wavebuffer, auto& index, constexpr auto EndWaveBuffer = [](auto& voice_state, auto& wavebuffer, auto& index, auto& played_samples, auto& consumed) -> void {
auto& played_samples, auto& consumed) -> void {
voice_state.wave_buffer_valid[index] = false; voice_state.wave_buffer_valid[index] = false;
voice_state.loop_count = 0; voice_state.loop_count = 0;
if (wavebuffer.stream_ended) { if (wavebuffer.stream_ended) {
played_samples = 0; played_samples = 0;
} }
index = (index + 1) % MaxWaveBuffers; index = (index + 1) % MaxWaveBuffers;
consumed++; consumed++;
}; };
@@ -255,8 +233,9 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
return; return;
} }
auto pitch{PitchBySrcQuality[static_cast<u32>(args.src_quality)]}; // 0 -> 4, 1 -> 8, 2 -> 4
if (static_cast<u32>(pitch + size_required.to_int_floor()) > TempBufferSize) { auto pitch = u32((0x040804ul >> (u32(args.src_quality) * 8)) & 0xfful);
if (u32(pitch + size_required.to_int_floor()) > TempBufferSize) {
return; return;
} }
@@ -272,7 +251,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
bool is_buffer_starved{false}; bool is_buffer_starved{false};
u32 offset{voice_state.offset}; u32 offset{voice_state.offset};
auto output_buffer{args.output}; auto output_buffer = args.output;
std::array<s16, TempBufferSize> temp_buffer{}; std::array<s16, TempBufferSize> temp_buffer{};
while (remaining_sample_count > 0) { while (remaining_sample_count > 0) {
@@ -294,8 +273,8 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
if (wavebuffer_index >= MaxWaveBuffers) { if (wavebuffer_index >= MaxWaveBuffers) {
LOG_ERROR(Service_Audio, "Invalid wavebuffer index! {}", wavebuffer_index); LOG_ERROR(Service_Audio, "Invalid wavebuffer index! {}", wavebuffer_index);
wavebuffer_index = 0; wavebuffer_index = 0;
voice_state.wave_buffer_valid.fill(false);
wavebuffers_consumed = MaxWaveBuffers; wavebuffers_consumed = MaxWaveBuffers;
voice_state.wave_buffer_valid.fill(false);
} }
if (!voice_state.wave_buffer_valid[wavebuffer_index]) { if (!voice_state.wave_buffer_valid[wavebuffer_index]) {
@@ -303,12 +282,9 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
break; break;
} }
auto& wavebuffer{args.wave_buffers[wavebuffer_index]}; auto& wavebuffer = args.wave_buffers[wavebuffer_index];
if (offset == 0 && args.sample_format == SampleFormat::Adpcm && wavebuffer.context != 0) {
if (offset == 0 && args.sample_format == SampleFormat::Adpcm && memory.ReadBlockUnsafe(wavebuffer.context, &voice_state.adpcm_context, wavebuffer.context_size);
wavebuffer.context != 0) {
memory.ReadBlockUnsafe(wavebuffer.context, &voice_state.adpcm_context,
wavebuffer.context_size);
} }
auto start_offset{wavebuffer.start_offset}; auto start_offset{wavebuffer.start_offset};
@@ -351,9 +327,7 @@ void DecodeFromWaveBuffers(Core::Memory::Memory& memory, const DecodeFromWaveBuf
case SampleFormat::Adpcm: { case SampleFormat::Adpcm: {
decode_arg.adpcm_context = &voice_state.adpcm_context; decode_arg.adpcm_context = &voice_state.adpcm_context;
memory.ReadBlockUnsafe(args.data_address, &decode_arg.coefficients, args.data_size); memory.ReadBlockUnsafe(args.data_address, &decode_arg.coefficients, args.data_size);
samples_decoded = DecodeAdpcm( samples_decoded = DecodeAdpcm( memory, {&temp_buffer[temp_buffer_pos], TempBufferSize - temp_buffer_pos}, decode_arg);
memory, {&temp_buffer[temp_buffer_pos], TempBufferSize - temp_buffer_pos},
decode_arg);
} break; } break;
default: default:
@@ -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-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -5,23 +8,15 @@
namespace AudioCore::Renderer { namespace AudioCore::Renderer {
static void ResampleLowQuality(std::span<s32> output, std::span<const s16> input, static void ResampleLowQuality(std::span<s32> output, std::span<const s16> input, const Common::FixedPoint<49, 15>& sample_rate_ratio, Common::FixedPoint<49, 15>& fraction, const u32 samples_to_write) {
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}; u32 read_index{0};
for (u32 i = 0; i < samples_to_write; i++) { for (u32 i = 0; i < samples_to_write; i++) {
output[i] = input[read_index + (fraction >= 0.5f)]; output[i] = input[read_index + (fraction >= 0.5f)];
fraction += sample_rate_ratio; fraction += sample_rate_ratio;
read_index += static_cast<u32>(fraction.to_int_floor()); read_index += u32(fraction.to_int_floor());
fraction.clear_int(); fraction.clear_int();
} }
} }
}
static void ResampleNormalQuality(std::span<s32> output, std::span<const s16> input, static void ResampleNormalQuality(std::span<s32> output, std::span<const s16> input,
const Common::FixedPoint<49, 15>& sample_rate_ratio, const Common::FixedPoint<49, 15>& sample_rate_ratio,
@@ -295,7 +290,7 @@ static void ResampleNormalQuality(std::span<s32> output, std::span<const s16> in
auto lut{get_lut()}; auto lut{get_lut()};
u32 read_index{0}; u32 read_index{0};
for (u32 i = 0; i < samples_to_write; i++) { 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> 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> sample1{input[read_index + 1] * lut[lut_index + 1]};
const Common::FixedPoint<56, 8> sample2{input[read_index + 2] * lut[lut_index + 2]}; const Common::FixedPoint<56, 8> sample2{input[read_index + 2] * lut[lut_index + 2]};
@@ -845,7 +840,7 @@ static void ResampleHighQuality(std::span<s32> output, std::span<const s16> inpu
auto lut{get_lut()}; auto lut{get_lut()};
u32 read_index{0}; u32 read_index{0};
for (u32 i = 0; i < samples_to_write; i++) { 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> 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> sample1{input[read_index + 1] * lut[lut_index + 1]};
const Common::FixedPoint<56, 8> sample2{input[read_index + 2] * lut[lut_index + 2]}; const Common::FixedPoint<56, 8> sample2{input[read_index + 2] * lut[lut_index + 2]};