From a6237e54733ccc77a7b32719b71e3f8a4f05fd19 Mon Sep 17 00:00:00 2001 From: capriots <29807355+capriots@users.noreply.github.com> Date: Sun, 19 Jun 2022 17:08:03 +0200 Subject: [PATCH] cellAudio(Out): properly implement all downmixing modes (#12252) --- rpcs3/Emu/Audio/AudioBackend.h | 2 +- rpcs3/Emu/Cell/Modules/cellAudio.cpp | 108 +++++++++++++++++++----- rpcs3/Emu/Cell/Modules/cellAudio.h | 2 +- rpcs3/Emu/Cell/Modules/cellAudioOut.cpp | 85 +++++++------------ 4 files changed, 119 insertions(+), 78 deletions(-) diff --git a/rpcs3/Emu/Audio/AudioBackend.h b/rpcs3/Emu/Audio/AudioBackend.h index f3eb6dfc84..40e3b6bf98 100644 --- a/rpcs3/Emu/Audio/AudioBackend.h +++ b/rpcs3/Emu/Audio/AudioBackend.h @@ -136,7 +136,7 @@ public: static void normalize(u32 sample_cnt, const f32* src, f32* dst); /* - * Returns the channel count and the downmix mode. + * Returns the output channel count and downmix mode. */ static std::pair get_channel_count_and_downmixer(u32 device_index); diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.cpp b/rpcs3/Emu/Cell/Modules/cellAudio.cpp index d7975b9908..780b45f5d2 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudio.cpp @@ -845,17 +845,55 @@ void cell_audio_thread::operator()() // Mix float* buf = ringbuffer->get_current_buffer(); - switch (cfg.audio_downmix) + switch (cfg.audio_channels) { - case AudioChannelCnt::STEREO: - mix(buf); + case 2: + switch (cfg.audio_downmix) + { + case AudioChannelCnt::SURROUND_7_1: + mix(buf); + break; + case AudioChannelCnt::STEREO: + mix(buf); + break; + case AudioChannelCnt::SURROUND_5_1: + mix(buf); + break; + } break; - case AudioChannelCnt::SURROUND_5_1: - mix(buf); + + case 6: + switch (cfg.audio_downmix) + { + case AudioChannelCnt::SURROUND_7_1: + mix(buf); + break; + case AudioChannelCnt::STEREO: + mix(buf); + break; + case AudioChannelCnt::SURROUND_5_1: + mix(buf); + break; + } break; - case AudioChannelCnt::SURROUND_7_1: - mix(buf); + + case 8: + switch (cfg.audio_downmix) + { + case AudioChannelCnt::SURROUND_7_1: + mix(buf); + break; + case AudioChannelCnt::STEREO: + mix(buf); + break; + case AudioChannelCnt::SURROUND_5_1: + mix(buf); + break; + } break; + + default: + fmt::throw_exception("Unsupported channel count in cell_audio_config: %d", static_cast(cfg.audio_channels)); } // Enqueue @@ -882,13 +920,13 @@ audio_port* cell_audio_thread::open_port() return nullptr; } -template +template void cell_audio_thread::mix(float* out_buffer, s32 offset) { AUDIT(out_buffer != nullptr); - constexpr u32 channels = static_cast(downmix); - constexpr u32 out_buffer_sz = channels * AUDIO_BUFFER_SAMPLES; + constexpr u32 out_channels = static_cast(channels); + constexpr u32 out_buffer_sz = out_channels * AUDIO_BUFFER_SAMPLES; const float master_volume = g_cfg.audio.volume / 100.0f; @@ -928,7 +966,7 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset) if (port.num_channels == 2) { - for (u32 out = 0, in = 0; out < out_buffer_sz; out += channels, in += 2) + for (u32 out = 0, in = 0; out < out_buffer_sz; out += out_channels, in += 2) { step_volume(port); @@ -941,7 +979,7 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset) } else if (port.num_channels == 8) { - for (u32 out = 0, in = 0; out < out_buffer_sz; out += channels, in += 8) + for (u32 out = 0, in = 0; out < out_buffer_sz; out += out_channels, in += 8) { step_volume(port); @@ -965,21 +1003,47 @@ void cell_audio_thread::mix(float* out_buffer, s32 offset) { out_buffer[out + 0] += left; out_buffer[out + 1] += right; - out_buffer[out + 2] += center; - out_buffer[out + 3] += low_freq; - out_buffer[out + 4] += side_left + rear_left; - out_buffer[out + 5] += side_right + rear_right; + + if constexpr (out_channels >= 6) // Only mix the surround channels into the output if surround output is configured + { + out_buffer[out + 2] += center; + out_buffer[out + 3] += low_freq; + + if constexpr (out_channels == 6) + { + out_buffer[out + 4] += side_left + rear_left; + out_buffer[out + 5] += side_right + rear_right; + } + else // When using 7.1 ouput, out_buffer[out + 4] and out_buffer[out + 5] are the rear channels, so the side channels need to be mixed into [out + 6] and [out + 7] + { + out_buffer[out + 6] += side_left + rear_left; + out_buffer[out + 7] += side_right + rear_right; + } + } } else { out_buffer[out + 0] += left; out_buffer[out + 1] += right; - out_buffer[out + 2] += center; - out_buffer[out + 3] += low_freq; - out_buffer[out + 4] += rear_left; - out_buffer[out + 5] += rear_right; - out_buffer[out + 6] += side_left; - out_buffer[out + 7] += side_right; + + if constexpr (out_channels >= 6) // Only mix the surround channels into the output if surround output is configured + { + out_buffer[out + 2] += center; + out_buffer[out + 3] += low_freq; + + if constexpr (out_channels == 6) + { + out_buffer[out + 4] += side_left; + out_buffer[out + 5] += side_right; + } + else + { + out_buffer[out + 4] += rear_left; + out_buffer[out + 5] += rear_right; + out_buffer[out + 6] += side_left; + out_buffer[out + 7] += side_right; + } + } } } } diff --git a/rpcs3/Emu/Cell/Modules/cellAudio.h b/rpcs3/Emu/Cell/Modules/cellAudio.h index 4438f0bec0..ac5342db72 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudio.h +++ b/rpcs3/Emu/Cell/Modules/cellAudio.h @@ -348,7 +348,7 @@ private: void reset_ports(s32 offset = 0); void advance(u64 timestamp); std::tuple count_port_buffer_tags(); - template + template void mix(float* out_buffer, s32 offset = 0); void finish_port_volume_stepping(); diff --git a/rpcs3/Emu/Cell/Modules/cellAudioOut.cpp b/rpcs3/Emu/Cell/Modules/cellAudioOut.cpp index 01645646e1..7a8f830289 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudioOut.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudioOut.cpp @@ -180,42 +180,27 @@ audio_out_configuration::audio_out_configuration() std::pair audio_out_configuration::audio_out::get_channel_count_and_downmixer() const { + std::pair ret; + + switch (sound_mode.channel) + { + case 2: ret.first = AudioChannelCnt::STEREO; break; + case 6: ret.first = AudioChannelCnt::SURROUND_5_1; break; + case 8: ret.first = AudioChannelCnt::SURROUND_7_1; break; + default: + fmt::throw_exception("Unsupported channel count in cellAudioOut sound_mode: %d", sound_mode.channel); + } + switch (downmixer) { - case CELL_AUDIO_OUT_DOWNMIXER_NONE: - { - switch (channels) - { - case 2: return { AudioChannelCnt::STEREO, AudioChannelCnt::STEREO }; - case 6: return { AudioChannelCnt::SURROUND_5_1, AudioChannelCnt::SURROUND_5_1 }; - case 8: return { AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::SURROUND_7_1 }; - default: - fmt::throw_exception("Unsupported channel count in cellAudioOut config: %d", channels); - } - } - case CELL_AUDIO_OUT_DOWNMIXER_TYPE_A: - { - switch (channels) - { - case 2: - return { AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::STEREO }; - default: - fmt::throw_exception("Unsupported channel count for CELL_AUDIO_OUT_DOWNMIXER_TYPE_A in cellAudioOut config: %d", channels); - } - } - case CELL_AUDIO_OUT_DOWNMIXER_TYPE_B: - { - switch (channels) - { - case 6: - return { AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::SURROUND_5_1 }; - default: - fmt::throw_exception("Unsupported channel count for CELL_AUDIO_OUT_DOWNMIXER_TYPE_B in cellAudioOut config: %d", channels); - } - } + case CELL_AUDIO_OUT_DOWNMIXER_NONE: ret.second = AudioChannelCnt::SURROUND_7_1; break; + case CELL_AUDIO_OUT_DOWNMIXER_TYPE_A: ret.second = AudioChannelCnt::STEREO; break; + case CELL_AUDIO_OUT_DOWNMIXER_TYPE_B: ret.second = AudioChannelCnt::SURROUND_5_1; break; default: - fmt::throw_exception("Unknown downmixer in cellAudioOut config: %d", downmixer); + fmt::throw_exception("Unsupported downmixer in cellAudioOut config: %d", downmixer); } + + return ret; } error_code cellAudioOutGetNumberOfDevice(u32 audioOut); @@ -361,35 +346,27 @@ error_code cellAudioOutConfigure(u32 audioOut, vm::ptrchannel || out.encoder != config->encoder || out.downmixer != config->downMixer) { out.channels = config->channel; out.encoder = config->encoder; - out.downmixer = config->downMixer; + + if (config->downMixer > CELL_AUDIO_OUT_DOWNMIXER_TYPE_B) // PS3 ignores invalid downMixer values and keeps the previous valid one instead + { + cellSysutil.warning("cellAudioOutConfigure: Invalid downmixing mode configured: %d. Keeping old mode: downMixer=%d", + config->downMixer, out.downmixer); + } + else + { + out.downmixer = config->downMixer; + } // Try to find the best sound mode for this configuration - const auto [channels, downmixer] = out.get_channel_count_and_downmixer(); - const auto it = std::find_if(out.sound_modes.cbegin(), out.sound_modes.cend(), [channels = channels, &out](const CellAudioOutSoundMode& mode) - { - if (mode.type != out.encoder) + const auto it = std::find_if(out.sound_modes.cbegin(), out.sound_modes.cend(), [&out](const CellAudioOutSoundMode& mode) { - return false; - } - - if (out.downmixer == CELL_AUDIO_OUT_DOWNMIXER_TYPE_A) - { - return mode.channel == CELL_AUDIO_OUT_CHNUM_2; - } - - if (out.downmixer == CELL_AUDIO_OUT_DOWNMIXER_TYPE_B) - { - return mode.channel == CELL_AUDIO_OUT_CHNUM_6; - } - - return mode.channel == static_cast(channels); - }); + return mode.type == out.encoder && mode.channel == out.channels; + }); if (it != out.sound_modes.cend()) { @@ -398,7 +375,7 @@ error_code cellAudioOutConfigure(u32 audioOut, vm::ptr(channels), out.sound_mode.channel, out.sound_mode.type, out.sound_mode.fs); + config->channel, out.sound_mode.channel, out.sound_mode.type, out.sound_mode.fs); } needs_reset = true;