SPU: Add time stretched audio output

This commit is contained in:
Connor McLaughlin
2022-07-28 00:42:41 +10:00
parent f54e32ff01
commit 68b5dd869c
27 changed files with 1165 additions and 808 deletions

View File

@ -13,6 +13,7 @@
struct WindowInfo;
enum class AudioBackend : u8;
enum class AudioStretchMode : u8;
class AudioStream;
class CDImage;
@ -77,7 +78,8 @@ std::optional<std::time_t> GetResourceFileTimestamp(const char* filename);
TinyString TranslateString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1);
std::string TranslateStdString(const char* context, const char* str, const char* disambiguation = nullptr, int n = -1);
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend);
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend, u32 sample_rate, u32 channels, u32 buffer_ms,
u32 latency_ms, AudioStretchMode stretch);
/// Returns the scale of OSD elements.
float GetOSDScale();

View File

@ -275,10 +275,15 @@ void Settings::Load(SettingsInterface& si)
audio_backend =
ParseAudioBackend(si.GetStringValue("Audio", "Backend", GetAudioBackendName(DEFAULT_AUDIO_BACKEND)).c_str())
.value_or(DEFAULT_AUDIO_BACKEND);
audio_output_volume = si.GetIntValue("Audio", "OutputVolume", 100);
audio_fast_forward_volume = si.GetIntValue("Audio", "FastForwardVolume", 100);
audio_buffer_size = si.GetIntValue("Audio", "BufferSize", DEFAULT_AUDIO_BUFFER_SIZE);
audio_resampling = si.GetBoolValue("Audio", "Resampling", true);
audio_stretch_mode =
AudioStream::ParseStretchMode(
si.GetStringValue("Audio", "StretchMode", AudioStream::GetStretchModeName(DEFAULT_AUDIO_STRETCH_MODE)).c_str())
.value_or(DEFAULT_AUDIO_STRETCH_MODE);
audio_output_latency_ms = si.GetUIntValue("Audio", "OutputLatencyMS", DEFAULT_AUDIO_OUTPUT_LATENCY_MS);
audio_buffer_ms = si.GetUIntValue("Audio", "BufferMS", DEFAULT_AUDIO_BUFFER_MS);
audio_output_volume = si.GetUIntValue("Audio", "OutputVolume", 100);
audio_fast_forward_volume = si.GetUIntValue("Audio", "FastForwardVolume", 100);
audio_output_muted = si.GetBoolValue("Audio", "OutputMuted", false);
audio_sync_enabled = si.GetBoolValue("Audio", "Sync", true);
audio_dump_on_boot = si.GetBoolValue("Audio", "DumpOnBoot", false);
@ -472,10 +477,11 @@ void Settings::Save(SettingsInterface& si) const
si.SetIntValue("CDROM", "SeekSpeedup", cdrom_seek_speedup);
si.SetStringValue("Audio", "Backend", GetAudioBackendName(audio_backend));
si.SetIntValue("Audio", "OutputVolume", audio_output_volume);
si.SetIntValue("Audio", "FastForwardVolume", audio_fast_forward_volume);
si.SetIntValue("Audio", "BufferSize", audio_buffer_size);
si.SetBoolValue("Audio", "Resampling", audio_resampling);
si.SetStringValue("Audio", "StretchMode", AudioStream::GetStretchModeName(audio_stretch_mode));
si.SetUIntValue("Audio", "BufferMS", audio_buffer_ms);
si.SetUIntValue("Audio", "OutputLatencyMS", audio_output_latency_ms);
si.SetUIntValue("Audio", "OutputVolume", audio_output_volume);
si.SetUIntValue("Audio", "FastForwardVolume", audio_fast_forward_volume);
si.SetBoolValue("Audio", "OutputMuted", audio_output_muted);
si.SetBoolValue("Audio", "Sync", audio_sync_enabled);
si.SetBoolValue("Audio", "DumpOnBoot", audio_dump_on_boot);

View File

@ -3,6 +3,7 @@
#include "common/settings_interface.h"
#include "common/string.h"
#include "types.h"
#include "util/audio_stream.h"
#include <array>
#include <optional>
#include <string>
@ -142,10 +143,11 @@ struct Settings
u32 cdrom_seek_speedup = 1;
AudioBackend audio_backend = DEFAULT_AUDIO_BACKEND;
s32 audio_output_volume = 100;
s32 audio_fast_forward_volume = 100;
u32 audio_buffer_size = DEFAULT_AUDIO_BUFFER_SIZE;
bool audio_resampling = true;
AudioStretchMode audio_stretch_mode = DEFAULT_AUDIO_STRETCH_MODE;
u32 audio_output_latency_ms = DEFAULT_AUDIO_OUTPUT_LATENCY_MS;
u32 audio_buffer_ms = DEFAULT_AUDIO_BUFFER_MS;
u32 audio_output_volume = 100;
u32 audio_fast_forward_volume = 100;
bool audio_output_muted = false;
bool audio_sync_enabled = true;
bool audio_dump_on_boot = false;
@ -400,7 +402,9 @@ struct Settings
static constexpr LOGLEVEL DEFAULT_LOG_LEVEL = LOGLEVEL_INFO;
static constexpr u32 DEFAULT_AUDIO_BUFFER_SIZE = 2048;
static constexpr u32 DEFAULT_AUDIO_BUFFER_MS = 50;
static constexpr u32 DEFAULT_AUDIO_OUTPUT_LATENCY_MS = 20;
static constexpr AudioStretchMode DEFAULT_AUDIO_STRETCH_MODE = AudioStretchMode::TimeStretch;
// Enable console logging by default on Linux platforms.
#if defined(__linux__) && !defined(__ANDROID__)

View File

@ -30,8 +30,7 @@ void SPU::Initialize()
"SPU Transfer", TRANSFER_TICKS_PER_HALFWORD, TRANSFER_TICKS_PER_HALFWORD,
[](void* param, TickCount ticks, TickCount ticks_late) { static_cast<SPU*>(param)->ExecuteTransfer(ticks); }, this,
false);
m_null_audio_stream = AudioStream::CreateNullAudioStream();
m_null_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, Settings::DEFAULT_AUDIO_BUFFER_SIZE);
m_null_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms);
CreateOutputStream();
Reset();
@ -39,22 +38,23 @@ void SPU::Initialize()
void SPU::CreateOutputStream()
{
Log_InfoPrintf("Creating '%s' audio stream, sample rate = %u, channels = %u, buffer size = %u",
Settings::GetAudioBackendName(g_settings.audio_backend), SAMPLE_RATE, NUM_CHANNELS,
g_settings.audio_buffer_size);
Log_InfoPrintf(
"Creating '%s' audio stream, sample rate = %u, channels = %u, buffer = %u, latency = %u, stretching = %s",
Settings::GetAudioBackendName(g_settings.audio_backend), SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms,
g_settings.audio_output_latency_ms, AudioStream::GetStretchModeName(g_settings.audio_stretch_mode));
m_audio_stream = Host::CreateAudioStream(g_settings.audio_backend);
if (!m_audio_stream ||
!m_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_size))
m_audio_stream =
Host::CreateAudioStream(g_settings.audio_backend, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms,
g_settings.audio_output_latency_ms, g_settings.audio_stretch_mode);
if (!m_audio_stream)
{
Host::ReportErrorAsync("Error", "Failed to create or configure audio stream, falling back to null output.");
m_audio_stream.reset();
m_audio_stream = AudioStream::CreateNullAudioStream();
m_audio_stream->Reconfigure(SAMPLE_RATE, SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_size);
m_audio_stream = AudioStream::CreateNullStream(SAMPLE_RATE, NUM_CHANNELS, g_settings.audio_buffer_ms);
}
m_audio_stream->SetOutputVolume(System::GetAudioOutputVolume());
m_audio_stream->SetPaused(System::IsPaused());
}
void SPU::RecreateOutputStream()
@ -77,7 +77,7 @@ void SPU::Shutdown()
m_tick_event.reset();
m_transfer_event.reset();
m_dump_writer.reset();
m_audio_stream = nullptr;
m_audio_stream.reset();
}
void SPU::Reset()

View File

@ -923,9 +923,7 @@ void System::PauseSystem(bool paused)
return;
SetState(paused ? State::Paused : State::Running);
if (!paused)
g_spu.GetOutputStream()->EmptyBuffers();
g_spu.GetOutputStream()->PauseOutput(paused);
g_spu.GetOutputStream()->SetPaused(paused);
if (paused)
{
@ -1179,7 +1177,7 @@ bool System::BootSystem(SystemBootParameters parameters)
// Good to go.
Host::OnSystemStarted();
UpdateSoftwareCursor();
g_spu.GetOutputStream()->PauseOutput(false);
g_spu.GetOutputStream()->SetPaused(false);
// Initial state must be set before loading state.
s_state =
@ -1813,7 +1811,7 @@ bool System::DoLoadState(ByteStream* state, bool force_software_renderer, bool u
if (s_state == State::Starting)
s_state = State::Running;
g_spu.GetOutputStream()->EmptyBuffers();
g_spu.GetOutputStream()->EmptyBuffer();
ResetPerformanceCounters();
ResetThrottler();
return true;
@ -2035,14 +2033,6 @@ void System::ResetThrottler()
void System::Throttle()
{
// Reset the throttler on audio buffer overflow, so we don't end up out of phase.
if (g_spu.GetOutputStream()->DidUnderflow() && s_target_speed >= 1.0f)
{
Log_VerbosePrintf("Audio buffer underflowed, resetting throttler");
ResetThrottler();
return;
}
// Allow variance of up to 40ms either way.
#ifndef __ANDROID__
static constexpr double MAX_VARIANCE_TIME_NS = 40 * 1000000;
@ -2181,7 +2171,8 @@ void System::UpdateSpeedLimiterState()
m_display_all_frames = !m_throttler_enabled || g_settings.display_all_frames;
bool syncing_to_host = false;
if (g_settings.sync_to_host_refresh_rate && g_settings.audio_resampling && target_speed == 1.0f && IsRunning())
if (g_settings.sync_to_host_refresh_rate && (g_settings.audio_stretch_mode != AudioStretchMode::Off) &&
target_speed == 1.0f && IsValid())
{
float host_refresh_rate;
if (g_host_display->GetHostRefreshRate(&host_refresh_rate))
@ -2212,21 +2203,18 @@ void System::UpdateSpeedLimiterState()
UpdateThrottlePeriod();
ResetThrottler();
const u32 input_sample_rate = (target_speed == 0.0f || !g_settings.audio_resampling) ?
SPU::SAMPLE_RATE :
static_cast<u32>(static_cast<float>(SPU::SAMPLE_RATE) * target_speed);
Log_InfoPrintf("Audio input sample rate: %u hz", input_sample_rate);
AudioStream* stream = g_spu.GetOutputStream();
stream->SetInputSampleRate(input_sample_rate);
stream->SetWaitForBufferFill(true);
if (g_settings.audio_fast_forward_volume != g_settings.audio_output_volume)
stream->SetOutputVolume(GetAudioOutputVolume());
stream->SetSync(audio_sync_enabled);
if (audio_sync_enabled)
stream->EmptyBuffers();
// Adjust nominal rate when resampling, or syncing to host.
const bool rate_adjust =
(syncing_to_host || g_settings.audio_stretch_mode == AudioStretchMode::Resample) && target_speed > 0.0f;
stream->SetNominalRate(rate_adjust ? target_speed : 1.0f);
// stream->SetSync(audio_sync_enabled);
// if (audio_sync_enabled)
// stream->EmptyBuffer();
}
g_host_display->SetDisplayMaxFPS(max_display_fps);
@ -3034,8 +3022,7 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
UpdateOverclock();
}
if (g_settings.audio_backend != old_settings.audio_backend ||
g_settings.audio_buffer_size != old_settings.audio_buffer_size)
if (g_settings.audio_backend != old_settings.audio_backend)
{
if (g_settings.audio_backend != old_settings.audio_backend)
{
@ -3044,7 +3031,15 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
}
g_spu.RecreateOutputStream();
g_spu.GetOutputStream()->PauseOutput(IsPaused());
}
if (g_settings.audio_stretch_mode != old_settings.audio_stretch_mode)
g_spu.GetOutputStream()->SetStretchMode(g_settings.audio_stretch_mode);
if (g_settings.audio_buffer_ms != old_settings.audio_buffer_ms ||
g_settings.audio_output_latency_ms != old_settings.audio_output_latency_ms ||
g_settings.audio_stretch_mode != old_settings.audio_stretch_mode)
{
g_spu.RecreateOutputStream();
UpdateSpeedLimiterState();
}
if (g_settings.emulation_speed != old_settings.emulation_speed)
@ -3169,7 +3164,6 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
g_dma.SetHaltTicks(g_settings.dma_halt_ticks);
if (g_settings.audio_backend != old_settings.audio_backend ||
g_settings.audio_buffer_size != old_settings.audio_buffer_size ||
g_settings.video_sync_enabled != old_settings.video_sync_enabled ||
g_settings.audio_sync_enabled != old_settings.audio_sync_enabled ||
g_settings.increase_timer_resolution != old_settings.increase_timer_resolution ||
@ -3177,7 +3171,6 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
g_settings.fast_forward_speed != old_settings.fast_forward_speed ||
g_settings.display_max_fps != old_settings.display_max_fps ||
g_settings.display_all_frames != old_settings.display_all_frames ||
g_settings.audio_resampling != old_settings.audio_resampling ||
g_settings.sync_to_host_refresh_rate != old_settings.sync_to_host_refresh_rate)
{
UpdateSpeedLimiterState();