diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index d42249ebd9..8e19ec0c4e 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp @@ -71,6 +71,10 @@ void SelectSink(std::string sink_id) { DSP::HLE::SetSink(iter->factory()); } +void EnableStretching(bool enable) { + DSP::HLE::EnableStretching(enable); +} + void Shutdown() { CoreTiming::UnscheduleEvent(tick_event, 0); DSP::HLE::Shutdown(); diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h index f618361f30..7e678aba58 100644 --- a/src/audio_core/audio_core.h +++ b/src/audio_core/audio_core.h @@ -23,6 +23,9 @@ void AddAddressSpace(Kernel::VMManager& vm_manager); /// Select the sink to use based on sink id. void SelectSink(std::string sink_id); +/// Enable/Disable stretching. +void EnableStretching(bool enable); + /// Shutdown Audio Core void Shutdown(); diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp index a195bc74c3..0cddeb82ad 100644 --- a/src/audio_core/hle/dsp.cpp +++ b/src/audio_core/hle/dsp.cpp @@ -85,13 +85,45 @@ static StereoFrame16 GenerateCurrentFrame() { // Audio output +static bool perform_time_stretching = true; static std::unique_ptr sink; static AudioCore::TimeStretcher time_stretcher; +static void FlushResidualStretcherAudio() { + time_stretcher.Flush(); + while (true) { + std::vector residual_audio = time_stretcher.Process(sink->SamplesInQueue()); + if (residual_audio.empty()) + break; + sink->EnqueueSamples(residual_audio.data(), residual_audio.size() / 2); + } +} + static void OutputCurrentFrame(const StereoFrame16& frame) { - time_stretcher.AddSamples(&frame[0][0], frame.size()); - std::vector stretched_samples = time_stretcher.Process(sink->SamplesInQueue()); - sink->EnqueueSamples(stretched_samples.data(), stretched_samples.size() / 2); + if (perform_time_stretching) { + time_stretcher.AddSamples(&frame[0][0], frame.size()); + std::vector stretched_samples = time_stretcher.Process(sink->SamplesInQueue()); + sink->EnqueueSamples(stretched_samples.data(), stretched_samples.size() / 2); + } else { + constexpr size_t maximum_sample_latency = 1024; // about 32 miliseconds + if (sink->SamplesInQueue() > maximum_sample_latency) { + // This can occur if we're running too fast and samples are starting to back up. + // Just drop the samples. + return; + } + + sink->EnqueueSamples(&frame[0][0], frame.size()); + } +} + +void EnableStretching(bool enable) { + if (perform_time_stretching == enable) + return; + + if (!enable) { + FlushResidualStretcherAudio(); + } + perform_time_stretching = enable; } // Public Interface @@ -112,12 +144,8 @@ void Init() { } void Shutdown() { - time_stretcher.Flush(); - while (true) { - std::vector residual_audio = time_stretcher.Process(sink->SamplesInQueue()); - if (residual_audio.empty()) - break; - sink->EnqueueSamples(residual_audio); + if (perform_time_stretching) { + FlushResidualStretcherAudio(); } } diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h index 9275cd7dee..565f20b6f7 100644 --- a/src/audio_core/hle/dsp.h +++ b/src/audio_core/hle/dsp.h @@ -544,5 +544,13 @@ bool Tick(); */ void SetSink(std::unique_ptr sink); +/** + * Enables/Disables audio-stretching. + * Audio stretching is an enhancement that stretches audio to match emulation + * speed to prevent stuttering at the cost of some audio latency. + * @param enable true to enable, false to disable. + */ +void EnableStretching(bool enable); + } // namespace HLE } // namespace DSP