Merge pull request #2035 from MerryMage/disable-stretch
User-configurable option to enable/disable time-stretching of audio
This commit is contained in:
		@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -85,12 +85,45 @@ static StereoFrame16 GenerateCurrentFrame() {
 | 
			
		||||
 | 
			
		||||
// Audio output
 | 
			
		||||
 | 
			
		||||
static bool perform_time_stretching = true;
 | 
			
		||||
static std::unique_ptr<AudioCore::Sink> sink;
 | 
			
		||||
static AudioCore::TimeStretcher time_stretcher;
 | 
			
		||||
 | 
			
		||||
static void FlushResidualStretcherAudio() {
 | 
			
		||||
    time_stretcher.Flush();
 | 
			
		||||
    while (true) {
 | 
			
		||||
        std::vector<s16> 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());
 | 
			
		||||
    sink->EnqueueSamples(time_stretcher.Process(sink->SamplesInQueue()));
 | 
			
		||||
    if (perform_time_stretching) {
 | 
			
		||||
        time_stretcher.AddSamples(&frame[0][0], frame.size());
 | 
			
		||||
        std::vector<s16> 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
 | 
			
		||||
@@ -111,12 +144,8 @@ void Init() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Shutdown() {
 | 
			
		||||
    time_stretcher.Flush();
 | 
			
		||||
    while (true) {
 | 
			
		||||
        std::vector<s16> residual_audio = time_stretcher.Process(sink->SamplesInQueue());
 | 
			
		||||
        if (residual_audio.empty())
 | 
			
		||||
            break;
 | 
			
		||||
        sink->EnqueueSamples(residual_audio);
 | 
			
		||||
    if (perform_time_stretching) {
 | 
			
		||||
        FlushResidualStretcherAudio();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -544,5 +544,13 @@ bool Tick();
 | 
			
		||||
 */
 | 
			
		||||
void SetSink(std::unique_ptr<AudioCore::Sink> 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
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ public:
 | 
			
		||||
        return native_sample_rate;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void EnqueueSamples(const std::vector<s16>&) override {}
 | 
			
		||||
    void EnqueueSamples(const s16*, size_t) override {}
 | 
			
		||||
 | 
			
		||||
    size_t SamplesInQueue() const override {
 | 
			
		||||
        return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -71,14 +71,12 @@ unsigned int SDL2Sink::GetNativeSampleRate() const {
 | 
			
		||||
    return impl->sample_rate;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void SDL2Sink::EnqueueSamples(const std::vector<s16>& samples) {
 | 
			
		||||
void SDL2Sink::EnqueueSamples(const s16* samples, size_t sample_count) {
 | 
			
		||||
    if (impl->audio_device_id <= 0)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    ASSERT_MSG(samples.size() % 2 == 0, "Samples must be in interleaved stereo PCM16 format (size must be a multiple of two)");
 | 
			
		||||
 | 
			
		||||
    SDL_LockAudioDevice(impl->audio_device_id);
 | 
			
		||||
    impl->queue.emplace_back(samples);
 | 
			
		||||
    impl->queue.emplace_back(samples, samples + sample_count * 2);
 | 
			
		||||
    SDL_UnlockAudioDevice(impl->audio_device_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    unsigned int GetNativeSampleRate() const override;
 | 
			
		||||
 | 
			
		||||
    void EnqueueSamples(const std::vector<s16>& samples) override;
 | 
			
		||||
    void EnqueueSamples(const s16* samples, size_t sample_count) override;
 | 
			
		||||
 | 
			
		||||
    size_t SamplesInQueue() const override;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,9 +23,10 @@ public:
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Feed stereo samples to sink.
 | 
			
		||||
     * @param samples Samples in interleaved stereo PCM16 format. Size of vector must be multiple of two.
 | 
			
		||||
     * @param samples Samples in interleaved stereo PCM16 format.
 | 
			
		||||
     * @param sample_count Number of samples.
 | 
			
		||||
     */
 | 
			
		||||
    virtual void EnqueueSamples(const std::vector<s16>& samples) = 0;
 | 
			
		||||
    virtual void EnqueueSamples(const s16* samples, size_t sample_count) = 0;
 | 
			
		||||
 | 
			
		||||
    /// Samples enqueued that have not been played yet.
 | 
			
		||||
    virtual std::size_t SamplesInQueue() const = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -78,6 +78,7 @@ void Config::ReadValues() {
 | 
			
		||||
 | 
			
		||||
    // Audio
 | 
			
		||||
    Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
 | 
			
		||||
    Settings::values.enable_audio_stretching = sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
 | 
			
		||||
 | 
			
		||||
    // Data Storage
 | 
			
		||||
    Settings::values.use_virtual_sd = sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
 | 
			
		||||
 
 | 
			
		||||
@@ -66,6 +66,12 @@ bg_green =
 | 
			
		||||
# auto (default): Auto-select, null: No audio output, sdl2: SDL2 (if available)
 | 
			
		||||
output_engine =
 | 
			
		||||
 | 
			
		||||
# Whether or not to enable the audio-stretching post-processing effect.
 | 
			
		||||
# This effect adjusts audio speed to match emulation speed and helps prevent audio stutter,
 | 
			
		||||
# at the cost of increasing audio latency.
 | 
			
		||||
# 0: No, 1 (default): Yes
 | 
			
		||||
enable_audio_stretching =
 | 
			
		||||
 | 
			
		||||
[Data Storage]
 | 
			
		||||
# Whether to create a virtual SD card.
 | 
			
		||||
# 1 (default): Yes, 0: No
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,7 @@ void Config::ReadValues() {
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("Audio");
 | 
			
		||||
    Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString();
 | 
			
		||||
    Settings::values.enable_audio_stretching = qt_config->value("enable_audio_stretching", true).toBool();
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("Data Storage");
 | 
			
		||||
@@ -148,6 +149,7 @@ void Config::SaveValues() {
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("Audio");
 | 
			
		||||
    qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id));
 | 
			
		||||
    qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching);
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
 | 
			
		||||
    qt_config->beginGroup("Data Storage");
 | 
			
		||||
 
 | 
			
		||||
@@ -36,9 +36,12 @@ void ConfigureAudio::setConfiguration() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
 | 
			
		||||
 | 
			
		||||
    ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureAudio::applyConfiguration() {
 | 
			
		||||
    Settings::values.sink_id = ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()).toStdString();
 | 
			
		||||
    Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
 | 
			
		||||
    Settings::Apply();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,16 @@
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QCheckBox" name="toggle_audio_stretching">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Enable audio stretching</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="toolTip">
 | 
			
		||||
         <string>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
 
 | 
			
		||||
@@ -24,6 +24,7 @@ void Apply() {
 | 
			
		||||
    VideoCore::g_scaled_resolution_enabled = values.use_scaled_resolution;
 | 
			
		||||
 | 
			
		||||
    AudioCore::SelectSink(values.sink_id);
 | 
			
		||||
    AudioCore::EnableStretching(values.enable_audio_stretching);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -81,6 +81,7 @@ struct Values {
 | 
			
		||||
 | 
			
		||||
    // Audio
 | 
			
		||||
    std::string sink_id;
 | 
			
		||||
    bool enable_audio_stretching;
 | 
			
		||||
 | 
			
		||||
    // Debugging
 | 
			
		||||
    bool use_gdbstub;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user