diff options
| author | 2022-10-08 19:27:54 +0100 | |
|---|---|---|
| committer | 2022-10-09 13:47:59 +0100 | |
| commit | 8c9e238a7baef2c3f8a9a608ec8bebeeccfa6676 (patch) | |
| tree | 1e13efa4263c8e06c28787175f54e57b39fe33dd | |
| parent | Merge pull request #6142 from lat9nq/prog_meta_ref_bind_address (diff) | |
| download | yuzu-8c9e238a7baef2c3f8a9a608ec8bebeeccfa6676.tar.gz yuzu-8c9e238a7baef2c3f8a9a608ec8bebeeccfa6676.tar.xz yuzu-8c9e238a7baef2c3f8a9a608ec8bebeeccfa6676.zip | |
Choose the SDL audio backend when Cubeb reports too high of a latency
Diffstat (limited to '')
| -rw-r--r-- | src/audio_core/sink/cubeb_sink.cpp | 31 | ||||
| -rw-r--r-- | src/audio_core/sink/cubeb_sink.h | 7 | ||||
| -rw-r--r-- | src/audio_core/sink/sdl2_sink.cpp | 10 | ||||
| -rw-r--r-- | src/audio_core/sink/sdl2_sink.h | 7 | ||||
| -rw-r--r-- | src/audio_core/sink/sink_details.cpp | 66 | ||||
| -rw-r--r-- | src/audio_core/sink/sink_details.h | 2 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_audio.cpp | 4 |
7 files changed, 95 insertions, 32 deletions
diff --git a/src/audio_core/sink/cubeb_sink.cpp b/src/audio_core/sink/cubeb_sink.cpp index 36b115ad6..32c1b1cb3 100644 --- a/src/audio_core/sink/cubeb_sink.cpp +++ b/src/audio_core/sink/cubeb_sink.cpp | |||
| @@ -66,10 +66,10 @@ public: | |||
| 66 | const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &minimum_latency); | 66 | const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &minimum_latency); |
| 67 | if (latency_error != CUBEB_OK) { | 67 | if (latency_error != CUBEB_OK) { |
| 68 | LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); | 68 | LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); |
| 69 | minimum_latency = 256U; | 69 | minimum_latency = TargetSampleCount * 2; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | minimum_latency = std::max(minimum_latency, 256u); | 72 | minimum_latency = std::max(minimum_latency, TargetSampleCount * 2); |
| 73 | 73 | ||
| 74 | LOG_INFO(Service_Audio, | 74 | LOG_INFO(Service_Audio, |
| 75 | "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) " | 75 | "Opening cubeb stream {} type {} with: rate {} channels {} (system channels {}) " |
| @@ -326,4 +326,31 @@ std::vector<std::string> ListCubebSinkDevices(bool capture) { | |||
| 326 | return device_list; | 326 | return device_list; |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | u32 GetCubebLatency() { | ||
| 330 | cubeb* ctx; | ||
| 331 | |||
| 332 | if (cubeb_init(&ctx, "yuzu Latency Getter", nullptr) != CUBEB_OK) { | ||
| 333 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | ||
| 334 | // Return a large latency so we choose SDL instead. | ||
| 335 | return 10000u; | ||
| 336 | } | ||
| 337 | |||
| 338 | cubeb_stream_params params{}; | ||
| 339 | params.rate = TargetSampleRate; | ||
| 340 | params.channels = 2; | ||
| 341 | params.format = CUBEB_SAMPLE_S16LE; | ||
| 342 | params.prefs = CUBEB_STREAM_PREF_NONE; | ||
| 343 | params.layout = CUBEB_LAYOUT_STEREO; | ||
| 344 | |||
| 345 | u32 latency{0}; | ||
| 346 | const auto latency_error = cubeb_get_min_latency(ctx, ¶ms, &latency); | ||
| 347 | if (latency_error != CUBEB_OK) { | ||
| 348 | LOG_CRITICAL(Audio_Sink, "Error getting minimum latency, error: {}", latency_error); | ||
| 349 | latency = TargetSampleCount * 2; | ||
| 350 | } | ||
| 351 | latency = std::max(latency, TargetSampleCount * 2); | ||
| 352 | cubeb_destroy(ctx); | ||
| 353 | return latency; | ||
| 354 | } | ||
| 355 | |||
| 329 | } // namespace AudioCore::Sink | 356 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/cubeb_sink.h b/src/audio_core/sink/cubeb_sink.h index 4b0cb160d..3302cb98d 100644 --- a/src/audio_core/sink/cubeb_sink.h +++ b/src/audio_core/sink/cubeb_sink.h | |||
| @@ -96,4 +96,11 @@ private: | |||
| 96 | */ | 96 | */ |
| 97 | std::vector<std::string> ListCubebSinkDevices(bool capture); | 97 | std::vector<std::string> ListCubebSinkDevices(bool capture); |
| 98 | 98 | ||
| 99 | /** | ||
| 100 | * Get the reported latency for this sink. | ||
| 101 | * | ||
| 102 | * @return Minimum latency for this sink. | ||
| 103 | */ | ||
| 104 | u32 GetCubebLatency(); | ||
| 105 | |||
| 99 | } // namespace AudioCore::Sink | 106 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sdl2_sink.cpp b/src/audio_core/sink/sdl2_sink.cpp index 1bd001b94..f12ebf7fe 100644 --- a/src/audio_core/sink/sdl2_sink.cpp +++ b/src/audio_core/sink/sdl2_sink.cpp | |||
| @@ -47,11 +47,7 @@ public: | |||
| 47 | spec.freq = TargetSampleRate; | 47 | spec.freq = TargetSampleRate; |
| 48 | spec.channels = static_cast<u8>(device_channels); | 48 | spec.channels = static_cast<u8>(device_channels); |
| 49 | spec.format = AUDIO_S16SYS; | 49 | spec.format = AUDIO_S16SYS; |
| 50 | if (type == StreamType::Render) { | 50 | spec.samples = TargetSampleCount * 2; |
| 51 | spec.samples = TargetSampleCount; | ||
| 52 | } else { | ||
| 53 | spec.samples = 1024; | ||
| 54 | } | ||
| 55 | spec.callback = &SDLSinkStream::DataCallback; | 51 | spec.callback = &SDLSinkStream::DataCallback; |
| 56 | spec.userdata = this; | 52 | spec.userdata = this; |
| 57 | 53 | ||
| @@ -240,4 +236,8 @@ std::vector<std::string> ListSDLSinkDevices(bool capture) { | |||
| 240 | return device_list; | 236 | return device_list; |
| 241 | } | 237 | } |
| 242 | 238 | ||
| 239 | u32 GetSDLLatency() { | ||
| 240 | return TargetSampleCount * 2; | ||
| 241 | } | ||
| 242 | |||
| 243 | } // namespace AudioCore::Sink | 243 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sdl2_sink.h b/src/audio_core/sink/sdl2_sink.h index f01eddc1b..27ed1ab94 100644 --- a/src/audio_core/sink/sdl2_sink.h +++ b/src/audio_core/sink/sdl2_sink.h | |||
| @@ -87,4 +87,11 @@ private: | |||
| 87 | */ | 87 | */ |
| 88 | std::vector<std::string> ListSDLSinkDevices(bool capture); | 88 | std::vector<std::string> ListSDLSinkDevices(bool capture); |
| 89 | 89 | ||
| 90 | /** | ||
| 91 | * Get the reported latency for this sink. | ||
| 92 | * | ||
| 93 | * @return Minimum latency for this sink. | ||
| 94 | */ | ||
| 95 | u32 GetSDLLatency(); | ||
| 96 | |||
| 90 | } // namespace AudioCore::Sink | 97 | } // namespace AudioCore::Sink |
diff --git a/src/audio_core/sink/sink_details.cpp b/src/audio_core/sink/sink_details.cpp index 67bdab779..b878bf23f 100644 --- a/src/audio_core/sink/sink_details.cpp +++ b/src/audio_core/sink/sink_details.cpp | |||
| @@ -21,58 +21,80 @@ namespace { | |||
| 21 | struct SinkDetails { | 21 | struct SinkDetails { |
| 22 | using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); | 22 | using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); |
| 23 | using ListDevicesFn = std::vector<std::string> (*)(bool); | 23 | using ListDevicesFn = std::vector<std::string> (*)(bool); |
| 24 | using LatencyFn = u32 (*)(); | ||
| 24 | 25 | ||
| 25 | /// Name for this sink. | 26 | /// Name for this sink. |
| 26 | const char* id; | 27 | std::string_view id; |
| 27 | /// A method to call to construct an instance of this type of sink. | 28 | /// A method to call to construct an instance of this type of sink. |
| 28 | FactoryFn factory; | 29 | FactoryFn factory; |
| 29 | /// A method to call to list available devices. | 30 | /// A method to call to list available devices. |
| 30 | ListDevicesFn list_devices; | 31 | ListDevicesFn list_devices; |
| 32 | /// Method to get the latency of this backend. | ||
| 33 | LatencyFn latency; | ||
| 31 | }; | 34 | }; |
| 32 | 35 | ||
| 33 | // sink_details is ordered in terms of desirability, with the best choice at the top. | 36 | // sink_details is ordered in terms of desirability, with the best choice at the top. |
| 34 | constexpr SinkDetails sink_details[] = { | 37 | constexpr SinkDetails sink_details[] = { |
| 35 | #ifdef HAVE_CUBEB | 38 | #ifdef HAVE_CUBEB |
| 36 | SinkDetails{"cubeb", | 39 | SinkDetails{ |
| 37 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | 40 | "cubeb", |
| 38 | return std::make_unique<CubebSink>(device_id); | 41 | [](std::string_view device_id) -> std::unique_ptr<Sink> { |
| 39 | }, | 42 | return std::make_unique<CubebSink>(device_id); |
| 40 | &ListCubebSinkDevices}, | 43 | }, |
| 44 | &ListCubebSinkDevices, | ||
| 45 | &GetCubebLatency, | ||
| 46 | }, | ||
| 41 | #endif | 47 | #endif |
| 42 | #ifdef HAVE_SDL2 | 48 | #ifdef HAVE_SDL2 |
| 43 | SinkDetails{"sdl2", | 49 | SinkDetails{ |
| 44 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | 50 | "sdl", |
| 45 | return std::make_unique<SDLSink>(device_id); | 51 | [](std::string_view device_id) -> std::unique_ptr<Sink> { |
| 46 | }, | 52 | return std::make_unique<SDLSink>(device_id); |
| 47 | &ListSDLSinkDevices}, | 53 | }, |
| 54 | &ListSDLSinkDevices, | ||
| 55 | &GetSDLLatency, | ||
| 56 | }, | ||
| 48 | #endif | 57 | #endif |
| 49 | SinkDetails{"null", | 58 | SinkDetails{"null", |
| 50 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | 59 | [](std::string_view device_id) -> std::unique_ptr<Sink> { |
| 51 | return std::make_unique<NullSink>(device_id); | 60 | return std::make_unique<NullSink>(device_id); |
| 52 | }, | 61 | }, |
| 53 | [](bool capture) { return std::vector<std::string>{"null"}; }}, | 62 | [](bool capture) { return std::vector<std::string>{"null"}; }, []() { return 0u; }}, |
| 54 | }; | 63 | }; |
| 55 | 64 | ||
| 56 | const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { | 65 | const SinkDetails& GetOutputSinkDetails(std::string_view sink_id) { |
| 57 | auto iter = | 66 | const auto find_backend{[](std::string_view id) { |
| 58 | std::find_if(std::begin(sink_details), std::end(sink_details), | 67 | return std::find_if(std::begin(sink_details), std::end(sink_details), |
| 59 | [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); | 68 | [&id](const auto& sink_detail) { return sink_detail.id == id; }); |
| 69 | }}; | ||
| 60 | 70 | ||
| 61 | if (sink_id == "auto" || iter == std::end(sink_details)) { | 71 | auto iter = find_backend(sink_id); |
| 62 | if (sink_id != "auto") { | 72 | |
| 63 | LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); | 73 | if (sink_id == "auto") { |
| 74 | // Auto-select a backend. Prefer CubeB, but it may report a large minimum latency which | ||
| 75 | // causes audio issues, in that case go with SDL. | ||
| 76 | #if defined(HAVE_CUBEB) && defined(HAVE_SDL2) | ||
| 77 | iter = find_backend("cubeb"); | ||
| 78 | if (iter->latency() > TargetSampleCount * 3) { | ||
| 79 | iter = find_backend("sdl"); | ||
| 64 | } | 80 | } |
| 65 | // Auto-select. | 81 | #else |
| 66 | // sink_details is ordered in terms of desirability, with the best choice at the front. | ||
| 67 | iter = std::begin(sink_details); | 82 | iter = std::begin(sink_details); |
| 83 | #endif | ||
| 84 | LOG_INFO(Service_Audio, "Auto-selecting the {} backend", iter->id); | ||
| 85 | } | ||
| 86 | |||
| 87 | if (iter == std::end(sink_details)) { | ||
| 88 | LOG_ERROR(Audio, "Invalid sink_id {}", sink_id); | ||
| 89 | iter = find_backend("null"); | ||
| 68 | } | 90 | } |
| 69 | 91 | ||
| 70 | return *iter; | 92 | return *iter; |
| 71 | } | 93 | } |
| 72 | } // Anonymous namespace | 94 | } // Anonymous namespace |
| 73 | 95 | ||
| 74 | std::vector<const char*> GetSinkIDs() { | 96 | std::vector<std::string_view> GetSinkIDs() { |
| 75 | std::vector<const char*> sink_ids(std::size(sink_details)); | 97 | std::vector<std::string_view> sink_ids(std::size(sink_details)); |
| 76 | 98 | ||
| 77 | std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), | 99 | std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), |
| 78 | [](const auto& sink) { return sink.id; }); | 100 | [](const auto& sink) { return sink.id; }); |
diff --git a/src/audio_core/sink/sink_details.h b/src/audio_core/sink/sink_details.h index 3ebdb1e30..e75932898 100644 --- a/src/audio_core/sink/sink_details.h +++ b/src/audio_core/sink/sink_details.h | |||
| @@ -19,7 +19,7 @@ class Sink; | |||
| 19 | * | 19 | * |
| 20 | * @return Vector of available sink names. | 20 | * @return Vector of available sink names. |
| 21 | */ | 21 | */ |
| 22 | std::vector<const char*> GetSinkIDs(); | 22 | std::vector<std::string_view> GetSinkIDs(); |
| 23 | 23 | ||
| 24 | /** | 24 | /** |
| 25 | * Gets the list of devices for a particular sink identified by the given ID. | 25 | * Gets the list of devices for a particular sink identified by the given ID. |
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 19b8b15ef..70cc6f84b 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -161,8 +161,8 @@ void ConfigureAudio::InitializeAudioSinkComboBox() { | |||
| 161 | ui->sink_combo_box->clear(); | 161 | ui->sink_combo_box->clear(); |
| 162 | ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); | 162 | ui->sink_combo_box->addItem(QString::fromUtf8(AudioCore::Sink::auto_device_name)); |
| 163 | 163 | ||
| 164 | for (const char* id : AudioCore::Sink::GetSinkIDs()) { | 164 | for (const auto& id : AudioCore::Sink::GetSinkIDs()) { |
| 165 | ui->sink_combo_box->addItem(QString::fromUtf8(id)); | 165 | ui->sink_combo_box->addItem(QString::fromUtf8(id.data(), static_cast<s32>(id.length()))); |
| 166 | } | 166 | } |
| 167 | } | 167 | } |
| 168 | 168 | ||