mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-05 22:41:25 +12: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
|
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
|
bool AudioBackend::get_convert_to_s16() const
|
||||||
|
@ -141,23 +146,63 @@ AudioChannelCnt AudioBackend::get_max_channel_count(u32 device_index)
|
||||||
return count;
|
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 audio_channel_layout::mono: return 1;
|
||||||
case 8:
|
case audio_channel_layout::stereo: return 2;
|
||||||
return AudioChannelCnt::SURROUND_7_1;
|
case audio_channel_layout::stereo_lfe: return 3;
|
||||||
case 7:
|
case audio_channel_layout::quadraphonic: return 4;
|
||||||
case 6:
|
case audio_channel_layout::quadraphonic_lfe: return 5;
|
||||||
return AudioChannelCnt::SURROUND_5_1;
|
case audio_channel_layout::surround_5_1: return 6;
|
||||||
case 5:
|
case audio_channel_layout::surround_7_1: return 8;
|
||||||
case 4:
|
default: fmt::throw_exception("Unsupported layout %d", static_cast<u32>(layout));
|
||||||
case 3:
|
|
||||||
case 2:
|
|
||||||
case 1:
|
|
||||||
return AudioChannelCnt::STEREO;
|
|
||||||
case 0:
|
|
||||||
fmt::throw_exception("Unsupported channel count");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
#pragma once
|
||||||
|
|
||||||
#include "util/types.hpp"
|
#include "util/types.hpp"
|
||||||
|
#include "util/logs.hpp"
|
||||||
#include "Utilities/mutex.h"
|
#include "Utilities/mutex.h"
|
||||||
#include "Utilities/StrFmt.h"
|
#include "Utilities/StrFmt.h"
|
||||||
|
#include "Emu/system_config_types.h"
|
||||||
#include <numbers>
|
#include <numbers>
|
||||||
|
|
||||||
enum : u32
|
enum : u32
|
||||||
|
@ -30,6 +32,7 @@ enum class AudioSampleSize : u32
|
||||||
S16 = sizeof(s16),
|
S16 = sizeof(s16),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This enum is only used for emulation
|
||||||
enum class AudioChannelCnt : u32
|
enum class AudioChannelCnt : u32
|
||||||
{
|
{
|
||||||
STEREO = 2,
|
STEREO = 2,
|
||||||
|
@ -69,7 +72,7 @@ public:
|
||||||
// If dev_id is empty, then default device will be selected.
|
// If dev_id is empty, then default device will be selected.
|
||||||
// May override channel count if device has smaller number of channels.
|
// May override channel count if device has smaller number of channels.
|
||||||
// Should return 'true' on success.
|
// 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.
|
// Reset backend state. Blocks until data callback returns.
|
||||||
virtual void Close() = 0;
|
virtual void Close() = 0;
|
||||||
|
@ -122,6 +125,7 @@ public:
|
||||||
u32 get_sample_size() const;
|
u32 get_sample_size() const;
|
||||||
|
|
||||||
u32 get_channels() const;
|
u32 get_channels() const;
|
||||||
|
audio_channel_layout get_channel_layout() const;
|
||||||
|
|
||||||
bool get_convert_to_s16() const;
|
bool get_convert_to_s16() const;
|
||||||
|
|
||||||
|
@ -158,51 +162,91 @@ public:
|
||||||
*/
|
*/
|
||||||
static AudioChannelCnt get_max_channel_count(u32 device_index);
|
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
|
* 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.
|
* 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 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");
|
const u32 dst_ch_cnt = default_layout_channel_count(dst_layout);
|
||||||
static_assert(static_cast<u32>(from) > static_cast<u32>(to), "FROM channel count must be bigger than TO");
|
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 center_coef = std::numbers::sqrt2_v<f32> / 2;
|
||||||
static constexpr f32 surround_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 left = src[src_sample + 0];
|
||||||
const f32 right = src[src_sample + 1];
|
const f32 right = src[src_sample + 1];
|
||||||
const f32 center = src[src_sample + 2];
|
|
||||||
const f32 low_freq = src[src_sample + 3];
|
if constexpr (src_ch_cnt == AudioChannelCnt::STEREO)
|
||||||
|
|
||||||
if constexpr (from == AudioChannelCnt::SURROUND_5_1)
|
|
||||||
{
|
{
|
||||||
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_left = src[src_sample + 4];
|
||||||
const f32 side_right = src[src_sample + 5];
|
const f32 side_right = src[src_sample + 5];
|
||||||
|
|
||||||
const f32 mid = center * center_coef;
|
if constexpr (dst_layout == audio_channel_layout::quadraphonic || dst_layout == audio_channel_layout::quadraphonic_lfe)
|
||||||
dst[dst_sample + 0] = left + mid + side_left * surround_coef;
|
{
|
||||||
dst[dst_sample + 1] = right + mid + side_right * surround_coef;
|
const f32 mid = center * center_coef;
|
||||||
}
|
dst[dst_sample + 0] = left + mid;
|
||||||
else if constexpr (from == AudioChannelCnt::SURROUND_7_1)
|
dst[dst_sample + 1] = right + mid;
|
||||||
{
|
dst[dst_sample + 2] = side_left;
|
||||||
static_assert(to == AudioChannelCnt::STEREO || to == AudioChannelCnt::SURROUND_5_1, "Invalid TO channel count");
|
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_left = src[src_sample + 4];
|
||||||
const f32 rear_right = src[src_sample + 5];
|
const f32 rear_right = src[src_sample + 5];
|
||||||
const f32 side_left = src[src_sample + 6];
|
const f32 side_left = src[src_sample + 6];
|
||||||
const f32 side_right = src[src_sample + 7];
|
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 + 0] = left;
|
||||||
dst[dst_sample + 1] = right;
|
dst[dst_sample + 1] = right;
|
||||||
|
@ -211,59 +255,126 @@ public:
|
||||||
dst[dst_sample + 4] = side_left + rear_left;
|
dst[dst_sample + 4] = side_left + rear_left;
|
||||||
dst[dst_sample + 5] = side_right + rear_right;
|
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;
|
const f32 mid = center * center_coef;
|
||||||
dst[dst_sample + 0] = left + mid + (side_left + rear_left) * surround_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;
|
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)
|
if (src_ch_cnt <= dst_ch_cnt)
|
||||||
{
|
{
|
||||||
return;
|
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);
|
case audio_channel_layout::mono:
|
||||||
}
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::mono>(sample_cnt, src, dst);
|
||||||
else if (dst_ch_cnt == static_cast<u32>(AudioChannelCnt::STEREO))
|
break;
|
||||||
{
|
case audio_channel_layout::stereo:
|
||||||
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, AudioChannelCnt::STEREO>(sample_cnt, src, dst);
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::stereo>(sample_cnt, src, dst);
|
||||||
}
|
break;
|
||||||
else
|
case audio_channel_layout::stereo_lfe:
|
||||||
{
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_7_1, audio_channel_layout::stereo_lfe>(sample_cnt, src, dst);
|
||||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", src_ch_cnt, dst_ch_cnt);
|
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);
|
case audio_channel_layout::mono:
|
||||||
}
|
AudioBackend::downmix<AudioChannelCnt::SURROUND_5_1, audio_channel_layout::mono>(sample_cnt, src, dst);
|
||||||
else
|
break;
|
||||||
{
|
case audio_channel_layout::stereo:
|
||||||
fmt::throw_exception("Invalid downmix combination: %u -> %u", src_ch_cnt, dst_ch_cnt);
|
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:
|
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;
|
AudioSampleSize m_sample_size = AudioSampleSize::FLOAT;
|
||||||
AudioFreq m_sampling_rate = AudioFreq::FREQ_48K;
|
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::timed_mutex m_cb_mutex{};
|
||||||
std::function<u32(u32, void *)> m_write_callback{};
|
std::function<u32(u32, void *)> m_write_callback{};
|
||||||
|
|
|
@ -96,7 +96,7 @@ bool CubebBackend::DefaultDeviceChanged()
|
||||||
return !device.handle || device.id != m_default_device;
|
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())
|
if (!Initialized())
|
||||||
{
|
{
|
||||||
|
@ -118,7 +118,7 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||||
{
|
{
|
||||||
if (use_default_device)
|
if (use_default_device)
|
||||||
{
|
{
|
||||||
device = GetDefaultDeviceAlt(freq, sample_size, ch_cnt);
|
device = GetDefaultDeviceAlt(freq, sample_size, static_cast<u32>(ch_cnt));
|
||||||
|
|
||||||
if (!device.handle)
|
if (!device.handle)
|
||||||
{
|
{
|
||||||
|
@ -148,7 +148,9 @@ bool CubebBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSize
|
||||||
|
|
||||||
m_sampling_rate = freq;
|
m_sampling_rate = freq;
|
||||||
m_sample_size = sample_size;
|
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();
|
full_sample_size = get_channels() * get_sample_size();
|
||||||
|
|
||||||
cubeb_stream_params stream_param{};
|
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.channels = get_channels();
|
||||||
stream_param.layout = [&]()
|
stream_param.layout = [&]()
|
||||||
{
|
{
|
||||||
switch (m_channels)
|
switch (m_layout)
|
||||||
{
|
{
|
||||||
case AudioChannelCnt::STEREO: return CUBEB_LAYOUT_STEREO;
|
case audio_channel_layout::automatic: break;
|
||||||
case AudioChannelCnt::SURROUND_5_1: return CUBEB_LAYOUT_3F2_LFE;
|
case audio_channel_layout::mono: return CUBEB_LAYOUT_MONO;
|
||||||
case AudioChannelCnt::SURROUND_7_1: return CUBEB_LAYOUT_3F4_LFE;
|
case audio_channel_layout::stereo: return CUBEB_LAYOUT_STEREO;
|
||||||
default:
|
case audio_channel_layout::stereo_lfe: return CUBEB_LAYOUT_STEREO_LFE;
|
||||||
fmt::throw_exception("Invalid audio channel count");
|
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;
|
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;
|
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));
|
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 Operational() override;
|
||||||
bool DefaultDeviceChanged() 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;
|
void Close() override;
|
||||||
|
|
||||||
f64 GetCallbackFrameLen() override;
|
f64 GetCallbackFrameLen() override;
|
||||||
|
@ -62,5 +62,5 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
device_handle GetDevice(std::string_view dev_id = "");
|
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();
|
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())
|
if (!Initialized())
|
||||||
{
|
{
|
||||||
|
@ -165,7 +165,8 @@ bool FAudioBackend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSiz
|
||||||
|
|
||||||
m_sampling_rate = freq;
|
m_sampling_rate = freq;
|
||||||
m_sample_size = sample_size;
|
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;
|
FAudioWaveFormatEx waveformatex;
|
||||||
waveformatex.wFormatTag = get_convert_to_s16() ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
|
waveformatex.wFormatTag = get_convert_to_s16() ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
|
||||||
|
|
|
@ -24,7 +24,7 @@ public:
|
||||||
bool Initialized() override;
|
bool Initialized() override;
|
||||||
bool Operational() 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;
|
void Close() override;
|
||||||
|
|
||||||
f64 GetCallbackFrameLen() override;
|
f64 GetCallbackFrameLen() override;
|
||||||
|
|
|
@ -10,7 +10,7 @@ public:
|
||||||
|
|
||||||
std::string_view GetName() const override { return "Null"sv; }
|
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();
|
Close();
|
||||||
return true;
|
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())
|
if (!Initialized())
|
||||||
{
|
{
|
||||||
|
@ -258,7 +258,8 @@ bool XAudio2Backend::Open(std::string_view dev_id, AudioFreq freq, AudioSampleSi
|
||||||
|
|
||||||
m_sampling_rate = freq;
|
m_sampling_rate = freq;
|
||||||
m_sample_size = sample_size;
|
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 waveformatex{};
|
||||||
waveformatex.wFormatTag = get_convert_to_s16() ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
waveformatex.wFormatTag = get_convert_to_s16() ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
||||||
|
|
|
@ -28,7 +28,7 @@ public:
|
||||||
bool Operational() override;
|
bool Operational() override;
|
||||||
bool DefaultDeviceChanged() 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;
|
void Close() override;
|
||||||
|
|
||||||
f64 GetCallbackFrameLen() 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
|
const auto& [req_ch_cnt, downmix] = AudioBackend::get_channel_count_and_downmixer(0); // CELL_AUDIO_OUT_PRIMARY
|
||||||
f64 cb_frame_len = 0.0;
|
f64 cb_frame_len = 0.0;
|
||||||
u32 ch_cnt = 2;
|
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();
|
cb_frame_len = backend->GetCallbackFrameLen();
|
||||||
ch_cnt = backend->get_channels();
|
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
|
else
|
||||||
{
|
{
|
||||||
|
@ -89,7 +91,8 @@ void cell_audio_config::reset(bool backend_changed)
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_downmix = downmix;
|
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_channels = static_cast<u32>(req_ch_cnt);
|
||||||
audio_sampling_rate = static_cast<u32>(freq);
|
audio_sampling_rate = static_cast<u32>(freq);
|
||||||
audio_block_period = AUDIO_BUFFER_SAMPLES * 1'000'000 / audio_sampling_rate;
|
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;
|
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->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));
|
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
|
u64 audio_ringbuffer::get_enqueued_samples() const
|
||||||
{
|
{
|
||||||
AUDIT(cfg.buffering_enabled);
|
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)
|
if (cfg.time_stretching_enabled)
|
||||||
{
|
{
|
||||||
|
@ -281,14 +284,14 @@ void audio_ringbuffer::process_resampled_data()
|
||||||
{
|
{
|
||||||
if (!cfg.time_stretching_enabled) return;
|
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);
|
commit_data(buffer, samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
|
void audio_ringbuffer::commit_data(f32* buf, u32 sample_cnt)
|
||||||
{
|
{
|
||||||
const u32 sample_cnt_in = sample_cnt * cfg.audio_channels;
|
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
|
// Dump audio if enabled
|
||||||
m_dump.WriteData(buf, sample_cnt_in * static_cast<u32>(AudioSampleSize::FLOAT));
|
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
|
// 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())
|
if (cfg.backend->get_convert_to_s16())
|
||||||
{
|
{
|
||||||
|
@ -615,6 +618,7 @@ namespace audio
|
||||||
.time_stretching_threshold = g_cfg.audio.time_stretching_threshold,
|
.time_stretching_threshold = g_cfg.audio.time_stretching_threshold,
|
||||||
.convert_to_s16 = static_cast<bool>(g_cfg.audio.convert_to_s16),
|
.convert_to_s16 = static_cast<bool>(g_cfg.audio.convert_to_s16),
|
||||||
.dump_to_file = static_cast<bool>(g_cfg.audio.dump_to_file),
|
.dump_to_file = static_cast<bool>(g_cfg.audio.dump_to_file),
|
||||||
|
.channel_layout = g_cfg.audio.channel_layout,
|
||||||
.renderer = g_cfg.audio.renderer,
|
.renderer = g_cfg.audio.renderer,
|
||||||
.provider = g_cfg.audio.provider
|
.provider = g_cfg.audio.provider
|
||||||
};
|
};
|
||||||
|
|
|
@ -212,6 +212,7 @@ struct cell_audio_config
|
||||||
s64 time_stretching_threshold = 0;
|
s64 time_stretching_threshold = 0;
|
||||||
bool convert_to_s16 = false;
|
bool convert_to_s16 = false;
|
||||||
bool dump_to_file = false;
|
bool dump_to_file = false;
|
||||||
|
audio_channel_layout channel_layout = audio_channel_layout::automatic;
|
||||||
audio_renderer renderer = audio_renderer::null;
|
audio_renderer renderer = audio_renderer::null;
|
||||||
audio_provider provider = audio_provider::none;
|
audio_provider provider = audio_provider::none;
|
||||||
};
|
};
|
||||||
|
@ -222,7 +223,8 @@ struct cell_audio_config
|
||||||
std::shared_ptr<AudioBackend> backend = nullptr;
|
std::shared_ptr<AudioBackend> backend = nullptr;
|
||||||
|
|
||||||
AudioChannelCnt audio_downmix = AudioChannelCnt::SURROUND_7_1;
|
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_channels = 2;
|
||||||
u32 audio_sampling_rate = DEFAULT_AUDIO_SAMPLING_RATE;
|
u32 audio_sampling_rate = DEFAULT_AUDIO_SAMPLING_RATE;
|
||||||
u32 audio_block_period = 0;
|
u32 audio_block_period = 0;
|
||||||
|
|
|
@ -752,7 +752,7 @@ void rec_info::start_video_provider()
|
||||||
if (sample.channels > channels)
|
if (sample.channels > channels)
|
||||||
{
|
{
|
||||||
// Downmix 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
|
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),
|
.enable_time_stretching = static_cast<bool>(g_cfg.audio.enable_time_stretching),
|
||||||
.dump_to_file = static_cast<bool>(g_cfg.audio.dump_to_file),
|
.dump_to_file = static_cast<bool>(g_cfg.audio.dump_to_file),
|
||||||
.channels = out_ch_cnt,
|
.channels = out_ch_cnt,
|
||||||
|
.channel_layout = g_cfg.audio.channel_layout,
|
||||||
.renderer = g_cfg.audio.renderer,
|
.renderer = g_cfg.audio.renderer,
|
||||||
.provider = g_cfg.audio.provider,
|
.provider = g_cfg.audio.provider,
|
||||||
.avport = convert_avport(g_cfg.audio.rsxaudio_port)
|
.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;
|
f64 cb_frame_len = 0.0;
|
||||||
u32 backend_ch_cnt = 2;
|
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();
|
cb_frame_len = backend->GetCallbackFrameLen();
|
||||||
|
backend_channel_layout = backend->get_channel_layout();
|
||||||
backend_ch_cnt = backend->get_channels();
|
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
|
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.freq = static_cast<u32>(port_cfg.freq);
|
||||||
val.input_ch_cnt = static_cast<u32>(port_cfg.ch_cnt);
|
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.convert_to_s16 = emu_cfg.convert_to_s16;
|
||||||
val.avport_idx = emu_cfg.avport;
|
val.avport_idx = emu_cfg.avport;
|
||||||
val.ready = true;
|
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())
|
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;
|
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);
|
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 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 = 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
|
// Buffer is in weird state - drop acquired data
|
||||||
if (sample_cnt == 0 || sample_cnt % cb_cfg.input_ch_cnt != 0)
|
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
|
// 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)
|
if (cb_cfg.target_volume != cb_cfg.current_volume)
|
||||||
{
|
{
|
||||||
|
|
|
@ -477,6 +477,7 @@ private:
|
||||||
bool enable_time_stretching = false;
|
bool enable_time_stretching = false;
|
||||||
bool dump_to_file = false;
|
bool dump_to_file = false;
|
||||||
AudioChannelCnt channels = AudioChannelCnt::STEREO;
|
AudioChannelCnt channels = AudioChannelCnt::STEREO;
|
||||||
|
audio_channel_layout channel_layout = audio_channel_layout::automatic;
|
||||||
audio_renderer renderer = audio_renderer::null;
|
audio_renderer renderer = audio_renderer::null;
|
||||||
audio_provider provider = audio_provider::none;
|
audio_provider provider = audio_provider::none;
|
||||||
RsxaudioAvportIdx avport = RsxaudioAvportIdx::HDMI_0;
|
RsxaudioAvportIdx avport = RsxaudioAvportIdx::HDMI_0;
|
||||||
|
@ -503,8 +504,8 @@ private:
|
||||||
RsxaudioAvportIdx avport_idx = RsxaudioAvportIdx::HDMI_0;
|
RsxaudioAvportIdx avport_idx = RsxaudioAvportIdx::HDMI_0;
|
||||||
u8 mute_state : SYS_RSXAUDIO_AVPORT_CNT = 0b11111;
|
u8 mute_state : SYS_RSXAUDIO_AVPORT_CNT = 0b11111;
|
||||||
|
|
||||||
u8 input_ch_cnt : 4 = 2;
|
u8 input_ch_cnt : 4 = 2;
|
||||||
u8 output_ch_cnt : 4 = 2;
|
u8 output_channel_layout : 4 = static_cast<u8>(audio_channel_layout::stereo);
|
||||||
|
|
||||||
bool ready : 1 = false;
|
bool ready : 1 = false;
|
||||||
bool convert_to_s16 : 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::_bool convert_to_s16{ this, "Convert to 16 bit", false, true };
|
||||||
cfg::_enum<audio_format> format{ this, "Audio Format", audio_format::stereo, false };
|
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::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::string audio_device{ this, "Audio Device", "@@@default@@@", true };
|
||||||
cfg::_int<0, 200> volume{ this, "Master Volume", 100, true };
|
cfg::_int<0, 200> volume{ this, "Master Volume", 100, true };
|
||||||
cfg::_bool enable_buffering{ this, "Enable Buffering", true, 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 <>
|
template <>
|
||||||
void fmt_class_string<detail_level>::format(std::string& out, u64 arg)
|
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.
|
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
|
enum class music_handler
|
||||||
{
|
{
|
||||||
null,
|
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");
|
case audio_avport::spdif_1: return tr("SPDIF 1", "Audio Avport");
|
||||||
}
|
}
|
||||||
break;
|
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:
|
case emu_settings_type::LicenseArea:
|
||||||
switch (static_cast<CellSysutilLicenseArea>(index))
|
switch (static_cast<CellSysutilLicenseArea>(index))
|
||||||
{
|
{
|
||||||
|
|
|
@ -134,6 +134,7 @@ enum class emu_settings_type
|
||||||
AudioProvider,
|
AudioProvider,
|
||||||
AudioAvport,
|
AudioAvport,
|
||||||
AudioDevice,
|
AudioDevice,
|
||||||
|
AudioChannelLayout,
|
||||||
MasterVolume,
|
MasterVolume,
|
||||||
EnableBuffering,
|
EnableBuffering,
|
||||||
AudioBufferDuration,
|
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::AudioProvider, { "Audio", "Audio Provider"}},
|
||||||
{ emu_settings_type::AudioAvport, { "Audio", "RSXAudio Avport"}},
|
{ emu_settings_type::AudioAvport, { "Audio", "RSXAudio Avport"}},
|
||||||
{ emu_settings_type::AudioDevice, { "Audio", "Audio Device"}},
|
{ 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::MasterVolume, { "Audio", "Master Volume"}},
|
||||||
{ emu_settings_type::EnableBuffering, { "Audio", "Enable Buffering"}},
|
{ emu_settings_type::EnableBuffering, { "Audio", "Enable Buffering"}},
|
||||||
{ emu_settings_type::AudioBufferDuration, { "Audio", "Desired Audio Buffer Duration"}},
|
{ 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);
|
get_audio_output_devices(false);
|
||||||
change_audio_output_device(0); // Set device to 'Default'
|
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)
|
connect(ui->combo_audio_format, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1161,6 +1161,18 @@
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</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>
|
<item>
|
||||||
<widget class="QGroupBox" name="gb_audio_provider">
|
<widget class="QGroupBox" name="gb_audio_provider">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
|
|
|
@ -65,7 +65,8 @@ public:
|
||||||
const QString audio_device = tr("Controls which device is used by audio backend.");
|
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 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 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 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 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.");
|
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