Try to make most audio configs dynamic

This commit is contained in:
Megamouse 2020-06-20 02:44:32 +02:00
parent 8ee953ef8e
commit 20d6664dc1
12 changed files with 228 additions and 109 deletions

View File

@ -12,8 +12,7 @@ LOG_CHANNEL(OpenAL);
#define checkForAlcError(sit) do { ALCenum g_last_alc_error = alcGetError(m_device); if(g_last_alc_error != ALC_NO_ERROR) { OpenAL.error("%s: OpenALC error 0x%04x", sit, g_last_alc_error); return; }} while(0) #define checkForAlcError(sit) do { ALCenum g_last_alc_error = alcGetError(m_device); if(g_last_alc_error != ALC_NO_ERROR) { OpenAL.error("%s: OpenALC error 0x%04x", sit, g_last_alc_error); return; }} while(0)
OpenALBackend::OpenALBackend() OpenALBackend::OpenALBackend()
: m_sampling_rate(get_sampling_rate()) : AudioBackend()
, m_sample_size(get_sample_size())
{ {
ALCdevice* m_device = alcOpenDevice(nullptr); ALCdevice* m_device = alcOpenDevice(nullptr);
checkForAlcError("alcOpenDevice"); checkForAlcError("alcOpenDevice");
@ -24,9 +23,7 @@ OpenALBackend::OpenALBackend()
alcMakeContextCurrent(m_context); alcMakeContextCurrent(m_context);
checkForAlcError("alcMakeContextCurrent"); checkForAlcError("alcMakeContextCurrent");
const auto channels = get_channels(); switch (m_channels)
switch (channels)
{ {
case 2: case 2:
m_format = (m_sample_size == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO_FLOAT32; m_format = (m_sample_size == 2) ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO_FLOAT32;

View File

@ -15,9 +15,6 @@ private:
u32 m_next_buffer = 0; u32 m_next_buffer = 0;
u32 m_num_unqueued = 0; u32 m_num_unqueued = 0;
const u32 m_sampling_rate;
const u32 m_sample_size;
void unqueue_processed(); void unqueue_processed();
public: public:

View File

@ -1,4 +1,4 @@
#ifndef HAVE_ALSA #ifndef HAVE_ALSA
#error "ALSA support disabled but still being built." #error "ALSA support disabled but still being built."
#endif #endif
@ -26,6 +26,7 @@ static bool check(int err, const char* reason)
} }
ALSABackend::ALSABackend() ALSABackend::ALSABackend()
: AudioBackend()
{ {
} }
@ -51,14 +52,14 @@ void ALSABackend::Open(u32 num_buffers)
if (!check(snd_pcm_hw_params_set_access(tls_handle, tls_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "snd_pcm_hw_params_set_access")) if (!check(snd_pcm_hw_params_set_access(tls_handle, tls_hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "snd_pcm_hw_params_set_access"))
return; return;
if (!check(snd_pcm_hw_params_set_format(tls_handle, tls_hw_params, g_cfg.audio.convert_to_u16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_FLOAT_LE), "snd_pcm_hw_params_set_format")) if (!check(snd_pcm_hw_params_set_format(tls_handle, tls_hw_params, m_convert_to_u16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_FLOAT_LE), "snd_pcm_hw_params_set_format"))
return; return;
uint rate = get_sampling_rate(); uint rate = m_sampling_rate;
if (!check(snd_pcm_hw_params_set_rate_near(tls_handle, tls_hw_params, &rate, nullptr), "snd_pcm_hw_params_set_rate_near")) if (!check(snd_pcm_hw_params_set_rate_near(tls_handle, tls_hw_params, &rate, nullptr), "snd_pcm_hw_params_set_rate_near"))
return; return;
if (!check(snd_pcm_hw_params_set_channels(tls_handle, tls_hw_params, get_channels()), "snd_pcm_hw_params_set_channels")) if (!check(snd_pcm_hw_params_set_channels(tls_handle, tls_hw_params, m_channels), "snd_pcm_hw_params_set_channels"))
return; return;
//uint period = 5333; //uint period = 5333;
@ -92,7 +93,7 @@ void ALSABackend::Open(u32 num_buffers)
if (!check(snd_pcm_sw_params_current(tls_handle, tls_sw_params), "snd_pcm_sw_params_current")) if (!check(snd_pcm_sw_params_current(tls_handle, tls_sw_params), "snd_pcm_sw_params_current"))
return; return;
period_frames *= g_cfg.audio.startt; period_frames *= m_start_threshold;
if (!check(snd_pcm_sw_params_set_start_threshold(tls_handle, tls_sw_params, period_frames + 1), "snd_pcm_sw_params_set_start_threshold")) if (!check(snd_pcm_sw_params_set_start_threshold(tls_handle, tls_sw_params, period_frames + 1), "snd_pcm_sw_params_set_start_threshold"))
return; return;
@ -132,7 +133,7 @@ void ALSABackend::Close()
bool ALSABackend::AddData(const void* src, u32 num_samples) bool ALSABackend::AddData(const void* src, u32 num_samples)
{ {
u32 num_frames = num_samples / get_channels(); u32 num_frames = num_samples / m_channels;
int res = snd_pcm_writei(tls_handle, src, num_frames); int res = snd_pcm_writei(tls_handle, src, num_frames);

View File

@ -2,45 +2,69 @@
#include "AudioBackend.h" #include "AudioBackend.h"
#include "Emu/system_config.h" #include "Emu/system_config.h"
/* AudioBackend::AudioBackend()
* Helper methods
*/
u32 AudioBackend::get_sampling_rate()
{ {
m_convert_to_u16 = static_cast<bool>(g_cfg.audio.convert_to_u16);
m_sample_size = m_convert_to_u16 ? sizeof(u16) : sizeof(float);
m_start_threshold = g_cfg.audio.start_threshold;
const u32 sampling_period_multiplier_u32 = g_cfg.audio.sampling_period_multiplier; const u32 sampling_period_multiplier_u32 = g_cfg.audio.sampling_period_multiplier;
if (sampling_period_multiplier_u32 == 100) if (sampling_period_multiplier_u32 == 100)
return DEFAULT_AUDIO_SAMPLING_RATE; {
m_sampling_rate = DEFAULT_AUDIO_SAMPLING_RATE;
}
else
{
const f32 sampling_period_multiplier = sampling_period_multiplier_u32 / 100.0f;
const f32 sampling_rate_multiplier = 1.0f / sampling_period_multiplier;
m_sampling_rate = static_cast<u32>(f32{ DEFAULT_AUDIO_SAMPLING_RATE } *sampling_rate_multiplier);
}
const f32 sampling_period_multiplier = sampling_period_multiplier_u32 / 100.0f;
const f32 sampling_rate_multiplier = 1.0f / sampling_period_multiplier;
return static_cast<u32>(f32{DEFAULT_AUDIO_SAMPLING_RATE} * sampling_rate_multiplier);
}
u32 AudioBackend::get_sample_size()
{
return g_cfg.audio.convert_to_u16 ? sizeof(u16) : sizeof(float);
}
u32 AudioBackend::get_channels()
{
const audio_channels channels = g_cfg.audio.audio_channel_downmix.get(); const audio_channels channels = g_cfg.audio.audio_channel_downmix.get();
switch (channels) switch (channels)
{ {
case audio_channels::use_application_settings: case audio_channels::use_application_settings:
return 2; // TODO m_channels = 2; // TODO
break;
case audio_channels::downmix_to_stereo: case audio_channels::downmix_to_stereo:
return 2; m_channels = 2;
break;
case audio_channels::downmix_to_5_1: case audio_channels::downmix_to_5_1:
return 6; m_channels = 6;
break;
case audio_channels::surround_7_1: case audio_channels::surround_7_1:
return 8; m_channels = 8;
break;
default: default:
fmt::throw_exception("Unknown audio channel mode %s (%d)", channels, static_cast<int>(channels)); fmt::throw_exception("Unknown audio channel mode %s (%d)", channels, static_cast<int>(channels));
} }
} }
/*
* Helper methods
*/
u32 AudioBackend::get_sampling_rate() const
{
return m_sampling_rate;
}
u32 AudioBackend::get_sample_size() const
{
return m_sample_size;
}
u32 AudioBackend::get_channels() const
{
return m_channels;
}
bool AudioBackend::get_convert_to_u16() const
{
return m_convert_to_u16;
}
bool AudioBackend::has_capability(u32 cap) const bool AudioBackend::has_capability(u32 cap) const
{ {
return (cap & GetCapabilities()) == cap; return (cap & GetCapabilities()) == cap;

View File

@ -20,6 +20,8 @@ public:
SET_FREQUENCY_RATIO = 0x8, // Implements SetFrequencyRatio SET_FREQUENCY_RATIO = 0x8, // Implements SetFrequencyRatio
}; };
AudioBackend();
virtual ~AudioBackend() = default; virtual ~AudioBackend() = default;
/* /*
@ -94,13 +96,22 @@ public:
/* /*
* Helper methods * Helper methods
*/ */
static u32 get_sampling_rate(); u32 get_sampling_rate() const;
static u32 get_sample_size(); u32 get_sample_size() const;
static u32 get_channels(); u32 get_channels() const;
bool get_convert_to_u16() const;
bool has_capability(u32 cap) const; bool has_capability(u32 cap) const;
void dump_capabilities(std::string& out) const; void dump_capabilities(std::string& out) const;
protected:
bool m_convert_to_u16 = false;
u32 m_sample_size = sizeof(float);
u32 m_sampling_rate = DEFAULT_AUDIO_SAMPLING_RATE;
u32 m_channels = 0;
u32 m_start_threshold = 1;
}; };

View File

@ -1,4 +1,4 @@
#ifndef HAVE_FAUDIO #ifndef HAVE_FAUDIO
#error "FAudio support disabled but still being built." #error "FAudio support disabled but still being built."
#endif #endif
@ -10,6 +10,7 @@
LOG_CHANNEL(FAudio_, "FAudio"); LOG_CHANNEL(FAudio_, "FAudio");
FAudioBackend::FAudioBackend() FAudioBackend::FAudioBackend()
: AudioBackend()
{ {
u32 res; u32 res;
@ -20,7 +21,7 @@ FAudioBackend::FAudioBackend()
return; return;
} }
res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, g_cfg.audio.downmix_to_2ch ? 2 : 8, 48000, 0, 0, nullptr); res = FAudio_CreateMasteringVoice(m_instance, &m_master_voice, m_channels, 48000, 0, 0, nullptr);
if (res) if (res)
{ {
FAudio_.fatal("FAudio_CreateMasteringVoice() failed(0x%08x)", res); FAudio_.fatal("FAudio_CreateMasteringVoice() failed(0x%08x)", res);
@ -102,17 +103,13 @@ void FAudioBackend::Close()
void FAudioBackend::Open(u32 /* num_buffers */) void FAudioBackend::Open(u32 /* num_buffers */)
{ {
const u32 sample_size = AudioBackend::get_sample_size();
const u32 channels = AudioBackend::get_channels();
const u32 sampling_rate = AudioBackend::get_sampling_rate();
FAudioWaveFormatEx waveformatex; FAudioWaveFormatEx waveformatex;
waveformatex.wFormatTag = g_cfg.audio.convert_to_u16 ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT; waveformatex.wFormatTag = m_convert_to_u16 ? FAUDIO_FORMAT_PCM : FAUDIO_FORMAT_IEEE_FLOAT;
waveformatex.nChannels = channels; waveformatex.nChannels = m_channels;
waveformatex.nSamplesPerSec = sampling_rate; waveformatex.nSamplesPerSec = m_sampling_rate;
waveformatex.nAvgBytesPerSec = static_cast<u32>(sampling_rate * channels * sample_size); waveformatex.nAvgBytesPerSec = static_cast<u32>(m_sampling_rate * m_channels * m_sample_size);
waveformatex.nBlockAlign = channels * sample_size; waveformatex.nBlockAlign = m_channels * m_sample_size;
waveformatex.wBitsPerSample = sample_size * 8; waveformatex.wBitsPerSample = m_sample_size * 8;
waveformatex.cbSize = 0; waveformatex.cbSize = 0;
u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, nullptr, nullptr, nullptr); u32 res = FAudio_CreateSourceVoice(m_instance, &m_source_voice, &waveformatex, 0, FAUDIO_DEFAULT_FREQ_RATIO, nullptr, nullptr, nullptr);
@ -140,7 +137,7 @@ bool FAudioBackend::AddData(const void* src, u32 num_samples)
} }
FAudioBuffer buffer; FAudioBuffer buffer;
buffer.AudioBytes = num_samples * AudioBackend::get_sample_size(); buffer.AudioBytes = num_samples * m_sample_size;
buffer.Flags = 0; buffer.Flags = 0;
buffer.LoopBegin = FAUDIO_NO_LOOP_REGION; buffer.LoopBegin = FAUDIO_NO_LOOP_REGION;
buffer.LoopCount = 0; buffer.LoopCount = 0;

View File

@ -1,4 +1,4 @@
#ifndef HAVE_PULSE #ifndef HAVE_PULSE
#error "PulseAudio support disabled but still being built." #error "PulseAudio support disabled but still being built."
#endif #endif
@ -9,6 +9,7 @@
#include <pulse/error.h> #include <pulse/error.h>
PulseBackend::PulseBackend() PulseBackend::PulseBackend()
: AudioBackend()
{ {
} }
@ -19,7 +20,8 @@ PulseBackend::~PulseBackend()
void PulseBackend::Close() void PulseBackend::Close()
{ {
if(this->connection) { if (this->connection)
{
pa_simple_free(this->connection); pa_simple_free(this->connection);
this->connection = nullptr; this->connection = nullptr;
} }
@ -28,20 +30,30 @@ void PulseBackend::Close()
void PulseBackend::Open(u32 /* num_buffers */) void PulseBackend::Open(u32 /* num_buffers */)
{ {
pa_sample_spec ss; pa_sample_spec ss;
ss.format = (get_sample_size() == 2) ? PA_SAMPLE_S16LE : PA_SAMPLE_FLOAT32LE; ss.format = (m_sample_size == 2) ? PA_SAMPLE_S16LE : PA_SAMPLE_FLOAT32LE;
ss.rate = get_sampling_rate(); ss.rate = m_sampling_rate;
pa_channel_map channel_map; pa_channel_map channel_map;
if (get_channels() == 2) if (m_channels == 2)
{ {
channel_map.channels = 2; channel_map.channels = 2;
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
} }
else if (m_channels == 6)
{
channel_map.channels = 6;
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
channel_map.map[3] = PA_CHANNEL_POSITION_LFE;
channel_map.map[4] = PA_CHANNEL_POSITION_REAR_LEFT;
channel_map.map[5] = PA_CHANNEL_POSITION_REAR_RIGHT;
}
else else
{ {
channel_map.channels = 8; channel_map.channels = 8;
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT; channel_map.map[1] = PA_CHANNEL_POSITION_FRONT_RIGHT;
channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; channel_map.map[2] = PA_CHANNEL_POSITION_FRONT_CENTER;
@ -55,7 +67,8 @@ void PulseBackend::Open(u32 /* num_buffers */)
int err; int err;
this->connection = pa_simple_new(NULL, "RPCS3", PA_STREAM_PLAYBACK, NULL, "Game", &ss, &channel_map, NULL, &err); this->connection = pa_simple_new(NULL, "RPCS3", PA_STREAM_PLAYBACK, NULL, "Game", &ss, &channel_map, NULL, &err);
if(!this->connection) { if (!this->connection)
{
fprintf(stderr, "PulseAudio: Failed to initialize audio: %s\n", pa_strerror(err)); fprintf(stderr, "PulseAudio: Failed to initialize audio: %s\n", pa_strerror(err));
} }
} }
@ -65,10 +78,11 @@ bool PulseBackend::AddData(const void* src, u32 num_samples)
AUDIT(this->connection); AUDIT(this->connection);
int err; int err;
if(pa_simple_write(this->connection, src, num_samples * get_sample_size(), &err) < 0) { if (pa_simple_write(this->connection, src, num_samples * m_sample_size, &err) < 0)
{
fprintf(stderr, "PulseAudio: Failed to write audio stream: %s\n", pa_strerror(err)); fprintf(stderr, "PulseAudio: Failed to write audio stream: %s\n", pa_strerror(err));
return false; return false;
} }
return true; return true;
} }

View File

@ -16,6 +16,7 @@
LOG_CHANNEL(XAudio); LOG_CHANNEL(XAudio);
XAudio2Backend::XAudio2Backend() XAudio2Backend::XAudio2Backend()
: AudioBackend()
{ {
Microsoft::WRL::ComPtr<IXAudio2> instance; Microsoft::WRL::ComPtr<IXAudio2> instance;
@ -30,7 +31,7 @@ XAudio2Backend::XAudio2Backend()
return; return;
} }
hr = instance->CreateMasteringVoice(&m_master_voice, get_channels(), 48000); hr = instance->CreateMasteringVoice(&m_master_voice, m_channels, 48000);
if (FAILED(hr)) if (FAILED(hr))
{ {
XAudio.error("CreateMasteringVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr)); XAudio.error("CreateMasteringVoice() failed: %s (0x%08x)", std::system_category().message(hr), static_cast<u32>(hr));
@ -94,17 +95,13 @@ void XAudio2Backend::Open(u32 /* num_buffers */)
{ {
HRESULT hr; HRESULT hr;
const u32 sample_size = AudioBackend::get_sample_size();
const u32 channels = AudioBackend::get_channels();
const u32 sampling_rate = AudioBackend::get_sampling_rate();
WAVEFORMATEX waveformatex; WAVEFORMATEX waveformatex;
waveformatex.wFormatTag = g_cfg.audio.convert_to_u16 ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT; waveformatex.wFormatTag = m_convert_to_u16 ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
waveformatex.nChannels = channels; waveformatex.nChannels = m_channels;
waveformatex.nSamplesPerSec = sampling_rate; waveformatex.nSamplesPerSec = m_sampling_rate;
waveformatex.nAvgBytesPerSec = static_cast<DWORD>(sampling_rate * channels * sample_size); waveformatex.nAvgBytesPerSec = static_cast<DWORD>(m_sampling_rate * m_channels * m_sample_size);
waveformatex.nBlockAlign = channels * sample_size; waveformatex.nBlockAlign = m_channels * m_sample_size;
waveformatex.wBitsPerSample = sample_size * 8; waveformatex.wBitsPerSample = m_sample_size * 8;
waveformatex.cbSize = 0; waveformatex.cbSize = 0;
hr = m_xaudio2_instance->CreateSourceVoice(&m_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO); hr = m_xaudio2_instance->CreateSourceVoice(&m_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO);
@ -144,7 +141,7 @@ bool XAudio2Backend::AddData(const void* src, u32 num_samples)
XAUDIO2_BUFFER buffer; XAUDIO2_BUFFER buffer;
buffer.AudioBytes = num_samples * AudioBackend::get_sample_size(); buffer.AudioBytes = num_samples * m_sample_size;
buffer.Flags = 0; buffer.Flags = 0;
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION; buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
buffer.LoopCount = 0; buffer.LoopCount = 0;

View File

@ -44,6 +44,43 @@ void fmt_class_string<CellAudioError>::format(std::string& out, u64 arg)
cell_audio_config::cell_audio_config() cell_audio_config::cell_audio_config()
{ {
reset();
}
void cell_audio_config::reset()
{
backend.reset();
backend = Emu.GetCallbacks().get_audio();
audio_channels = backend->get_channels();
audio_sampling_rate = backend->get_sampling_rate();
audio_block_period = AUDIO_BUFFER_SAMPLES * 1000000 / audio_sampling_rate;
audio_buffer_length = AUDIO_BUFFER_SAMPLES * audio_channels;
audio_buffer_size = audio_buffer_length * backend->get_sample_size();
desired_buffer_duration = g_cfg.audio.desired_buffer_duration * 1000llu;
const bool raw_buffering_enabled = static_cast<bool>(g_cfg.audio.enable_buffering);
buffering_enabled = raw_buffering_enabled && backend->has_capability(AudioBackend::PLAY_PAUSE_FLUSH | AudioBackend::IS_PLAYING);;
minimum_block_period = audio_block_period / 2;
maximum_block_period = (6 * audio_block_period) / 5;
desired_full_buffers = buffering_enabled ? static_cast<u32>(desired_buffer_duration / audio_block_period) + 3 : 2;
num_allocated_buffers = desired_full_buffers + EXTRA_AUDIO_BUFFERS;
fully_untouched_timeout = static_cast<u64>(audio_block_period) * 2;
partially_untouched_timeout = static_cast<u64>(audio_block_period) * 4;
const s64 raw_time_stretching_threshold = g_cfg.audio.time_stretching_threshold;
const bool raw_time_stretching_enabled = buffering_enabled && g_cfg.audio.enable_time_stretching && (raw_time_stretching_threshold > 0);
time_stretching_enabled = raw_time_stretching_enabled && backend->has_capability(AudioBackend::SET_FREQUENCY_RATIO);
time_stretching_threshold = raw_time_stretching_threshold / 100.0f;
// Warn if audio backend does not support all requested features // Warn if audio backend does not support all requested features
if (raw_buffering_enabled && !buffering_enabled) if (raw_buffering_enabled && !buffering_enabled)
{ {
@ -500,6 +537,31 @@ void cell_audio_thread::advance(u64 timestamp, bool reset)
} }
} }
namespace audio
{
void configure_audio()
{
if (const auto g_audio = g_fxo->get<cell_audio>())
{
g_audio->m_update_configuration = true;
}
}
}
void cell_audio_thread::update_config()
{
std::lock_guard lock(mutex);
// Clear ringbuffer
ringbuffer.reset();
// Reload config
cfg.reset();
// Allocate ringbuffer
ringbuffer.reset(new audio_ringbuffer(cfg));
}
void cell_audio_thread::operator()() void cell_audio_thread::operator()()
{ {
thread_ctrl::set_native_priority(1); thread_ctrl::set_native_priority(1);
@ -519,6 +581,12 @@ void cell_audio_thread::operator()()
// Main cellAudio loop // Main cellAudio loop
while (thread_ctrl::state() != thread_state::aborting) while (thread_ctrl::state() != thread_state::aborting)
{ {
if (m_update_configuration)
{
update_config();
m_update_configuration = false;
}
const u64 timestamp = ringbuffer->update(); const u64 timestamp = ringbuffer->update();
if (Emu.IsPaused()) if (Emu.IsPaused())
@ -959,7 +1027,7 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
{ {
std::memset(out_buffer, 0, out_buffer_sz * sizeof(float)); std::memset(out_buffer, 0, out_buffer_sz * sizeof(float));
} }
else if (g_cfg.audio.convert_to_u16) else if (cfg.backend->get_convert_to_u16())
{ {
// convert the data from float to u16 with clipping: // convert the data from float to u16 with clipping:
// 2x MULPS // 2x MULPS

View File

@ -187,49 +187,46 @@ struct audio_port
struct cell_audio_config struct cell_audio_config
{ {
const std::shared_ptr<AudioBackend> backend = Emu.GetCallbacks().get_audio(); std::shared_ptr<AudioBackend> backend = nullptr;
const u32 audio_channels = AudioBackend::get_channels(); u32 audio_channels = 0;
const u32 audio_sampling_rate = AudioBackend::get_sampling_rate(); u32 audio_sampling_rate = 0;
const u32 audio_block_period = AUDIO_BUFFER_SAMPLES * 1000000 / audio_sampling_rate; u32 audio_block_period = 0;
const u32 audio_buffer_length = AUDIO_BUFFER_SAMPLES * audio_channels; u32 audio_buffer_length = 0;
const u32 audio_buffer_size = audio_buffer_length * AudioBackend::get_sample_size(); u32 audio_buffer_size = 0;
/* /*
* Buffering * Buffering
*/ */
const u64 desired_buffer_duration = g_cfg.audio.desired_buffer_duration * 1000llu;
private: u64 desired_buffer_duration = 0;
const bool raw_buffering_enabled = static_cast<bool>(g_cfg.audio.enable_buffering);
public:
// We need a non-blocking backend (implementing play/pause/flush) to be able to do buffering correctly // We need a non-blocking backend (implementing play/pause/flush) to be able to do buffering correctly
// We also need to be able to query the current playing state // We also need to be able to query the current playing state
const bool buffering_enabled = raw_buffering_enabled && backend->has_capability(AudioBackend::PLAY_PAUSE_FLUSH | AudioBackend::IS_PLAYING); bool buffering_enabled = false;
const u64 minimum_block_period = audio_block_period / 2; // the block period will not be dynamically lowered below this value (usecs) u64 minimum_block_period = 0; // the block period will not be dynamically lowered below this value (usecs)
const u64 maximum_block_period = (6 * audio_block_period) / 5; // the block period will not be dynamically increased above this value (usecs) u64 maximum_block_period = 0; // the block period will not be dynamically increased above this value (usecs)
const u32 desired_full_buffers = buffering_enabled ? static_cast<u32>(desired_buffer_duration / audio_block_period) + 3 : 2; u32 desired_full_buffers = 0;
const u32 num_allocated_buffers = desired_full_buffers + EXTRA_AUDIO_BUFFERS; // number of ringbuffer buffers u32 num_allocated_buffers = 0; // number of ringbuffer buffers
const f32 period_average_alpha = 0.02f; // alpha factor for the m_average_period rolling average const f32 period_average_alpha = 0.02f; // alpha factor for the m_average_period rolling average
const s64 period_comparison_margin = 250; // when comparing the current period time with the desired period, if it is below this number of usecs we do not wait any longer const s64 period_comparison_margin = 250; // when comparing the current period time with the desired period, if it is below this number of usecs we do not wait any longer
const u64 fully_untouched_timeout = 2 * audio_block_period; // timeout if the game has not touched any audio buffer yet u64 fully_untouched_timeout = 0; // timeout if the game has not touched any audio buffer yet
const u64 partially_untouched_timeout = 4 * audio_block_period; // timeout if the game has not touched all audio buffers yet u64 partially_untouched_timeout = 0; // timeout if the game has not touched all audio buffers yet
/* /*
* Time Stretching * Time Stretching
*/ */
private:
const bool raw_time_stretching_enabled = buffering_enabled && g_cfg.audio.enable_time_stretching && (g_cfg.audio.time_stretching_threshold > 0);
public:
// We need to be able to set a dynamic frequency ratio to be able to do time stretching
const bool time_stretching_enabled = raw_time_stretching_enabled && backend->has_capability(AudioBackend::SET_FREQUENCY_RATIO);
const f32 time_stretching_threshold = g_cfg.audio.time_stretching_threshold / 100.0f; // we only apply time stretching below this buffer fill rate (adjusted for average period) // We need to be able to set a dynamic frequency ratio to be able to do time stretching
bool time_stretching_enabled = false;
f32 time_stretching_threshold = 0.0f; // we only apply time stretching below this buffer fill rate (adjusted for average period)
const f32 time_stretching_step = 0.1f; // will only reduce/increase the frequency ratio in steps of at least this value const f32 time_stretching_step = 0.1f; // will only reduce/increase the frequency ratio in steps of at least this value
const f32 time_stretching_scale = 0.9f; const f32 time_stretching_scale = 0.9f;
@ -237,6 +234,11 @@ public:
* Constructor * Constructor
*/ */
cell_audio_config(); cell_audio_config();
/*
* Config changes
*/
void reset();
}; };
class audio_ringbuffer class audio_ringbuffer
@ -337,6 +339,7 @@ public:
class cell_audio_thread class cell_audio_thread
{ {
private:
std::unique_ptr<audio_ringbuffer> ringbuffer; std::unique_ptr<audio_ringbuffer> ringbuffer;
void reset_ports(s32 offset = 0); void reset_ports(s32 offset = 0);
@ -351,8 +354,11 @@ class cell_audio_thread
return (time_left > 350) ? time_left - 250 : 100; return (time_left > 350) ? time_left - 250 : 100;
} }
void update_config();
public: public:
cell_audio_config cfg; cell_audio_config cfg;
atomic_t<bool> m_update_configuration = false;
shared_mutex mutex; shared_mutex mutex;
atomic_t<u32> init = 0; atomic_t<u32> init = 0;
@ -375,7 +381,7 @@ public:
u64 m_counter = 0; u64 m_counter = 0;
u64 m_start_time = 0; u64 m_start_time = 0;
u64 m_dynamic_period = 0; u64 m_dynamic_period = 0;
f32 m_average_playtime; f32 m_average_playtime = 0.0f;
void operator()(); void operator()();
@ -405,3 +411,8 @@ public:
}; };
using cell_audio = named_thread<cell_audio_thread>; using cell_audio = named_thread<cell_audio_thread>;
namespace audio
{
void configure_audio();
}

View File

@ -208,23 +208,23 @@ struct cfg_root : cfg::node
cfg::_enum<audio_renderer> renderer{ this, "Renderer", cfg::_enum<audio_renderer> renderer{ this, "Renderer",
#ifdef _WIN32 #ifdef _WIN32
audio_renderer::xaudio }; audio_renderer::xaudio, true };
#elif HAVE_FAUDIO #elif HAVE_FAUDIO
audio_renderer::faudio }; audio_renderer::faudio, true };
#else #else
audio_renderer::openal }; audio_renderer::openal, true };
#endif #endif
cfg::_bool dump_to_file{ this, "Dump to file" }; cfg::_bool dump_to_file{ this, "Dump to file" };
cfg::_bool convert_to_u16{ this, "Convert to 16 bit" }; cfg::_bool convert_to_u16{ this, "Convert to 16 bit", false, true };
cfg::_enum<audio_channels> audio_channel_downmix{ this, "Audio Channels", audio_channels::downmix_to_stereo }; cfg::_enum<audio_channels> audio_channel_downmix{ this, "Audio Channels", audio_channels::downmix_to_stereo, true };
cfg::_int<1, 128> startt{ this, "Start Threshold", 1 }; // TODO: used only by ALSA, should probably be removed once ALSA is upgraded cfg::_int<1, 128> start_threshold{ this, "Start Threshold", 1, true }; // TODO: used only by ALSA, should probably be removed once ALSA is upgraded
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 }; cfg::_bool enable_buffering{ this, "Enable Buffering", true, true };
cfg::_int <4, 250> desired_buffer_duration{ this, "Desired Audio Buffer Duration", 100 }; cfg::_int <4, 250> desired_buffer_duration{ this, "Desired Audio Buffer Duration", 100, true };
cfg::_int<1, 1000> sampling_period_multiplier{ this, "Sampling Period Multiplier", 100 }; cfg::_int<1, 1000> sampling_period_multiplier{ this, "Sampling Period Multiplier", 100, true };
cfg::_bool enable_time_stretching{ this, "Enable Time Stretching", false }; cfg::_bool enable_time_stretching{ this, "Enable Time Stretching", false, true };
cfg::_int<0, 100> time_stretching_threshold{ this, "Time Stretching Threshold", 75 }; cfg::_int<0, 100> time_stretching_threshold{ this, "Time Stretching Threshold", 75, true };
cfg::_enum<microphone_handler> microphone_type{ this, "Microphone Type", microphone_handler::null }; cfg::_enum<microphone_handler> microphone_type{ this, "Microphone Type", microphone_handler::null };
cfg::string microphone_devices{ this, "Microphone Devices", ";;;;" }; cfg::string microphone_devices{ this, "Microphone Devices", ";;;;" };
} audio{ this }; } audio{ this };

View File

@ -14,6 +14,7 @@
#include "_discord_utils.h" #include "_discord_utils.h"
#endif #endif
#include "Emu/Cell/Modules/cellAudio.h"
#include "Emu/RSX/Overlays/overlay_perf_metrics.h" #include "Emu/RSX/Overlays/overlay_perf_metrics.h"
#include "trophy_notification_helper.h" #include "trophy_notification_helper.h"
#include "save_data_dialog.h" #include "save_data_dialog.h"
@ -474,6 +475,7 @@ void gui_application::OnEmuSettingsChange()
} }
Emu.ConfigureLogs(); Emu.ConfigureLogs();
audio::configure_audio();
rsx::overlays::reset_performance_overlay(); rsx::overlays::reset_performance_overlay();
} }