mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-22 18:53:28 +01:00
Implement Audio Backend Capabilities querying
Also renames "AudioThread" to "AudioBackend". The new name is more descriptive of what the class really is responsible for, since the backends are not responsible for managing the audio thread. NOTE: Right now only XAudio2 is supported
This commit is contained in:
parent
2addbe6be2
commit
5159d3559e
@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
/*#include "Emu/Audio/AudioThread.h"
|
||||
/*#include "Emu/Audio/AudioBackend.h"
|
||||
#include "3rdparty/OpenAL/include/alext.h"
|
||||
#include <memory>
|
||||
|
||||
class OpenALThread : public AudioThread
|
||||
class OpenALThread : public AudioBackend
|
||||
{
|
||||
private:
|
||||
ALint m_format;
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include "Emu/Audio/AudioThread.h"
|
||||
|
||||
class ALSAThread : public AudioThread
|
||||
class ALSAThread : public AudioBackend
|
||||
{
|
||||
public:
|
||||
ALSAThread();
|
||||
|
@ -10,22 +10,42 @@ enum : u32
|
||||
AUDIO_BUFFER_SAMPLES = 256
|
||||
};
|
||||
|
||||
class AudioThread
|
||||
class AudioBackend
|
||||
{
|
||||
public:
|
||||
virtual ~AudioThread() = default;
|
||||
enum Capabilities : u32
|
||||
{
|
||||
NON_BLOCKING = 0x1,
|
||||
IS_PLAYING = 0x2,
|
||||
GET_NUM_ENQUEUED_SAMPLES = 0x4,
|
||||
};
|
||||
|
||||
virtual ~AudioBackend() = default;
|
||||
|
||||
// Callbacks
|
||||
virtual const char* GetName() const = 0;
|
||||
virtual u32 GetCapabilities() const = 0;
|
||||
|
||||
virtual void Open() = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
virtual void Play() = 0;
|
||||
virtual void Pause() = 0;
|
||||
virtual bool IsPlaying() = 0;
|
||||
|
||||
virtual bool IsPlaying()
|
||||
{
|
||||
fmt::throw_exception("IsPlaying() not implemented");
|
||||
};
|
||||
|
||||
virtual bool AddData(const void* src, int size) = 0;
|
||||
virtual void Flush() = 0;
|
||||
|
||||
virtual u64 GetNumEnqueuedSamples()
|
||||
{
|
||||
fmt::throw_exception("GetNumEnqueuedSamples() not implemented");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
static u32 get_sampling_rate()
|
||||
{
|
24
rpcs3/Emu/Audio/Null/NullAudioBackend.h
Normal file
24
rpcs3/Emu/Audio/Null/NullAudioBackend.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
class NullAudioBackend : public AudioBackend
|
||||
{
|
||||
public:
|
||||
NullAudioBackend() {}
|
||||
virtual ~NullAudioBackend() {}
|
||||
|
||||
virtual const char* GetName() const override { return "NullAudioBackend"; }
|
||||
|
||||
static const u32 capabilities = NON_BLOCKING;
|
||||
virtual u32 GetCapabilities() const override { return capabilities; };
|
||||
|
||||
virtual void Open() override {};
|
||||
virtual void Close() override {};
|
||||
|
||||
virtual void Play() override {};
|
||||
virtual void Pause() override {};
|
||||
|
||||
virtual bool AddData(const void* src, int size) override { return true; };
|
||||
virtual void Flush() override {};
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Audio/AudioThread.h"
|
||||
|
||||
class NullAudioThread : public AudioThread
|
||||
{
|
||||
public:
|
||||
NullAudioThread() {}
|
||||
virtual ~NullAudioThread() {}
|
||||
|
||||
virtual void Open() {};
|
||||
virtual void Close() {};
|
||||
|
||||
virtual void Play() {};
|
||||
virtual void Pause() {};
|
||||
virtual bool IsPlaying() { return true; };
|
||||
|
||||
virtual bool AddData(const void* src, int size) { return true; };
|
||||
virtual void Flush() {};
|
||||
};
|
@ -4,7 +4,7 @@
|
||||
#include <pulse/simple.h>
|
||||
#include "Emu/Audio/AudioThread.h"
|
||||
|
||||
class PulseThread : public AudioThread
|
||||
class PulseThread : public AudioBackend
|
||||
{
|
||||
public:
|
||||
PulseThread();
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "XAudio2Thread.h"
|
||||
#include "XAudio2Backend.h"
|
||||
#include "3rdparty/XAudio2_7/XAudio2.h"
|
||||
|
||||
static thread_local HMODULE s_tls_xaudio2_lib{};
|
||||
@ -12,7 +12,7 @@ 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 XAudio2Thread::xa27_init(void* lib2_7)
|
||||
void XAudio2Backend::xa27_init(void* lib2_7)
|
||||
{
|
||||
s_tls_xaudio2_lib = (HMODULE)lib2_7;
|
||||
|
||||
@ -21,7 +21,7 @@ void XAudio2Thread::xa27_init(void* lib2_7)
|
||||
hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : CoInitializeEx() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : CoInitializeEx() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
return;
|
||||
}
|
||||
@ -29,7 +29,7 @@ void XAudio2Thread::xa27_init(void* lib2_7)
|
||||
hr = XAudio2Create(&s_tls_xaudio2_instance, 0, XAUDIO2_DEFAULT_PROCESSOR);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : XAudio2Create() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : XAudio2Create() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
return;
|
||||
}
|
||||
@ -37,13 +37,13 @@ void XAudio2Thread::xa27_init(void* lib2_7)
|
||||
hr = s_tls_xaudio2_instance->CreateMasteringVoice(&s_tls_master_voice, g_cfg.audio.downmix_to_2ch ? 2 : 8, 48000);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : CreateMasteringVoice() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : CreateMasteringVoice() failed(0x%08x)", (u32)hr);
|
||||
s_tls_xaudio2_instance->Release();
|
||||
Emu.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa27_destroy()
|
||||
void XAudio2Backend::xa27_destroy()
|
||||
{
|
||||
if (s_tls_source_voice != nullptr)
|
||||
{
|
||||
@ -67,37 +67,37 @@ void XAudio2Thread::xa27_destroy()
|
||||
FreeLibrary(s_tls_xaudio2_lib);
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa27_play()
|
||||
void XAudio2Backend::xa27_play()
|
||||
{
|
||||
HRESULT hr = s_tls_source_voice->Start();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : Start() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : Start() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa27_flush()
|
||||
void XAudio2Backend::xa27_flush()
|
||||
{
|
||||
HRESULT hr = s_tls_source_voice->FlushSourceBuffers();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : FlushSourceBuffers() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : FlushSourceBuffers() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa27_stop()
|
||||
void XAudio2Backend::xa27_stop()
|
||||
{
|
||||
HRESULT hr = s_tls_source_voice->Stop();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : Stop() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : Stop() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
bool XAudio2Thread::xa27_is_playing()
|
||||
bool XAudio2Backend::xa27_is_playing()
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
s_tls_source_voice->GetState(&state);
|
||||
@ -105,7 +105,7 @@ bool XAudio2Thread::xa27_is_playing()
|
||||
return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr;
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa27_open()
|
||||
void XAudio2Backend::xa27_open()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
@ -125,7 +125,7 @@ void XAudio2Thread::xa27_open()
|
||||
hr = s_tls_xaudio2_instance->CreateSourceVoice(&s_tls_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : CreateSourceVoice() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : CreateSourceVoice() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
return;
|
||||
}
|
||||
@ -133,7 +133,7 @@ void XAudio2Thread::xa27_open()
|
||||
s_tls_source_voice->SetVolume(channels == 2 ? 1.0f : 4.0f);
|
||||
}
|
||||
|
||||
bool XAudio2Thread::xa27_add(const void* src, int size)
|
||||
bool XAudio2Backend::xa27_add(const void* src, int size)
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
s_tls_source_voice->GetState(&state);
|
||||
@ -141,7 +141,7 @@ bool XAudio2Thread::xa27_add(const void* src, int size)
|
||||
// XAudio 2.7 bug workaround, when it says "SimpList: non-growable list ran out of room for new elements" and hits int 3
|
||||
if (state.BuffersQueued >= MAX_AUDIO_BUFFERS)
|
||||
{
|
||||
LOG_WARNING(GENERAL, "XAudio2Thread : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed);
|
||||
LOG_WARNING(GENERAL, "XAudio2Backend : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -160,11 +160,21 @@ bool XAudio2Thread::xa27_add(const void* src, int size)
|
||||
HRESULT hr = s_tls_source_voice->SubmitSourceBuffer(&buffer);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : AddData() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : AddData() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 XAudio2Backend::xa27_enqueued_samples()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
@ -4,7 +4,7 @@
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "XAudio2Thread.h"
|
||||
#include "XAudio2Backend.h"
|
||||
#include "3rdparty/minidx12/Include/xaudio2.h"
|
||||
|
||||
static thread_local HMODULE s_tls_xaudio2_lib{};
|
||||
@ -12,7 +12,7 @@ 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 XAudio2Thread::xa28_init(void* lib)
|
||||
void XAudio2Backend::xa28_init(void* lib)
|
||||
{
|
||||
s_tls_xaudio2_lib = (HMODULE)lib;
|
||||
|
||||
@ -23,7 +23,7 @@ void XAudio2Thread::xa28_init(void* lib)
|
||||
hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : CoInitializeEx() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : CoInitializeEx() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
return;
|
||||
}
|
||||
@ -31,7 +31,7 @@ void XAudio2Thread::xa28_init(void* lib)
|
||||
hr = create(&s_tls_xaudio2_instance, 0, XAUDIO2_DEFAULT_PROCESSOR);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : XAudio2Create() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : XAudio2Create() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
return;
|
||||
}
|
||||
@ -39,13 +39,13 @@ void XAudio2Thread::xa28_init(void* lib)
|
||||
hr = s_tls_xaudio2_instance->CreateMasteringVoice(&s_tls_master_voice, g_cfg.audio.downmix_to_2ch ? 2 : 8, 48000);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : CreateMasteringVoice() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : CreateMasteringVoice() failed(0x%08x)", (u32)hr);
|
||||
s_tls_xaudio2_instance->Release();
|
||||
Emu.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa28_destroy()
|
||||
void XAudio2Backend::xa28_destroy()
|
||||
{
|
||||
if (s_tls_source_voice != nullptr)
|
||||
{
|
||||
@ -69,43 +69,43 @@ void XAudio2Thread::xa28_destroy()
|
||||
FreeLibrary(s_tls_xaudio2_lib);
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa28_play()
|
||||
void XAudio2Backend::xa28_play()
|
||||
{
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
|
||||
HRESULT hr = s_tls_source_voice->Start();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : Start() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : Start() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa28_flush()
|
||||
void XAudio2Backend::xa28_flush()
|
||||
{
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
|
||||
HRESULT hr = s_tls_source_voice->FlushSourceBuffers();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : FlushSourceBuffers() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : FlushSourceBuffers() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa28_stop()
|
||||
void XAudio2Backend::xa28_stop()
|
||||
{
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
|
||||
HRESULT hr = s_tls_source_voice->Stop();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : Stop() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : Stop() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
bool XAudio2Thread::xa28_is_playing()
|
||||
bool XAudio2Backend::xa28_is_playing()
|
||||
{
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
|
||||
@ -115,7 +115,7 @@ bool XAudio2Thread::xa28_is_playing()
|
||||
return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr;
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa28_open()
|
||||
void XAudio2Backend::xa28_open()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
@ -135,7 +135,7 @@ void XAudio2Thread::xa28_open()
|
||||
hr = s_tls_xaudio2_instance->CreateSourceVoice(&s_tls_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : CreateSourceVoice() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : CreateSourceVoice() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
return;
|
||||
}
|
||||
@ -144,7 +144,7 @@ void XAudio2Thread::xa28_open()
|
||||
s_tls_source_voice->SetVolume(channels == 2 ? 1.0 : 4.0);
|
||||
}
|
||||
|
||||
bool XAudio2Thread::xa28_add(const void* src, int size)
|
||||
bool XAudio2Backend::xa28_add(const void* src, int size)
|
||||
{
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
|
||||
@ -153,7 +153,7 @@ bool XAudio2Thread::xa28_add(const void* src, int size)
|
||||
|
||||
if (state.BuffersQueued >= MAX_AUDIO_BUFFERS)
|
||||
{
|
||||
LOG_WARNING(GENERAL, "XAudio2Thread : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed);
|
||||
LOG_WARNING(GENERAL, "XAudio2Backend : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -172,11 +172,21 @@ bool XAudio2Thread::xa28_add(const void* src, int size)
|
||||
HRESULT hr = s_tls_source_voice->SubmitSourceBuffer(&buffer);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : AddData() failed(0x%08x)", (u32)hr);
|
||||
LOG_ERROR(GENERAL, "XAudio2Backend : AddData() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 XAudio2Backend::xa28_enqueued_samples()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
@ -3,16 +3,11 @@
|
||||
#include "Utilities/Log.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
|
||||
#include "XAudio2Thread.h"
|
||||
#include "XAudio2Backend.h"
|
||||
#include <Windows.h>
|
||||
|
||||
XAudio2Thread::XAudio2Thread()
|
||||
XAudio2Backend::XAudio2Backend()
|
||||
{
|
||||
if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "XAudio: failed to increase thread priority");
|
||||
}
|
||||
|
||||
if (auto lib2_9 = LoadLibraryExW(L"XAudio2_9.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32))
|
||||
{
|
||||
// xa28* implementation is fully compatible with library 2.9
|
||||
@ -25,11 +20,29 @@ XAudio2Thread::XAudio2Thread()
|
||||
m_funcs.open = &xa28_open;
|
||||
m_funcs.is_playing = &xa28_is_playing;
|
||||
m_funcs.add = &xa28_add;
|
||||
m_funcs.enqueued_samples = &xa28_enqueued_samples;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
@ -41,69 +54,59 @@ XAudio2Thread::XAudio2Thread()
|
||||
m_funcs.open = &xa27_open;
|
||||
m_funcs.is_playing = &xa27_is_playing;
|
||||
m_funcs.add = &xa27_add;
|
||||
m_funcs.enqueued_samples = &xa27_enqueued_samples;
|
||||
|
||||
LOG_SUCCESS(GENERAL, "XAudio 2.7 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;
|
||||
|
||||
LOG_SUCCESS(GENERAL, "XAudio 2.8 initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
fmt::throw_exception("No supported XAudio2 library found");
|
||||
}
|
||||
|
||||
XAudio2Thread::~XAudio2Thread()
|
||||
XAudio2Backend::~XAudio2Backend()
|
||||
{
|
||||
m_funcs.destroy();
|
||||
}
|
||||
|
||||
void XAudio2Thread::Play()
|
||||
void XAudio2Backend::Play()
|
||||
{
|
||||
m_funcs.play();
|
||||
}
|
||||
|
||||
void XAudio2Thread::Close()
|
||||
void XAudio2Backend::Close()
|
||||
{
|
||||
m_funcs.stop();
|
||||
m_funcs.flush();
|
||||
}
|
||||
|
||||
void XAudio2Thread::Pause()
|
||||
void XAudio2Backend::Pause()
|
||||
{
|
||||
m_funcs.stop();
|
||||
}
|
||||
|
||||
void XAudio2Thread::Open()
|
||||
void XAudio2Backend::Open()
|
||||
{
|
||||
m_funcs.open();
|
||||
}
|
||||
|
||||
bool XAudio2Thread::IsPlaying()
|
||||
bool XAudio2Backend::IsPlaying()
|
||||
{
|
||||
return m_funcs.is_playing();
|
||||
}
|
||||
|
||||
bool XAudio2Thread::AddData(const void* src, int size)
|
||||
bool XAudio2Backend::AddData(const void* src, int size)
|
||||
{
|
||||
return m_funcs.add(src, size);
|
||||
}
|
||||
|
||||
void XAudio2Thread::Flush()
|
||||
void XAudio2Backend::Flush()
|
||||
{
|
||||
m_funcs.flush();
|
||||
}
|
||||
|
||||
u64 XAudio2Backend::GetNumEnqueuedSamples()
|
||||
{
|
||||
return m_funcs.enqueued_samples();
|
||||
}
|
||||
|
||||
#endif
|
@ -2,9 +2,9 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Emu/Audio/AudioThread.h"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
class XAudio2Thread : public AudioThread
|
||||
class XAudio2Backend : public AudioBackend
|
||||
{
|
||||
struct vtable
|
||||
{
|
||||
@ -15,6 +15,7 @@ class XAudio2Thread : public AudioThread
|
||||
void(*open)();
|
||||
bool(*is_playing)();
|
||||
bool(*add)(const void*, int);
|
||||
u64(*enqueued_samples)();
|
||||
};
|
||||
|
||||
vtable m_funcs;
|
||||
@ -27,6 +28,7 @@ class XAudio2Thread : public AudioThread
|
||||
static void xa27_open();
|
||||
static bool xa27_is_playing();
|
||||
static bool xa27_add(const void*, int);
|
||||
static u64 xa27_enqueued_samples();
|
||||
|
||||
static void xa28_init(void*);
|
||||
static void xa28_destroy();
|
||||
@ -36,10 +38,16 @@ class XAudio2Thread : public AudioThread
|
||||
static void xa28_open();
|
||||
static bool xa28_is_playing();
|
||||
static bool xa28_add(const void*, int);
|
||||
static u64 xa28_enqueued_samples();
|
||||
|
||||
public:
|
||||
XAudio2Thread();
|
||||
virtual ~XAudio2Thread() override;
|
||||
XAudio2Backend();
|
||||
virtual ~XAudio2Backend() override;
|
||||
|
||||
virtual const char* GetName() const override { return "XAudio2Backend"; };
|
||||
|
||||
static const u32 capabilities = NON_BLOCKING | IS_PLAYING | GET_NUM_ENQUEUED_SAMPLES;
|
||||
virtual u32 GetCapabilities() const override { return capabilities; };
|
||||
|
||||
virtual void Open() override;
|
||||
virtual void Close() override;
|
||||
@ -50,6 +58,8 @@ public:
|
||||
|
||||
virtual bool AddData(const void* src, int size) override;
|
||||
virtual void Flush() override;
|
||||
|
||||
virtual u64 GetNumEnqueuedSamples() override;
|
||||
};
|
||||
|
||||
#endif
|
@ -37,21 +37,19 @@ void fmt_class_string<CellAudioError>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
audio_ringbuffer::audio_ringbuffer(u32 num_buffers, u32 audio_sampling_rate, u32 channels)
|
||||
: num_allocated_buffers(num_buffers)
|
||||
, audio_sampling_rate(audio_sampling_rate)
|
||||
audio_ringbuffer::audio_ringbuffer(cell_audio_config& _cfg)
|
||||
: cfg(_cfg)
|
||||
, backend(Emu.GetCallbacks().get_audio())
|
||||
, channels(channels)
|
||||
, buf_sz(AUDIO_BUFFER_SAMPLES * channels)
|
||||
, buf_sz(AUDIO_BUFFER_SAMPLES * cfg.audio_channels)
|
||||
, emu_paused(Emu.IsPaused())
|
||||
{
|
||||
// Initialize buffers
|
||||
if (num_allocated_buffers >= MAX_AUDIO_BUFFERS)
|
||||
if (cfg.num_allocated_buffers >= MAX_AUDIO_BUFFERS)
|
||||
{
|
||||
fmt::throw_exception("MAX_AUDIO_BUFFERS is too small");
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < num_allocated_buffers; i++)
|
||||
for (u32 i = 0; i < cfg.num_allocated_buffers; i++)
|
||||
{
|
||||
buffer[i].reset(new float[buf_sz]{});
|
||||
}
|
||||
@ -59,37 +57,53 @@ audio_ringbuffer::audio_ringbuffer(u32 num_buffers, u32 audio_sampling_rate, u32
|
||||
// Init audio dumper if enabled
|
||||
if (g_cfg.audio.dump_to_file)
|
||||
{
|
||||
m_dump.reset(new AudioDumper(channels));
|
||||
m_dump.reset(new AudioDumper(cfg.audio_channels));
|
||||
}
|
||||
|
||||
// Sanity check configuration vs. capabilities
|
||||
backend_capabilities = backend->GetCapabilities();
|
||||
if (cfg.buffering_enabled)
|
||||
{
|
||||
if (!(backend_capabilities & AudioBackend::NON_BLOCKING) || !(backend_capabilities & AudioBackend::IS_PLAYING))
|
||||
{
|
||||
// We need a non-blocking backend to be able to do buffering correctly
|
||||
fmt::throw_exception("Audio backend %s does not support buffering.", backend->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize backend
|
||||
backend->Open();
|
||||
backend_open = true;
|
||||
ASSERT(!backend->IsPlaying());
|
||||
|
||||
ASSERT(!backend_is_playing());
|
||||
}
|
||||
|
||||
audio_ringbuffer::~audio_ringbuffer()
|
||||
{
|
||||
if (!backend_open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (backend->IsPlaying())
|
||||
if (backend_is_playing())
|
||||
{
|
||||
backend->Pause();
|
||||
}
|
||||
|
||||
backend->Close();
|
||||
}
|
||||
|
||||
void audio_ringbuffer::enqueue(const float* in_buffer)
|
||||
{
|
||||
AUDIT(next_buf < num_allocated_buffers);
|
||||
AUDIT(cur_pos < cfg.num_allocated_buffers);
|
||||
|
||||
// Prepare buffer
|
||||
const void* buf = in_buffer;
|
||||
|
||||
if (buf == nullptr)
|
||||
{
|
||||
buf = buffer[next_buf].get();
|
||||
next_buf = (next_buf + 1) % num_allocated_buffers;
|
||||
buf = buffer[cur_pos].get();
|
||||
cur_pos = (cur_pos + 1) % cfg.num_allocated_buffers;
|
||||
}
|
||||
|
||||
// Dump audio if enabled
|
||||
@ -165,35 +179,42 @@ u64 audio_ringbuffer::update()
|
||||
}
|
||||
|
||||
const u64 timestamp = get_timestamp();
|
||||
const bool new_playing = !emu_paused && backend->IsPlaying();
|
||||
const bool new_playing = !emu_paused && backend_is_playing();
|
||||
|
||||
// Calculate how many audio samples have played since last time
|
||||
// TODO: Natively query backend for remaining samples
|
||||
if (playing || new_playing)
|
||||
if (cfg.buffering_enabled && (playing || new_playing))
|
||||
{
|
||||
const u64 play_delta = timestamp - (play_timestamp > update_timestamp ? play_timestamp : update_timestamp);
|
||||
|
||||
// NOTE: Only works with a fixed sampling rate
|
||||
const u64 delta_samples_tmp = (play_delta * audio_sampling_rate) + last_remainder;
|
||||
last_remainder = delta_samples_tmp % 1'000'000;
|
||||
const u64 delta_samples = delta_samples_tmp / 1'000'000;
|
||||
|
||||
//cellAudio.error("play_delta=%llu delta_samples=%llu", play_delta, delta_samples);
|
||||
if (delta_samples > 0)
|
||||
if (backend_capabilities & AudioBackend::GET_NUM_ENQUEUED_SAMPLES)
|
||||
{
|
||||
// Backend supports querying for the remaining playtime, so just ask it
|
||||
enqueued_samples = backend->GetNumEnqueuedSamples();
|
||||
}
|
||||
else
|
||||
{
|
||||
const u64 play_delta = timestamp - (play_timestamp > update_timestamp ? play_timestamp : update_timestamp);
|
||||
|
||||
if (enqueued_samples < delta_samples)
|
||||
{
|
||||
enqueued_samples = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
enqueued_samples -= delta_samples;
|
||||
}
|
||||
// NOTE: Only works with a fixed sampling rate
|
||||
const u64 delta_samples_tmp = (play_delta * cfg.audio_sampling_rate) + last_remainder;
|
||||
last_remainder = delta_samples_tmp % 1'000'000;
|
||||
const u64 delta_samples = delta_samples_tmp / 1'000'000;
|
||||
|
||||
if (enqueued_samples == 0)
|
||||
//cellAudio.error("play_delta=%llu delta_samples=%llu", play_delta, delta_samples);
|
||||
if (delta_samples > 0)
|
||||
{
|
||||
cellAudio.warning("Audio buffer about to underrun!");
|
||||
|
||||
if (enqueued_samples < delta_samples)
|
||||
{
|
||||
enqueued_samples = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
enqueued_samples -= delta_samples;
|
||||
}
|
||||
|
||||
if (enqueued_samples == 0)
|
||||
{
|
||||
cellAudio.warning("Audio buffer about to underrun!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -261,7 +282,7 @@ void audio_port::apply_tag_backups(s32 offset)
|
||||
|
||||
std::tuple<u32, u32, u32, u32> cell_audio_thread::count_port_buffer_tags()
|
||||
{
|
||||
AUDIT(buffering_enabled);
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
|
||||
u32 active = 0;
|
||||
u32 in_progress = 0;
|
||||
@ -339,7 +360,7 @@ void cell_audio_thread::reset_ports(s32 offset)
|
||||
|
||||
memset(port.get_vm_ptr(offset), 0, port.block_size() * sizeof(float));
|
||||
|
||||
if (buffering_enabled)
|
||||
if (cfg.buffering_enabled)
|
||||
{
|
||||
//port.reset_tag_backups(offset);
|
||||
port.tag(offset);
|
||||
@ -398,7 +419,7 @@ void cell_audio_thread::operator()()
|
||||
thread_ctrl::set_native_priority(1);
|
||||
|
||||
// Allocate ringbuffer
|
||||
ringbuffer.reset(new audio_ringbuffer(num_allocated_buffers, audio_sampling_rate, audio_channels));
|
||||
ringbuffer.reset(new audio_ringbuffer(cfg));
|
||||
|
||||
// Initialize loop variables
|
||||
m_counter = 0;
|
||||
@ -425,12 +446,12 @@ void cell_audio_thread::operator()()
|
||||
const u64 time_since_last_period = timestamp - m_last_period_end;
|
||||
const bool playing = !ringbuffer->is_playing();
|
||||
|
||||
if (!buffering_enabled)
|
||||
if (!cfg.buffering_enabled)
|
||||
{
|
||||
const u64 period_end = (m_counter * audio_block_period) + m_start_time;
|
||||
const u64 period_end = (m_counter * cfg.audio_block_period) + m_start_time;
|
||||
const s64 time_left = period_end - timestamp;
|
||||
|
||||
if (time_left > period_comparison_margin)
|
||||
if (time_left > cfg.period_comparison_margin)
|
||||
{
|
||||
thread_ctrl::wait_for(get_thread_wait_delay(time_left));
|
||||
continue;
|
||||
@ -438,8 +459,8 @@ void cell_audio_thread::operator()()
|
||||
}
|
||||
else
|
||||
{
|
||||
const u64 enqueued_playtime = ringbuffer->get_enqueued_samples() * 1'000'000 / audio_sampling_rate;
|
||||
const u64 enqueued_buffers = (enqueued_playtime) / audio_block_period;
|
||||
const u64 enqueued_playtime = ringbuffer->get_enqueued_samples() * 1'000'000 / cfg.audio_sampling_rate;
|
||||
const u64 enqueued_buffers = (enqueued_playtime) / cfg.audio_block_period;
|
||||
|
||||
const bool playing = ringbuffer->is_playing();
|
||||
|
||||
@ -455,32 +476,32 @@ void cell_audio_thread::operator()()
|
||||
if (!playing)
|
||||
{
|
||||
// When the buffer is empty, always use the correct block period
|
||||
m_dynamic_period = audio_block_period;
|
||||
m_dynamic_period = cfg.audio_block_period;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 1.0 means exactly as desired
|
||||
// <1.0 means not as full as desired
|
||||
// >1.0 means more full than desired
|
||||
const f32 desired_duration_rate = (enqueued_playtime) / static_cast<f32>(desired_buffer_duration);
|
||||
const f32 desired_duration_rate = (enqueued_playtime) / static_cast<f32>(cfg.desired_buffer_duration);
|
||||
|
||||
if (desired_duration_rate >= 1.0f)
|
||||
{
|
||||
// more full than desired
|
||||
const f32 multiplier = 1.0f / desired_duration_rate;
|
||||
m_dynamic_period = maximum_block_period - static_cast<u64>((maximum_block_period - audio_block_period) * multiplier);
|
||||
m_dynamic_period = cfg.maximum_block_period - static_cast<u64>((cfg.maximum_block_period - cfg.audio_block_period) * multiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
// not as full as desired
|
||||
const f32 multiplier = desired_duration_rate;
|
||||
m_dynamic_period = minimum_block_period + static_cast<u64>((audio_block_period - minimum_block_period) * multiplier);
|
||||
m_dynamic_period = cfg.minimum_block_period + static_cast<u64>((cfg.audio_block_period - cfg.minimum_block_period) * multiplier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s64 time_left = m_dynamic_period - time_since_last_period;
|
||||
if (time_left > period_comparison_margin)
|
||||
if (time_left > cfg.period_comparison_margin)
|
||||
{
|
||||
thread_ctrl::wait_for(get_thread_wait_delay(time_left));
|
||||
continue;
|
||||
@ -549,33 +570,33 @@ void cell_audio_thread::operator()()
|
||||
{
|
||||
// We are not playing (likely buffer underrun)
|
||||
// align to 5.(3)ms on global clock
|
||||
const s64 audio_period_alignment_delta = (timestamp - m_start_time) % audio_block_period;
|
||||
if (audio_period_alignment_delta > period_comparison_margin)
|
||||
const s64 audio_period_alignment_delta = (timestamp - m_start_time) % cfg.audio_block_period;
|
||||
if (audio_period_alignment_delta > cfg.period_comparison_margin)
|
||||
{
|
||||
thread_ctrl::wait_for(audio_period_alignment_delta - period_comparison_margin);
|
||||
thread_ctrl::wait_for(audio_period_alignment_delta - cfg.period_comparison_margin);
|
||||
}
|
||||
|
||||
// Flush, add silence, restart algorithm
|
||||
cellAudio.error("play/resume audio: received first audio buffer");
|
||||
ringbuffer->flush();
|
||||
ringbuffer->enqueue_silence(desired_full_buffers);
|
||||
ringbuffer->enqueue_silence(cfg.desired_full_buffers);
|
||||
finish_port_volume_stepping();
|
||||
}
|
||||
}
|
||||
|
||||
// Mix
|
||||
float *buf = ringbuffer->get_current_buffer();
|
||||
if (audio_channels == 2)
|
||||
if (cfg.audio_channels == 2)
|
||||
{
|
||||
mix<true>(buf);
|
||||
}
|
||||
else if (audio_channels == 8)
|
||||
else if (cfg.audio_channels == 8)
|
||||
{
|
||||
mix<false>(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Unsupported number of audio channels: %u", audio_channels);
|
||||
fmt::throw_exception("Unsupported number of audio channels: %u", cfg.audio_channels);
|
||||
}
|
||||
|
||||
// Enqueue
|
||||
@ -604,7 +625,7 @@ void cell_audio_thread::mix(float *out_buffer, s32 offset)
|
||||
{
|
||||
if (port.state != audio_port_state::started) continue;
|
||||
|
||||
if (buffering_enabled)
|
||||
if (cfg.buffering_enabled)
|
||||
{
|
||||
port.apply_tag_backups(offset);
|
||||
}
|
||||
@ -1094,7 +1115,7 @@ error_code cellAudioGetPortTimestamp(u32 portNum, u64 tag, vm::ptr<u64> stamp)
|
||||
}
|
||||
|
||||
u64 delta_tag = port.global_counter - tag;
|
||||
u64 delta_tag_stamp = delta_tag * g_audio->audio_block_period;
|
||||
u64 delta_tag_stamp = delta_tag * g_audio->cfg.audio_block_period;
|
||||
*stamp = port.timestamp - delta_tag_stamp;
|
||||
|
||||
return CELL_OK;
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "Utilities/Thread.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/Audio/AudioThread.h"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
#include "Emu/Audio/AudioDumper.h"
|
||||
|
||||
// Error codes
|
||||
@ -170,15 +170,34 @@ struct audio_port
|
||||
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 u32 audio_channels = AudioBackend::get_channels();
|
||||
const u32 audio_sampling_rate = AudioBackend::get_sampling_rate();
|
||||
const u64 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 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 = audio_block_period + (audio_block_period - minimum_block_period); // 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 num_allocated_buffers = desired_full_buffers + EXTRA_AUDIO_BUFFERS; // number of ringbuffer buffers
|
||||
};
|
||||
|
||||
class audio_ringbuffer
|
||||
{
|
||||
private:
|
||||
const std::shared_ptr<AudioThread> backend;
|
||||
const std::shared_ptr<AudioBackend> backend;
|
||||
|
||||
const cell_audio_config& cfg;
|
||||
|
||||
const u32 num_allocated_buffers;
|
||||
const u32 buf_sz;
|
||||
const u32 audio_sampling_rate;
|
||||
const u32 channels;
|
||||
|
||||
std::unique_ptr<AudioDumper> m_dump;
|
||||
|
||||
@ -189,16 +208,23 @@ private:
|
||||
bool playing = false;
|
||||
bool emu_paused = false;
|
||||
|
||||
u32 backend_capabilities;
|
||||
|
||||
u64 update_timestamp = 0;
|
||||
u64 play_timestamp = 0;
|
||||
|
||||
u64 last_remainder = 0;
|
||||
u64 enqueued_samples = 0;
|
||||
|
||||
u32 next_buf = 0;
|
||||
u32 cur_pos = 0;
|
||||
|
||||
bool backend_is_playing() const
|
||||
{
|
||||
return (backend_capabilities & AudioBackend::IS_PLAYING) ? backend->IsPlaying() : playing;
|
||||
}
|
||||
|
||||
public:
|
||||
audio_ringbuffer(u32 num_buffers, u32 audio_sampling_rate, u32 channels);
|
||||
audio_ringbuffer(cell_audio_config &cfg);
|
||||
~audio_ringbuffer();
|
||||
|
||||
void play();
|
||||
@ -209,16 +235,11 @@ public:
|
||||
|
||||
float* get_buffer(u32 num) const
|
||||
{
|
||||
AUDIT(num < num_allocated_buffers);
|
||||
AUDIT(num < cfg.num_allocated_buffers);
|
||||
AUDIT(buffer[num].get() != nullptr);
|
||||
return buffer[num].get();
|
||||
}
|
||||
|
||||
u32 get_buf_sz() const
|
||||
{
|
||||
return buf_sz;
|
||||
}
|
||||
|
||||
u64 get_timestamp() const
|
||||
{
|
||||
return get_system_time() - Emu.GetPauseTime();
|
||||
@ -226,11 +247,12 @@ public:
|
||||
|
||||
float* get_current_buffer() const
|
||||
{
|
||||
return get_buffer(next_buf);
|
||||
return get_buffer(cur_pos);
|
||||
}
|
||||
|
||||
u64 get_enqueued_samples() const
|
||||
{
|
||||
AUDIT(cfg.buffering_enabled);
|
||||
return enqueued_samples;
|
||||
}
|
||||
|
||||
@ -238,6 +260,11 @@ public:
|
||||
{
|
||||
return playing;
|
||||
}
|
||||
|
||||
u32 capabilities() const
|
||||
{
|
||||
return backend_capabilities;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -260,19 +287,7 @@ class cell_audio_thread
|
||||
}
|
||||
|
||||
public:
|
||||
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 u32 audio_channels = AudioThread::get_channels();
|
||||
const u32 audio_sampling_rate = AudioThread::get_sampling_rate();
|
||||
const u64 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 bool buffering_enabled = g_cfg.audio.enable_buffering && (desired_buffer_duration >= audio_block_period);
|
||||
|
||||
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 = audio_block_period + (audio_block_period - minimum_block_period); // 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 num_allocated_buffers = desired_full_buffers + EXTRA_AUDIO_BUFFERS; // number of ringbuffer buffers
|
||||
cell_audio_config cfg;
|
||||
|
||||
std::vector<u64> keys;
|
||||
std::array<audio_port, AUDIO_PORT_COUNT> ports;
|
||||
|
@ -633,7 +633,7 @@ s32 cellSurMixerGetTimestamp(u64 tag, vm::ptr<u64> stamp)
|
||||
|
||||
const auto g_audio = fxm::get<cell_audio>();
|
||||
|
||||
*stamp = g_audio->m_start_time + tag * AUDIO_BUFFER_SAMPLES * 1'000'000 / g_audio->audio_sampling_rate;
|
||||
*stamp = g_audio->m_start_time + tag * AUDIO_BUFFER_SAMPLES * 1'000'000 / g_audio->cfg.audio_sampling_rate;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ struct EmuCallbacks
|
||||
std::function<std::shared_ptr<class pad_thread>()> get_pad_handler;
|
||||
std::function<std::unique_ptr<class GSFrameBase>()> get_gs_frame;
|
||||
std::function<std::shared_ptr<class GSRender>()> get_gs_render;
|
||||
std::function<std::shared_ptr<class AudioThread>()> get_audio;
|
||||
std::function<std::shared_ptr<class AudioBackend>()> get_audio;
|
||||
std::function<std::shared_ptr<class MsgDialogBase>()> get_msg_dialog;
|
||||
std::function<std::shared_ptr<class OskDialogBase>()> get_osk_dialog;
|
||||
std::function<std::unique_ptr<class SaveDialogBase>()> get_save_dialog;
|
||||
|
@ -72,12 +72,12 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\rpcs3\Emu\Audio\XAudio2\XAudio2Thread.h" />
|
||||
<ClInclude Include="Emu\Audio\XAudio2\XAudio2Backend.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\rpcs3\Emu\Audio\XAudio2\XAudio2Thread.cpp" />
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio27Thread.cpp" />
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio28Thread.cpp" />
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio2Backend.cpp" />
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio27Backend.cpp" />
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio28Backend.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
@ -7,18 +7,18 @@
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\rpcs3\Emu\Audio\XAudio2\XAudio2Thread.cpp">
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio2Backend.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio28Thread.cpp">
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio27Backend.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio27Thread.cpp">
|
||||
<ClCompile Include="Emu\Audio\XAudio2\XAudio28Backend.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\rpcs3\Emu\Audio\XAudio2\XAudio2Thread.h">
|
||||
<ClInclude Include="Emu\Audio\XAudio2\XAudio2Backend.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
|
@ -406,8 +406,8 @@
|
||||
<ClInclude Include="Emu\CPU\CPUTranslator.h" />
|
||||
<ClInclude Include="Emu\IPC.h" />
|
||||
<ClInclude Include="Emu\Audio\AudioDumper.h" />
|
||||
<ClInclude Include="Emu\Audio\AudioThread.h" />
|
||||
<ClInclude Include="Emu\Audio\Null\NullAudioThread.h" />
|
||||
<ClInclude Include="Emu\Audio\AudioBackend.h" />
|
||||
<ClInclude Include="Emu\Audio\Null\NullAudioBackend.h" />
|
||||
<ClInclude Include="Emu\Cell\Common.h" />
|
||||
<ClInclude Include="Emu\Cell\ErrorCodes.h" />
|
||||
<ClInclude Include="Emu\Cell\lv2\sys_cond.h" />
|
||||
|
@ -925,9 +925,6 @@
|
||||
<ClInclude Include="..\Utilities\Thread.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Audio\Null\NullAudioThread.h">
|
||||
<Filter>Emu\Audio\Null</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Utilities\File.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
@ -1456,8 +1453,11 @@
|
||||
<ClInclude Include="Emu\RSX\Common\texture_cache_predictor.h">
|
||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Audio\AudioThread.h">
|
||||
<ClInclude Include="Emu\Audio\AudioBackend.h">
|
||||
<Filter>Emu\Audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Audio\Null\NullAudioBackend.h">
|
||||
<Filter>Emu\Audio\Null</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -36,7 +36,7 @@
|
||||
|
||||
#include "Emu/RSX/Null/NullGSRender.h"
|
||||
#include "Emu/RSX/GL/GLGSRender.h"
|
||||
#include "Emu/Audio/Null/NullAudioThread.h"
|
||||
#include "Emu/Audio/Null/NullAudioBackend.h"
|
||||
//#include "Emu/Audio/AL/OpenALThread.h"
|
||||
#ifdef _MSC_VER
|
||||
#include "Emu/RSX/D3D12/D3D12GSRender.h"
|
||||
@ -45,7 +45,7 @@
|
||||
#include "Emu/RSX/VK/VKGSRender.h"
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
#include "Emu/Audio/XAudio2/XAudio2Thread.h"
|
||||
#include "Emu/Audio/XAudio2/XAudio2Backend.h"
|
||||
#endif
|
||||
#ifdef HAVE_ALSA
|
||||
#include "Emu/Audio/ALSA/ALSAThread.h"
|
||||
@ -254,13 +254,13 @@ void rpcs3_app::InitializeCallbacks()
|
||||
}
|
||||
};
|
||||
|
||||
callbacks.get_audio = []() -> std::shared_ptr<AudioThread>
|
||||
callbacks.get_audio = []() -> std::shared_ptr<AudioBackend>
|
||||
{
|
||||
switch (audio_renderer type = g_cfg.audio.renderer)
|
||||
{
|
||||
case audio_renderer::null: return std::make_shared<NullAudioThread>();
|
||||
case audio_renderer::null: return std::make_shared<NullAudioBackend>();
|
||||
#ifdef _WIN32
|
||||
case audio_renderer::xaudio: return std::make_shared<XAudio2Thread>();
|
||||
case audio_renderer::xaudio: return std::make_shared<XAudio2Backend>();
|
||||
#endif
|
||||
#ifdef HAVE_ALSA
|
||||
case audio_renderer::alsa: return std::make_shared<ALSAThread>();
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "Emu/Io/KeyboardHandler.h"
|
||||
#include "Emu/Io/PadHandler.h"
|
||||
#include "Emu/Io/MouseHandler.h"
|
||||
#include "Emu/Audio/AudioThread.h"
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
#include "rpcs3qt/msg_dialog_frame.h"
|
||||
#include "rpcs3qt/osk_dialog_frame.h"
|
||||
|
Loading…
Reference in New Issue
Block a user