mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 11:36:13 +00:00
cellAudioOut: properly implement downMixer
If the PS3 downMixer is enabled, the game uses 8 channel input and 2 or 6 channel output
This commit is contained in:
parent
fd8931c87e
commit
ba96c6f3bb
8 changed files with 54 additions and 43 deletions
|
@ -100,7 +100,7 @@ void AudioBackend::normalize(u32 sample_cnt, const f32* src, f32* dst)
|
|||
}
|
||||
}
|
||||
|
||||
AudioChannelCnt AudioBackend::get_channel_count(u32 device_index)
|
||||
std::pair<AudioChannelCnt, AudioChannelCnt> AudioBackend::get_channel_count_and_downmixer(u32 device_index)
|
||||
{
|
||||
audio_out_configuration& audio_out_cfg = g_fxo->get<audio_out_configuration>();
|
||||
std::lock_guard lock(audio_out_cfg.mtx);
|
||||
|
@ -113,9 +113,9 @@ AudioChannelCnt AudioBackend::get_channel_count(u32 device_index)
|
|||
{
|
||||
switch (out.channels)
|
||||
{
|
||||
case 2: return AudioChannelCnt::STEREO;
|
||||
case 6: return AudioChannelCnt::SURROUND_5_1;
|
||||
case 8: return AudioChannelCnt::SURROUND_7_1;
|
||||
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);
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ AudioChannelCnt AudioBackend::get_channel_count(u32 device_index)
|
|||
switch (out.channels)
|
||||
{
|
||||
case 2:
|
||||
return AudioChannelCnt::STEREO;
|
||||
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);
|
||||
}
|
||||
|
@ -135,8 +135,7 @@ AudioChannelCnt AudioBackend::get_channel_count(u32 device_index)
|
|||
switch (out.channels)
|
||||
{
|
||||
case 6:
|
||||
case 8:
|
||||
return AudioChannelCnt::SURROUND_5_1;
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -136,9 +136,9 @@ public:
|
|||
static void normalize(u32 sample_cnt, const f32* src, f32* dst);
|
||||
|
||||
/*
|
||||
* Returns the channel count based on the downmix mode.
|
||||
* Returns the channel count and the downmix mode.
|
||||
*/
|
||||
static AudioChannelCnt get_channel_count(u32 device_index);
|
||||
static std::pair<AudioChannelCnt, AudioChannelCnt> get_channel_count_and_downmixer(u32 device_index);
|
||||
|
||||
/*
|
||||
* Downmix audio stream.
|
||||
|
|
|
@ -66,9 +66,10 @@ void cell_audio_config::reset(bool backend_changed)
|
|||
|
||||
const AudioFreq freq = AudioFreq::FREQ_48K;
|
||||
const AudioSampleSize sample_size = raw.convert_to_s16 ? AudioSampleSize::S16 : AudioSampleSize::FLOAT;
|
||||
const AudioChannelCnt ch_cnt = AudioBackend::get_channel_count(0); // CELL_AUDIO_OUT_PRIMARY
|
||||
const auto [ch_cnt, downmix] = AudioBackend::get_channel_count_and_downmixer(0); // CELL_AUDIO_OUT_PRIMARY
|
||||
const f64 cb_frame_len = backend->Open(freq, sample_size, ch_cnt) ? backend->GetCallbackFrameLen() : 0.0;
|
||||
|
||||
audio_downmix = downmix;
|
||||
audio_channels = static_cast<u32>(ch_cnt);
|
||||
audio_sampling_rate = static_cast<u32>(freq);
|
||||
audio_block_period = AUDIO_BUFFER_SAMPLES * 1'000'000 / audio_sampling_rate;
|
||||
|
@ -843,21 +844,19 @@ void cell_audio_thread::operator()()
|
|||
}
|
||||
|
||||
// Mix
|
||||
float *buf = ringbuffer->get_current_buffer();
|
||||
float* buf = ringbuffer->get_current_buffer();
|
||||
|
||||
switch (cfg.audio_channels)
|
||||
switch (cfg.audio_downmix)
|
||||
{
|
||||
case 2:
|
||||
case AudioChannelCnt::STEREO:
|
||||
mix<AudioChannelCnt::STEREO>(buf);
|
||||
break;
|
||||
case 6:
|
||||
case AudioChannelCnt::SURROUND_5_1:
|
||||
mix<AudioChannelCnt::SURROUND_5_1>(buf);
|
||||
break;
|
||||
case 8:
|
||||
case AudioChannelCnt::SURROUND_7_1:
|
||||
mix<AudioChannelCnt::SURROUND_7_1>(buf);
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Unsupported number of audio channels: %u", cfg.audio_channels);
|
||||
}
|
||||
|
||||
// Enqueue
|
||||
|
@ -885,7 +884,7 @@ audio_port* cell_audio_thread::open_port()
|
|||
}
|
||||
|
||||
template <AudioChannelCnt downmix>
|
||||
void cell_audio_thread::mix(float *out_buffer, s32 offset)
|
||||
void cell_audio_thread::mix(float* out_buffer, s32 offset)
|
||||
{
|
||||
AUDIT(out_buffer != nullptr);
|
||||
|
||||
|
|
|
@ -211,6 +211,7 @@ struct cell_audio_config
|
|||
|
||||
std::shared_ptr<AudioBackend> backend = nullptr;
|
||||
|
||||
AudioChannelCnt audio_downmix = AudioChannelCnt::SURROUND_7_1;
|
||||
u32 audio_channels = 0;
|
||||
u32 audio_sampling_rate = 0;
|
||||
u32 audio_block_period = 0;
|
||||
|
@ -349,7 +350,7 @@ private:
|
|||
void advance(u64 timestamp);
|
||||
std::tuple<u32, u32, u32, u32> count_port_buffer_tags();
|
||||
template <AudioChannelCnt downmix>
|
||||
void mix(float *out_buffer, s32 offset = 0);
|
||||
void mix(float* out_buffer, s32 offset = 0);
|
||||
void finish_port_volume_stepping();
|
||||
|
||||
constexpr static u64 get_thread_wait_delay(u64 time_left)
|
||||
|
|
|
@ -74,11 +74,6 @@ audio_out_configuration::audio_out_configuration()
|
|||
output.channels = channel;
|
||||
output.encoder = type;
|
||||
|
||||
// Set the initially selected configuration
|
||||
output.config.channel = channel;
|
||||
output.config.encoder = type;
|
||||
output.config.downMixer = CELL_AUDIO_OUT_DOWNMIXER_NONE;
|
||||
|
||||
selected = true;
|
||||
}
|
||||
};
|
||||
|
@ -249,7 +244,6 @@ error_code cellAudioOutGetState(u32 audioOut, u32 deviceIndex, vm::ptr<CellAudio
|
|||
return CELL_AUDIO_OUT_ERROR_ILLEGAL_PARAMETER;
|
||||
}
|
||||
|
||||
|
||||
const auto num = cellAudioOutGetNumberOfDevice(audioOut);
|
||||
|
||||
if (num < 0)
|
||||
|
@ -280,15 +274,30 @@ error_code cellAudioOutGetState(u32 audioOut, u32 deviceIndex, vm::ptr<CellAudio
|
|||
case CELL_AUDIO_OUT_PRIMARY:
|
||||
case CELL_AUDIO_OUT_SECONDARY:
|
||||
{
|
||||
const AudioChannelCnt channels = AudioBackend::get_channel_count(audioOut);
|
||||
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, &out](const CellAudioOutSoundMode& mode)
|
||||
const auto it = std::find_if(out.sound_modes.cbegin(), out.sound_modes.cend(), [channels = channels, &out](const CellAudioOutSoundMode& mode)
|
||||
{
|
||||
return mode.type == out.encoder && mode.channel == static_cast<u8>(channels);
|
||||
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());
|
||||
|
@ -334,12 +343,10 @@ error_code cellAudioOutConfigure(u32 audioOut, vm::ptr<CellAudioOutConfiguration
|
|||
|
||||
audio_out_configuration::audio_out& out = cfg.out.at(audioOut);
|
||||
|
||||
const bool found_mode = (out.sound_modes.cend() != std::find_if(out.sound_modes.cbegin(), out.sound_modes.cend(), [&config](const CellAudioOutSoundMode& mode)
|
||||
{
|
||||
return mode.channel == config->channel && mode.type == config->encoder && config->downMixer <= CELL_AUDIO_OUT_DOWNMIXER_TYPE_B;
|
||||
}));
|
||||
// Apparently the set config does not necessarily have to exist in the list of sound modes.
|
||||
// For example 8 channels are used if the downMixer is set even if the PS3 only supports 2 channel output.
|
||||
|
||||
if (found_mode && (out.channels != config->channel || out.encoder != config->encoder || out.downmixer != config->downMixer))
|
||||
if (out.channels != config->channel || out.encoder != config->encoder || out.downmixer != config->downMixer)
|
||||
{
|
||||
out.channels = config->channel;
|
||||
out.encoder = config->encoder;
|
||||
|
@ -347,9 +354,6 @@ error_code cellAudioOutConfigure(u32 audioOut, vm::ptr<CellAudioOutConfiguration
|
|||
|
||||
needs_reset = true;
|
||||
}
|
||||
|
||||
// Apparently the set config is not necessarily equal to the active config, so we need to store it seperately.
|
||||
out.config = *config;
|
||||
}
|
||||
|
||||
if (needs_reset)
|
||||
|
@ -410,8 +414,13 @@ error_code cellAudioOutGetConfiguration(u32 audioOut, vm::ptr<CellAudioOutConfig
|
|||
|
||||
const audio_out_configuration::audio_out& out = cfg.out.at(audioOut);
|
||||
|
||||
// Return the set config, which might not necessarily be the active config.
|
||||
*config = out.config;
|
||||
// Return the active config.
|
||||
CellAudioOutConfiguration _config{};
|
||||
_config.channel = out.channels;
|
||||
_config.encoder = out.encoder;
|
||||
_config.downMixer = out.downmixer;
|
||||
|
||||
*config = _config;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
|
|
@ -204,7 +204,6 @@ 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;
|
||||
CellAudioOutConfiguration config{}; // Selected by the game. Does not necessarily mean the active config.
|
||||
};
|
||||
|
||||
std::array<audio_out, 2> out;
|
||||
|
|
|
@ -1322,7 +1322,7 @@ void rsxaudio_backend_thread::update_emu_cfg()
|
|||
|
||||
rsxaudio_backend_thread::emu_audio_cfg rsxaudio_backend_thread::get_emu_cfg()
|
||||
{
|
||||
const AudioChannelCnt out_ch_cnt = AudioBackend::get_channel_count(0); // CELL_AUDIO_OUT_PRIMARY
|
||||
const auto [out_ch_cnt, out_downmix] = AudioBackend::get_channel_count_and_downmixer(0); // CELL_AUDIO_OUT_PRIMARY
|
||||
|
||||
emu_audio_cfg cfg =
|
||||
{
|
||||
|
@ -1332,7 +1332,8 @@ rsxaudio_backend_thread::emu_audio_cfg rsxaudio_backend_thread::get_emu_cfg()
|
|||
.convert_to_s16 = static_cast<bool>(g_cfg.audio.convert_to_s16),
|
||||
.enable_time_stretching = static_cast<bool>(g_cfg.audio.enable_time_stretching),
|
||||
.dump_to_file = static_cast<bool>(g_cfg.audio.dump_to_file),
|
||||
.downmix = out_ch_cnt,
|
||||
.channels = out_ch_cnt,
|
||||
.downmix = out_downmix,
|
||||
.renderer = g_cfg.audio.renderer,
|
||||
.provider = g_cfg.audio.provider,
|
||||
.avport = convert_avport(g_cfg.audio.rsxaudio_port)
|
||||
|
@ -1671,9 +1672,11 @@ void rsxaudio_backend_thread::backend_init(const rsxaudio_state& ra_state, const
|
|||
backend->SetErrorCallback(std::bind(&rsxaudio_backend_thread::error_callback, this));
|
||||
}
|
||||
|
||||
// TODO: properly handle dowmnix
|
||||
|
||||
const port_config& port_cfg = ra_state.port[static_cast<u8>(emu_cfg.avport)];
|
||||
const AudioSampleSize sample_size = emu_cfg.convert_to_s16 ? AudioSampleSize::S16 : AudioSampleSize::FLOAT;
|
||||
const AudioChannelCnt ch_cnt = static_cast<AudioChannelCnt>(std::min<u32>(static_cast<u32>(port_cfg.ch_cnt), static_cast<u32>(emu_cfg.downmix)));
|
||||
const AudioChannelCnt ch_cnt = static_cast<AudioChannelCnt>(std::min<u32>(static_cast<u32>(port_cfg.ch_cnt), static_cast<u32>(emu_cfg.channels)));
|
||||
|
||||
static constexpr f64 _10ms = 512.0 / 48000.0;
|
||||
const f64 cb_frame_len = backend->Open(port_cfg.freq, sample_size, ch_cnt) ? backend->GetCallbackFrameLen() : 0.0;
|
||||
|
|
|
@ -474,7 +474,8 @@ private:
|
|||
bool convert_to_s16 = false;
|
||||
bool enable_time_stretching = false;
|
||||
bool dump_to_file = false;
|
||||
AudioChannelCnt downmix = AudioChannelCnt::STEREO;
|
||||
AudioChannelCnt channels = AudioChannelCnt::STEREO;
|
||||
AudioChannelCnt downmix = AudioChannelCnt::SURROUND_7_1;
|
||||
audio_renderer renderer = audio_renderer::null;
|
||||
audio_provider provider = audio_provider::none;
|
||||
RsxaudioAvportIdx avport = RsxaudioAvportIdx::HDMI_0;
|
||||
|
|
Loading…
Add table
Reference in a new issue