Просмотр исходного кода

Merge pull request #2035 from MerryMage/disable-stretch

User-configurable option to enable/disable time-stretching of audio
bunnei 10 лет назад
Родитель
Сommit
549d0c1715

+ 4 - 0
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();

+ 3 - 0
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();
 

+ 37 - 8
src/audio_core/hle/dsp.cpp

@@ -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();
     }
 }
 

+ 8 - 0
src/audio_core/hle/dsp.h

@@ -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

+ 1 - 1
src/audio_core/null_sink.h

@@ -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;

+ 2 - 4
src/audio_core/sdl2_sink.cpp

@@ -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);
 }
 

+ 1 - 1
src/audio_core/sdl2_sink.h

@@ -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;
 

+ 3 - 2
src/audio_core/sink.h

@@ -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;

+ 1 - 0
src/citra/config.cpp

@@ -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);

+ 6 - 0
src/citra/default_ini.h

@@ -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

+ 2 - 0
src/citra_qt/config.cpp

@@ -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");

+ 3 - 0
src/citra_qt/configure_audio.cpp

@@ -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();
 }

+ 10 - 0
src/citra_qt/configure_audio.ui

@@ -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>

+ 1 - 0
src/core/settings.cpp

@@ -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);
 
 }
 

+ 1 - 0
src/core/settings.h

@@ -81,6 +81,7 @@ struct Values {
 
     // Audio
     std::string sink_id;
+    bool enable_audio_stretching;
 
     // Debugging
     bool use_gdbstub;