mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 19:45:20 +00:00
audio: allow to choose channel layouts
This commit is contained in:
parent
a6fa091ab3
commit
bbb4c109d7
22 changed files with 343 additions and 99 deletions
|
@ -33,7 +33,12 @@ u32 AudioBackend::get_sample_size() const
|
|||
|
||||
u32 AudioBackend::get_channels() const
|
||||
{
|
||||
return static_cast<std::underlying_type_t<decltype(m_channels)>>(m_channels);
|
||||
return m_channels;
|
||||
}
|
||||
|
||||
audio_channel_layout AudioBackend::get_channel_layout() const
|
||||
{
|
||||
return m_layout;
|
||||
}
|
||||
|
||||
bool AudioBackend::get_convert_to_s16() const
|
||||
|
@ -141,23 +146,63 @@ AudioChannelCnt AudioBackend::get_max_channel_count(u32 device_index)
|
|||
return count;
|
||||
}
|
||||
|
||||
AudioChannelCnt AudioBackend::convert_channel_count(u64 raw)
|
||||
u32 AudioBackend::default_layout_channel_count(audio_channel_layout layout)
|
||||
{
|
||||
switch (raw)
|
||||
switch (layout)
|
||||
{
|
||||
default:
|
||||
case 8:
|
||||
return AudioChannelCnt::SURROUND_7_1;
|
||||
case 7:
|
||||
case 6:
|
||||
return AudioChannelCnt::SURROUND_5_1;
|
||||
case 5:
|
||||
case 4:
|
||||
case 3:
|
||||
case 2:
|
||||
case 1:
|
||||
return AudioChannelCnt::STEREO;
|
||||
case 0:
|
||||
fmt::throw_exception("Unsupported channel count");
|
||||
case audio_channel_layout::mono: return 1;
|
||||
case audio_channel_layout::stereo: return 2;
|
||||
case audio_channel_layout::stereo_lfe: return 3;
|
||||
case audio_channel_layout::quadraphonic: return 4;
|
||||
case audio_channel_layout::quadraphonic_lfe: return 5;
|
||||
case audio_channel_layout::surround_5_1: return 6;
|
||||
case audio_channel_layout::surround_7_1: return 8;
|
||||
default: fmt::throw_exception("Unsupported layout %d", static_cast<u32>(layout));
|
||||
}
|
||||
}
|
||||
|
||||
u32 AudioBackend::layout_channel_count(u32 channels, audio_channel_layout layout)
|
||||
{
|
||||
if (channels == 0)
|
||||
{
|
||||
fmt::throw_exception("Unsupported channel count");
|
||||
}
|
||||
|
||||
return std::min(channels, default_layout_channel_count(layout));
|
||||
}
|
||||
|
||||
audio_channel_layout AudioBackend::default_layout(u32 channels)
|
||||
{
|
||||
switch (channels)
|
||||
{
|
||||
case 1: return audio_channel_layout::mono;
|
||||
case 2: return audio_channel_layout::stereo;
|
||||
case 3: return audio_channel_layout::stereo_lfe;
|
||||
case 4: return audio_channel_layout::quadraphonic;
|
||||
case 5: return audio_channel_layout::quadraphonic_lfe;
|
||||
case 6: return audio_channel_layout::surround_5_1;
|
||||
case 7: return audio_channel_layout::surround_5_1;
|
||||
case 8: return audio_channel_layout::surround_7_1;
|
||||
default: return audio_channel_layout::stereo;
|
||||
}
|
||||
}
|
||||
|
||||
void AudioBackend::setup_channel_layout(u32 input_channel_count, u32 output_channel_count, audio_channel_layout layout, logs::channel& log)
|
||||
{
|
||||
const u32 channels = std::min(input_channel_count, output_channel_count);
|
||||
|
||||
if (layout != audio_channel_layout::automatic && output_channel_count > input_channel_count)
|
||||
{
|
||||
log.warning("Mixing from %d to %d channels is not implemented. Falling back to automatic layout.", input_channel_count, output_channel_count);
|
||||
layout = audio_channel_layout::automatic;
|
||||
}
|
||||
|
||||
if (layout != audio_channel_layout::automatic && channels < default_layout_channel_count(layout))
|
||||
{
|
||||
log.warning("Can't use layout %s with %d channels. Falling back to automatic layout.", layout, channels);
|
||||
layout = audio_channel_layout::automatic;
|
||||
}
|
||||
|
||||
m_layout = layout == audio_channel_layout::automatic ? default_layout(channels) : layout;
|
||||
m_channels = layout_channel_count(channels, m_layout);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/logs.hpp"
|
||||
#include "Utilities/mutex.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Emu/system_config_types.h"
|
||||
#include <numbers>
|
||||
|
||||
enum : u32
|
||||
|
@ -30,6 +32,7 @@ enum class AudioSampleSize : u32
|
|||
S16 = sizeof(s16),
|
||||
};
|
||||
|
||||
// This enum is only used for emulation
|
||||
enum class AudioChannelCnt : u32
|
||||
{
|
||||
STEREO = 2,
|
||||
|
@ -69,7 +72,7 @@ public:
|
|||
// If dev_id is empty, then default device will be selected.
|
||||
// May override channel count if device has smaller number of channels.
|
||||
// Should return 'true' on success.
|
||||
virtual bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) = 0;
|
||||
virtual bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout) = 0;
|
||||
|
||||
// Reset backend state. Blocks until data callback returns.
|
||||
virtual void Close() = 0;
|
||||
|
@ -122,6 +125,7 @@ public:
|
|||
u32 get_sample_size() const;
|
||||
|
||||
u32 get_channels() const;
|
||||
audio_channel_layout get_channel_layout() const;
|
||||
|
||||
bool get_convert_to_s16() const;
|
||||
|
||||
|
@ -158,51 +162,91 @@ public:
|
|||
*/
|
||||
static AudioChannelCnt get_max_channel_count(u32 device_index);
|
||||
|
||||
/*
|
||||
* Get default channel count for a layout
|
||||
*/
|
||||
static u32 default_layout_channel_count(audio_channel_layout layout);
|
||||
|
||||
/*
|
||||
* Converts raw channel count to value usable by backends
|
||||
*/
|
||||
static AudioChannelCnt convert_channel_count(u64 raw);
|
||||
static u32 layout_channel_count(u32 channels, audio_channel_layout layout);
|
||||
|
||||
/*
|
||||
* Get the default layout for raw channel count
|
||||
*/
|
||||
static audio_channel_layout default_layout(u32 channels);
|
||||
|
||||
/*
|
||||
* Downmix audio stream.
|
||||
*/
|
||||
template<AudioChannelCnt from, AudioChannelCnt to>
|
||||
template<AudioChannelCnt src_ch_cnt, audio_channel_layout dst_layout>
|
||||
static void downmix(u32 sample_cnt, const f32* src, f32* dst)
|
||||
{
|
||||
static_assert(from == AudioChannelCnt::SURROUND_5_1 || from == AudioChannelCnt::SURROUND_7_1, "Cannot downmix FROM channel count");
|
||||
static_assert(static_cast<u32>(from) > static_cast<u32>(to), "FROM channel count must be bigger than TO");
|
||||
const u32 dst_ch_cnt = default_layout_channel_count(dst_layout);
|
||||
if (static_cast<u32>(src_ch_cnt) <= dst_ch_cnt) fmt::throw_exception("src channel count must be bigger than dst channel count");
|
||||
|
||||
static constexpr f32 center_coef = std::numbers::sqrt2_v<f32> / 2;
|
||||
static constexpr f32 surround_coef = std::numbers::sqrt2_v<f32> / 2;
|
||||
|
||||
for (u32 src_sample = 0, dst_sample = 0; src_sample < sample_cnt; src_sample += static_cast<u32>(from), dst_sample += static_cast<u32>(to))
|
||||
for (u32 src_sample = 0, dst_sample = 0; src_sample < sample_cnt; src_sample += static_cast<u32>(src_ch_cnt), dst_sample += dst_ch_cnt)
|
||||
{
|
||||
const f32 left = src[src_sample + 0];
|
||||
const f32 right = src[src_sample + 1];
|
||||
const f32 center = src[src_sample + 2];
|
||||
const f32 low_freq = src[src_sample + 3];
|
||||
|
||||
if constexpr (from == AudioChannelCnt::SURROUND_5_1)
|
||||
const f32 left = src[src_sample + 0];
|
||||
const f32 right = src[src_sample + 1];
|
||||
|
||||
if constexpr (src_ch_cnt == AudioChannelCnt::STEREO)
|
||||
{
|
||||
static_assert(to == AudioChannelCnt::STEREO, "Invalid TO channel count");
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::mono)
|
||||
{
|
||||
dst[dst_sample + 0] = left + right;
|
||||
}
|
||||
}
|
||||
else if constexpr (src_ch_cnt == AudioChannelCnt::SURROUND_5_1)
|
||||
{
|
||||
const f32 center = src[src_sample + 2];
|
||||
const f32 low_freq = src[src_sample + 3];
|
||||
const f32 side_left = src[src_sample + 4];
|
||||
const f32 side_right = src[src_sample + 5];
|
||||
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid + side_left * surround_coef;
|
||||
dst[dst_sample + 1] = right + mid + side_right * surround_coef;
|
||||
}
|
||||
else if constexpr (from == AudioChannelCnt::SURROUND_7_1)
|
||||
{
|
||||
static_assert(to == AudioChannelCnt::STEREO || to == AudioChannelCnt::SURROUND_5_1, "Invalid TO channel count");
|
||||
if constexpr (dst_layout == audio_channel_layout::quadraphonic || dst_layout == audio_channel_layout::quadraphonic_lfe)
|
||||
{
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid;
|
||||
dst[dst_sample + 1] = right + mid;
|
||||
dst[dst_sample + 2] = side_left;
|
||||
dst[dst_sample + 3] = side_right;
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::quadraphonic_lfe)
|
||||
{
|
||||
dst[dst_sample + 4] = low_freq;
|
||||
}
|
||||
}
|
||||
else if constexpr (dst_layout == audio_channel_layout::stereo || dst_layout == audio_channel_layout::stereo_lfe)
|
||||
{
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid + side_left * surround_coef;
|
||||
dst[dst_sample + 1] = right + mid + side_right * surround_coef;
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::stereo_lfe)
|
||||
{
|
||||
dst[dst_sample + 2] = low_freq;
|
||||
}
|
||||
}
|
||||
else if constexpr (dst_layout == audio_channel_layout::mono)
|
||||
{
|
||||
dst[dst_sample + 0] = left + right + center + side_left + side_right;
|
||||
}
|
||||
}
|
||||
else if constexpr (src_ch_cnt == AudioChannelCnt::SURROUND_7_1)
|
||||
{
|
||||
const f32 center = src[src_sample + 2];
|
||||
const f32 low_freq = src[src_sample + 3];
|
||||
const f32 rear_left = src[src_sample + 4];
|
||||
const f32 rear_right = src[src_sample + 5];
|
||||
const f32 side_left = src[src_sample + 6];
|
||||
const f32 side_right = src[src_sample + 7];
|
||||
|
||||
if constexpr (to == AudioChannelCnt::SURROUND_5_1)
|
||||
if constexpr (dst_layout == audio_channel_layout::surround_5_1)
|
||||
{
|
||||
dst[dst_sample + 0] = left;
|
||||
dst[dst_sample + 1] = right;
|
||||
|
@ -211,59 +255,126 @@ public:
|
|||
dst[dst_sample + 4] = side_left + rear_left;
|
||||
dst[dst_sample + 5] = side_right + rear_right;
|
||||
}
|
||||
else
|
||||
else if constexpr (dst_layout == audio_channel_layout::quadraphonic || dst_layout == audio_channel_layout::quadraphonic_lfe)
|
||||
{
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid;
|
||||
dst[dst_sample + 1] = right + mid;
|
||||
dst[dst_sample + 2] = side_left + rear_left;
|
||||
dst[dst_sample + 3] = side_right + rear_right;
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::quadraphonic_lfe)
|
||||
{
|
||||
dst[dst_sample + 4] = low_freq;
|
||||
}
|
||||
}
|
||||
else if constexpr (dst_layout == audio_channel_layout::stereo || dst_layout == audio_channel_layout::stereo_lfe)
|
||||
{
|
||||
const f32 mid = center * center_coef;
|
||||
dst[dst_sample + 0] = left + mid + (side_left + rear_left) * surround_coef;
|
||||
dst[dst_sample + 1] = right + mid + (side_right + rear_right) * surround_coef;
|
||||
|
||||
if constexpr (dst_layout == audio_channel_layout::stereo_lfe)
|
||||
{
|
||||
dst[dst_sample + 2] = low_freq;
|
||||
}
|
||||
}
|
||||
else if constexpr (dst_layout == audio_channel_layout::mono)
|
||||
{
|
||||
dst[dst_sample + 0] = left + right + center + side_left + rear_left + side_right + rear_right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void downmix(u32 sample_cnt, u32 src_ch_cnt, u32 dst_ch_cnt, const f32* src, f32* dst)
|
||||
static void downmix(u32 sample_cnt, u32 src_ch_cnt, audio_channel_layout dst_layout, const f32* src, f32* dst)
|
||||
{
|
||||
const u32 dst_ch_cnt = default_layout_channel_count(dst_layout);
|
||||
|
||||
if (src_ch_cnt <= dst_ch_cnt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (src_ch_cnt == static_cast<u32>(AudioChannelCnt::SURROUND_7_1))
|
||||
switch (src_ch_cnt)
|
||||
{
|
||||
if (dst_ch_cnt == static_cast<u32>(AudioChannelCnt::SURROUND_5_1))
|
||||
case static_cast<u32>(AudioChannelCnt::SURROUND_7_1):
|
||||
{
|
||||
switch (dst_layout)
|
||||
{
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::SURROUND_5_1>(sample_cnt, src, dst);
|
||||
}
|
||||
else if (dst_ch_cnt == static_cast<u32>(AudioChannelCnt::STEREO))
|
||||
{
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::STEREO>(sample_cnt, src, dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", src_ch_cnt, dst_ch_cnt);
|
||||
case audio_channel_layout::mono:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::mono>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::stereo:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::stereo>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::stereo_lfe:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::stereo_lfe>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::quadraphonic:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::quadraphonic>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::quadraphonic_lfe:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::quadraphonic_lfe>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::surround_5_1:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::surround_5_1>(sample_cnt, src, dst);
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (src_ch_cnt == static_cast<u32>(AudioChannelCnt::SURROUND_5_1))
|
||||
case static_cast<u32>(AudioChannelCnt::SURROUND_5_1):
|
||||
{
|
||||
if (dst_ch_cnt == static_cast<u32>(AudioChannelCnt::STEREO))
|
||||
switch (dst_layout)
|
||||
{
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, AudioChannelCnt::STEREO>(sample_cnt, src, dst);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", src_ch_cnt, dst_ch_cnt);
|
||||
case audio_channel_layout::mono:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::mono>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::stereo:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::stereo>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::stereo_lfe:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::stereo_lfe>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::quadraphonic:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::quadraphonic>(sample_cnt, src, dst);
|
||||
break;
|
||||
case audio_channel_layout::quadraphonic_lfe:
|
||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::quadraphonic_lfe>(sample_cnt, src, dst);
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else
|
||||
case static_cast<u32>(AudioChannelCnt::STEREO):
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", src_ch_cnt, dst_ch_cnt);
|
||||
switch (dst_layout)
|
||||
{
|
||||
case audio_channel_layout::mono:
|
||||
AudioBackend::downmix<AudioChannelCnt::STEREO, audio_channel_layout::mono>(sample_cnt, src, dst);
|
||||
break;
|
||||
default:
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
fmt::throw_exception("Invalid downmix combination: %u -> %s", src_ch_cnt, dst_layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void setup_channel_layout(u32 input_channel_count, u32 output_channel_count, audio_channel_layout layout, logs::channel& log);
|
||||
|
||||
AudioSampleSize m_sample_size = AudioSampleSize::FLOAT;
|
||||
AudioFreq m_sampling_rate = AudioFreq::FREQ_48K;
|
||||
AudioChannelCnt m_channels = AudioChannelCnt::STEREO;
|
||||
u32 m_channels = 2;
|
||||
audio_channel_layout m_layout = audio_channel_layout::automatic;
|
||||
|
||||
std::timed_mutex m_cb_mutex{};
|
||||
std::function<u32(u32, void *)> m_write_callback{};
|
||||
|
|
|
@ -96,7 +96,7 @@ bool CubebBackend::DefaultDeviceChanged()
|
|||
return !device.handle || device.id != m_default_device;
|
||||
}
|
||||
|
||||
bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
|
@ -118,7 +118,7 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
|||
{
|
||||
if (use_default_device)
|
||||
{
|
||||
device = GetDefaultDeviceAlt(freq, sample_size, ch_cnt);
|
||||
device = GetDefaultDeviceAlt(freq, sample_size, static_cast<u32>(ch_cnt));
|
||||
|
||||
if (!device.handle)
|
||||
{
|
||||
|
@ -148,7 +148,9 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
|||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(device.ch_cnt)), static_cast<u32>(ch_cnt)));
|
||||
|
||||
setup_channel_layout(static_cast<u32>(ch_cnt), device.ch_cnt, layout, Cubeb);
|
||||
|
||||
full_sample_size = get_channels() * get_sample_size();
|
||||
|
||||
cubeb_stream_params stream_param{};
|
||||
|
@ -157,14 +159,19 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
|||
stream_param.channels = get_channels();
|
||||
stream_param.layout = [&]()
|
||||
{
|
||||
switch (m_channels)
|
||||
switch (m_layout)
|
||||
{
|
||||
case AudioChannelCnt::STEREO: return CUBEB_LAYOUT_STEREO;
|
||||
case AudioChannelCnt::SURROUND_5_1: return CUBEB_LAYOUT_3F2_LFE;
|
||||
case AudioChannelCnt::SURROUND_7_1: return CUBEB_LAYOUT_3F4_LFE;
|
||||
default:
|
||||
fmt::throw_exception("Invalid audio channel count");
|
||||
case audio_channel_layout::automatic: break;
|
||||
case audio_channel_layout::mono: return CUBEB_LAYOUT_MONO;
|
||||
case audio_channel_layout::stereo: return CUBEB_LAYOUT_STEREO;
|
||||
case audio_channel_layout::stereo_lfe: return CUBEB_LAYOUT_STEREO_LFE;
|
||||
case audio_channel_layout::quadraphonic: return CUBEB_LAYOUT_QUAD;
|
||||
case audio_channel_layout::quadraphonic_lfe: return CUBEB_LAYOUT_QUAD_LFE;
|
||||
case audio_channel_layout::surround_5_1: return CUBEB_LAYOUT_3F2_LFE;
|
||||
case audio_channel_layout::surround_7_1: return CUBEB_LAYOUT_3F4_LFE;
|
||||
}
|
||||
|
||||
fmt::throw_exception("Invalid audio layout %d", static_cast<u32>(m_layout));
|
||||
}();
|
||||
stream_param.prefs = m_dev_collection_cb_enabled && device.handle ? CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING : CUBEB_STREAM_PREF_NONE;
|
||||
|
||||
|
@ -348,7 +355,7 @@ CubebBackend::device_handle CubebBackend::GetDevice(std::string_view dev_id)
|
|||
return result;
|
||||
};
|
||||
|
||||
CubebBackend::device_handle CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
CubebBackend::device_handle CubebBackend::GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, u32 ch_cnt)
|
||||
{
|
||||
Cubeb.notice("Starting alternative search for default device with freq=%d, sample_size=%d and ch_cnt=%d", static_cast<u32>(freq), static_cast<u32>(sample_size), static_cast<u32>(ch_cnt));
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
bool Operational() override;
|
||||
bool DefaultDeviceChanged() override;
|
||||
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout) override;
|
||||
void Close() override;
|
||||
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
@ -62,5 +62,5 @@ private:
|
|||
};
|
||||
|
||||
device_handle GetDevice(std::string_view dev_id = "");
|
||||
device_handle GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt);
|
||||
device_handle GetDefaultDeviceAlt(AudioFreq freq, AudioSampleSize sample_size, u32 ch_cnt);
|
||||
};
|
||||
|
|
|
@ -122,7 +122,7 @@ bool FAudioBackend::Operational()
|
|||
return m_source_voice != nullptr && !m_reset_req.observe();
|
||||
}
|
||||
|
||||
bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
|
@ -165,7 +165,8 @@ bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSiz
|
|||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(vd.InputChannels)), static_cast<u32>(ch_cnt)));;
|
||||
|
||||
setup_channel_layout(static_cast<u32>(ch_cnt), vd.InputChannels, layout, FAudio_);
|
||||
|
||||
FAudioWaveFormatEx waveformatex;
|
||||
waveformatex.wFormatTag = get_convert_to_s16() ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
|
||||
|
|
|
@ -24,7 +24,7 @@ public:
|
|||
bool Initialized() override;
|
||||
bool Operational() override;
|
||||
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout) override;
|
||||
void Close() override;
|
||||
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
|
|
@ -10,7 +10,7 @@ public:
|
|||
|
||||
std::string_view GetName() const override { return "Null"sv; }
|
||||
|
||||
bool Open(std::string_view /* dev_id */, AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */) override
|
||||
bool Open(std::string_view /* dev_id */, AudioFreq /* freq */, AudioSampleSize /* sample_size */, AudioChannelCnt /* ch_cnt */, audio_channel_layout /*layout*/) override
|
||||
{
|
||||
Close();
|
||||
return true;
|
||||
|
|
|
@ -198,7 +198,7 @@ void XAudio2Backend::Pause()
|
|||
}
|
||||
}
|
||||
|
||||
bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt)
|
||||
bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout)
|
||||
{
|
||||
if (!Initialized())
|
||||
{
|
||||
|
@ -258,7 +258,8 @@ bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSi
|
|||
|
||||
m_sampling_rate = freq;
|
||||
m_sample_size = sample_size;
|
||||
m_channels = static_cast<AudioChannelCnt>(std::min(static_cast<u32>(convert_channel_count(vd.InputChannels)), static_cast<u32>(ch_cnt)));
|
||||
|
||||
setup_channel_layout(static_cast<u32>(ch_cnt), vd.InputChannels, layout, XAudio);
|
||||
|
||||
WAVEFORMATEX waveformatex{};
|
||||
waveformatex.wFormatTag = get_convert_to_s16() ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
||||
|
|
|
@ -28,7 +28,7 @@ public:
|
|||
bool Operational() override;
|
||||
bool DefaultDeviceChanged() override;
|
||||
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt) override;
|
||||
bool Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize sample_size, AudioChannelCnt ch_cnt, audio_channel_layout layout) override;
|
||||
void Close() override;
|
||||
|
||||
f64 GetCallbackFrameLen() override;
|
||||
|
|
|
@ -76,12 +76,14 @@ void cell_audio_config::reset(bool backend_changed)
|
|||
const auto& [req_ch_cnt, downmix] = AudioBackend::get_channel_count_and_downmixer(0); // CELL_AUDIO_OUT_PRIMARY
|
||||
f64 cb_frame_len = 0.0;
|
||||
u32 ch_cnt = 2;
|
||||
audio_channel_layout ch_layout = audio_channel_layout::stereo;
|
||||
|
||||
if (backend->Open(raw.audio_device, freq, sample_size, req_ch_cnt))
|
||||
if (backend->Open(raw.audio_device, freq, sample_size, req_ch_cnt, raw.channel_layout))
|
||||
{
|
||||
cb_frame_len = backend->GetCallbackFrameLen();
|
||||
ch_cnt = backend->get_channels();
|
||||
cellAudio.notice("Opened audio backend (sampling_rate=%d, sample_size=%d, channels=%d)", backend->get_sampling_rate(), backend->get_sample_size(), backend->get_channels());
|
||||
ch_layout = backend->get_channel_layout();
|
||||
cellAudio.notice("Opened audio backend (sampling_rate=%d, sample_size=%d, channels=%d, layout=%s)", backend->get_sampling_rate(), backend->get_sample_size(), backend->get_channels(), backend->get_channel_layout());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -89,7 +91,8 @@ void cell_audio_config::reset(bool backend_changed)
|
|||
}
|
||||
|
||||
audio_downmix = downmix;
|
||||
backend_ch_cnt = AudioChannelCnt{ch_cnt};
|
||||
backend_ch_cnt = ch_cnt;
|
||||
backend_channel_layout = ch_layout;
|
||||
audio_channels = static_cast<u32>(req_ch_cnt);
|
||||
audio_sampling_rate = static_cast<u32>(freq);
|
||||
audio_block_period = AUDIO_BUFFER_SAMPLES * 1'000'000 / audio_sampling_rate;
|
||||
|
@ -163,7 +166,7 @@ audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
|
|||
return cfg.audio_min_buffer_duration;
|
||||
}();
|
||||
|
||||
cb_ringbuf.set_buf_size(static_cast<u32>(static_cast<u32>(cfg.backend_ch_cnt) * cfg.audio_sampling_rate * cfg.audio_sample_size * buffer_dur_mult));
|
||||
cb_ringbuf.set_buf_size(static_cast<u32>(cfg.backend_ch_cnt * cfg.audio_sampling_rate * cfg.audio_sample_size * buffer_dur_mult));
|
||||
backend->SetWriteCallback(std::bind(&audio_ringbuffer::backend_write_callback, this, std::placeholders::_1, std::placeholders::_2));
|
||||
backend->SetStateCallback(std::bind(&audio_ringbuffer::backend_state_callback, this, std::placeholders::_1));
|
||||
}
|
||||
|
@ -220,7 +223,7 @@ float* audio_ringbuffer::get_current_buffer() const
|
|||
u64 audio_ringbuffer::get_enqueued_samples() const
|
||||
{
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
const u64 ringbuf_samples = cb_ringbuf.get_used_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt));
|
||||
const u64 ringbuf_samples = cb_ringbuf.get_used_size() / (cfg.audio_sample_size * cfg.backend_ch_cnt);
|
||||
|
||||
if (cfg.time_stretching_enabled)
|
||||
{
|
||||
|
@ -281,14 +284,14 @@ void audio_ringbuffer::process_resampled_data()
|
|||
{
|
||||
if (!cfg.time_stretching_enabled) return;
|
||||
|
||||
const auto& [buffer, samples] = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * static_cast<u32>(cfg.backend_ch_cnt))));
|
||||
const auto& [buffer, samples] = resampler.get_samples(static_cast<u32>(cb_ringbuf.get_free_size() / (cfg.audio_sample_size * cfg.backend_ch_cnt)));
|
||||
commit_data(buffer, samples);
|
||||
}
|
||||
|
||||
void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
|
||||
{
|
||||
const u32 sample_cnt_in = sample_cnt * cfg.audio_channels;
|
||||
const u32 sample_cnt_out = sample_cnt * static_cast<u32>(cfg.backend_ch_cnt);
|
||||
const u32 sample_cnt_out = sample_cnt * cfg.backend_ch_cnt;
|
||||
|
||||
// Dump audio if enabled
|
||||
m_dump.WriteData(buf, sample_cnt_in * static_cast<u32>(AudioSampleSize::FLOAT));
|
||||
|
@ -301,7 +304,7 @@ void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
|
|||
}
|
||||
|
||||
// Downmix if necessary
|
||||
AudioBackend::downmix(sample_cnt_in, cfg.audio_channels, static_cast<u32>(cfg.backend_ch_cnt), buf, buf);
|
||||
AudioBackend::downmix(sample_cnt_in, cfg.audio_channels, cfg.backend_channel_layout, buf, buf);
|
||||
|
||||
if (cfg.backend->get_convert_to_s16())
|
||||
{
|
||||
|
@ -615,6 +618,7 @@ namespace audio
|
|||
.time_stretching_threshold = g_cfg.audio.time_stretching_threshold,
|
||||
.convert_to_s16 = static_cast<bool>(g_cfg.audio.convert_to_s16),
|
||||
.dump_to_file = static_cast<bool>(g_cfg.audio.dump_to_file),
|
||||
.channel_layout = g_cfg.audio.channel_layout,
|
||||
.renderer = g_cfg.audio.renderer,
|
||||
.provider = g_cfg.audio.provider
|
||||
};
|
||||
|
|
|
@ -212,6 +212,7 @@ struct cell_audio_config
|
|||
s64 time_stretching_threshold = 0;
|
||||
bool convert_to_s16 = false;
|
||||
bool dump_to_file = false;
|
||||
audio_channel_layout channel_layout = audio_channel_layout::automatic;
|
||||
audio_renderer renderer = audio_renderer::null;
|
||||
audio_provider provider = audio_provider::none;
|
||||
};
|
||||
|
@ -222,7 +223,8 @@ struct cell_audio_config
|
|||
std::shared_ptr<AudioBackend> backend = nullptr;
|
||||
|
||||
AudioChannelCnt audio_downmix = AudioChannelCnt::SURROUND_7_1;
|
||||
AudioChannelCnt backend_ch_cnt = AudioChannelCnt::SURROUND_7_1;
|
||||
audio_channel_layout backend_channel_layout = audio_channel_layout::surround_7_1;
|
||||
u32 backend_ch_cnt = 8;
|
||||
u32 audio_channels = 2;
|
||||
u32 audio_sampling_rate = DEFAULT_AUDIO_SAMPLING_RATE;
|
||||
u32 audio_block_period = 0;
|
||||
|
|
|
@ -752,7 +752,7 @@ void rec_info::start_video_provider()
|
|||
if (sample.channels > channels)
|
||||
{
|
||||
// Downmix channels
|
||||
AudioBackend::downmix(CELL_REC_AUDIO_BLOCK_SAMPLES * sample.channels, sample.channels, channels, src, reinterpret_cast<f32*>(dst_buffer.block.data()));
|
||||
AudioBackend::downmix(CELL_REC_AUDIO_BLOCK_SAMPLES * sample.channels, sample.channels, audio_channel_layout::stereo, src, reinterpret_cast<f32*>(dst_buffer.block.data()));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1374,6 +1374,7 @@ rsxaudio_backend_thread::emu_audio_cfg rsxaudio_backend_thread::get_emu_cfg()
|
|||
.enable_time_stretching = static_cast<bool>(g_cfg.audio.enable_time_stretching),
|
||||
.dump_to_file = static_cast<bool>(g_cfg.audio.dump_to_file),
|
||||
.channels = out_ch_cnt,
|
||||
.channel_layout = g_cfg.audio.channel_layout,
|
||||
.renderer = g_cfg.audio.renderer,
|
||||
.provider = g_cfg.audio.provider,
|
||||
.avport = convert_avport(g_cfg.audio.rsxaudio_port)
|
||||
|
@ -1731,10 +1732,14 @@ void rsxaudio_backend_thread::backend_init(const rsxaudio_state& ra_state, const
|
|||
|
||||
f64 cb_frame_len = 0.0;
|
||||
u32 backend_ch_cnt = 2;
|
||||
if (backend->Open(emu_cfg.audio_device, port_cfg.freq, sample_size, ch_cnt))
|
||||
audio_channel_layout backend_channel_layout = audio_channel_layout::stereo;
|
||||
|
||||
if (backend->Open(emu_cfg.audio_device, port_cfg.freq, sample_size, ch_cnt, emu_cfg.channel_layout))
|
||||
{
|
||||
cb_frame_len = backend->GetCallbackFrameLen();
|
||||
backend_channel_layout = backend->get_channel_layout();
|
||||
backend_ch_cnt = backend->get_channels();
|
||||
sys_rsxaudio.notice("Opened audio backend (sampling_rate=%d, sample_size=%d, channels=%d, layout=%s)", backend->get_sampling_rate(), backend->get_sample_size(), backend->get_channels(), backend->get_channel_layout());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1775,7 +1780,7 @@ void rsxaudio_backend_thread::backend_init(const rsxaudio_state& ra_state, const
|
|||
{
|
||||
val.freq = static_cast<u32>(port_cfg.freq);
|
||||
val.input_ch_cnt = static_cast<u32>(port_cfg.ch_cnt);
|
||||
val.output_ch_cnt = backend_ch_cnt;
|
||||
val.output_channel_layout = static_cast<u8>(backend_channel_layout);
|
||||
val.convert_to_s16 = emu_cfg.convert_to_s16;
|
||||
val.avport_idx = emu_cfg.avport;
|
||||
val.ready = true;
|
||||
|
@ -1834,14 +1839,16 @@ u32 rsxaudio_backend_thread::write_data_callback(u32 bytes, void* buf)
|
|||
|
||||
if (cb_cfg.ready && !mute_state[static_cast<u8>(cb_cfg.avport_idx)] && Emu.IsRunning())
|
||||
{
|
||||
const u32 bytes_ch_adjusted = bytes / cb_cfg.output_ch_cnt * cb_cfg.input_ch_cnt;
|
||||
const audio_channel_layout output_channel_layout = static_cast<audio_channel_layout>(cb_cfg.output_channel_layout);
|
||||
const u32 output_ch_cnt = AudioBackend::default_layout_channel_count(output_channel_layout);
|
||||
const u32 bytes_ch_adjusted = bytes / output_ch_cnt * cb_cfg.input_ch_cnt;
|
||||
const u32 bytes_from_rb = cb_cfg.convert_to_s16 ? bytes_ch_adjusted / static_cast<u32>(AudioSampleSize::S16) * static_cast<u32>(AudioSampleSize::FLOAT) : bytes_ch_adjusted;
|
||||
|
||||
ensure(callback_tmp_buf.size() * static_cast<u32>(AudioSampleSize::FLOAT) >= bytes_from_rb);
|
||||
|
||||
const u32 byte_cnt = static_cast<u32>(ringbuf.pop(callback_tmp_buf.data(), bytes_from_rb, true));
|
||||
const u32 sample_cnt = byte_cnt / static_cast<u32>(AudioSampleSize::FLOAT);
|
||||
const u32 sample_cnt_out = sample_cnt / cb_cfg.input_ch_cnt * cb_cfg.output_ch_cnt;
|
||||
const u32 sample_cnt_out = sample_cnt / cb_cfg.input_ch_cnt * output_ch_cnt;
|
||||
|
||||
// Buffer is in weird state - drop acquired data
|
||||
if (sample_cnt == 0 || sample_cnt % cb_cfg.input_ch_cnt != 0)
|
||||
|
@ -1858,7 +1865,7 @@ u32 rsxaudio_backend_thread::write_data_callback(u32 bytes, void* buf)
|
|||
}
|
||||
|
||||
// Downmix if necessary
|
||||
AudioBackend::downmix(sample_cnt, cb_cfg.input_ch_cnt, cb_cfg.output_ch_cnt, callback_tmp_buf.data(), callback_tmp_buf.data());
|
||||
AudioBackend::downmix(sample_cnt, cb_cfg.input_ch_cnt, output_channel_layout, callback_tmp_buf.data(), callback_tmp_buf.data());
|
||||
|
||||
if (cb_cfg.target_volume != cb_cfg.current_volume)
|
||||
{
|
||||
|
|
|
@ -477,6 +477,7 @@ private:
|
|||
bool enable_time_stretching = false;
|
||||
bool dump_to_file = false;
|
||||
AudioChannelCnt channels = AudioChannelCnt::STEREO;
|
||||
audio_channel_layout channel_layout = audio_channel_layout::automatic;
|
||||
audio_renderer renderer = audio_renderer::null;
|
||||
audio_provider provider = audio_provider::none;
|
||||
RsxaudioAvportIdx avport = RsxaudioAvportIdx::HDMI_0;
|
||||
|
@ -503,8 +504,8 @@ private:
|
|||
RsxaudioAvportIdx avport_idx = RsxaudioAvportIdx::HDMI_0;
|
||||
u8 mute_state : SYS_RSXAUDIO_AVPORT_CNT = 0b11111;
|
||||
|
||||
u8 input_ch_cnt : 4 = 2;
|
||||
u8 output_ch_cnt : 4 = 2;
|
||||
u8 input_ch_cnt : 4 = 2;
|
||||
u8 output_channel_layout : 4 = static_cast<u8>(audio_channel_layout::stereo);
|
||||
|
||||
bool ready : 1 = false;
|
||||
bool convert_to_s16 : 1 = false;
|
||||
|
|
|
@ -247,6 +247,7 @@ struct cfg_root : cfg::node
|
|||
cfg::_bool convert_to_s16{ this, "Convert to 16 bit", false, true };
|
||||
cfg::_enum<audio_format> format{ this, "Audio Format", audio_format::stereo, false };
|
||||
cfg::uint<0, 0xFF> formats{ this, "Audio Formats", static_cast<u32>(audio_format_flag::lpcm_2_48khz), false };
|
||||
cfg::_enum<audio_channel_layout> channel_layout{ this, "Audio Channel Layout", audio_channel_layout::automatic, false };
|
||||
cfg::string audio_device{ this, "Audio Device", "@@@default@@@", true };
|
||||
cfg::_int<0, 200> volume{ this, "Master Volume", 100, true };
|
||||
cfg::_bool enable_buffering{ this, "Enable Buffering", true, true };
|
||||
|
|
|
@ -120,6 +120,27 @@ void fmt_class_string<audio_renderer>::format(std::string& out, u64 arg)
|
|||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<audio_channel_layout>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](audio_channel_layout value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case audio_channel_layout::automatic: return "Automatic";
|
||||
case audio_channel_layout::mono: return "Mono";
|
||||
case audio_channel_layout::stereo: return "Stereo";
|
||||
case audio_channel_layout::stereo_lfe: return "Stereo LFE";
|
||||
case audio_channel_layout::quadraphonic: return "Quadraphonic";
|
||||
case audio_channel_layout::quadraphonic_lfe: return "Quadraphonic LFE";
|
||||
case audio_channel_layout::surround_5_1: return "Surround 5.1";
|
||||
case audio_channel_layout::surround_7_1: return "Surround 7.1";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<detail_level>::format(std::string& out, u64 arg)
|
||||
{
|
||||
|
|
|
@ -94,6 +94,18 @@ enum class audio_format_flag : unsigned
|
|||
dts = 0x00000008, // DTS 5.1 Ch.
|
||||
};
|
||||
|
||||
enum class audio_channel_layout
|
||||
{
|
||||
automatic,
|
||||
mono,
|
||||
stereo,
|
||||
stereo_lfe,
|
||||
quadraphonic,
|
||||
quadraphonic_lfe,
|
||||
surround_5_1,
|
||||
surround_7_1,
|
||||
};
|
||||
|
||||
enum class music_handler
|
||||
{
|
||||
null,
|
||||
|
|
|
@ -1200,6 +1200,19 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_
|
|||
case audio_avport::spdif_1: return tr("SPDIF 1", "Audio Avport");
|
||||
}
|
||||
break;
|
||||
case emu_settings_type::AudioChannelLayout:
|
||||
switch (static_cast<audio_channel_layout>(index))
|
||||
{
|
||||
case audio_channel_layout::automatic: return tr("Auto", "Audio Channel Layout");
|
||||
case audio_channel_layout::mono: return tr("Mono", "Audio Channel Layout");
|
||||
case audio_channel_layout::stereo: return tr("Stereo", "Audio Channel Layout");
|
||||
case audio_channel_layout::stereo_lfe: return tr("Stereo LFE", "Audio Channel Layout");
|
||||
case audio_channel_layout::quadraphonic: return tr("Quadraphonic", "Audio Channel Layout");
|
||||
case audio_channel_layout::quadraphonic_lfe: return tr("Quadraphonic LFE", "Audio Channel Layout");
|
||||
case audio_channel_layout::surround_5_1: return tr("Surround 5.1", "Audio Channel Layout");
|
||||
case audio_channel_layout::surround_7_1: return tr("Surround 7.1", "Audio Channel Layout");
|
||||
}
|
||||
break;
|
||||
case emu_settings_type::LicenseArea:
|
||||
switch (static_cast<CellSysutilLicenseArea>(index))
|
||||
{
|
||||
|
|
|
@ -134,6 +134,7 @@ enum class emu_settings_type
|
|||
AudioProvider,
|
||||
AudioAvport,
|
||||
AudioDevice,
|
||||
AudioChannelLayout,
|
||||
MasterVolume,
|
||||
EnableBuffering,
|
||||
AudioBufferDuration,
|
||||
|
@ -321,6 +322,7 @@ inline static const QMap<emu_settings_type, cfg_location> settings_location =
|
|||
{ emu_settings_type::AudioProvider, { "Audio", "Audio Provider"}},
|
||||
{ emu_settings_type::AudioAvport, { "Audio", "RSXAudio Avport"}},
|
||||
{ emu_settings_type::AudioDevice, { "Audio", "Audio Device"}},
|
||||
{ emu_settings_type::AudioChannelLayout, { "Audio", "Audio Channel Layout"}},
|
||||
{ emu_settings_type::MasterVolume, { "Audio", "Master Volume"}},
|
||||
{ emu_settings_type::EnableBuffering, { "Audio", "Enable Buffering"}},
|
||||
{ emu_settings_type::AudioBufferDuration, { "Audio", "Desired Audio Buffer Duration"}},
|
||||
|
|
|
@ -1007,6 +1007,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
|||
get_audio_output_devices(false);
|
||||
change_audio_output_device(0); // Set device to 'Default'
|
||||
});
|
||||
|
||||
m_emu_settings->EnhanceComboBox(ui->combo_audio_channel_layout, emu_settings_type::AudioChannelLayout);
|
||||
SubscribeTooltip(ui->gb_audio_channel_layout, tooltips.settings.audio_channel_layout);
|
||||
|
||||
connect(ui->combo_audio_format, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index)
|
||||
{
|
||||
|
|
|
@ -1161,6 +1161,18 @@
|
|||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_audio_channel_layout">
|
||||
<property name="title">
|
||||
<string>Audio Output Format</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_audio_channel_layout_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="combo_audio_channel_layout"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_audio_provider">
|
||||
<property name="title">
|
||||
|
|
|
@ -65,7 +65,8 @@ public:
|
|||
const QString audio_device = tr("Controls which device is used by audio backend.");
|
||||
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 audio_format = tr("Determines the sound format.\nConfigure this setting if you want to switch between stereo and surround sound.\nChanging these values requires a restart of the game.\nThe manual setting will use your selected formats while the automatic setting will let the game choose from all available formats.");
|
||||
const QString audio_format = tr("Determines the sound format of the emulation.\nConfigure this setting if you want to switch between stereo and surround sound.\nChanging these values requires a restart of the game.\nThe manual setting will use your selected formats while the automatic setting will let the game choose from all available formats.");
|
||||
const QString audio_channel_layout = tr("Determines the sound format of RPCS3.\nUse 'Auto' to let RPCS3 decide the best format based on the audio device and the emulated audio format.");
|
||||
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.");
|
||||
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
Reference in a new issue