mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-29 09:58:05 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eded728f91 | |||
| c8799e42c6 | |||
| d87ffba82a | |||
| 09c6b57c99 |
@@ -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]};
|
||||||
|
|||||||
Reference in New Issue
Block a user