CoreAudioStream: Handle parameter changes without restarting

This commit is contained in:
Stenzek
2026-01-05 16:28:07 +10:00
parent fa39e63bbb
commit f2adc39709
3 changed files with 107 additions and 1 deletions

View File

@@ -4507,7 +4507,10 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
if (g_settings.audio_backend != old_settings.audio_backend ||
g_settings.audio_driver != old_settings.audio_driver ||
g_settings.audio_output_device != old_settings.audio_output_device ||
g_settings.audio_stream_parameters != old_settings.audio_stream_parameters)
g_settings.audio_stream_parameters.output_latency_ms !=
old_settings.audio_stream_parameters.output_latency_ms ||
g_settings.audio_stream_parameters.output_latency_minimal !=
old_settings.audio_stream_parameters.output_latency_minimal)
{
if (g_settings.audio_backend != old_settings.audio_backend)
{
@@ -4524,6 +4527,10 @@ void System::CheckForSettingsChanges(const Settings& old_settings)
if (sound_effects_active)
SoundEffectManager::EnsureInitialized();
}
else if (g_settings.audio_stream_parameters != old_settings.audio_stream_parameters)
{
SPU::GetOutputStream().UpdateParameters(g_settings.audio_stream_parameters);
}
if (g_settings.emulation_speed != old_settings.emulation_speed)
UpdateThrottlePeriod();

View File

@@ -134,6 +134,64 @@ bool CoreAudioStream::Initialize(AudioBackend backend, u32 sample_rate, const Au
return true;
}
void CoreAudioStream::UpdateParameters(const AudioStreamParameters& params)
{
constexpr auto copy_stretch_params = [](AudioStreamParameters& dest, const AudioStreamParameters& src) {
dest.stretch_sequence_length_ms = src.stretch_sequence_length_ms;
dest.stretch_seekwindow_ms = src.stretch_seekwindow_ms;
dest.stretch_overlap_ms = src.stretch_overlap_ms;
dest.stretch_use_quickseek = src.stretch_use_quickseek;
dest.stretch_use_aa_filter = src.stretch_use_aa_filter;
};
if (params.buffer_ms != m_parameters.buffer_ms)
{
Error error;
// have to pause the stream to change buffer size
if (m_stream && !m_paused)
{
if (!m_stream->Stop(&error))
{
ERROR_LOG("Failed to stop audio stream for buffer size change: {}", error.GetDescription());
return;
}
}
StretchDestroy();
DestroyBuffer();
m_parameters.buffer_ms = params.buffer_ms;
copy_stretch_params(m_parameters, params);
AllocateBuffer();
StretchAllocate();
if (m_stream && !m_paused)
{
if (!m_stream->Start(&error))
{
ERROR_LOG("Failed to start audio stream after buffer size change: {}", error.GetDescription());
m_paused = true;
}
}
return;
}
if (params.stretch_mode != m_parameters.stretch_mode)
{
StretchDestroy();
copy_stretch_params(m_parameters, params);
StretchAllocate();
}
else
{
// easier case: just changing stretch settings
StretchUpdateParameters(params);
}
}
void CoreAudioStream::Destroy()
{
m_stream.reset();
@@ -583,6 +641,42 @@ void CoreAudioStream::StretchAllocate()
m_staging_buffer_pos = 0;
}
void CoreAudioStream::StretchUpdateParameters(const AudioStreamParameters& params)
{
if (m_parameters.stretch_mode == AudioStretchMode::Off)
return;
if (params.stretch_use_quickseek != m_parameters.stretch_use_quickseek)
{
m_parameters.stretch_use_quickseek = params.stretch_use_quickseek;
soundtouch_setSetting(m_soundtouch, SETTING_USE_QUICKSEEK, m_parameters.stretch_use_quickseek);
}
if (params.stretch_use_aa_filter != m_parameters.stretch_use_aa_filter)
{
m_parameters.stretch_use_aa_filter = params.stretch_use_aa_filter;
soundtouch_setSetting(m_soundtouch, SETTING_USE_AA_FILTER, m_parameters.stretch_use_aa_filter);
}
if (params.stretch_sequence_length_ms != m_parameters.stretch_sequence_length_ms)
{
m_parameters.stretch_sequence_length_ms = params.stretch_sequence_length_ms;
soundtouch_setSetting(m_soundtouch, SETTING_SEQUENCE_MS, m_parameters.stretch_sequence_length_ms);
}
if (params.stretch_seekwindow_ms != m_parameters.stretch_seekwindow_ms)
{
m_parameters.stretch_seekwindow_ms = params.stretch_seekwindow_ms;
soundtouch_setSetting(m_soundtouch, SETTING_SEEKWINDOW_MS, m_parameters.stretch_seekwindow_ms);
}
if (params.stretch_overlap_ms != m_parameters.stretch_overlap_ms)
{
m_parameters.stretch_overlap_ms = params.stretch_overlap_ms;
soundtouch_setSetting(m_soundtouch, SETTING_OVERLAP_MS, m_parameters.stretch_overlap_ms);
}
}
void CoreAudioStream::StretchDestroy()
{
if (m_soundtouch)

View File

@@ -94,6 +94,10 @@ public:
std::string_view driver_name, std::string_view device_name, Error* error);
void Destroy();
/// Updates stream parameters without recreating the stream.
/// NOTE: Cannot handle changes in output latency.
void UpdateParameters(const AudioStreamParameters& params);
/// Temporarily pauses the stream, preventing it from requesting data.
void SetPaused(bool paused);
@@ -125,6 +129,7 @@ private:
void InternalWriteFrames(SampleType* samples, u32 num_frames);
void StretchAllocate();
void StretchUpdateParameters(const AudioStreamParameters& params);
void StretchDestroy();
void StretchWriteBlock(const float* block);
void StretchUnderrun();