Add audio stretching support

This commit is contained in:
fearlessTobi 2018-08-23 14:33:03 +02:00 committed by MerryMage
parent 9cd79c25ed
commit a6efff8b02
15 changed files with 50 additions and 0 deletions

3
.gitmodules vendored
View file

@ -31,3 +31,6 @@
[submodule "opus"] [submodule "opus"]
path = externals/opus path = externals/opus
url = https://github.com/ogniK5377/opus.git url = https://github.com/ogniK5377/opus.git
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git

View file

@ -47,6 +47,9 @@ target_include_directories(microprofile INTERFACE ./microprofile)
add_library(unicorn-headers INTERFACE) add_library(unicorn-headers INTERFACE)
target_include_directories(unicorn-headers INTERFACE ./unicorn/include) target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
# SoundTouch
add_subdirectory(soundtouch)
# Xbyak # Xbyak
if (ARCHITECTURE_x86_64) if (ARCHITECTURE_x86_64)
# Defined before "dynarmic" above # Defined before "dynarmic" above

1
externals/soundtouch vendored Submodule

@ -0,0 +1 @@
Subproject commit 060181eaf273180d3a7e87349895bd0cb6ccbf4a

View file

@ -24,6 +24,7 @@ add_library(audio_core STATIC
create_target_directory_groups(audio_core) create_target_directory_groups(audio_core)
target_link_libraries(audio_core PUBLIC common core) target_link_libraries(audio_core PUBLIC common core)
target_link_libraries(audio_core PRIVATE SoundTouch)
if(ENABLE_CUBEB) if(ENABLE_CUBEB)
target_link_libraries(audio_core PRIVATE cubeb) target_link_libraries(audio_core PRIVATE cubeb)

View file

@ -85,6 +85,13 @@ public:
} }
} }
size_t SamplesInQueue(u32 num_channels) const {
if (!ctx)
return 0;
return queue.size() / num_channels;
}
u32 GetNumChannels() const { u32 GetNumChannels() const {
return num_channels; return num_channels;
} }

View file

@ -21,6 +21,10 @@ public:
private: private:
struct NullSinkStreamImpl final : SinkStream { struct NullSinkStreamImpl final : SinkStream {
void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {} void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
size_t SamplesInQueue(u32 /*num_channels*/) const override {
return 0;
}
} null_sink_stream; } null_sink_stream;
}; };

View file

@ -25,6 +25,8 @@ public:
* @param samples Samples in interleaved stereo PCM16 format. * @param samples Samples in interleaved stereo PCM16 format.
*/ */
virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0; virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
virtual std::size_t SamplesInQueue(u32 num_channels) const = 0;
}; };
using SinkStreamPtr = std::unique_ptr<SinkStream>; using SinkStreamPtr = std::unique_ptr<SinkStream>;

View file

@ -90,6 +90,7 @@ void Stream::PlayNextBuffer() {
queued_buffers.pop(); queued_buffers.pop();
VolumeAdjustSamples(active_buffer->Samples()); VolumeAdjustSamples(active_buffer->Samples());
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples()); sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {}); CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});

View file

@ -146,6 +146,7 @@ struct Values {
// Audio // Audio
std::string sink_id; std::string sink_id;
bool enable_audio_stretching;
std::string audio_device_id; std::string audio_device_id;
float volume; float volume;

View file

@ -120,6 +120,9 @@ TelemetrySession::TelemetrySession() {
Telemetry::AppendOSInfo(field_collection); Telemetry::AppendOSInfo(field_collection);
// Log user configuration information // Log user configuration information
AddField(Telemetry::FieldType::UserConfig, "Audio_SinkId", Settings::values.sink_id);
AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching",
Settings::values.enable_audio_stretching);
AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit); AddField(Telemetry::FieldType::UserConfig, "Core_UseCpuJit", Settings::values.use_cpu_jit);
AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore", AddField(Telemetry::FieldType::UserConfig, "Core_UseMultiCore",
Settings::values.use_multi_core); Settings::values.use_multi_core);

View file

@ -95,6 +95,8 @@ void Config::ReadValues() {
qt_config->beginGroup("Audio"); qt_config->beginGroup("Audio");
Settings::values.sink_id = qt_config->value("output_engine", "auto").toString().toStdString(); 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();
Settings::values.audio_device_id = Settings::values.audio_device_id =
qt_config->value("output_device", "auto").toString().toStdString(); qt_config->value("output_device", "auto").toString().toStdString();
Settings::values.volume = qt_config->value("volume", 1).toFloat(); Settings::values.volume = qt_config->value("volume", 1).toFloat();
@ -230,6 +232,7 @@ void Config::SaveValues() {
qt_config->beginGroup("Audio"); qt_config->beginGroup("Audio");
qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id)); qt_config->setValue("output_engine", QString::fromStdString(Settings::values.sink_id));
qt_config->setValue("enable_audio_stretching", Settings::values.enable_audio_stretching);
qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id)); qt_config->setValue("output_device", QString::fromStdString(Settings::values.audio_device_id));
qt_config->setValue("volume", Settings::values.volume); qt_config->setValue("volume", Settings::values.volume);
qt_config->endGroup(); qt_config->endGroup();

View file

@ -46,6 +46,8 @@ void ConfigureAudio::setConfiguration() {
} }
ui->output_sink_combo_box->setCurrentIndex(new_sink_index); ui->output_sink_combo_box->setCurrentIndex(new_sink_index);
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching);
// The device list cannot be pre-populated (nor listed) until the output sink is known. // The device list cannot be pre-populated (nor listed) until the output sink is known.
updateAudioDevices(new_sink_index); updateAudioDevices(new_sink_index);
@ -67,6 +69,7 @@ void ConfigureAudio::applyConfiguration() {
Settings::values.sink_id = Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex()) ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString(); .toStdString();
Settings::values.enable_audio_stretching = ui->toggle_audio_stretching->isChecked();
Settings::values.audio_device_id = Settings::values.audio_device_id =
ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex()) ui->audio_device_combo_box->itemText(ui->audio_device_combo_box->currentIndex())
.toStdString(); .toStdString();

View file

@ -31,6 +31,16 @@
</item> </item>
</layout> </layout>
</item> </item>
<item>
<widget class="QCheckBox" name="toggle_audio_stretching">
<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>
<property name="text">
<string>Enable audio stretching</string>
</property>
</widget>
</item>
<item> <item>
<layout class="QHBoxLayout"> <layout class="QHBoxLayout">
<item> <item>

View file

@ -108,6 +108,8 @@ void Config::ReadValues() {
// Audio // Audio
Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto"); Settings::values.sink_id = sdl2_config->Get("Audio", "output_engine", "auto");
Settings::values.enable_audio_stretching =
sdl2_config->GetBoolean("Audio", "enable_audio_stretching", true);
Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto"); Settings::values.audio_device_id = sdl2_config->Get("Audio", "output_device", "auto");
Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1); Settings::values.volume = sdl2_config->GetReal("Audio", "volume", 1);

View file

@ -150,6 +150,12 @@ swap_screen =
# auto (default): Auto-select, null: No audio output, cubeb: Cubeb audio engine (if available) # auto (default): Auto-select, null: No audio output, cubeb: Cubeb audio engine (if available)
output_engine = 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 =
# Which audio device to use. # Which audio device to use.
# auto (default): Auto-select # auto (default): Auto-select
output_device = output_device =