mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-17 11:43:19 +00:00
Implement basic cellAudio buffering
This commit is contained in:
parent
56962aa707
commit
2addbe6be2
@ -1,10 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "AudioDumper.h"
|
||||
#include "AudioThread.h"
|
||||
|
||||
AudioThread::~AudioThread()
|
||||
{
|
||||
}
|
||||
|
||||
AudioDumper::AudioDumper(u16 ch)
|
||||
: m_header(ch)
|
||||
|
@ -1,13 +1,51 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
enum : u32
|
||||
{
|
||||
DEFAULT_AUDIO_SAMPLING_RATE = 48000,
|
||||
MAX_AUDIO_BUFFERS = 64,
|
||||
AUDIO_BUFFER_SAMPLES = 256
|
||||
};
|
||||
|
||||
class AudioThread
|
||||
{
|
||||
public:
|
||||
virtual ~AudioThread();
|
||||
virtual ~AudioThread() = default;
|
||||
|
||||
// Callbacks
|
||||
virtual void Open() = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
virtual void Play() = 0;
|
||||
virtual void Open(const void* src, int size) = 0;
|
||||
virtual void Close() = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void AddData(const void* src, int size) = 0;
|
||||
virtual void Pause() = 0;
|
||||
virtual bool IsPlaying() = 0;
|
||||
|
||||
virtual bool AddData(const void* src, int size) = 0;
|
||||
virtual void Flush() = 0;
|
||||
|
||||
// Helper methods
|
||||
static u32 get_sampling_rate()
|
||||
{
|
||||
const u32 sampling_period_multiplier_u32 = g_cfg.audio.sampling_period_multiplier;
|
||||
|
||||
if (sampling_period_multiplier_u32 == 100)
|
||||
return DEFAULT_AUDIO_SAMPLING_RATE;
|
||||
|
||||
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>(DEFAULT_AUDIO_SAMPLING_RATE * sampling_rate_multiplier);
|
||||
}
|
||||
|
||||
static u32 get_sample_size()
|
||||
{
|
||||
return g_cfg.audio.convert_to_u16 ? sizeof(u16) : sizeof(float);
|
||||
}
|
||||
|
||||
static u32 get_channels()
|
||||
{
|
||||
return g_cfg.audio.downmix_to_2ch ? 2 : 8;
|
||||
}
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Audio/AudioThread.h"
|
||||
|
||||
@ -8,11 +8,13 @@ public:
|
||||
NullAudioThread() {}
|
||||
virtual ~NullAudioThread() {}
|
||||
|
||||
virtual void Init() {}
|
||||
virtual void Quit() {}
|
||||
virtual void Play() {}
|
||||
virtual void Open(const void* src, int size) {}
|
||||
virtual void Close() {}
|
||||
virtual void Stop() {}
|
||||
virtual void AddData(const void* src, int size) {}
|
||||
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() {};
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Utilities/Log.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
@ -97,18 +97,27 @@ void XAudio2Thread::xa27_stop()
|
||||
}
|
||||
}
|
||||
|
||||
bool XAudio2Thread::xa27_is_playing()
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
s_tls_source_voice->GetState(&state);
|
||||
|
||||
return state.BuffersQueued > 0 || state.pCurrentBufferContext != nullptr;
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa27_open()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
WORD sample_size = g_cfg.audio.convert_to_u16 ? sizeof(u16) : sizeof(float);
|
||||
WORD channels = g_cfg.audio.downmix_to_2ch ? 2 : 8;
|
||||
const u32 sample_size = get_sample_size();
|
||||
const u32 channels = get_channels();
|
||||
const u32 sampling_rate = get_sampling_rate();
|
||||
|
||||
WAVEFORMATEX waveformatex;
|
||||
waveformatex.wFormatTag = g_cfg.audio.convert_to_u16 ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
||||
waveformatex.nChannels = channels;
|
||||
waveformatex.nSamplesPerSec = 48000;
|
||||
waveformatex.nAvgBytesPerSec = 48000 * (DWORD)channels * (DWORD)sample_size;
|
||||
waveformatex.nSamplesPerSec = sampling_rate;
|
||||
waveformatex.nAvgBytesPerSec = static_cast<DWORD>(sampling_rate * channels * sample_size);
|
||||
waveformatex.nBlockAlign = channels * sample_size;
|
||||
waveformatex.wBitsPerSample = sample_size * 8;
|
||||
waveformatex.cbSize = 0;
|
||||
@ -121,25 +130,24 @@ void XAudio2Thread::xa27_open()
|
||||
return;
|
||||
}
|
||||
|
||||
s_tls_source_voice->SetVolume(g_cfg.audio.downmix_to_2ch ? 1.0f : 4.0f);
|
||||
s_tls_source_voice->SetVolume(channels == 2 ? 1.0f : 4.0f);
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa27_add(const void* src, int size)
|
||||
bool XAudio2Thread::xa27_add(const void* src, int size)
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
s_tls_source_voice->GetState(&state);
|
||||
|
||||
// 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 > 32)
|
||||
if (state.BuffersQueued >= MAX_AUDIO_BUFFERS)
|
||||
{
|
||||
LOG_WARNING(GENERAL, "XAudio2Thread : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed);
|
||||
|
||||
return xa27_flush();
|
||||
return false;
|
||||
}
|
||||
|
||||
XAUDIO2_BUFFER buffer;
|
||||
|
||||
buffer.AudioBytes = size;
|
||||
buffer.AudioBytes = size * get_sample_size();
|
||||
buffer.Flags = 0;
|
||||
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
|
||||
buffer.LoopCount = 0;
|
||||
@ -147,7 +155,7 @@ void XAudio2Thread::xa27_add(const void* src, int size)
|
||||
buffer.pAudioData = (const BYTE*)src;
|
||||
buffer.pContext = 0;
|
||||
buffer.PlayBegin = 0;
|
||||
buffer.PlayLength = 256;
|
||||
buffer.PlayLength = AUDIO_BUFFER_SAMPLES;
|
||||
|
||||
HRESULT hr = s_tls_source_voice->SubmitSourceBuffer(&buffer);
|
||||
if (FAILED(hr))
|
||||
@ -155,6 +163,8 @@ void XAudio2Thread::xa27_add(const void* src, int size)
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : AddData() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Utilities/Log.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
@ -71,6 +71,8 @@ void XAudio2Thread::xa28_destroy()
|
||||
|
||||
void XAudio2Thread::xa28_play()
|
||||
{
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
|
||||
HRESULT hr = s_tls_source_voice->Start();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
@ -81,6 +83,8 @@ void XAudio2Thread::xa28_play()
|
||||
|
||||
void XAudio2Thread::xa28_flush()
|
||||
{
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
|
||||
HRESULT hr = s_tls_source_voice->FlushSourceBuffers();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
@ -91,6 +95,8 @@ void XAudio2Thread::xa28_flush()
|
||||
|
||||
void XAudio2Thread::xa28_stop()
|
||||
{
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
|
||||
HRESULT hr = s_tls_source_voice->Stop();
|
||||
if (FAILED(hr))
|
||||
{
|
||||
@ -99,18 +105,29 @@ void XAudio2Thread::xa28_stop()
|
||||
}
|
||||
}
|
||||
|
||||
bool XAudio2Thread::xa28_is_playing()
|
||||
{
|
||||
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 XAudio2Thread::xa28_open()
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
WORD sample_size = g_cfg.audio.convert_to_u16 ? sizeof(u16) : sizeof(float);
|
||||
WORD channels = g_cfg.audio.downmix_to_2ch ? 2 : 8;
|
||||
const u32 sample_size = get_sample_size();
|
||||
const u32 channels = get_channels();
|
||||
const u32 sampling_rate = get_sampling_rate();
|
||||
|
||||
WAVEFORMATEX waveformatex;
|
||||
waveformatex.wFormatTag = g_cfg.audio.convert_to_u16 ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT;
|
||||
waveformatex.nChannels = channels;
|
||||
waveformatex.nSamplesPerSec = 48000;
|
||||
waveformatex.nAvgBytesPerSec = 48000 * (DWORD)channels * (DWORD)sample_size;
|
||||
waveformatex.nSamplesPerSec = sampling_rate;
|
||||
waveformatex.nAvgBytesPerSec = static_cast<DWORD>(sampling_rate * channels * sample_size);
|
||||
waveformatex.nBlockAlign = channels * sample_size;
|
||||
waveformatex.wBitsPerSample = sample_size * 8;
|
||||
waveformatex.cbSize = 0;
|
||||
@ -123,24 +140,26 @@ void XAudio2Thread::xa28_open()
|
||||
return;
|
||||
}
|
||||
|
||||
s_tls_source_voice->SetVolume(g_cfg.audio.downmix_to_2ch ? 1.0 : 4.0);
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
s_tls_source_voice->SetVolume(channels == 2 ? 1.0 : 4.0);
|
||||
}
|
||||
|
||||
void XAudio2Thread::xa28_add(const void* src, int size)
|
||||
bool XAudio2Thread::xa28_add(const void* src, int size)
|
||||
{
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
s_tls_source_voice->GetState(&state);
|
||||
AUDIT(s_tls_source_voice != nullptr);
|
||||
|
||||
if (state.BuffersQueued > 32)
|
||||
XAUDIO2_VOICE_STATE state;
|
||||
s_tls_source_voice->GetState(&state, XAUDIO2_VOICE_NOSAMPLESPLAYED);
|
||||
|
||||
if (state.BuffersQueued >= MAX_AUDIO_BUFFERS)
|
||||
{
|
||||
LOG_WARNING(GENERAL, "XAudio2Thread : too many buffers enqueued (%d, pos=%u)", state.BuffersQueued, state.SamplesPlayed);
|
||||
|
||||
return xa28_flush();
|
||||
return false;
|
||||
}
|
||||
|
||||
XAUDIO2_BUFFER buffer;
|
||||
|
||||
buffer.AudioBytes = size;
|
||||
buffer.AudioBytes = size * get_sample_size();
|
||||
buffer.Flags = 0;
|
||||
buffer.LoopBegin = XAUDIO2_NO_LOOP_REGION;
|
||||
buffer.LoopCount = 0;
|
||||
@ -148,7 +167,7 @@ void XAudio2Thread::xa28_add(const void* src, int size)
|
||||
buffer.pAudioData = (const BYTE*)src;
|
||||
buffer.pContext = 0;
|
||||
buffer.PlayBegin = 0;
|
||||
buffer.PlayLength = 256;
|
||||
buffer.PlayLength = AUDIO_BUFFER_SAMPLES;
|
||||
|
||||
HRESULT hr = s_tls_source_voice->SubmitSourceBuffer(&buffer);
|
||||
if (FAILED(hr))
|
||||
@ -156,6 +175,8 @@ void XAudio2Thread::xa28_add(const void* src, int size)
|
||||
LOG_ERROR(GENERAL, "XAudio2Thread : AddData() failed(0x%08x)", (u32)hr);
|
||||
Emu.Pause();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
#ifdef _WIN32
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Utilities/Log.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
@ -18,12 +18,13 @@ XAudio2Thread::XAudio2Thread()
|
||||
// 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.add = &xa28_add;
|
||||
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.9 initialized");
|
||||
return;
|
||||
@ -33,12 +34,13 @@ XAudio2Thread::XAudio2Thread()
|
||||
{
|
||||
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.add = &xa27_add;
|
||||
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;
|
||||
|
||||
LOG_SUCCESS(GENERAL, "XAudio 2.7 initialized");
|
||||
return;
|
||||
@ -48,12 +50,13 @@ XAudio2Thread::XAudio2Thread()
|
||||
{
|
||||
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.add = &xa28_add;
|
||||
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;
|
||||
@ -78,21 +81,29 @@ void XAudio2Thread::Close()
|
||||
m_funcs.flush();
|
||||
}
|
||||
|
||||
void XAudio2Thread::Stop()
|
||||
void XAudio2Thread::Pause()
|
||||
{
|
||||
m_funcs.stop();
|
||||
}
|
||||
|
||||
void XAudio2Thread::Open(const void* src, int size)
|
||||
void XAudio2Thread::Open()
|
||||
{
|
||||
m_funcs.open();
|
||||
m_funcs.add(src, size);
|
||||
m_funcs.play();
|
||||
}
|
||||
|
||||
void XAudio2Thread::AddData(const void* src, int size)
|
||||
bool XAudio2Thread::IsPlaying()
|
||||
{
|
||||
m_funcs.add(src, size);
|
||||
return m_funcs.is_playing();
|
||||
}
|
||||
|
||||
bool XAudio2Thread::AddData(const void* src, int size)
|
||||
{
|
||||
return m_funcs.add(src, size);
|
||||
}
|
||||
|
||||
void XAudio2Thread::Flush()
|
||||
{
|
||||
m_funcs.flush();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@ -13,7 +13,8 @@ class XAudio2Thread : public AudioThread
|
||||
void(*flush)();
|
||||
void(*stop)();
|
||||
void(*open)();
|
||||
void(*add)(const void*, int);
|
||||
bool(*is_playing)();
|
||||
bool(*add)(const void*, int);
|
||||
};
|
||||
|
||||
vtable m_funcs;
|
||||
@ -24,7 +25,8 @@ class XAudio2Thread : public AudioThread
|
||||
static void xa27_flush();
|
||||
static void xa27_stop();
|
||||
static void xa27_open();
|
||||
static void xa27_add(const void*, int);
|
||||
static bool xa27_is_playing();
|
||||
static bool xa27_add(const void*, int);
|
||||
|
||||
static void xa28_init(void*);
|
||||
static void xa28_destroy();
|
||||
@ -32,17 +34,22 @@ class XAudio2Thread : public AudioThread
|
||||
static void xa28_flush();
|
||||
static void xa28_stop();
|
||||
static void xa28_open();
|
||||
static void xa28_add(const void*, int);
|
||||
static bool xa28_is_playing();
|
||||
static bool xa28_add(const void*, int);
|
||||
|
||||
public:
|
||||
XAudio2Thread();
|
||||
virtual ~XAudio2Thread() override;
|
||||
|
||||
virtual void Play() override;
|
||||
virtual void Open(const void* src, int size) override;
|
||||
virtual void Open() override;
|
||||
virtual void Close() override;
|
||||
virtual void Stop() override;
|
||||
virtual void AddData(const void* src, int size) override;
|
||||
|
||||
virtual void Play() override;
|
||||
virtual void Pause() override;
|
||||
virtual bool IsPlaying() override;
|
||||
|
||||
virtual bool AddData(const void* src, int size) override;
|
||||
virtual void Flush() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/Thread.h"
|
||||
#include "Emu/Memory/vm.h"
|
||||
#include "Emu/Audio/AudioThread.h"
|
||||
#include "Emu/Audio/AudioDumper.h"
|
||||
|
||||
// Error codes
|
||||
enum CellAudioError : u32
|
||||
@ -76,11 +79,26 @@ struct CellAudioPortConfig
|
||||
|
||||
enum : u32
|
||||
{
|
||||
BUFFER_NUM = 32,
|
||||
BUFFER_SIZE = 256,
|
||||
AUDIO_PORT_COUNT = 8,
|
||||
AUDIO_PORT_OFFSET = 256 * 1024,
|
||||
AUDIO_SAMPLES = CELL_AUDIO_BLOCK_SAMPLES,
|
||||
AUDIO_MAX_BLOCK_COUNT = 32,
|
||||
AUDIO_MAX_CHANNELS_COUNT = 8,
|
||||
|
||||
AUDIO_PORT_OFFSET = AUDIO_BUFFER_SAMPLES * AUDIO_MAX_BLOCK_COUNT * AUDIO_MAX_CHANNELS_COUNT * sizeof(f32),
|
||||
EXTRA_AUDIO_BUFFERS = 8,
|
||||
MAX_AUDIO_EVENT_QUEUES = 64,
|
||||
|
||||
AUDIO_BLOCK_SIZE_2CH = 2 * AUDIO_BUFFER_SAMPLES,
|
||||
AUDIO_BLOCK_SIZE_8CH = 8 * AUDIO_BUFFER_SAMPLES,
|
||||
|
||||
PORT_BUFFER_TAG_COUNT = 4,
|
||||
|
||||
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),
|
||||
PORT_BUFFER_TAG_FIRST_2CH = PORT_BUFFER_TAG_LAST_2CH % (PORT_BUFFER_TAG_COUNT - 1),
|
||||
|
||||
PORT_BUFFER_TAG_LAST_8CH = AUDIO_BLOCK_SIZE_8CH - 1,
|
||||
PORT_BUFFER_TAG_DELTA_8CH = PORT_BUFFER_TAG_LAST_8CH / (PORT_BUFFER_TAG_COUNT - 1),
|
||||
PORT_BUFFER_TAG_FIRST_8CH = PORT_BUFFER_TAG_LAST_8CH % (PORT_BUFFER_TAG_COUNT - 1),
|
||||
};
|
||||
|
||||
enum class audio_port_state : u32
|
||||
@ -98,12 +116,14 @@ struct audio_port
|
||||
vm::ptr<char> addr{};
|
||||
vm::ptr<u64> index{};
|
||||
|
||||
u32 channel;
|
||||
u32 block;
|
||||
u32 num_channels;
|
||||
u32 num_blocks;
|
||||
u64 attr;
|
||||
u64 tag;
|
||||
u64 counter; // copy of global counter
|
||||
u64 cur_pos;
|
||||
u64 global_counter; // copy of global counter
|
||||
u64 active_counter;
|
||||
u32 size;
|
||||
u64 timestamp; // copy of global timestamp
|
||||
|
||||
struct alignas(8) level_set_t
|
||||
{
|
||||
@ -113,25 +133,158 @@ struct audio_port
|
||||
|
||||
float level;
|
||||
atomic_t<level_set_t> level_set;
|
||||
|
||||
u32 block_size() const
|
||||
{
|
||||
return num_channels * AUDIO_BUFFER_SAMPLES;
|
||||
}
|
||||
|
||||
u32 buf_size() const
|
||||
{
|
||||
return block_size() * sizeof(float);
|
||||
}
|
||||
|
||||
u32 position(s32 offset = 0) const
|
||||
{
|
||||
s32 ofs = (offset % num_blocks) + num_blocks;
|
||||
return (cur_pos + ofs) % num_blocks;
|
||||
}
|
||||
|
||||
u32 buf_addr(s32 offset = 0) const
|
||||
{
|
||||
return addr.addr() + position(offset) * buf_size();
|
||||
}
|
||||
|
||||
to_be_t<float>* get_vm_ptr(s32 offset = 0) const
|
||||
{
|
||||
return vm::_ptr<f32>(buf_addr(offset));
|
||||
}
|
||||
|
||||
|
||||
// Tags
|
||||
u32 prev_touched_tag_nr;
|
||||
f32 tag_backup[AUDIO_MAX_BLOCK_COUNT][PORT_BUFFER_TAG_COUNT] = { 0 };
|
||||
|
||||
constexpr static bool is_tag(float val);
|
||||
void tag(s32 offset = 0);
|
||||
void apply_tag_backups(s32 offset = 0);
|
||||
};
|
||||
|
||||
class audio_thread
|
||||
class audio_ringbuffer
|
||||
{
|
||||
private:
|
||||
const std::shared_ptr<AudioThread> backend;
|
||||
|
||||
const u32 num_allocated_buffers;
|
||||
const u32 buf_sz;
|
||||
const u32 audio_sampling_rate;
|
||||
const u32 channels;
|
||||
|
||||
std::unique_ptr<AudioDumper> m_dump;
|
||||
|
||||
std::unique_ptr<float[]> buffer[MAX_AUDIO_BUFFERS];
|
||||
const float silence_buffer[8 * AUDIO_BUFFER_SAMPLES] = { 0 };
|
||||
|
||||
bool backend_open = false;
|
||||
bool playing = false;
|
||||
bool emu_paused = false;
|
||||
|
||||
u64 update_timestamp = 0;
|
||||
u64 play_timestamp = 0;
|
||||
|
||||
u64 last_remainder = 0;
|
||||
u64 enqueued_samples = 0;
|
||||
|
||||
u32 next_buf = 0;
|
||||
|
||||
public:
|
||||
audio_ringbuffer(u32 num_buffers, u32 audio_sampling_rate, u32 channels);
|
||||
~audio_ringbuffer();
|
||||
|
||||
void play();
|
||||
void enqueue(const float* in_buffer = nullptr);
|
||||
void flush();
|
||||
u64 update();
|
||||
void enqueue_silence(u32 buf_count = 1);
|
||||
|
||||
float* get_buffer(u32 num) const
|
||||
{
|
||||
AUDIT(num < 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();
|
||||
}
|
||||
|
||||
float* get_current_buffer() const
|
||||
{
|
||||
return get_buffer(next_buf);
|
||||
}
|
||||
|
||||
u64 get_enqueued_samples() const
|
||||
{
|
||||
return enqueued_samples;
|
||||
}
|
||||
|
||||
bool is_playing() const
|
||||
{
|
||||
return playing;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class cell_audio_thread
|
||||
{
|
||||
vm::ptr<char> m_buffer;
|
||||
vm::ptr<u64> m_indexes;
|
||||
|
||||
u64 m_counter{};
|
||||
std::unique_ptr<audio_ringbuffer> ringbuffer;
|
||||
|
||||
void reset_ports(s32 offset = 0);
|
||||
void advance(u64 timestamp, bool reset = true);
|
||||
std::tuple<u32, u32, u32, u32> count_port_buffer_tags();
|
||||
template<bool downmix_to_2ch> void mix(float *out_buffer, s32 offset = 0);
|
||||
void finish_port_volume_stepping();
|
||||
|
||||
constexpr static u64 get_thread_wait_delay(u64 time_left)
|
||||
{
|
||||
return (time_left > 1000) ? time_left - 750 : 100;
|
||||
}
|
||||
|
||||
public:
|
||||
const u64 start_time = get_system_time();
|
||||
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
|
||||
|
||||
std::array<audio_port, AUDIO_PORT_COUNT> ports;
|
||||
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
|
||||
|
||||
std::vector<u64> keys;
|
||||
std::array<audio_port, AUDIO_PORT_COUNT> ports;
|
||||
|
||||
u64 m_last_period_end = 0;
|
||||
u64 m_counter = 0;
|
||||
u64 m_start_time = 0;
|
||||
u64 m_dynamic_period = 0;
|
||||
|
||||
void operator()();
|
||||
|
||||
audio_thread(vm::ptr<char> buf, vm::ptr<u64> ind)
|
||||
cell_audio_thread(vm::ptr<char> buf, vm::ptr<u64> ind)
|
||||
: m_buffer(buf)
|
||||
, m_indexes(ind)
|
||||
{
|
||||
@ -157,4 +310,4 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using audio_config = named_thread<audio_thread>;
|
||||
using cell_audio = named_thread<cell_audio_thread>;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Cell/PPUModule.h"
|
||||
@ -328,13 +328,13 @@ struct surmixer_thread : ppu_thread
|
||||
|
||||
void non_task()
|
||||
{
|
||||
const auto g_audio = fxm::get<audio_config>();
|
||||
const auto g_audio = fxm::get<cell_audio>();
|
||||
|
||||
audio_port& port = g_audio->ports[g_surmx.audio_port];
|
||||
|
||||
while (port.state != audio_port_state::closed)
|
||||
{
|
||||
if (g_surmx.mixcount > (port.tag + 0)) // adding positive value (1-15): preemptive buffer filling (hack)
|
||||
if (g_surmx.mixcount > (port.active_counter + 0)) // adding positive value (1-15): preemptive buffer filling (hack)
|
||||
{
|
||||
thread_ctrl::wait_for(1000); // hack
|
||||
continue;
|
||||
@ -432,7 +432,7 @@ struct surmixer_thread : ppu_thread
|
||||
|
||||
//u64 stamp2 = get_system_time();
|
||||
|
||||
auto buf = vm::_ptr<f32>(port.addr.addr() + (g_surmx.mixcount % port.block) * port.channel * AUDIO_SAMPLES * sizeof(float));
|
||||
auto buf = vm::_ptr<f32>(port.addr.addr() + (g_surmx.mixcount % port.num_blocks) * port.num_channels * AUDIO_BUFFER_SAMPLES * sizeof(float));
|
||||
|
||||
for (auto& mixdata : g_surmx.mixdata)
|
||||
{
|
||||
@ -456,7 +456,7 @@ s32 cellSurMixerCreate(vm::cptr<CellSurMixerConfig> config)
|
||||
{
|
||||
libmixer.warning("cellSurMixerCreate(config=*0x%x)", config);
|
||||
|
||||
const auto g_audio = fxm::get<audio_config>();
|
||||
const auto g_audio = fxm::get<cell_audio>();
|
||||
|
||||
const auto port = g_audio->open_port();
|
||||
|
||||
@ -472,11 +472,10 @@ s32 cellSurMixerCreate(vm::cptr<CellSurMixerConfig> config)
|
||||
g_surmx.ch_strips_6 = config->chStrips6;
|
||||
g_surmx.ch_strips_8 = config->chStrips8;
|
||||
|
||||
port->channel = 8;
|
||||
port->block = 16;
|
||||
port->num_channels = 8;
|
||||
port->num_blocks = 16;
|
||||
port->attr = 0;
|
||||
port->size = port->channel * port->block * AUDIO_SAMPLES * sizeof(float);
|
||||
port->tag = 0;
|
||||
port->size = port->num_channels * port->num_blocks * AUDIO_BUFFER_SAMPLES * sizeof(float);
|
||||
port->level = 1.0f;
|
||||
port->level_set.store({ 1.0f, 0.0f });
|
||||
|
||||
@ -541,7 +540,7 @@ s32 cellSurMixerStart()
|
||||
{
|
||||
libmixer.warning("cellSurMixerStart()");
|
||||
|
||||
const auto g_audio = fxm::get<audio_config>();
|
||||
const auto g_audio = fxm::get<cell_audio>();
|
||||
|
||||
if (g_surmx.audio_port >= AUDIO_PORT_COUNT)
|
||||
{
|
||||
@ -563,7 +562,7 @@ s32 cellSurMixerFinalize()
|
||||
{
|
||||
libmixer.warning("cellSurMixerFinalize()");
|
||||
|
||||
const auto g_audio = fxm::get<audio_config>();
|
||||
const auto g_audio = fxm::get<cell_audio>();
|
||||
|
||||
if (g_surmx.audio_port >= AUDIO_PORT_COUNT)
|
||||
{
|
||||
@ -608,7 +607,7 @@ s32 cellSurMixerPause(u32 type)
|
||||
{
|
||||
libmixer.warning("cellSurMixerPause(type=%d)", type);
|
||||
|
||||
const auto g_audio = fxm::get<audio_config>();
|
||||
const auto g_audio = fxm::get<cell_audio>();
|
||||
|
||||
if (g_surmx.audio_port >= AUDIO_PORT_COUNT)
|
||||
{
|
||||
@ -630,10 +629,12 @@ s32 cellSurMixerGetCurrentBlockTag(vm::ptr<u64> tag)
|
||||
|
||||
s32 cellSurMixerGetTimestamp(u64 tag, vm::ptr<u64> stamp)
|
||||
{
|
||||
libmixer.trace("cellSurMixerGetTimestamp(tag=0x%llx, stamp=*0x%x)", tag, stamp);
|
||||
libmixer.error("cellSurMixerGetTimestamp(tag=0x%llx, stamp=*0x%x)", tag, 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;
|
||||
|
||||
const auto g_audio = fxm::get<audio_config>();
|
||||
*stamp = g_audio->start_time + (tag) * 256000000 / 48000; // ???
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
@ -526,9 +526,11 @@ 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<2, 128> frames{this, "Buffer Count", 32};
|
||||
cfg::_int<1, 128> startt{this, "Start Threshold", 1};
|
||||
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};
|
||||
|
||||
} audio{this};
|
||||
|
||||
|
@ -928,9 +928,6 @@
|
||||
<ClInclude Include="Emu\Audio\Null\NullAudioThread.h">
|
||||
<Filter>Emu\Audio\Null</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Audio\AudioThread.h">
|
||||
<Filter>Emu\Audio</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Utilities\File.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
@ -1443,7 +1440,7 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Utilities\date_time.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Utilities\address_range.h">
|
||||
<Filter>Utilities</Filter>
|
||||
</ClInclude>
|
||||
@ -1452,12 +1449,15 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\Common\texture_cache_utils.h">
|
||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||
</ClInclude>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\RSXFIFO.h">
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
<Filter>Emu\GPU\RSX</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\RSX\Common\texture_cache_predictor.h">
|
||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Audio\AudioThread.h">
|
||||
<Filter>Emu\Audio</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -37,7 +37,7 @@
|
||||
#include "Emu/RSX/Null/NullGSRender.h"
|
||||
#include "Emu/RSX/GL/GLGSRender.h"
|
||||
#include "Emu/Audio/Null/NullAudioThread.h"
|
||||
#include "Emu/Audio/AL/OpenALThread.h"
|
||||
//#include "Emu/Audio/AL/OpenALThread.h"
|
||||
#ifdef _MSC_VER
|
||||
#include "Emu/RSX/D3D12/D3D12GSRender.h"
|
||||
#endif
|
||||
@ -269,7 +269,7 @@ void rpcs3_app::InitializeCallbacks()
|
||||
case audio_renderer::pulse: return std::make_shared<PulseThread>();
|
||||
#endif
|
||||
|
||||
case audio_renderer::openal: return std::make_shared<OpenALThread>();
|
||||
//case audio_renderer::openal: return std::make_shared<OpenALThread>();
|
||||
default: fmt::throw_exception("Invalid audio renderer: %s" HERE, type);
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user