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).
This commit is contained in:
Megamouse 2022-06-15 23:38:35 +02:00
parent 11c5230628
commit ebabdd37b4
3 changed files with 81 additions and 63 deletions

View file

@ -106,43 +106,7 @@ std::pair<AudioChannelCnt, AudioChannelCnt> AudioBackend::get_channel_count_and_
std::lock_guard lock(audio_out_cfg.mtx); std::lock_guard lock(audio_out_cfg.mtx);
ensure(device_index < audio_out_cfg.out.size()); ensure(device_index < audio_out_cfg.out.size());
const audio_out_configuration::audio_out& out = audio_out_cfg.out.at(device_index); const audio_out_configuration::audio_out& out = audio_out_cfg.out.at(device_index);
return out.get_channel_count_and_downmixer();
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);
}
} }
AudioChannelCnt AudioBackend::get_max_channel_count(u32 device_index) AudioChannelCnt AudioBackend::get_max_channel_count(u32 device_index)

View file

@ -73,6 +73,7 @@ audio_out_configuration::audio_out_configuration()
// Pre-select the first available sound mode // Pre-select the first available sound mode
output.channels = channel; output.channels = channel;
output.encoder = type; output.encoder = type;
output.sound_mode = output.sound_modes.back();
selected = true; 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); cellSysutil.notice("cellAudioOut: initial secondary output configuration: channels=%d, encoder=%d, downmixer=%d", secondary_output.channels, secondary_output.encoder, secondary_output.downmixer);
} }
std::pair<AudioChannelCnt, AudioChannelCnt> 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 cellAudioOutGetNumberOfDevice(u32 audioOut);
error_code cellAudioOutGetSoundAvailability(u32 audioOut, u32 type, u32 fs, u32 option) error_code cellAudioOutGetSoundAvailability(u32 audioOut, u32 type, u32 fs, u32 option)
@ -274,38 +315,14 @@ error_code cellAudioOutGetState(u32 audioOut, u32 deviceIndex, vm::ptr<CellAudio
case CELL_AUDIO_OUT_PRIMARY: case CELL_AUDIO_OUT_PRIMARY:
case CELL_AUDIO_OUT_SECONDARY: case CELL_AUDIO_OUT_SECONDARY:
{ {
const auto [channels, downmixer] = AudioBackend::get_channel_count_and_downmixer(audioOut);
audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>(); audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>();
std::lock_guard lock(cfg.mtx); std::lock_guard lock(cfg.mtx);
const audio_out_configuration::audio_out& out = cfg.out.at(audioOut); 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<u8>(channels);
});
ensure(it != out.sound_modes.cend());
_state.state = out.state; _state.state = out.state;
_state.encoder = out.encoder; _state.encoder = out.encoder;
_state.downMixer = out.downmixer; _state.downMixer = out.downmixer;
_state.soundMode = *it; _state.soundMode = out.sound_mode;
break; break;
} }
default: default:
@ -352,6 +369,38 @@ error_code cellAudioOutConfigure(u32 audioOut, vm::ptr<CellAudioOutConfiguration
out.encoder = config->encoder; out.encoder = config->encoder;
out.downmixer = config->downMixer; 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<u8>(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<u32>(channels), out.sound_mode.channel, out.sound_mode.type, out.sound_mode.fs);
}
needs_reset = true; needs_reset = true;
} }
} }

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "Emu/Audio/AudioBackend.h"
// Error codes // Error codes
enum CellAudioOutError : u32 enum CellAudioOutError : u32
{ {
@ -194,7 +196,7 @@ struct CellAudioOutDeviceConfiguration
struct audio_out_configuration struct audio_out_configuration
{ {
std::mutex mtx; shared_mutex mtx;
struct audio_out struct audio_out
{ {
@ -204,6 +206,9 @@ struct audio_out_configuration
u32 downmixer = CELL_AUDIO_OUT_DOWNMIXER_NONE; u32 downmixer = CELL_AUDIO_OUT_DOWNMIXER_NONE;
u32 copy_control = CELL_AUDIO_OUT_COPY_CONTROL_COPY_FREE; u32 copy_control = CELL_AUDIO_OUT_COPY_CONTROL_COPY_FREE;
std::vector<CellAudioOutSoundMode> sound_modes; std::vector<CellAudioOutSoundMode> sound_modes;
CellAudioOutSoundMode sound_mode{};
std::pair<AudioChannelCnt, AudioChannelCnt> get_channel_count_and_downmixer() const;
}; };
std::array<audio_out, 2> out; std::array<audio_out, 2> out;