AudioStream: Make speed changes less poppy

This commit is contained in:
Connor McLaughlin 2022-07-29 21:16:02 +10:00
parent f6ff7e48c1
commit be5b979713
3 changed files with 63 additions and 7 deletions

View file

@ -2199,10 +2199,6 @@ void System::UpdateSpeedLimiterState()
if (IsValid()) if (IsValid())
{ {
s_target_speed = target_speed;
UpdateThrottlePeriod();
ResetThrottler();
AudioStream* stream = g_spu.GetOutputStream(); AudioStream* stream = g_spu.GetOutputStream();
if (g_settings.audio_fast_forward_volume != g_settings.audio_output_volume) if (g_settings.audio_fast_forward_volume != g_settings.audio_output_volume)
stream->SetOutputVolume(GetAudioOutputVolume()); stream->SetOutputVolume(GetAudioOutputVolume());
@ -2211,10 +2207,16 @@ void System::UpdateSpeedLimiterState()
const bool rate_adjust = const bool rate_adjust =
(syncing_to_host || g_settings.audio_stretch_mode == AudioStretchMode::Resample) && target_speed > 0.0f; (syncing_to_host || g_settings.audio_stretch_mode == AudioStretchMode::Resample) && target_speed > 0.0f;
stream->SetNominalRate(rate_adjust ? target_speed : 1.0f); stream->SetNominalRate(rate_adjust ? target_speed : 1.0f);
if (s_target_speed < target_speed)
stream->UpdateTargetTempo(target_speed);
// stream->SetSync(audio_sync_enabled); // stream->SetSync(audio_sync_enabled);
// if (audio_sync_enabled) // if (audio_sync_enabled)
// stream->EmptyBuffer(); // stream->EmptyBuffer();
s_target_speed = target_speed;
UpdateThrottlePeriod();
ResetThrottler();
} }
g_host_display->SetDisplayMaxFPS(max_display_fps); g_host_display->SetDisplayMaxFPS(max_display_fps);

View file

@ -8,7 +8,12 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
Log_SetChannel(AudioStream);
#ifdef __APPLE__
#include <stdlib.h> // alloca
#else
#include <malloc.h> // alloca
#endif
#if defined(_M_ARM64) #if defined(_M_ARM64)
#include <arm64_neon.h> #include <arm64_neon.h>
@ -18,6 +23,8 @@ Log_SetChannel(AudioStream);
#include <emmintrin.h> #include <emmintrin.h>
#endif #endif
Log_SetChannel(AudioStream);
static constexpr bool LOG_TIMESTRETCH_STATS = false; static constexpr bool LOG_TIMESTRETCH_STATS = false;
AudioStream::AudioStream(u32 sample_rate, u32 channels, u32 buffer_ms, AudioStretchMode stretch) 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); m_rpos.store(rpos, std::memory_order_release);
} }
// TODO: Bring back the crappy resampler?
if (silence_frames > 0) if (silence_frames > 0)
{
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<u32>(65536.0f * (static_cast<float>(frames_to_read / m_channels) / static_cast<float>(nFrames)));
s16* resample_ptr = static_cast<s16*>(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); std::memset(bData + frames_to_read, 0, sizeof(s32) * silence_frames);
}
}
} }
void AudioStream::InternalWriteFrames(s32* bData, u32 nSamples) void AudioStream::InternalWriteFrames(s32* bData, u32 nSamples)
@ -228,6 +265,22 @@ void AudioStream::SetNominalRate(float tempo)
m_soundtouch->setRate(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) void AudioStream::SetStretchMode(AudioStretchMode mode)
{ {
if (m_stretch_mode == mode) if (m_stretch_mode == mode)

View file

@ -67,6 +67,7 @@ public:
/// Nominal rate is used for both resampling and timestretching, input samples are assumed to be this amount faster /// Nominal rate is used for both resampling and timestretching, input samples are assumed to be this amount faster
/// than the sample rate. /// than the sample rate.
void SetNominalRate(float tempo); void SetNominalRate(float tempo);
void UpdateTargetTempo(float tempo);
void SetStretchMode(AudioStretchMode mode); void SetStretchMode(AudioStretchMode mode);