1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2025-01-31 12:31:45 +01:00

Various fixes

In addition, linux builds (and ALSA/PA) now work again
This commit is contained in:
Rui Pinheiro 2018-12-21 02:13:22 +00:00 committed by kd-11
parent 4f39457858
commit 67f9397746
18 changed files with 708 additions and 686 deletions

View File

@ -118,7 +118,7 @@ void OpenALBackend::Close()
}
}
bool OpenALBackend::AddData(const void* src, u32 size)
bool OpenALBackend::AddData(const void* src, u32 num_samples)
{
AUDIT(alIsSource(m_source));
@ -133,7 +133,7 @@ bool OpenALBackend::AddData(const void* src, u32 size)
}
// Copy data to the next available buffer
alBufferData(m_buffers[m_next_buffer], m_format, src, size * m_sample_size, m_sampling_rate);
alBufferData(m_buffers[m_next_buffer], m_format, src, num_samples * m_sample_size, m_sampling_rate);
checkForAlError("AddData->alBufferData");
// Enqueue buffer
@ -186,7 +186,7 @@ u64 OpenALBackend::GetNumEnqueuedSamples()
ALint num_queued;
alGetSourcei(m_source, AL_BUFFERS_QUEUED, &num_queued);
checkForAlError("GetNumEnqueuedSamples->alGetSourcei(AL_BUFFERS_QUEUED)");
AUDIT(num_queued <= m_num_buffers - m_num_unqueued);
AUDIT(static_cast<u32>(num_queued) <= m_num_buffers - m_num_unqueued);
// Get sample position
ALint sample_pos;

View File

@ -26,7 +26,7 @@ public:
virtual const char* GetName() const override { return "OpenAL"; };
static const u32 capabilities = NON_BLOCKING | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES | SET_FREQUENCY_RATIO;
static const u32 capabilities = PLAY_PAUSE_FLUSH | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES | SET_FREQUENCY_RATIO;
virtual u32 GetCapabilities() const override { return capabilities; };
virtual void Open(u32 num_buffers) override;

View File

@ -1,15 +1,10 @@
#include "stdafx.h"
#include "Emu/System.h"
#include "ALSAThread.h"
#include "ALSABackend.h"
#ifdef HAVE_ALSA
#include <alsa/asoundlib.h>
static thread_local snd_pcm_t* s_tls_handle{nullptr};
static thread_local snd_pcm_hw_params_t* s_tls_hw_params{nullptr};
static thread_local snd_pcm_sw_params_t* s_tls_sw_params{nullptr};
static void error(int err, const char* reason)
{
@ -27,7 +22,19 @@ static bool check(int err, const char* reason)
return true;
}
ALSAThread::ALSAThread()
ALSABackend::ALSABackend()
{
}
ALSABackend::~ALSABackend()
{
if (s_tls_sw_params || s_tls_hw_params || s_tls_handle)
{
Close();
}
}
void ALSABackend::Open(u32 num_buffers)
{
if (!check(snd_pcm_open(&s_tls_handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK), "snd_pcm_open"))
return;
@ -44,11 +51,11 @@ ALSAThread::ALSAThread()
if (!check(snd_pcm_hw_params_set_format(s_tls_handle, s_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"))
return;
uint rate = 48000;
uint rate = get_sampling_rate();
if (!check(snd_pcm_hw_params_set_rate_near(s_tls_handle, s_tls_hw_params, &rate, nullptr), "snd_pcm_hw_params_set_rate_near"))
return;
if (!check(snd_pcm_hw_params_set_channels(s_tls_handle, s_tls_hw_params, g_cfg.audio.downmix_to_2ch ? 2 : 8), "snd_pcm_hw_params_set_channels"))
if (!check(snd_pcm_hw_params_set_channels(s_tls_handle, s_tls_hw_params, get_channels()), "snd_pcm_hw_params_set_channels"))
return;
//uint period = 5333;
@ -58,8 +65,8 @@ ALSAThread::ALSAThread()
//if (!check(snd_pcm_hw_params_set_periods(s_tls_handle, s_tls_hw_params, 4, 0), "snd_pcm_hw_params_set_periods"))
// return;
snd_pcm_uframes_t bufsize_frames = g_cfg.audio.frames * 256;
snd_pcm_uframes_t period_frames = 256;
snd_pcm_uframes_t bufsize_frames = num_buffers * AUDIO_BUFFER_SAMPLES;
snd_pcm_uframes_t period_frames = AUDIO_BUFFER_SAMPLES;
if (!check(snd_pcm_hw_params_set_buffer_size_near(s_tls_handle, s_tls_hw_params, &bufsize_frames), "snd_pcm_hw_params_set_buffer_size_near"))
return;
@ -84,7 +91,7 @@ ALSAThread::ALSAThread()
period_frames *= g_cfg.audio.startt;
if (!check(snd_pcm_sw_params_set_start_threshold(s_tls_handle, s_tls_sw_params, period_frames), "snd_pcm_sw_params_set_start_threshold"))
if (!check(snd_pcm_sw_params_set_start_threshold(s_tls_handle, s_tls_sw_params, period_frames + 1), "snd_pcm_sw_params_set_start_threshold"))
return;
if (!check(snd_pcm_sw_params_set_stop_threshold(s_tls_handle, s_tls_sw_params, bufsize_frames), "snd_pcm_sw_params_set_stop_threshold"))
@ -99,52 +106,37 @@ ALSAThread::ALSAThread()
LOG_NOTICE(GENERAL, "ALSA: bufsize_frames=%u, period_frames=%u", bufsize_frames, period_frames);
}
ALSAThread::~ALSAThread()
void ALSABackend::Close()
{
if (s_tls_sw_params)
{
snd_pcm_sw_params_free(s_tls_sw_params);
s_tls_sw_params = nullptr;
}
if (s_tls_hw_params)
{
snd_pcm_hw_params_free(s_tls_hw_params);
s_tls_hw_params = nullptr;
}
if (s_tls_handle)
{
snd_pcm_close(s_tls_handle);
s_tls_handle = nullptr;
}
}
void ALSAThread::Play()
bool ALSABackend::AddData(const void* src, u32 num_samples)
{
}
u32 num_frames = num_samples / get_channels();
void ALSAThread::Close()
{
}
void ALSAThread::Stop()
{
}
void ALSAThread::Open(const void* src, int size)
{
AddData(src, size);
}
void ALSAThread::AddData(const void* src, int size)
{
size /= g_cfg.audio.convert_to_u16 ? 2 : 4;
size /= g_cfg.audio.downmix_to_2ch ? 2 : 8;
int res = snd_pcm_writei(s_tls_handle, src, size);
int res = snd_pcm_writei(s_tls_handle, src, num_frames);
if (res == -EAGAIN)
{
LOG_WARNING(GENERAL, "ALSA: EAGAIN");
return;
return false;
}
if (res < 0)
@ -154,16 +146,19 @@ void ALSAThread::AddData(const void* src, int size)
if (res < 0)
{
LOG_WARNING(GENERAL, "ALSA: failed to recover (%d)", res);
return;
return false;
}
res = snd_pcm_writei(s_tls_handle, src, size);
res = snd_pcm_writei(s_tls_handle, src, num_frames);
}
if (res != size)
if (res != num_frames)
{
LOG_WARNING(GENERAL, "ALSA: error (%d)", res);
return false;
}
return true;
}
#endif

View File

@ -0,0 +1,30 @@
#pragma once
#ifdef HAVE_ALSA
#include "Emu/Audio/AudioBackend.h"
#include <alsa/asoundlib.h>
class ALSABackend : public AudioBackend
{
snd_pcm_t* s_tls_handle{nullptr};
snd_pcm_hw_params_t* s_tls_hw_params{nullptr};
snd_pcm_sw_params_t* s_tls_sw_params{nullptr};
public:
ALSABackend();
virtual ~ALSABackend() override;
virtual const char* GetName() const override { return "ALSA"; };
static const u32 capabilities = 0;
virtual u32 GetCapabilities() const override { return capabilities; };
virtual void Open(u32) override;
virtual void Close() override;
virtual bool AddData(const void* src, u32 num_samples) override;
};
#endif

View File

@ -1,20 +0,0 @@
#pragma once
#ifdef HAVE_ALSA
#include "Emu/Audio/AudioThread.h"
class ALSAThread : public AudioBackend
{
public:
ALSAThread();
virtual ~ALSAThread() override;
virtual void Play() override;
virtual void Open(const void* src, int size) override;
virtual void Close() override;
virtual void Stop() override;
virtual void AddData(const void* src, int size) override;
};
#endif

View File

@ -15,45 +15,80 @@ class AudioBackend
public:
enum Capabilities : u32
{
NON_BLOCKING = 0x1,
IS_PLAYING = 0x2,
GET_NUM_ENQUEUED_SAMPLES = 0x4,
SET_FREQUENCY_RATIO = 0x8,
PLAY_PAUSE_FLUSH = 0x1, // AddData implements Play, Pause, Flush
IS_PLAYING = 0x2, // Implements IsPlaying method
GET_NUM_ENQUEUED_SAMPLES = 0x4, // Supports GetNumEnqueuedSamples method
SET_FREQUENCY_RATIO = 0x8, // Implements SetFrequencyRatio method
};
virtual ~AudioBackend() = default;
// Callbacks
/*
* Pure virtual methods
*/
virtual const char* GetName() const = 0;
virtual u32 GetCapabilities() const = 0;
virtual void Open(u32 num_buffers) = 0;
virtual void Close() = 0;
virtual void Play() = 0;
virtual void Pause() = 0;
virtual bool AddData(const void* src, u32 num_samples) = 0;
/*
* Virtual methods - should be implemented depending on backend capabilities
*/
// Start playing enqueued data
// Should be implemented if capabilities & PLAY_PAUSE_FLUSH
virtual void Play()
{
fmt::throw_exception("Play() not implemented");
}
// Pause playing enqueued data
// Should be implemented if capabilities & PLAY_PAUSE_FLUSH
virtual void Pause()
{
fmt::throw_exception("Pause() not implemented");
}
// Pause audio, and unqueue all currently queued buffers
// Should be implemented if capabilities & PLAY_PAUSE_FLUSH
virtual void Flush()
{
fmt::throw_exception("Flush() not implemented");
}
// Returns true if audio is currently being played, false otherwise
// Should be implemented if capabilities & IS_PLAYING
virtual bool IsPlaying()
{
fmt::throw_exception("IsPlaying() not implemented");
};
virtual bool AddData(const void* src, u32 size) = 0;
virtual void Flush() = 0;
}
// Returns the number of currently enqueued samples
// Should be implemented if capabilities & GET_NUM_ENQUEUED_SAMPLES
virtual u64 GetNumEnqueuedSamples()
{
fmt::throw_exception("GetNumEnqueuedSamples() not implemented");
return 0;
}
// Sets a new frequency ratio. Backend is allowed to modify the ratio value, e.g. clamping it to the allowed range
// Returns the new frequency ratio set
// Should be implemented if capabilities & SET_FREQUENCY_RATIO
virtual f32 SetFrequencyRatio(f32 /* new_ratio */) // returns the new ratio
{
fmt::throw_exception("SetFrequencyRatio() not implemented");
return 1.0f;
}
// Helper methods
/*
* Helper methods
*/
static u32 get_sampling_rate()
{
const u32 sampling_period_multiplier_u32 = g_cfg.audio.sampling_period_multiplier;
@ -76,9 +111,9 @@ public:
return g_cfg.audio.downmix_to_2ch ? 2 : 8;
}
bool has_capability(Capabilities cap) const
bool has_capability(u32 cap) const
{
return (cap & GetCapabilities()) != 0;
return (cap & GetCapabilities()) == cap;
}
void dump_capabilities(std::string& out) const
@ -86,9 +121,9 @@ public:
u32 count = 0;
u32 capabilities = GetCapabilities();
if (capabilities & NON_BLOCKING)
if (capabilities & PLAY_PAUSE_FLUSH)
{
fmt::append(out, "NON_BLOCKING");
fmt::append(out, "PLAY_PAUSE_FLUSH");
count++;
}

View File

@ -10,7 +10,7 @@ public:
virtual const char* GetName() const override { return "Null"; }
static const u32 capabilities = NON_BLOCKING;
static const u32 capabilities = PLAY_PAUSE_FLUSH;
virtual u32 GetCapabilities() const override { return capabilities; };
virtual void Open(u32) override {};

View File

@ -1,25 +1,21 @@
#include "Emu/System.h"
#include "PulseThread.h"
#include "PulseBackend.h"
#ifdef HAVE_PULSE
#include <pulse/simple.h>
#include <pulse/error.h>
PulseThread::PulseThread()
PulseBackend::PulseBackend()
{
}
PulseThread::~PulseThread()
PulseBackend::~PulseBackend()
{
this->Close();
}
void PulseThread::Play()
{
}
void PulseThread::Close()
void PulseBackend::Close()
{
if(this->connection) {
pa_simple_free(this->connection);
@ -27,19 +23,15 @@ void PulseThread::Close()
}
}
void PulseThread::Stop()
{
}
void PulseThread::Open(const void* src, int size)
void PulseBackend::Open(u32 /* num_buffers */)
{
pa_sample_spec ss;
ss.format = g_cfg.audio.convert_to_u16 ? PA_SAMPLE_S16LE : PA_SAMPLE_FLOAT32LE;
ss.rate = 48000;
ss.format = (get_sample_size() == 2) ? PA_SAMPLE_S16LE : PA_SAMPLE_FLOAT32LE;
ss.rate = get_sampling_rate();
pa_channel_map channel_map;
if (g_cfg.audio.downmix_to_2ch)
if (get_channels() == 2)
{
channel_map.channels = 2;
channel_map.map[0] = PA_CHANNEL_POSITION_FRONT_LEFT;
@ -64,18 +56,19 @@ void PulseThread::Open(const void* src, int size)
if(!this->connection) {
fprintf(stderr, "PulseAudio: Failed to initialize audio: %s\n", pa_strerror(err));
}
this->AddData(src, size);
}
void PulseThread::AddData(const void* src, int size)
bool PulseBackend::AddData(const void* src, u32 num_samples)
{
if(this->connection) {
AUDIT(this->connection);
int err;
if(pa_simple_write(this->connection, src, size, &err) < 0) {
if(pa_simple_write(this->connection, src, num_samples * get_sample_size(), &err) < 0) {
fprintf(stderr, "PulseAudio: Failed to write audio stream: %s\n", pa_strerror(err));
return false;
}
}
return true;
}
#endif

View File

@ -0,0 +1,27 @@
#pragma once
#ifdef HAVE_PULSE
#include <pulse/simple.h>
#include "Emu/Audio/AudioBackend.h"
class PulseBackend : public AudioBackend
{
public:
PulseBackend();
virtual ~PulseBackend() override;
virtual const char* GetName() const override { return "Pulse"; };
static const u32 capabilities = 0;
virtual u32 GetCapabilities() const override { return capabilities; };
virtual void Open(u32) override;
virtual void Close() override;
virtual bool AddData(const void* src, u32 num_samples) override;
private:
pa_simple *connection = nullptr;
};
#endif

View File

@ -1,23 +0,0 @@
#pragma once
#ifdef HAVE_PULSE
#include <pulse/simple.h>
#include "Emu/Audio/AudioThread.h"
class PulseThread : public AudioBackend
{
public:
PulseThread();
virtual ~PulseThread() override;
virtual void Play() override;
virtual void Open(const void* src, int size) override;
virtual void Close() override;
virtual void Stop() override;
virtual void AddData(const void* src, int size) override;
private:
pa_simple *connection = nullptr;
};
#endif

View File

@ -7,15 +7,17 @@
#include "XAudio2Backend.h"
#include "3rdparty/XAudio2_7/XAudio2.h"
static thread_local HMODULE s_tls_xaudio2_lib{};
static thread_local IXAudio2* s_tls_xaudio2_instance{};
static thread_local IXAudio2MasteringVoice* s_tls_master_voice{};
static thread_local IXAudio2SourceVoice* s_tls_source_voice{};
void XAudio2Backend::xa27_init(void* lib2_7)
class XAudio27Library : public XAudio2Backend::XAudio2Library
{
s_tls_xaudio2_lib = (HMODULE)lib2_7;
const HMODULE s_tls_xaudio2_lib;
IXAudio2* s_tls_xaudio2_instance{};
IXAudio2MasteringVoice* s_tls_master_voice{};
IXAudio2SourceVoice* s_tls_source_voice{};
public:
XAudio27Library(void* lib2_7)
: s_tls_xaudio2_lib(static_cast<HMODULE>(lib2_7))
{
HRESULT hr = S_OK;
hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
@ -41,10 +43,10 @@ void XAudio2Backend::xa27_init(void* lib2_7)
s_tls_xaudio2_instance->Release();
Emu.Pause();
}
}
}
void XAudio2Backend::xa27_destroy()
{
~XAudio27Library()
{
if (s_tls_source_voice != nullptr)
{
s_tls_source_voice->Stop();
@ -65,53 +67,53 @@ void XAudio2Backend::xa27_destroy()
CoUninitialize();
FreeLibrary(s_tls_xaudio2_lib);
}
}
void XAudio2Backend::xa27_play()
{
virtual void play() override
{
HRESULT hr = s_tls_source_voice->Start();
if (FAILED(hr))
{
LOG_ERROR(GENERAL, "XAudio2Backend : Start() failed(0x%08x)", (u32)hr);
Emu.Pause();
}
}
}
void XAudio2Backend::xa27_flush()
{
virtual void flush() override
{
HRESULT hr = s_tls_source_voice->FlushSourceBuffers();
if (FAILED(hr))
{
LOG_ERROR(GENERAL, "XAudio2Backend : FlushSourceBuffers() failed(0x%08x)", (u32)hr);
Emu.Pause();
}
}
}
void XAudio2Backend::xa27_stop()
{
virtual void stop() override
{
HRESULT hr = s_tls_source_voice->Stop();
if (FAILED(hr))
{
LOG_ERROR(GENERAL, "XAudio2Backend : Stop() failed(0x%08x)", (u32)hr);
Emu.Pause();
}
}
}
bool XAudio2Backend::xa27_is_playing()
{
virtual bool is_playing() override
{
XAUDIO2_VOICE_STATE state;
s_tls_source_voice->GetState(&state);
return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr;
}
}
void XAudio2Backend::xa27_open()
{
virtual void open() override
{
HRESULT hr;
const u32 sample_size = get_sample_size();
const u32 channels = get_channels();
const u32 sampling_rate = get_sampling_rate();
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.wFormatTag = g_cfg.audio.convert_to_u16 ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
@ -131,10 +133,10 @@ void XAudio2Backend::xa27_open()
}
s_tls_source_voice->SetVolume(channels == 2 ? 1.0f : 4.0f);
}
}
bool XAudio2Backend::xa27_add(const void* src, u32 size)
{
virtual bool add(const void* src, u32 num_samples) override
{
XAUDIO2_VOICE_STATE state;
s_tls_source_voice->GetState(&state);
@ -147,7 +149,7 @@ bool XAudio2Backend::xa27_add(const void* src, u32 size)
XAUDIO2_BUFFER buffer;
buffer.AudioBytes = size * get_sample_size();
buffer.AudioBytes = num_samples * AudioBackend::get_sample_size();
buffer.Flags = 0;
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
buffer.LoopCount = 0;
@ -166,19 +168,19 @@ bool XAudio2Backend::xa27_add(const void* src, u32 size)
}
return true;
}
}
u64 XAudio2Backend::xa27_enqueued_samples()
{
virtual u64 enqueued_samples() override
{
XAUDIO2_VOICE_STATE state;
s_tls_source_voice->GetState(&state);
// all buffers contain AUDIO_BUFFER_SAMPLES, so we can easily calculate how many samples there are remaining
return (AUDIO_BUFFER_SAMPLES - state.SamplesPlayed % AUDIO_BUFFER_SAMPLES) + (state.BuffersQueued * AUDIO_BUFFER_SAMPLES);
}
}
f32 XAudio2Backend::xa27_set_freq_ratio(f32 new_ratio)
{
virtual f32 set_freq_ratio(f32 new_ratio) override
{
new_ratio = std::clamp(new_ratio, XAUDIO2_MIN_FREQ_RATIO, XAUDIO2_DEFAULT_FREQ_RATIO);
HRESULT hr = s_tls_source_voice->SetFrequencyRatio(new_ratio);
@ -190,6 +192,12 @@ f32 XAudio2Backend::xa27_set_freq_ratio(f32 new_ratio)
}
return new_ratio;
}
};
XAudio2Backend::XAudio2Library* XAudio2Backend::xa27_init(void* lib2_7)
{
return new XAudio27Library(lib2_7);
}
#endif

View File

@ -7,15 +7,17 @@
#include "XAudio2Backend.h"
#include "3rdparty/minidx12/Include/xaudio2.h"
static thread_local HMODULE s_tls_xaudio2_lib{};
static thread_local IXAudio2* s_tls_xaudio2_instance{};
static thread_local IXAudio2MasteringVoice* s_tls_master_voice{};
static thread_local IXAudio2SourceVoice* s_tls_source_voice{};
void XAudio2Backend::xa28_init(void* lib)
class XAudio28Library : public XAudio2Backend::XAudio2Library
{
s_tls_xaudio2_lib = (HMODULE)lib;
const HMODULE s_tls_xaudio2_lib;
IXAudio2* s_tls_xaudio2_instance{};
IXAudio2MasteringVoice* s_tls_master_voice{};
IXAudio2SourceVoice* s_tls_source_voice{};
public:
XAudio28Library(void* lib2_8)
: s_tls_xaudio2_lib(static_cast<HMODULE>(lib2_8))
{
const auto create = (XAudio2Create)GetProcAddress(s_tls_xaudio2_lib, "XAudio2Create");
HRESULT hr = S_OK;
@ -43,10 +45,10 @@ void XAudio2Backend::xa28_init(void* lib)
s_tls_xaudio2_instance->Release();
Emu.Pause();
}
}
}
void XAudio2Backend::xa28_destroy()
{
~XAudio28Library()
{
if (s_tls_source_voice != nullptr)
{
s_tls_source_voice->Stop();
@ -67,10 +69,10 @@ void XAudio2Backend::xa28_destroy()
CoUninitialize();
FreeLibrary(s_tls_xaudio2_lib);
}
}
void XAudio2Backend::xa28_play()
{
virtual void play() override
{
AUDIT(s_tls_source_voice != nullptr);
HRESULT hr = s_tls_source_voice->Start();
@ -79,10 +81,10 @@ void XAudio2Backend::xa28_play()
LOG_ERROR(GENERAL, "XAudio2Backend : Start() failed(0x%08x)", (u32)hr);
Emu.Pause();
}
}
}
void XAudio2Backend::xa28_flush()
{
virtual void flush() override
{
AUDIT(s_tls_source_voice != nullptr);
HRESULT hr = s_tls_source_voice->FlushSourceBuffers();
@ -91,10 +93,10 @@ void XAudio2Backend::xa28_flush()
LOG_ERROR(GENERAL, "XAudio2Backend : FlushSourceBuffers() failed(0x%08x)", (u32)hr);
Emu.Pause();
}
}
}
void XAudio2Backend::xa28_stop()
{
virtual void stop() override
{
AUDIT(s_tls_source_voice != nullptr);
HRESULT hr = s_tls_source_voice->Stop();
@ -103,25 +105,25 @@ void XAudio2Backend::xa28_stop()
LOG_ERROR(GENERAL, "XAudio2Backend : Stop() failed(0x%08x)", (u32)hr);
Emu.Pause();
}
}
}
bool XAudio2Backend::xa28_is_playing()
{
virtual bool is_playing() override
{
AUDIT(s_tls_source_voice != nullptr);
XAUDIO2_VOICE_STATE state;
s_tls_source_voice->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr;
}
}
void XAudio2Backend::xa28_open()
{
virtual void open() override
{
HRESULT hr;
const u32 sample_size = get_sample_size();
const u32 channels = get_channels();
const u32 sampling_rate = get_sampling_rate();
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.wFormatTag = g_cfg.audio.convert_to_u16 ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
@ -142,10 +144,10 @@ void XAudio2Backend::xa28_open()
AUDIT(s_tls_source_voice != nullptr);
s_tls_source_voice->SetVolume(channels == 2 ? 1.0f : 4.0f);
}
}
bool XAudio2Backend::xa28_add(const void* src, u32 size)
{
virtual bool add(const void* src, u32 num_samples) override
{
AUDIT(s_tls_source_voice != nullptr);
XAUDIO2_VOICE_STATE state;
@ -159,7 +161,7 @@ bool XAudio2Backend::xa28_add(const void* src, u32 size)
XAUDIO2_BUFFER buffer;
buffer.AudioBytes = size * get_sample_size();
buffer.AudioBytes = num_samples * AudioBackend::get_sample_size();
buffer.Flags = 0;
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
buffer.LoopCount = 0;
@ -178,19 +180,19 @@ bool XAudio2Backend::xa28_add(const void* src, u32 size)
}
return true;
}
}
u64 XAudio2Backend::xa28_enqueued_samples()
{
virtual u64 enqueued_samples() override
{
XAUDIO2_VOICE_STATE state;
s_tls_source_voice->GetState(&state);
// all buffers contain AUDIO_BUFFER_SAMPLES, so we can easily calculate how many samples there are remaining
return (AUDIO_BUFFER_SAMPLES - state.SamplesPlayed % AUDIO_BUFFER_SAMPLES) + (state.BuffersQueued * AUDIO_BUFFER_SAMPLES);
}
}
f32 XAudio2Backend::xa28_set_freq_ratio(f32 new_ratio)
{
virtual f32 set_freq_ratio(f32 new_ratio) override
{
new_ratio = std::clamp(new_ratio, XAUDIO2_MIN_FREQ_RATIO, XAUDIO2_DEFAULT_FREQ_RATIO);
HRESULT hr = s_tls_source_voice->SetFrequencyRatio(new_ratio);
@ -202,6 +204,12 @@ f32 XAudio2Backend::xa28_set_freq_ratio(f32 new_ratio)
}
return new_ratio;
}
};
XAudio2Backend::XAudio2Library* XAudio2Backend::xa28_init(void* lib2_8)
{
return new XAudio28Library(lib2_8);
}
#endif

View File

@ -8,113 +8,87 @@
XAudio2Backend::XAudio2Backend()
{
if (auto lib2_9 = LoadLibraryExW(L"XAudio2_9.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
{
// xa28* implementation is fully compatible with library 2.9
xa28_init(lib2_9);
m_funcs.destroy = &xa28_destroy;
m_funcs.play = &xa28_play;
m_funcs.flush = &xa28_flush;
m_funcs.stop = &xa28_stop;
m_funcs.open = &xa28_open;
m_funcs.is_playing = &xa28_is_playing;
m_funcs.add = &xa28_add;
m_funcs.enqueued_samples = &xa28_enqueued_samples;
m_funcs.set_freq_ratio = &xa28_set_freq_ratio;
LOG_SUCCESS(GENERAL, "XAudio 2.9 initialized");
return;
}
if (auto lib2_8 = LoadLibraryExW(L"XAudio2_8.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
{
xa28_init(lib2_8);
m_funcs.destroy = &xa28_destroy;
m_funcs.play = &xa28_play;
m_funcs.flush = &xa28_flush;
m_funcs.stop = &xa28_stop;
m_funcs.open = &xa28_open;
m_funcs.is_playing = &xa28_is_playing;
m_funcs.add = &xa28_add;
m_funcs.enqueued_samples = &xa28_enqueued_samples;
m_funcs.set_freq_ratio = &xa28_set_freq_ratio;
LOG_SUCCESS(GENERAL, "XAudio 2.8 initialized");
return;
}
if (auto lib2_7 = LoadLibraryExW(L"XAudio2_7.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
{
xa27_init(lib2_7);
m_funcs.destroy = &xa27_destroy;
m_funcs.play = &xa27_play;
m_funcs.flush = &xa27_flush;
m_funcs.stop = &xa27_stop;
m_funcs.open = &xa27_open;
m_funcs.is_playing = &xa27_is_playing;
m_funcs.add = &xa27_add;
m_funcs.enqueued_samples = &xa27_enqueued_samples;
m_funcs.set_freq_ratio = &xa27_set_freq_ratio;
LOG_SUCCESS(GENERAL, "XAudio 2.7 initialized");
return;
}
fmt::throw_exception("No supported XAudio2 library found");
}
XAudio2Backend::~XAudio2Backend()
{
m_funcs.destroy();
}
void XAudio2Backend::Play()
{
m_funcs.play();
lib->play();
}
void XAudio2Backend::Close()
{
m_funcs.stop();
m_funcs.flush();
lib->stop();
lib->flush();
}
void XAudio2Backend::Pause()
{
m_funcs.stop();
lib->stop();
}
void XAudio2Backend::Open(u32 /* num_buffers */)
{
m_funcs.open();
if (lib.get() == nullptr)
{
void* hmodule;
if (hmodule = LoadLibraryExW(L"XAudio2_9.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
{
// XAudio 2.9 uses the same code as XAudio 2.8
lib.reset(xa28_init(hmodule));
LOG_SUCCESS(GENERAL, "XAudio 2.9 initialized");
}
else if (hmodule = LoadLibraryExW(L"XAudio2_8.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
{
// XAudio 2.9 uses the same code as XAudio 2.8
lib.reset(xa28_init(hmodule));
LOG_SUCCESS(GENERAL, "XAudio 2.8 initialized");
}
else if (hmodule = LoadLibraryExW(L"XAudio2_7.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
{
// XAudio 2.9 uses the same code as XAudio 2.8
lib.reset(xa27_init(hmodule));
LOG_SUCCESS(GENERAL, "XAudio 2.7 initialized");
}
else
{
fmt::throw_exception("No supported XAudio2 library found");
}
}
lib->open();
}
bool XAudio2Backend::IsPlaying()
{
return m_funcs.is_playing();
return lib->is_playing();
}
bool XAudio2Backend::AddData(const void* src, u32 size)
bool XAudio2Backend::AddData(const void* src, u32 num_samples)
{
return m_funcs.add(src, size);
return lib->add(src, num_samples);
}
void XAudio2Backend::Flush()
{
m_funcs.flush();
lib->flush();
}
u64 XAudio2Backend::GetNumEnqueuedSamples()
{
return m_funcs.enqueued_samples();
return lib->enqueued_samples();
}
f32 XAudio2Backend::SetFrequencyRatio(f32 new_ratio)
{
return m_funcs.set_freq_ratio(new_ratio);
return lib->set_freq_ratio(new_ratio);
}
#endif

View File

@ -4,44 +4,28 @@
#include "Emu/Audio/AudioBackend.h"
class XAudio2Backend : public AudioBackend
{
struct vtable
public:
class XAudio2Library
{
void(*destroy)();
void(*play)();
void(*flush)();
void(*stop)();
void(*open)();
bool(*is_playing)();
bool(*add)(const void*, u32);
u64(*enqueued_samples)();
f32(*set_freq_ratio)(f32);
public:
virtual void play() = 0;
virtual void flush() = 0;
virtual void stop() = 0;
virtual void open() = 0;
virtual bool is_playing() = 0;
virtual bool add(const void*, u32) = 0;
virtual u64 enqueued_samples() = 0;
virtual f32 set_freq_ratio(f32) = 0;
};
vtable m_funcs;
private:
static XAudio2Library* xa27_init(void*);
static XAudio2Library* xa28_init(void*);
static void xa27_init(void*);
static void xa27_destroy();
static void xa27_play();
static void xa27_flush();
static void xa27_stop();
static void xa27_open();
static bool xa27_is_playing();
static bool xa27_add(const void*, u32);
static u64 xa27_enqueued_samples();
static f32 xa27_set_freq_ratio(f32);
static void xa28_init(void*);
static void xa28_destroy();
static void xa28_play();
static void xa28_flush();
static void xa28_stop();
static void xa28_open();
static bool xa28_is_playing();
static bool xa28_add(const void*, u32);
static u64 xa28_enqueued_samples();
static f32 xa28_set_freq_ratio(f32);
std::unique_ptr<XAudio2Library> lib = nullptr;
public:
XAudio2Backend();
@ -49,7 +33,7 @@ public:
virtual const char* GetName() const override { return "XAudio2"; };
static const u32 capabilities = NON_BLOCKING | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES | SET_FREQUENCY_RATIO;
static const u32 capabilities = PLAY_PAUSE_FLUSH | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES | SET_FREQUENCY_RATIO;
virtual u32 GetCapabilities() const override { return capabilities; };
virtual void Open(u32 /* num_buffers */) override;
@ -59,7 +43,7 @@ public:
virtual void Pause() override;
virtual bool IsPlaying() override;
virtual bool AddData(const void* src, u32 size) override;
virtual bool AddData(const void* src, u32 num_samples) override;
virtual void Flush() override;
virtual u64 GetNumEnqueuedSamples() override;

View File

@ -6,6 +6,7 @@
#include "Emu/Cell/lv2/sys_event.h"
#include "cellAudio.h"
#include <atomic>
#include <cmath>
LOG_CHANNEL(cellAudio);
@ -37,10 +38,25 @@ void fmt_class_string<CellAudioError>::format(std::string& out, u64 arg)
});
}
cell_audio_config::cell_audio_config()
{
// Warn if audio backend does not support all requested features
if (raw_buffering_enabled && !buffering_enabled)
{
cellAudio.error("Audio backend %s does not support buffering, this option will be ignored.", backend->GetName());
}
if (raw_time_stretching_enabled && !time_stretching_enabled)
{
cellAudio.error("Audio backend %s does not support time stretching, this option will be ignored.", backend->GetName());
}
}
audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
: cfg(_cfg)
, backend(Emu.GetCallbacks().get_audio())
, buf_sz(AUDIO_BUFFER_SAMPLES * cfg.audio_channels)
, backend(_cfg.backend)
, buf_sz(AUDIO_BUFFER_SAMPLES * _cfg.audio_channels)
, emu_paused(Emu.IsPaused())
{
// Initialize buffers
@ -64,13 +80,13 @@ audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
{
std::string str;
backend->dump_capabilities(str);
cellAudio.error("cellAudio initializing. Backend: %s, Capabilities: %s", backend->GetName(), str.c_str());
cellAudio.notice("cellAudio initializing. Backend: %s, Capabilities: %s", backend->GetName(), str.c_str());
}
backend->Open(cfg.num_allocated_buffers);
backend_open = true;
ASSERT(!backend_is_playing());
ASSERT(!get_backend_playing());
}
audio_ringbuffer::~audio_ringbuffer()
@ -80,7 +96,7 @@ audio_ringbuffer::~audio_ringbuffer()
return;
}
if (backend_is_playing())
if (get_backend_playing() && has_capability(AudioBackend::PLAY_PAUSE_FLUSH))
{
backend->Pause();
}
@ -98,7 +114,7 @@ f32 audio_ringbuffer::set_frequency_ratio(f32 new_ratio)
else
{
frequency_ratio = backend->SetFrequencyRatio(new_ratio);
//cellAudio.error("set_frequency_ratio(%1.2f) -> %1.2f", new_ratio, frequency_ratio);
//cellAudio.trace("set_frequency_ratio(%1.2f) -> %1.2f", new_ratio, frequency_ratio);
}
return frequency_ratio;
}
@ -119,11 +135,11 @@ void audio_ringbuffer::enqueue(const float* in_buffer)
// Dump audio if enabled
if (m_dump)
{
m_dump->WriteData(buf, buf_sz);
m_dump->WriteData(buf, cfg.audio_buffer_size);
}
// Enqueue audio
bool success = backend->AddData(buf, buf_sz);
bool success = backend->AddData(buf, AUDIO_BUFFER_SAMPLES * cfg.audio_channels);
if (!success)
{
cellAudio.error("Could not enqueue buffer onto audio backend. Attempting to recover...");
@ -131,14 +147,19 @@ void audio_ringbuffer::enqueue(const float* in_buffer)
return;
}
if (has_capability(AudioBackend::PLAY_PAUSE_FLUSH))
{
enqueued_samples += AUDIO_BUFFER_SAMPLES;
// Start playing audio
play();
}
}
void audio_ringbuffer::enqueue_silence(u32 buf_count)
{
AUDIT(has_capability(AudioBackend::PLAY_PAUSE_FLUSH));
for (u32 i = 0; i < buf_count; i++)
{
enqueue(silence_buffer);
@ -147,8 +168,16 @@ void audio_ringbuffer::enqueue_silence(u32 buf_count)
void audio_ringbuffer::play()
{
if (has_capability(AudioBackend::IS_PLAYING) && playing)
if (!has_capability(AudioBackend::PLAY_PAUSE_FLUSH))
{
playing = true;
return;
}
if (has_capability(AudioBackend::IS_PLAYING) && playing)
{
return;
}
if (frequency_ratio != 1.0f)
{
@ -165,7 +194,13 @@ void audio_ringbuffer::play()
void audio_ringbuffer::flush()
{
cellAudio.trace("Flushing an estimated %llu enqueued samples", enqueued_samples);
if (!has_capability(AudioBackend::PLAY_PAUSE_FLUSH))
{
playing = false;
return;
}
//cellAudio.trace("Flushing an estimated %llu enqueued samples", enqueued_samples);
backend->Pause();
playing = false;
@ -186,7 +221,7 @@ u64 audio_ringbuffer::update()
if (Emu.IsPaused())
{
// Emulator paused
if (playing)
if (has_capability(AudioBackend::PLAY_PAUSE_FLUSH) && playing)
{
backend->Pause();
}
@ -200,7 +235,7 @@ u64 audio_ringbuffer::update()
}
const u64 timestamp = get_timestamp();
const bool new_playing = !emu_paused && backend_is_playing();
const bool new_playing = !emu_paused && get_backend_playing();
// Calculate how many audio samples have played since last time
if (cfg.buffering_enabled && (playing || new_playing))
@ -234,7 +269,7 @@ u64 audio_ringbuffer::update()
if (enqueued_samples == 0)
{
cellAudio.warning("Audio buffer about to underrun!");
cellAudio.trace("Audio buffer about to underrun!");
}
}
}
@ -245,7 +280,7 @@ u64 audio_ringbuffer::update()
{
if (!new_playing)
{
cellAudio.error("Audio backend stopped unexpectedly, likely due to a buffer underrun");
cellAudio.warning("Audio backend stopped unexpectedly, likely due to a buffer underrun");
flush();
playing = false;
@ -261,11 +296,6 @@ u64 audio_ringbuffer::update()
return timestamp;
}
constexpr bool audio_port::is_tag(float val)
{
return val == 0.0f && std::signbit(val);
}
void audio_port::tag(s32 offset)
{
auto port_pos = position(offset);
@ -281,26 +311,12 @@ void audio_port::tag(s32 offset)
for (u32 tag_pos = tag_first_pos, tag_nr = 0; tag_nr < PORT_BUFFER_TAG_COUNT; tag_pos += tag_delta, tag_nr++)
{
port_buf[tag_pos] = tag;
tag_backup[port_pos][tag_nr] = 0.0f;
last_tag_value[tag_nr] = -0.0f;
}
prev_touched_tag_nr = UINT32_MAX;
}
void audio_port::apply_tag_backups(s32 offset)
{
auto port_pos = position(offset);
auto port_buf = get_vm_ptr(offset);
const u32 tag_first_pos = num_channels == 2 ? PORT_BUFFER_TAG_FIRST_2CH : PORT_BUFFER_TAG_FIRST_8CH;
const u32 tag_delta = num_channels == 2 ? PORT_BUFFER_TAG_DELTA_2CH : PORT_BUFFER_TAG_DELTA_8CH;
for (u32 tag_pos = tag_first_pos, tag_nr = 0; tag_nr < PORT_BUFFER_TAG_COUNT; tag_pos += tag_delta, tag_nr++)
{
port_buf[tag_pos] += tag_backup[port_pos][tag_nr];
}
}
std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
{
AUDIT(cfg.buffering_enabled);
@ -322,18 +338,17 @@ std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
const u32 tag_first_pos = port.num_channels == 2 ? PORT_BUFFER_TAG_FIRST_2CH : PORT_BUFFER_TAG_FIRST_8CH;
const u32 tag_delta = port.num_channels == 2 ? PORT_BUFFER_TAG_DELTA_2CH : PORT_BUFFER_TAG_DELTA_8CH;
const f32 tag = -0.0f;
u32 last_touched_tag_nr = port.prev_touched_tag_nr;
bool retouched = false;
for (u32 tag_pos = tag_first_pos, tag_nr = 0; tag_nr < PORT_BUFFER_TAG_COUNT; tag_pos += tag_delta, tag_nr++)
{
// grab current value and re-tag atomically
f32 val = atomic_storage<to_be_t<float>, 4>::exchange(port_buf[tag_pos], tag);
const f32 val = port_buf[tag_pos];
f32& last_val = port.last_tag_value[tag_nr];
if (!audio_port::is_tag(val))
if (val != last_val || (last_val == -0.0f && std::signbit(last_val) && !std::signbit(val)))
{
port.tag_backup[port_pos][tag_nr] += val;
last_val = val;
retouched |= tag_nr < port.prev_touched_tag_nr && port.prev_touched_tag_nr != UINT32_MAX;
last_touched_tag_nr = tag_nr;
@ -374,7 +389,7 @@ std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
void cell_audio_thread::reset_ports(s32 offset)
{
// Memset previous buffer to 0
// Memset previous buffer to 0 and tag
for (auto& port : ports)
{
if (port.state != audio_port_state::started) continue;
@ -383,7 +398,6 @@ void cell_audio_thread::reset_ports(s32 offset)
if (cfg.buffering_enabled)
{
//port.reset_tag_backups(offset);
port.tag(offset);
}
}
@ -414,7 +428,7 @@ void cell_audio_thread::advance(u64 timestamp, bool reset)
if (cfg.buffering_enabled)
{
// Calculate rolling average of enqueued playtime
const u64 enqueued_playtime = ringbuffer->get_enqueued_playtime();
const u64 enqueued_playtime = ringbuffer->get_enqueued_playtime(/* raw */ true);
m_average_playtime = cfg.period_average_alpha * enqueued_playtime + (1.0f - cfg.period_average_alpha) * m_average_playtime;
//cellAudio.error("m_average_playtime=%4.2f, enqueued_playtime=%u", m_average_playtime, enqueued_playtime);
}
@ -450,23 +464,6 @@ void cell_audio_thread::operator()()
// Allocate ringbuffer
ringbuffer.reset(new audio_ringbuffer(cfg));
// Check backend capabilities
if (cfg.buffering_enabled)
{
if (!has_capability(AudioBackend::NON_BLOCKING) || !has_capability(AudioBackend::IS_PLAYING))
{
// We need a non-blocking backend to be able to do buffering correctly
// We also need to be able to query the current playing state
fmt::throw_exception("Audio backend %s does not support buffering.", ringbuffer->get_backend_name());
}
if (cfg.time_stretching_enabled && !has_capability(AudioBackend::SET_FREQUENCY_RATIO))
{
// We need to be able to set a dynamic frequency ratio to be able to do time stretching
fmt::throw_exception("Audio backend %s does not support time stretching", ringbuffer->get_backend_name());
}
}
// Initialize loop variables
m_counter = 0;
m_start_time = ringbuffer->get_timestamp();
@ -490,7 +487,6 @@ void cell_audio_thread::operator()()
// TODO: send beforemix event (in ~2,6 ms before mixing)
const u64 time_since_last_period = timestamp - m_last_period_end;
const bool playing = !ringbuffer->is_playing();
if (!cfg.buffering_enabled)
{
@ -533,7 +529,7 @@ void cell_audio_thread::operator()()
f32 desired_duration_adjusted = cfg.desired_buffer_duration + (cfg.audio_block_period / 2.0f);
if (average_playtime_ratio < 1.0f)
{
desired_duration_adjusted /= average_playtime_ratio;
desired_duration_adjusted /= std::max(average_playtime_ratio, 0.25f);
}
if (cfg.time_stretching_enabled)
@ -548,13 +544,12 @@ void cell_audio_thread::operator()()
// update frequency ratio if necessary
f32 new_ratio = frequency_ratio;
if (desired_duration_rate < cfg.time_stretching_threshold)
if (
(desired_duration_rate < cfg.time_stretching_threshold && desired_duration_rate < frequency_ratio - cfg.time_stretching_step) || // Reduce frequency ratio below threshold in 0.1f steps
(desired_duration_rate > frequency_ratio + cfg.time_stretching_step) // Increase frequency ratio in 0.1f steps
)
{
new_ratio = ringbuffer->set_frequency_ratio(desired_duration_rate * cfg.time_stretching_frequency_scale_factor);
}
else if (frequency_ratio != 1.0f)
{
new_ratio = ringbuffer->set_frequency_ratio(1.0f);
new_ratio = ringbuffer->set_frequency_ratio(std::min(desired_duration_rate, 1.0f));
}
if (new_ratio != frequency_ratio)
@ -714,11 +709,6 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
{
if (port.state != audio_port_state::started) continue;
if (cfg.buffering_enabled)
{
port.apply_tag_backups(offset);
}
auto buf = port.get_vm_ptr(offset);
static const float k = 1.0f;
float& m = port.level;
@ -876,7 +866,6 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
for (size_t i = 0; i < out_buffer_sz; i += 8)
{
// TODO ruipin: Revisit this
const auto scale = _mm_set1_ps(0x8000);
_mm_store_ps(out_buffer + i / 2, _mm_castsi128_ps(_mm_packs_epi32(
_mm_cvtps_epi32(_mm_mul_ps(_mm_load_ps(out_buffer + i), scale)),

View File

@ -90,7 +90,7 @@ enum : u32
AUDIO_BLOCK_SIZE_2CH = 2 * AUDIO_BUFFER_SAMPLES,
AUDIO_BLOCK_SIZE_8CH = 8 * AUDIO_BUFFER_SAMPLES,
PORT_BUFFER_TAG_COUNT = 8,
PORT_BUFFER_TAG_COUNT = 6,
PORT_BUFFER_TAG_LAST_2CH = AUDIO_BLOCK_SIZE_2CH - 1,
PORT_BUFFER_TAG_DELTA_2CH = PORT_BUFFER_TAG_LAST_2CH / (PORT_BUFFER_TAG_COUNT - 1),
@ -163,37 +163,59 @@ struct audio_port
// Tags
u32 prev_touched_tag_nr;
f32 tag_backup[AUDIO_MAX_BLOCK_COUNT][PORT_BUFFER_TAG_COUNT] = { 0 };
f32 last_tag_value[PORT_BUFFER_TAG_COUNT] = { 0 };
constexpr static bool is_tag(float val);
void tag(s32 offset = 0);
void apply_tag_backups(s32 offset = 0);
};
struct cell_audio_config
{
const s64 period_comparison_margin = 100; // 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 std::shared_ptr<AudioBackend> backend = Emu.GetCallbacks().get_audio();
const u32 audio_channels = AudioBackend::get_channels();
const u32 audio_sampling_rate = AudioBackend::get_sampling_rate();
const u32 audio_block_period = AUDIO_BUFFER_SAMPLES * 1000000 / audio_sampling_rate;
const u64 desired_buffer_duration = g_cfg.audio.enable_buffering ? g_cfg.audio.desired_buffer_duration : 0;
const u32 audio_buffer_length = AUDIO_BUFFER_SAMPLES * audio_channels;
const u32 audio_buffer_size = audio_buffer_length * sizeof(f32);
const bool buffering_enabled = g_cfg.audio.enable_buffering && (desired_buffer_duration >= audio_block_period);
const u32 audio_buffer_size = audio_buffer_length * AudioBackend::get_sample_size();
/*
* Buffering
*/
const u64 desired_buffer_duration = g_cfg.audio.enable_buffering ? g_cfg.audio.desired_buffer_duration : 0;
private:
const bool raw_buffering_enabled = g_cfg.audio.enable_buffering && (desired_buffer_duration >= audio_block_period);
public:
// 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
const bool buffering_enabled = raw_buffering_enabled && backend->has_capability(AudioBackend::PLAY_PAUSE_FLUSH | AudioBackend::IS_PLAYING);
const u64 minimum_block_period = audio_block_period / 2; // 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)
const u32 desired_full_buffers = buffering_enabled ? static_cast<u32>(desired_buffer_duration / audio_block_period) + 1 : 1;
const u32 desired_full_buffers = buffering_enabled ? static_cast<u32>(desired_buffer_duration / audio_block_period) + 1 : 2;
const u32 num_allocated_buffers = desired_full_buffers + EXTRA_AUDIO_BUFFERS; // number of ringbuffer buffers
const f32 period_average_alpha = 0.02f; // alpha factor for the m_average_period rolling average
const bool time_stretching_enabled = buffering_enabled && g_cfg.audio.enable_time_stretching && (g_cfg.audio.time_stretching_threshold > 0);
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
/*
* 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)
const f32 time_stretching_frequency_scale_factor = 1.0f / time_stretching_threshold;
const f32 time_stretching_step = 0.1f;
/*
* Constructor
*/
cell_audio_config();
};
class audio_ringbuffer
@ -224,9 +246,9 @@ private:
u32 cur_pos = 0;
bool backend_is_playing() const
bool get_backend_playing() const
{
return has_capability(AudioBackend::IS_PLAYING) ? backend->IsPlaying() : playing;
return has_capability(AudioBackend::PLAY_PAUSE_FLUSH | AudioBackend::IS_PLAYING) ? backend->IsPlaying() : playing;
}
public:
@ -280,7 +302,7 @@ public:
return frequency_ratio;
}
u32 has_capability(AudioBackend::Capabilities cap) const
u32 has_capability(u32 cap) const
{
return backend->has_capability(cap);
}
@ -307,7 +329,7 @@ class cell_audio_thread
constexpr static u64 get_thread_wait_delay(u64 time_left)
{
return (time_left > 1000) ? time_left - 750 : 100;
return (time_left > 350) ? time_left - 250 : 100;
}
public:
@ -349,7 +371,7 @@ public:
return nullptr;
}
bool has_capability(AudioBackend::Capabilities cap) const
bool has_capability(u32 cap) const
{
return ringbuffer->has_capability(cap);
}

View File

@ -526,12 +526,12 @@ struct cfg_root : cfg::node
cfg::_bool dump_to_file{this, "Dump to file"};
cfg::_bool convert_to_u16{this, "Convert to 16 bit"};
cfg::_bool downmix_to_2ch{this, "Downmix to Stereo", true};
cfg::_int<1, 128> startt{this, "Start Threshold", 1};
cfg::_int<1, 128> startt{this, "Start Threshold", 1}; // TODO: used only by ALSA, should probably be removed once ALSA is upgraded
cfg::_int<0, 200> volume{this, "Master Volume", 100};
cfg::_bool enable_buffering{this, "Enable Buffering", true};
cfg::_int <0, 250'000> desired_buffer_duration{this, "Desired Audio Buffer Duration", 100'000};
cfg::_int<1, 1000> sampling_period_multiplier{this, "Sampling Period Multiplier", 100};
cfg::_bool enable_time_stretching{this, "Enable Time Stretching", true};
cfg::_bool enable_time_stretching{this, "Enable Time Stretching", false};
cfg::_int<0, 100> time_stretching_threshold{this, "Time Stretching Threshold", 75};
} audio{this};

View File

@ -48,10 +48,10 @@
#include "Emu/Audio/XAudio2/XAudio2Backend.h"
#endif
#ifdef HAVE_ALSA
#include "Emu/Audio/ALSA/ALSAThread.h"
#include "Emu/Audio/ALSA/ALSABackend.h"
#endif
#ifdef HAVE_PULSE
#include "Emu/Audio/Pulse/PulseThread.h"
#include "Emu/Audio/Pulse/PulseBackend.h"
#endif
#ifdef _WIN32
@ -263,10 +263,10 @@ void rpcs3_app::InitializeCallbacks()
case audio_renderer::xaudio: return std::make_shared<XAudio2Backend>();
#endif
#ifdef HAVE_ALSA
case audio_renderer::alsa: return std::make_shared<ALSAThread>();
case audio_renderer::alsa: return std::make_shared<ALSABackend>();
#endif
#ifdef HAVE_PULSE
case audio_renderer::pulse: return std::make_shared<PulseThread>();
case audio_renderer::pulse: return std::make_shared<PulseBackend>();
#endif
case audio_renderer::openal: return std::make_shared<OpenALBackend>();