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);
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)

View file

@ -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<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 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_SECONDARY:
{
const auto [channels, downmixer] = AudioBackend::get_channel_count_and_downmixer(audioOut);
audio_out_configuration& cfg = g_fxo->get<audio_out_configuration>();
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<u8>(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::ptr<CellAudioOutConfiguration
out.encoder = config->encoder;
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;
}
}

View file

@ -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<CellAudioOutSoundMode> sound_modes;
CellAudioOutSoundMode sound_mode{};
std::pair<AudioChannelCnt, AudioChannelCnt> get_channel_count_and_downmixer() const;
};
std::array<audio_out, 2> out;