mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-04 22:11:26 +12:00
cellAudio: implement downmix to 5.1
This commit is contained in:
parent
987ede2e6c
commit
bb0aaea92d
13 changed files with 133 additions and 45 deletions
|
@ -24,13 +24,19 @@ OpenALBackend::OpenALBackend()
|
|||
alcMakeContextCurrent(m_context);
|
||||
checkForAlcError("alcMakeContextCurrent");
|
||||
|
||||
if (get_channels() == 2)
|
||||
const auto channels = get_channels();
|
||||
|
||||
switch (channels)
|
||||
{
|
||||
case 2:
|
||||
m_format = (m_sample_size == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO_FLOAT32;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
case 6:
|
||||
m_format = (m_sample_size == 2) ? AL_FORMAT_51CHN16 : AL_FORMAT_51CHN32;
|
||||
break;
|
||||
default:
|
||||
m_format = (m_sample_size == 2) ? AL_FORMAT_71CHN16 : AL_FORMAT_71CHN32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,21 @@ u32 AudioBackend::get_sample_size()
|
|||
|
||||
u32 AudioBackend::get_channels()
|
||||
{
|
||||
return g_cfg.audio.downmix_to_2ch ? 2 : 8;
|
||||
const audio_channels channels = g_cfg.audio.audio_channel_downmix.get();
|
||||
|
||||
switch (channels)
|
||||
{
|
||||
case audio_channels::use_application_settings:
|
||||
return 2; // TODO
|
||||
case audio_channels::downmix_to_stereo:
|
||||
return 2;
|
||||
case audio_channels::downmix_to_5_1:
|
||||
return 6;
|
||||
case audio_channels::surround_7_1:
|
||||
return 8;
|
||||
default:
|
||||
fmt::throw_exception("Unknown audio channel mode %s (%d)", channels, static_cast<int>(channels));
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioBackend::has_capability(u32 cap) const
|
||||
|
|
|
@ -25,7 +25,7 @@ XAudio2Backend::XAudio2Backend()
|
|||
return;
|
||||
}
|
||||
|
||||
hr = instance->CreateMasteringVoice(&m_master_voice, g_cfg.audio.downmix_to_2ch ? 2 : 8, 48000);
|
||||
hr = instance->CreateMasteringVoice(&m_master_voice, get_channels(), 48000);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
XAudio.error("CreateMasteringVoice() failed(0x%08x)", (u32)hr);
|
||||
|
|
|
@ -142,7 +142,7 @@ void audio_ringbuffer::enqueue(const float* in_buffer)
|
|||
}
|
||||
|
||||
// Enqueue audio
|
||||
bool success = backend->AddData(buf, AUDIO_BUFFER_SAMPLES * cfg.audio_channels);
|
||||
const bool success = backend->AddData(buf, AUDIO_BUFFER_SAMPLES * cfg.audio_channels);
|
||||
if (!success)
|
||||
{
|
||||
cellAudio.error("Could not enqueue buffer onto audio backend. Attempting to recover...");
|
||||
|
@ -311,8 +311,8 @@ void audio_port::tag(s32 offset)
|
|||
// We use -0.0f in case games check if the buffer is empty. -0.0f == 0.0f evaluates to true, but std::signbit can be used to distinguish them
|
||||
const f32 tag = -0.0f;
|
||||
|
||||
const u32 tag_first_pos = num_channels == 2 ? PORT_BUFFER_TAG_FIRST_2CH : PORT_BUFFER_TAG_FIRST_8CH;
|
||||
const u32 tag_delta = num_channels == 2 ? PORT_BUFFER_TAG_DELTA_2CH : PORT_BUFFER_TAG_DELTA_8CH;
|
||||
const u32 tag_first_pos = num_channels == 2 ? PORT_BUFFER_TAG_FIRST_2CH : num_channels == 6 ? PORT_BUFFER_TAG_FIRST_6CH : PORT_BUFFER_TAG_FIRST_8CH;
|
||||
const u32 tag_delta = num_channels == 2 ? PORT_BUFFER_TAG_DELTA_2CH : num_channels == 6 ? PORT_BUFFER_TAG_DELTA_6CH : PORT_BUFFER_TAG_DELTA_8CH;
|
||||
|
||||
for (u32 tag_pos = tag_first_pos, tag_nr = 0; tag_nr < PORT_BUFFER_TAG_COUNT; tag_pos += tag_delta, tag_nr++)
|
||||
{
|
||||
|
@ -341,8 +341,8 @@ std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
|
|||
u32 port_pos = port.position();
|
||||
|
||||
// Find the last tag that has been touched
|
||||
const u32 tag_first_pos = port.num_channels == 2 ? PORT_BUFFER_TAG_FIRST_2CH : PORT_BUFFER_TAG_FIRST_8CH;
|
||||
const u32 tag_delta = port.num_channels == 2 ? PORT_BUFFER_TAG_DELTA_2CH : PORT_BUFFER_TAG_DELTA_8CH;
|
||||
const u32 tag_first_pos = port.num_channels == 2 ? PORT_BUFFER_TAG_FIRST_2CH : port.num_channels == 6 ? PORT_BUFFER_TAG_FIRST_6CH : PORT_BUFFER_TAG_FIRST_8CH;
|
||||
const u32 tag_delta = port.num_channels == 2 ? PORT_BUFFER_TAG_DELTA_2CH : port.num_channels == 6 ? PORT_BUFFER_TAG_DELTA_6CH : PORT_BUFFER_TAG_DELTA_8CH;
|
||||
|
||||
u32 last_touched_tag_nr = port.prev_touched_tag_nr;
|
||||
bool retouched = false;
|
||||
|
@ -742,16 +742,19 @@ void cell_audio_thread::operator()()
|
|||
|
||||
// Mix
|
||||
float *buf = ringbuffer->get_current_buffer();
|
||||
if (cfg.audio_channels == 2)
|
||||
{
|
||||
mix<true>(buf);
|
||||
}
|
||||
else if (cfg.audio_channels == 8)
|
||||
{
|
||||
mix<false>(buf);
|
||||
}
|
||||
else
|
||||
|
||||
switch (cfg.audio_channels)
|
||||
{
|
||||
case 2:
|
||||
mix<audio_channels::downmix_to_stereo>(buf);
|
||||
break;
|
||||
case 6:
|
||||
mix<audio_channels::downmix_to_5_1>(buf);
|
||||
break;
|
||||
case 8:
|
||||
mix<audio_channels::surround_7_1>(buf);
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Unsupported number of audio channels: %u", cfg.audio_channels);
|
||||
}
|
||||
|
||||
|
@ -766,12 +769,12 @@ void cell_audio_thread::operator()()
|
|||
ringbuffer.reset();
|
||||
}
|
||||
|
||||
template <bool DownmixToStereo>
|
||||
template <audio_channels downmix>
|
||||
void cell_audio_thread::mix(float *out_buffer, s32 offset)
|
||||
{
|
||||
AUDIT(out_buffer != nullptr);
|
||||
|
||||
constexpr u32 channels = DownmixToStereo ? 2 : 8;
|
||||
constexpr u32 channels = downmix == audio_channels::surround_7_1 ? 8 : downmix == audio_channels::downmix_to_5_1 ? 6 : 2;
|
||||
constexpr u32 out_buffer_sz = channels * AUDIO_BUFFER_SAMPLES;
|
||||
|
||||
bool first_mix = true;
|
||||
|
@ -823,16 +826,20 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
|
|||
out_buffer[out + 0] = left;
|
||||
out_buffer[out + 1] = right;
|
||||
|
||||
if constexpr (!DownmixToStereo)
|
||||
if constexpr (downmix != audio_channels::downmix_to_stereo)
|
||||
{
|
||||
out_buffer[out + 2] = 0.0f;
|
||||
out_buffer[out + 3] = 0.0f;
|
||||
out_buffer[out + 4] = 0.0f;
|
||||
out_buffer[out + 5] = 0.0f;
|
||||
|
||||
if constexpr (downmix != audio_channels::downmix_to_5_1)
|
||||
{
|
||||
out_buffer[out + 6] = 0.0f;
|
||||
out_buffer[out + 7] = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
first_mix = false;
|
||||
}
|
||||
else
|
||||
|
@ -866,13 +873,22 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
|
|||
const float side_left = buf[in + 6] * m;
|
||||
const float side_right = buf[in + 7] * m;
|
||||
|
||||
if constexpr (DownmixToStereo)
|
||||
if constexpr (downmix == audio_channels::downmix_to_stereo)
|
||||
{
|
||||
// Don't mix in the lfe as per dolby specification and based on documentation
|
||||
const float mid = center * 0.5;
|
||||
out_buffer[out + 0] = left * minus_3db + mid + side_left * 0.5 + rear_left * 0.5;
|
||||
out_buffer[out + 1] = right * minus_3db + mid + side_right * 0.5 + rear_right * 0.5;
|
||||
}
|
||||
else if constexpr (downmix == audio_channels::downmix_to_5_1)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_buffer[out + 0] = left;
|
||||
|
@ -902,13 +918,22 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
|
|||
const float side_left = buf[in + 6] * m;
|
||||
const float side_right = buf[in + 7] * m;
|
||||
|
||||
if constexpr (DownmixToStereo)
|
||||
if constexpr (downmix == audio_channels::downmix_to_stereo)
|
||||
{
|
||||
// Don't mix in the lfe as per dolby specification and based on documentation
|
||||
const float mid = center * 0.5;
|
||||
out_buffer[out + 0] += left * minus_3db + mid + side_left * 0.5 + rear_left * 0.5;
|
||||
out_buffer[out + 1] += right * minus_3db + mid + side_right * 0.5 + rear_right * 0.5;
|
||||
}
|
||||
else if constexpr (downmix == audio_channels::downmix_to_5_1)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
out_buffer[out + 0] += left;
|
||||
|
|
|
@ -100,6 +100,7 @@ enum : u32
|
|||
MAX_AUDIO_EVENT_QUEUES = 64,
|
||||
|
||||
AUDIO_BLOCK_SIZE_2CH = 2 * AUDIO_BUFFER_SAMPLES,
|
||||
AUDIO_BLOCK_SIZE_6CH = 6 * AUDIO_BUFFER_SAMPLES,
|
||||
AUDIO_BLOCK_SIZE_8CH = 8 * AUDIO_BUFFER_SAMPLES,
|
||||
|
||||
PORT_BUFFER_TAG_COUNT = 6,
|
||||
|
@ -108,6 +109,10 @@ enum : u32
|
|||
PORT_BUFFER_TAG_DELTA_2CH = PORT_BUFFER_TAG_LAST_2CH / (PORT_BUFFER_TAG_COUNT - 1),
|
||||
PORT_BUFFER_TAG_FIRST_2CH = PORT_BUFFER_TAG_LAST_2CH % (PORT_BUFFER_TAG_COUNT - 1),
|
||||
|
||||
PORT_BUFFER_TAG_LAST_6CH = AUDIO_BLOCK_SIZE_6CH - 1,
|
||||
PORT_BUFFER_TAG_DELTA_6CH = PORT_BUFFER_TAG_LAST_6CH / (PORT_BUFFER_TAG_COUNT - 1),
|
||||
PORT_BUFFER_TAG_FIRST_6CH = PORT_BUFFER_TAG_LAST_6CH % (PORT_BUFFER_TAG_COUNT - 1),
|
||||
|
||||
PORT_BUFFER_TAG_LAST_8CH = AUDIO_BLOCK_SIZE_8CH - 1,
|
||||
PORT_BUFFER_TAG_DELTA_8CH = PORT_BUFFER_TAG_LAST_8CH / (PORT_BUFFER_TAG_COUNT - 1),
|
||||
PORT_BUFFER_TAG_FIRST_8CH = PORT_BUFFER_TAG_LAST_8CH % (PORT_BUFFER_TAG_COUNT - 1),
|
||||
|
@ -337,7 +342,7 @@ class cell_audio_thread
|
|||
void reset_ports(s32 offset = 0);
|
||||
void advance(u64 timestamp, bool reset = true);
|
||||
std::tuple<u32, u32, u32, u32> count_port_buffer_tags();
|
||||
template <bool DownmixToStereo>
|
||||
template <audio_channels downmix>
|
||||
void mix(float *out_buffer, s32 offset = 0);
|
||||
void finish_port_volume_stepping();
|
||||
|
||||
|
|
|
@ -217,7 +217,7 @@ struct cfg_root : cfg::node
|
|||
|
||||
cfg::_bool dump_to_file{ this, "Dump to file" };
|
||||
cfg::_bool convert_to_u16{ this, "Convert to 16 bit" };
|
||||
cfg::_bool downmix_to_2ch{ this, "Downmix to Stereo", true };
|
||||
cfg::_enum<audio_channels> audio_channel_downmix{ this, "Audio Channels", audio_channels::downmix_to_stereo };
|
||||
cfg::_int<1, 128> startt{ this, "Start Threshold", 1 }; // TODO: used only by ALSA, should probably be removed once ALSA is upgraded
|
||||
cfg::_int<0, 200> volume{ this, "Master Volume", 100, true };
|
||||
cfg::_bool enable_buffering{ this, "Enable Buffering", true };
|
||||
|
|
|
@ -404,3 +404,20 @@ void fmt_class_string<shader_mode>::format(std::string& out, u64 arg)
|
|||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<audio_channels>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](audio_channels value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case audio_channels::use_application_settings: return "Use application settings";
|
||||
case audio_channels::downmix_to_stereo: return "Downmix to Stereo";
|
||||
case audio_channels::downmix_to_5_1: return "Downmix to 5.1";
|
||||
case audio_channels::surround_7_1: return "Surround 7.1";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -75,6 +75,14 @@ enum class audio_renderer
|
|||
#endif
|
||||
};
|
||||
|
||||
enum class audio_channels
|
||||
{
|
||||
use_application_settings,
|
||||
downmix_to_stereo,
|
||||
downmix_to_5_1,
|
||||
surround_7_1
|
||||
};
|
||||
|
||||
enum class camera_handler
|
||||
{
|
||||
null,
|
||||
|
|
|
@ -691,6 +691,15 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_
|
|||
case enter_button_assign::cross: return tr("Enter with cross", "Enter button assignment");
|
||||
}
|
||||
break;
|
||||
case emu_settings_type::AudioChannels:
|
||||
switch (static_cast<audio_channels>(index))
|
||||
{
|
||||
case audio_channels::use_application_settings: return tr("Use application settings", "Audio channels");
|
||||
case audio_channels::downmix_to_stereo: return tr("Downmix to Stereo", "Audio channels");
|
||||
case audio_channels::downmix_to_5_1: return tr("Downmix to 5.1", "Audio channels");
|
||||
case audio_channels::surround_7_1: return tr("Surround 7.1", "Audio channels");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -96,7 +96,7 @@ enum class emu_settings_type
|
|||
AudioRenderer,
|
||||
DumpToFile,
|
||||
ConvertTo16Bit,
|
||||
DownmixStereo,
|
||||
AudioChannels,
|
||||
MasterVolume,
|
||||
EnableBuffering,
|
||||
AudioBufferDuration,
|
||||
|
@ -236,7 +236,7 @@ static const QMap<emu_settings_type, cfg_location> settings_location =
|
|||
{ emu_settings_type::AudioRenderer, { "Audio", "Renderer"}},
|
||||
{ emu_settings_type::DumpToFile, { "Audio", "Dump to file"}},
|
||||
{ emu_settings_type::ConvertTo16Bit, { "Audio", "Convert to 16 bit"}},
|
||||
{ emu_settings_type::DownmixStereo, { "Audio", "Downmix to Stereo"}},
|
||||
{ emu_settings_type::AudioChannels, { "Audio", "Audio Channels"}},
|
||||
{ emu_settings_type::MasterVolume, { "Audio", "Master Volume"}},
|
||||
{ emu_settings_type::EnableBuffering, { "Audio", "Enable Buffering"}},
|
||||
{ emu_settings_type::AudioBufferDuration, { "Audio", "Desired Audio Buffer Duration"}},
|
||||
|
|
|
@ -727,6 +727,11 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
|||
#endif
|
||||
connect(ui->audioOutBox, QOverload<int>::of(&QComboBox::currentIndexChanged), enable_buffering);
|
||||
|
||||
m_emu_settings->EnhanceComboBox(ui->combo_audio_channels, emu_settings_type::AudioChannels);
|
||||
SubscribeTooltip(ui->gb_audio_channels, tooltips.settings.audio_channels);
|
||||
// TODO: enable this setting once cellAudioOutConfigure can change channels on the fly
|
||||
ui->combo_audio_channels->removeItem(static_cast<int>(audio_channels::use_application_settings));
|
||||
|
||||
// Microphone Comboboxes
|
||||
m_mics_combo[0] = ui->microphone1Box;
|
||||
m_mics_combo[1] = ui->microphone2Box;
|
||||
|
@ -772,9 +777,6 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
|||
m_emu_settings->EnhanceCheckBox(ui->convert, emu_settings_type::ConvertTo16Bit);
|
||||
SubscribeTooltip(ui->convert, tooltips.settings.convert);
|
||||
|
||||
m_emu_settings->EnhanceCheckBox(ui->downmix, emu_settings_type::DownmixStereo);
|
||||
SubscribeTooltip(ui->downmix, tooltips.settings.downmix);
|
||||
|
||||
m_emu_settings->EnhanceCheckBox(ui->enableBuffering, emu_settings_type::EnableBuffering);
|
||||
SubscribeTooltip(ui->enableBuffering, tooltips.settings.enable_buffering);
|
||||
connect(ui->enableBuffering, &QCheckBox::clicked, enable_buffering_options);
|
||||
|
|
|
@ -833,6 +833,18 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_audio_channels">
|
||||
<property name="title">
|
||||
<string>Audio Channels</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_audio_channels_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_audio_channels"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_audio_settings">
|
||||
<property name="title">
|
||||
|
@ -853,16 +865,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="downmix">
|
||||
<property name="text">
|
||||
<string>Downmix to Stereo</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="spacer_audio_settings">
|
||||
<property name="orientation">
|
||||
|
|
|
@ -45,7 +45,7 @@ public:
|
|||
const QString audio_out_linux = tr("OpenAL uses a cross-platform approach and supports audio buffering, so it is the recommended option.\nPulseAudio uses the native Linux sound system, and is the next best alternative. If neither are available, ALSA can be used instead.");
|
||||
const QString audio_dump = tr("Saves all audio as a raw wave file. If unsure, leave this unchecked.");
|
||||
const QString convert = tr("Uses 16-bit audio samples instead of default 32-bit floating point.\nUse with buggy audio drivers if you have no sound or completely broken sound.");
|
||||
const QString downmix = tr("Uses stereo audio output instead of default 7.1 surround sound.\nUse with stereo audio devices. Disable it only if you are using a surround sound audio system.");
|
||||
const QString audio_channels = tr("Uses chosen audio output instead of default 7.1 surround sound.\nUse downmix to stereo with stereo audio devices. Use 5.1 or higher only if you are using a surround sound audio system.");
|
||||
const QString master_volume = tr("Controls the overall volume of the emulation.\nValues above 100% might reduce the audio quality.");
|
||||
const QString enable_buffering = tr("Enables audio buffering, which reduces crackle/stutter but increases audio latency (requires XAudio2 or OpenAL).");
|
||||
const QString audio_buffer_duration = tr("Target buffer duration in milliseconds.\nHigher values make the buffering algorithm's job easier, but may introduce noticeable audio latency.");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue