From be5b9797130cad83a7d6330a1abe21b5ce6f2a73 Mon Sep 17 00:00:00 2001 From: Connor McLaughlin Date: Fri, 29 Jul 2022 21:16:02 +1000 Subject: [PATCH] AudioStream: Make speed changes less poppy --- src/core/system.cpp | 10 ++++--- src/util/audio_stream.cpp | 59 +++++++++++++++++++++++++++++++++++++-- src/util/audio_stream.h | 1 + 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/core/system.cpp b/src/core/system.cpp index 6e97bde7f..3149460a2 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -2199,10 +2199,6 @@ void System::UpdateSpeedLimiterState() if (IsValid()) { - s_target_speed = target_speed; - UpdateThrottlePeriod(); - ResetThrottler(); - AudioStream* stream = g_spu.GetOutputStream(); if (g_settings.audio_fast_forward_volume != g_settings.audio_output_volume) stream->SetOutputVolume(GetAudioOutputVolume()); @@ -2211,10 +2207,16 @@ void System::UpdateSpeedLimiterState() 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); + if (s_target_speed < target_speed) + stream->UpdateTargetTempo(target_speed); // stream->SetSync(audio_sync_enabled); // if (audio_sync_enabled) // stream->EmptyBuffer(); + + s_target_speed = target_speed; + UpdateThrottlePeriod(); + ResetThrottler(); } g_host_display->SetDisplayMaxFPS(max_display_fps); diff --git a/src/util/audio_stream.cpp b/src/util/audio_stream.cpp index a2cff1911..6dba456c3 100644 --- a/src/util/audio_stream.cpp +++ b/src/util/audio_stream.cpp @@ -8,7 +8,12 @@ #include #include #include -Log_SetChannel(AudioStream); + +#ifdef __APPLE__ +#include // alloca +#else +#include // alloca +#endif #if defined(_M_ARM64) #include @@ -18,6 +23,8 @@ Log_SetChannel(AudioStream); #include #endif +Log_SetChannel(AudioStream); + static constexpr bool LOG_TIMESTRETCH_STATS = false; AudioStream::AudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, AudioStretchMode stretch) @@ -136,9 +143,39 @@ void AudioStream::ReadFrames(s16* bData, u32 nFrames) m_rpos.store(rpos, std::memory_order_release); } - // TODO: Bring back the crappy resampler? if (silence_frames > 0) - std::memset(bData + frames_to_read, 0, sizeof(s32) * silence_frames); + { + if (frames_to_read > 0) + { + // super basic resampler - spread the input samples evenly across the output samples. will sound like ass and have + // aliasing, but better than popping by inserting silence. + const u32 increment = + static_cast(65536.0f * (static_cast(frames_to_read / m_channels) / static_cast(nFrames))); + + s16* resample_ptr = static_cast(alloca(sizeof(s16) * frames_to_read)); + std::memcpy(resample_ptr, bData, sizeof(s16) * frames_to_read); + + s16* out_ptr = bData; + const u32 copy_stride = sizeof(SampleType) * m_channels; + u32 resample_subpos = 0; + for (u32 i = 0; i < nFrames; i++) + { + std::memcpy(out_ptr, resample_ptr, copy_stride); + out_ptr += m_channels; + + resample_subpos += increment; + resample_ptr += (resample_subpos >> 16) * m_channels; + resample_subpos %= 65536u; + } + + Log_VerbosePrintf("Audio buffer underflow, resampled %u frames to %u", frames_to_read, nFrames); + } + else + { + // no data, fall back to silence + std::memset(bData + frames_to_read, 0, sizeof(s32) * silence_frames); + } + } } void AudioStream::InternalWriteFrames(s32* bData, u32 nSamples) @@ -228,6 +265,22 @@ void AudioStream::SetNominalRate(float tempo) m_soundtouch->setRate(tempo); } +void AudioStream::UpdateTargetTempo(float tempo) +{ + if (m_stretch_mode != AudioStretchMode::TimeStretch) + return; + + // undo sqrt() + if (tempo) + tempo *= tempo; + + m_average_position = AVERAGING_WINDOW; + m_average_available = AVERAGING_WINDOW; + std::fill_n(m_average_fullness.data(), AVERAGING_WINDOW, tempo); + m_soundtouch->setTempo(tempo); + m_stretch_reset = 0; +} + void AudioStream::SetStretchMode(AudioStretchMode mode) { if (m_stretch_mode == mode) diff --git a/src/util/audio_stream.h b/src/util/audio_stream.h index b2d9802be..e9b07a826 100644 --- a/src/util/audio_stream.h +++ b/src/util/audio_stream.h @@ -67,6 +67,7 @@ public: /// Nominal rate is used for both resampling and timestretching, input samples are assumed to be this amount faster /// than the sample rate. void SetNominalRate(float tempo); + void UpdateTargetTempo(float tempo); void SetStretchMode(AudioStretchMode mode);