audio_core: Preserve front channel volume after 6 to 2 downmix
Many games report 6 channel output while only providing data for 2. We only output 2-channel audio regardless, and in the downmixing, front left/right only provide 36% of their volume. This is done assuming all of the other channels also contain valid data, but in many games they don't. This PR alters the downmixing to preserve front left/right, so volume is not lost. This improves volume in Link's Awakening, New Super Mario Bros U, Disgaea 6, Super Kirby Clash.
This commit is contained in:
parent
2eb018c80f
commit
7636fefb71
5 changed files with 81 additions and 75 deletions
|
@ -29,10 +29,9 @@ namespace {
|
||||||
(static_cast<float>(r_channel) * r_mix_amount)));
|
(static_cast<float>(r_channel) * r_mix_amount)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(s16 fl_channel, s16 fr_channel,
|
[[maybe_unused, nodiscard]] static constexpr std::tuple<s16, s16> Mix6To2(
|
||||||
s16 fc_channel,
|
s16 fl_channel, s16 fr_channel, s16 fc_channel, [[maybe_unused]] s16 lf_channel, s16 bl_channel,
|
||||||
[[maybe_unused]] s16 lf_channel,
|
s16 br_channel) {
|
||||||
s16 bl_channel, s16 br_channel) {
|
|
||||||
// Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
|
// Front channels are mixed 36.94%, Center channels are mixed to be 26.12% & the back channels
|
||||||
// are mixed to be 36.94%
|
// are mixed to be 36.94%
|
||||||
|
|
||||||
|
@ -57,11 +56,11 @@ namespace {
|
||||||
const std::array<float_le, 4>& coeff) {
|
const std::array<float_le, 4>& coeff) {
|
||||||
const auto left =
|
const auto left =
|
||||||
static_cast<float>(fl_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
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 =
|
const auto right =
|
||||||
static_cast<float>(fr_channel) * coeff[0] + static_cast<float>(fc_channel) * coeff[1] +
|
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))};
|
return {ClampToS16(static_cast<s32>(left)), ClampToS16(static_cast<s32>(right))};
|
||||||
}
|
}
|
||||||
|
@ -241,7 +240,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
const auto channel_count = buffer_offsets.size();
|
const auto channel_count = buffer_offsets.size();
|
||||||
const auto& final_mix = mix_context.GetFinalMixInfo();
|
const auto& final_mix = mix_context.GetFinalMixInfo();
|
||||||
const auto& in_params = final_mix.GetInParams();
|
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++) {
|
for (std::size_t i = 0; i < channel_count; i++) {
|
||||||
mix_buffers[i] =
|
mix_buffers[i] =
|
||||||
command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[i]);
|
command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[i]);
|
||||||
|
@ -294,18 +293,11 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||||
buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
|
buffer[i * stream_channel_count + 0] = Mix2To1(fl_sample, fr_sample);
|
||||||
} else if (stream_channel_count == 2) {
|
} else if (stream_channel_count == 2) {
|
||||||
// Mix all channels into 2 channels
|
// Mix all channels into 2 channels
|
||||||
if (sink_context.HasDownMixingCoefficients()) {
|
|
||||||
const auto [left, right] = Mix6To2WithCoefficients(
|
const auto [left, right] = Mix6To2WithCoefficients(
|
||||||
fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
|
fl_sample, fr_sample, fc_sample, lf_sample, bl_sample, br_sample,
|
||||||
sink_context.GetDownmixCoefficients());
|
sink_context.GetDownmixCoefficients());
|
||||||
buffer[i * stream_channel_count + 0] = left;
|
buffer[i * stream_channel_count + 0] = left;
|
||||||
buffer[i * stream_channel_count + 1] = right;
|
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;
|
|
||||||
}
|
|
||||||
} else if (stream_channel_count == 6) {
|
} else if (stream_channel_count == 6) {
|
||||||
// Pass through
|
// Pass through
|
||||||
buffer[i * stream_channel_count + 0] = fl_sample;
|
buffer[i * stream_channel_count + 0] = fl_sample;
|
||||||
|
|
|
@ -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};
|
0.72867f, 0.69794f, 0.5464f, 0.24563f, 0.45214f, 0.44042f};
|
||||||
|
|
||||||
template <std::size_t N>
|
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 i = 0; i < static_cast<std::size_t>(sample_count); i += N) {
|
||||||
for (std::size_t j = 0; j < N; j++) {
|
for (std::size_t j = 0; j < N; j++) {
|
||||||
output[i + 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;
|
s32 x = 0;
|
||||||
for (s32 i = 0; i < sample_count; i++) {
|
for (s32 i = 0; i < sample_count; i++) {
|
||||||
x = static_cast<s32>(static_cast<float>(input[i]) * gain);
|
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;
|
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++) {
|
for (s32 i = 0; i < sample_count; i++) {
|
||||||
output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
|
output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
|
||||||
gain += delta;
|
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++) {
|
for (s32 i = 0; i < sample_count; i++) {
|
||||||
output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
|
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;
|
const bool positive = first_sample > 0;
|
||||||
auto final_sample = std::abs(first_sample);
|
auto final_sample = std::abs(first_sample);
|
||||||
for (s32 i = 0; i < sample_count; i++) {
|
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};
|
1, 1, 1, 0, 0, 0, 0, 3, 3, 3};
|
||||||
|
|
||||||
template <std::size_t CHANNEL_COUNT>
|
template <std::size_t CHANNEL_COUNT>
|
||||||
void ApplyReverbGeneric(I3dl2ReverbState& state,
|
void ApplyReverbGeneric(
|
||||||
const std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT>& input,
|
I3dl2ReverbState& state,
|
||||||
const std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT>& output,
|
const std::array<std::span<const s32>, AudioCommon::MAX_CHANNEL_COUNT>& input,
|
||||||
s32 sample_count) {
|
const std::array<std::span<s32>, AudioCommon::MAX_CHANNEL_COUNT>& output, s32 sample_count) {
|
||||||
|
|
||||||
auto GetTapLookup = []() {
|
auto GetTapLookup = []() {
|
||||||
if constexpr (CHANNEL_COUNT == 1) {
|
if constexpr (CHANNEL_COUNT == 1) {
|
||||||
|
@ -454,8 +457,8 @@ void CommandGenerator::GenerateBiquadFilterCommand([[maybe_unused]] s32 mix_buff
|
||||||
"input_mix_buffer={}, output_mix_buffer={}",
|
"input_mix_buffer={}, output_mix_buffer={}",
|
||||||
node_id, input_offset, output_offset);
|
node_id, input_offset, output_offset);
|
||||||
}
|
}
|
||||||
const auto* input = GetMixBuffer(input_offset);
|
std::span<const s32> input = GetMixBuffer(input_offset);
|
||||||
auto* output = GetMixBuffer(output_offset);
|
std::span<s32> output = GetMixBuffer(output_offset);
|
||||||
|
|
||||||
// Biquad filter parameters
|
// Biquad filter parameters
|
||||||
const auto [n0, n1, n2] = params.numerator;
|
const auto [n0, n1, n2] = params.numerator;
|
||||||
|
@ -548,8 +551,8 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<const s32*, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
std::array<std::span<const s32>, AudioCommon::MAX_CHANNEL_COUNT> input{};
|
||||||
std::array<s32*, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
std::array<std::span<s32>, AudioCommon::MAX_CHANNEL_COUNT> output{};
|
||||||
|
|
||||||
const auto status = params.status;
|
const auto status = params.status;
|
||||||
for (s32 i = 0; i < channel_count; i++) {
|
for (s32 i = 0; i < channel_count; i++) {
|
||||||
|
@ -584,7 +587,8 @@ void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, E
|
||||||
for (s32 i = 0; i < channel_count; i++) {
|
for (s32 i = 0; i < channel_count; i++) {
|
||||||
// Only copy if the buffer input and output do not match!
|
// Only copy if the buffer input and output do not match!
|
||||||
if ((mix_buffer_offset + params.input[i]) != (mix_buffer_offset + params.output[i])) {
|
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 +604,8 @@ void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset,
|
||||||
for (s32 i = 0; i < channel_count; i++) {
|
for (s32 i = 0; i < channel_count; i++) {
|
||||||
// TODO(ogniK): Actually implement biquad filter
|
// TODO(ogniK): Actually implement biquad filter
|
||||||
if (params.input[i] != params.output[i]) {
|
if (params.input[i] != params.output[i]) {
|
||||||
const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
std::span<const s32> input = GetMixBuffer(mix_buffer_offset + params.input[i]);
|
||||||
auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
std::span<s32> output = GetMixBuffer(mix_buffer_offset + params.output[i]);
|
||||||
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
|
ApplyMix<1>(output, input, 32768, worker_params.sample_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -640,14 +644,15 @@ void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* inf
|
||||||
|
|
||||||
if (samples_read != static_cast<int>(worker_params.sample_count) &&
|
if (samples_read != static_cast<int>(worker_params.sample_count) &&
|
||||||
samples_read <= 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 {
|
} else {
|
||||||
AuxInfoDSP empty{};
|
AuxInfoDSP empty{};
|
||||||
memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP));
|
memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP));
|
||||||
memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP));
|
memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP));
|
||||||
if (output_index != input_index) {
|
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));
|
worker_params.sample_count * sizeof(s32));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -665,7 +670,7 @@ ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
|
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) {
|
u32 write_count) {
|
||||||
if (max_samples == 0) {
|
if (max_samples == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -675,14 +680,14 @@ s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u3
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t data_offset{};
|
s32 data_offset{};
|
||||||
u32 remaining = sample_count;
|
u32 remaining = sample_count;
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
// Get position in buffer
|
// Get position in buffer
|
||||||
const auto base = send_buffer + (offset * sizeof(u32));
|
const auto base = send_buffer + (offset * sizeof(u32));
|
||||||
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
||||||
// Write to output
|
// 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;
|
offset = (offset + samples_to_grab) % max_samples;
|
||||||
remaining -= samples_to_grab;
|
remaining -= samples_to_grab;
|
||||||
data_offset += samples_to_grab;
|
data_offset += samples_to_grab;
|
||||||
|
@ -695,7 +700,7 @@ s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u3
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples,
|
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) {
|
u32 read_count) {
|
||||||
if (max_samples == 0) {
|
if (max_samples == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -707,15 +712,16 @@ s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u3
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 remaining = sample_count;
|
u32 remaining = sample_count;
|
||||||
|
s32 data_offset{};
|
||||||
while (remaining > 0) {
|
while (remaining > 0) {
|
||||||
const auto base = recv_buffer + (offset * sizeof(u32));
|
const auto base = recv_buffer + (offset * sizeof(u32));
|
||||||
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
const auto samples_to_grab = std::min(max_samples - offset, remaining);
|
||||||
std::vector<s32> buffer(samples_to_grab);
|
std::vector<s32> buffer(samples_to_grab);
|
||||||
memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32));
|
memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32));
|
||||||
std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32));
|
std::memcpy(out_data.data() + data_offset, buffer.data(), buffer.size() * sizeof(u32));
|
||||||
out_data += samples_to_grab;
|
|
||||||
offset = (offset + samples_to_grab) % max_samples;
|
offset = (offset + samples_to_grab) % max_samples;
|
||||||
remaining -= samples_to_grab;
|
remaining -= samples_to_grab;
|
||||||
|
data_offset += samples_to_grab;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (read_count != 0) {
|
if (read_count != 0) {
|
||||||
|
@ -962,8 +968,8 @@ void CommandGenerator::GenerateMixCommand(std::size_t output_offset, std::size_t
|
||||||
node_id, input_offset, output_offset, volume);
|
node_id, input_offset, output_offset, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* output = GetMixBuffer(output_offset);
|
std::span<s32> output = GetMixBuffer(output_offset);
|
||||||
const auto* input = GetMixBuffer(input_offset);
|
std::span<const s32> input = GetMixBuffer(input_offset);
|
||||||
|
|
||||||
const s32 gain = static_cast<s32>(volume * 32768.0f);
|
const s32 gain = static_cast<s32>(volume * 32768.0f);
|
||||||
// Mix with loop unrolling
|
// Mix with loop unrolling
|
||||||
|
@ -1155,12 +1161,14 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
|
||||||
return samples_processed;
|
return samples_processed;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32* CommandGenerator::GetMixBuffer(std::size_t index) {
|
std::span<s32> CommandGenerator::GetMixBuffer(std::size_t index) {
|
||||||
return mix_buffer.data() + (index * worker_params.sample_count);
|
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 {
|
std::span<const s32> CommandGenerator::GetMixBuffer(std::size_t index) const {
|
||||||
return mix_buffer.data() + (index * worker_params.sample_count);
|
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 {
|
std::size_t CommandGenerator::GetMixChannelBufferOffset(s32 channel) const {
|
||||||
|
@ -1171,15 +1179,15 @@ std::size_t CommandGenerator::GetTotalMixBufferCount() const {
|
||||||
return worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT;
|
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);
|
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);
|
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,
|
VoiceState& dsp_state, s32 channel,
|
||||||
s32 target_sample_rate, s32 sample_count,
|
s32 target_sample_rate, s32 sample_count,
|
||||||
s32 node_id) {
|
s32 node_id) {
|
||||||
|
@ -1191,7 +1199,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||||
node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate,
|
node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate,
|
||||||
in_params.mix_id, in_params.splitter_info_id);
|
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>(
|
const auto resample_rate = static_cast<s32>(
|
||||||
static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
|
static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
|
||||||
|
@ -1208,6 +1216,7 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t temp_mix_offset{};
|
std::size_t temp_mix_offset{};
|
||||||
|
s32 samples_output{};
|
||||||
auto samples_remaining = sample_count;
|
auto samples_remaining = sample_count;
|
||||||
while (samples_remaining > 0) {
|
while (samples_remaining > 0) {
|
||||||
const auto samples_to_output = std::min(samples_remaining, min_required_samples);
|
const auto samples_to_output = std::min(samples_remaining, min_required_samples);
|
||||||
|
@ -1296,20 +1305,21 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
|
||||||
|
|
||||||
if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) {
|
if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) {
|
||||||
// No need to resample
|
// 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 {
|
} else {
|
||||||
std::fill(sample_buffer.begin() + temp_mix_offset,
|
std::fill(sample_buffer.begin() + temp_mix_offset,
|
||||||
sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read),
|
sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read),
|
||||||
0);
|
0);
|
||||||
AudioCore::Resample(output, sample_buffer.data(), resample_rate, dsp_state.fraction,
|
AudioCore::Resample(output.data() + samples_output, sample_buffer.data(), resample_rate,
|
||||||
samples_to_output);
|
dsp_state.fraction, samples_to_output);
|
||||||
// Resample
|
// Resample
|
||||||
for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
|
for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
|
||||||
dsp_state.sample_history[i] = sample_buffer[samples_to_read + i];
|
dsp_state.sample_history[i] = sample_buffer[samples_to_read + i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
output += samples_to_output;
|
|
||||||
samples_remaining -= samples_to_output;
|
samples_remaining -= samples_to_output;
|
||||||
|
samples_output += samples_to_output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <span>
|
||||||
#include "audio_core/common.h"
|
#include "audio_core/common.h"
|
||||||
#include "audio_core/voice_context.h"
|
#include "audio_core/voice_context.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
@ -41,10 +42,10 @@ public:
|
||||||
void PreCommand();
|
void PreCommand();
|
||||||
void PostCommand();
|
void PostCommand();
|
||||||
|
|
||||||
[[nodiscard]] s32* GetChannelMixBuffer(s32 channel);
|
[[nodiscard]] std::span<s32> GetChannelMixBuffer(s32 channel);
|
||||||
[[nodiscard]] const s32* GetChannelMixBuffer(s32 channel) const;
|
[[nodiscard]] std::span<const s32> GetChannelMixBuffer(s32 channel) const;
|
||||||
[[nodiscard]] s32* GetMixBuffer(std::size_t index);
|
[[nodiscard]] std::span<s32> GetMixBuffer(std::size_t index);
|
||||||
[[nodiscard]] const s32* GetMixBuffer(std::size_t index) const;
|
[[nodiscard]] std::span<const s32> GetMixBuffer(std::size_t index) const;
|
||||||
[[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
[[nodiscard]] std::size_t GetMixChannelBufferOffset(s32 channel) const;
|
||||||
|
|
||||||
[[nodiscard]] std::size_t GetTotalMixBufferCount() const;
|
[[nodiscard]] std::size_t GetTotalMixBufferCount() const;
|
||||||
|
@ -77,10 +78,11 @@ private:
|
||||||
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
|
||||||
[[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
[[nodiscard]] ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
|
||||||
|
|
||||||
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
|
s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
|
||||||
u32 sample_count, u32 write_offset, u32 write_count);
|
std::span<const s32> data, u32 sample_count, u32 write_offset,
|
||||||
s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
|
u32 write_count);
|
||||||
u32 sample_count, u32 read_offset, u32 read_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,
|
void InitializeI3dl2Reverb(I3dl2ReverbParams& info, I3dl2ReverbState& state,
|
||||||
std::vector<u8>& work_buffer);
|
std::vector<u8>& work_buffer);
|
||||||
|
@ -90,8 +92,9 @@ private:
|
||||||
s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_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 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);
|
s32 sample_end_offset, s32 sample_count, s32 channel, std::size_t mix_offset);
|
||||||
void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state,
|
void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, std::span<s32> output,
|
||||||
s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id);
|
VoiceState& dsp_state, s32 channel, s32 target_sample_rate,
|
||||||
|
s32 sample_count, s32 node_id);
|
||||||
|
|
||||||
AudioCommon::AudioRendererParameter& worker_params;
|
AudioCommon::AudioRendererParameter& worker_params;
|
||||||
VoiceContext& voice_context;
|
VoiceContext& voice_context;
|
||||||
|
|
|
@ -15,10 +15,17 @@ std::size_t SinkContext::GetCount() const {
|
||||||
void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
|
void SinkContext::UpdateMainSink(const SinkInfo::InParams& in) {
|
||||||
ASSERT(in.type == SinkTypes::Device);
|
ASSERT(in.type == SinkTypes::Device);
|
||||||
|
|
||||||
has_downmix_coefs = in.device.down_matrix_enabled;
|
if (in.device.down_matrix_enabled) {
|
||||||
if (has_downmix_coefs) {
|
|
||||||
downmix_coefficients = in.device.down_matrix_coef;
|
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;
|
in_use = in.in_use;
|
||||||
use_count = in.device.input_count;
|
use_count = in.device.input_count;
|
||||||
buffers = in.device.input;
|
buffers = in.device.input;
|
||||||
|
@ -34,10 +41,6 @@ std::vector<u8> SinkContext::OutputBuffers() const {
|
||||||
return buffer_ret;
|
return buffer_ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SinkContext::HasDownMixingCoefficients() const {
|
|
||||||
return has_downmix_coefs;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const {
|
const DownmixCoefficients& SinkContext::GetDownmixCoefficients() const {
|
||||||
return downmix_coefficients;
|
return downmix_coefficients;
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,6 @@ public:
|
||||||
[[nodiscard]] bool InUse() const;
|
[[nodiscard]] bool InUse() const;
|
||||||
[[nodiscard]] std::vector<u8> OutputBuffers() const;
|
[[nodiscard]] std::vector<u8> OutputBuffers() const;
|
||||||
|
|
||||||
[[nodiscard]] bool HasDownMixingCoefficients() const;
|
|
||||||
[[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const;
|
[[nodiscard]] const DownmixCoefficients& GetDownmixCoefficients() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -92,7 +91,6 @@ private:
|
||||||
s32 use_count{};
|
s32 use_count{};
|
||||||
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
|
std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
|
||||||
std::size_t sink_count{};
|
std::size_t sink_count{};
|
||||||
bool has_downmix_coefs{false};
|
|
||||||
DownmixCoefficients downmix_coefficients{};
|
DownmixCoefficients downmix_coefficients{};
|
||||||
};
|
};
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
|
Loading…
Reference in a new issue