From ebabdd37b43c7e8b846f2caed6a1b46351f9e262 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Wed, 15 Jun 2022 23:38:35 +0200 Subject: [PATCH] cellAudioOut: fix sound_mode exception Turns out some games don't configure proper channel counts after all, which triggers an ensure in cellAudioOutGetState. Let's select the current sound_mode in cellAudioOutConfigure. Keep the old one if no match was found. Also moves some code from AudioBackend to cellAudioOut for thread safety (see mutex). --- rpcs3/Emu/Audio/AudioBackend.cpp | 38 +--------- rpcs3/Emu/Cell/Modules/cellAudioOut.cpp | 99 ++++++++++++++++++------- rpcs3/Emu/Cell/Modules/cellAudioOut.h | 7 +- 3 files changed, 81 insertions(+), 63 deletions(-) diff --git a/rpcs3/Emu/Audio/AudioBackend.cpp b/rpcs3/Emu/Audio/AudioBackend.cpp index 792ab77a31..696b8fac9b 100644 --- a/rpcs3/Emu/Audio/AudioBackend.cpp +++ b/rpcs3/Emu/Audio/AudioBackend.cpp @@ -106,43 +106,7 @@ std::pair AudioBackend::get_channel_count_and_ std::lock_guard lock(audio_out_cfg.mtx); ensure(device_index < audio_out_cfg.out.size()); const audio_out_configuration::audio_out& out = audio_out_cfg.out.at(device_index); - - switch (out.downmixer) - { - case CELL_AUDIO_OUT_DOWNMIXER_NONE: - { - switch (out.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", out.channels); - } - } - case CELL_AUDIO_OUT_DOWNMIXER_TYPE_A: - { - switch (out.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", out.channels); - } - } - case CELL_AUDIO_OUT_DOWNMIXER_TYPE_B: - { - switch (out.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", out.channels); - } - } - default: - fmt::throw_exception("Unknown downmixer in cellAudioOut config: %d", out.downmixer); - } + return out.get_channel_count_and_downmixer(); } AudioChannelCnt AudioBackend::get_max_channel_count(u32 device_index) diff --git a/rpcs3/Emu/Cell/Modules/cellAudioOut.cpp b/rpcs3/Emu/Cell/Modules/cellAudioOut.cpp index 0fe14e0041..cb927ccfac 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudioOut.cpp +++ b/rpcs3/Emu/Cell/Modules/cellAudioOut.cpp @@ -73,6 +73,7 @@ audio_out_configuration::audio_out_configuration() // Pre-select the first available sound mode output.channels = channel; output.encoder = type; + output.sound_mode = output.sound_modes.back(); selected = true; } @@ -177,6 +178,46 @@ audio_out_configuration::audio_out_configuration() cellSysutil.notice("cellAudioOut: initial secondary output configuration: channels=%d, encoder=%d, downmixer=%d", secondary_output.channels, secondary_output.encoder, secondary_output.downmixer); } +std::pair audio_out_configuration::audio_out::get_channel_count_and_downmixer() const +{ + 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); + } + } + default: + fmt::throw_exception("Unknown downmixer in cellAudioOut config: %d", downmixer); + } +} + error_code cellAudioOutGetNumberOfDevice(u32 audioOut); error_code cellAudioOutGetSoundAvailability(u32 audioOut, u32 type, u32 fs, u32 option) @@ -274,38 +315,14 @@ error_code cellAudioOutGetState(u32 audioOut, u32 deviceIndex, vm::ptrget(); std::lock_guard lock(cfg.mtx); const audio_out_configuration::audio_out& out = cfg.out.at(audioOut); - 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) - { - 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); - }); - - ensure(it != out.sound_modes.cend()); - _state.state = out.state; _state.encoder = out.encoder; _state.downMixer = out.downmixer; - _state.soundMode = *it; + _state.soundMode = out.sound_mode; break; } default: @@ -352,6 +369,38 @@ error_code cellAudioOutConfigure(u32 audioOut, vm::ptrencoder; 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) + { + 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); + }); + + if (it != out.sound_modes.cend()) + { + out.sound_mode = *it; + } + else + { + cellSysutil.warning("cellAudioOutConfigure: Could not find an ideal sound mode for %d channel output. Keeping old mode: channels=%d, encoder=%d, fs=%d", + static_cast(channels), out.sound_mode.channel, out.sound_mode.type, out.sound_mode.fs); + } + needs_reset = true; } } diff --git a/rpcs3/Emu/Cell/Modules/cellAudioOut.h b/rpcs3/Emu/Cell/Modules/cellAudioOut.h index 81f503fd3e..f7e9e7b716 100644 --- a/rpcs3/Emu/Cell/Modules/cellAudioOut.h +++ b/rpcs3/Emu/Cell/Modules/cellAudioOut.h @@ -1,5 +1,7 @@ #pragma once +#include "Emu/Audio/AudioBackend.h" + // Error codes enum CellAudioOutError : u32 { @@ -194,7 +196,7 @@ struct CellAudioOutDeviceConfiguration struct audio_out_configuration { - std::mutex mtx; + shared_mutex mtx; struct audio_out { @@ -204,6 +206,9 @@ struct audio_out_configuration u32 downmixer = CELL_AUDIO_OUT_DOWNMIXER_NONE; u32 copy_control = CELL_AUDIO_OUT_COPY_CONTROL_COPY_FREE; std::vector sound_modes; + CellAudioOutSoundMode sound_mode{}; + + std::pair get_channel_count_and_downmixer() const; }; std::array out;