From f3ac6f054f4d0ff51e769f875bf383bde34e6f1e Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:43:07 -0700 Subject: [PATCH] audio_core: Clean up cubeb backend. (#6677) --- src/audio_core/cubeb_input.cpp | 67 +++++++++++++++--------------- src/audio_core/cubeb_sink.cpp | 74 +++++++++++++++++----------------- 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/src/audio_core/cubeb_input.cpp b/src/audio_core/cubeb_input.cpp index 3a42ea974..eccdafecc 100644 --- a/src/audio_core/cubeb_input.cpp +++ b/src/audio_core/cubeb_input.cpp @@ -37,39 +37,33 @@ CubebInput::CubebInput(std::string device_id) } CubebInput::~CubebInput() { - if (!impl->ctx) - return; - if (impl->stream) { if (cubeb_stream_stop(impl->stream) != CUBEB_OK) { LOG_ERROR(Audio, "Error stopping cubeb input stream."); } - cubeb_stream_destroy(impl->stream); } - cubeb_destroy(impl->ctx); + if (impl->ctx) { + cubeb_destroy(impl->ctx); + } } void CubebInput::StartSampling(const InputParameters& params) { // Cubeb apparently only supports signed 16 bit PCM (and float32 which the 3ds doesn't support) - // TODO resample the input stream + // TODO: Resample the input stream. if (params.sign == Signedness::Unsigned) { LOG_ERROR(Audio, - "Application requested unsupported unsigned pcm format. Falling back to signed"); + "Application requested unsupported unsigned pcm format. Falling back to signed."); } - impl->sample_size_in_bytes = params.sample_size / 8; - parameters = params; - is_sampling = true; + impl->sample_size_in_bytes = params.sample_size / 8; cubeb_devid input_device = nullptr; if (device_id != auto_device_name && !device_id.empty()) { cubeb_device_collection collection; - if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) { - LOG_WARNING(Audio, "Audio input device enumeration not supported"); - } else { + if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) == CUBEB_OK) { const auto collection_end = collection.device + collection.count; const auto device = std::find_if( collection.device, collection_end, [this](const cubeb_device_info& info) { @@ -79,39 +73,42 @@ void CubebInput::StartSampling(const InputParameters& params) { input_device = device->devid; } cubeb_device_collection_destroy(impl->ctx, &collection); + } else { + LOG_WARNING(Audio_Sink, + "Audio input device enumeration not supported, using default device."); } } - cubeb_stream_params input_params; - input_params.channels = 1; - input_params.layout = CUBEB_LAYOUT_UNDEFINED; - input_params.prefs = CUBEB_STREAM_PREF_NONE; - input_params.format = CUBEB_SAMPLE_S16LE; - input_params.rate = params.sample_rate; + cubeb_stream_params input_params = { + .format = CUBEB_SAMPLE_S16LE, + .rate = params.sample_rate, + .channels = 1, + .layout = CUBEB_LAYOUT_UNDEFINED, + }; u32 latency_frames = 512; // Firefox default if (cubeb_get_min_latency(impl->ctx, &input_params, &latency_frames) != CUBEB_OK) { - LOG_ERROR(Audio, "Could not get minimum latency"); + LOG_WARNING(Audio, "Error getting minimum input latency, falling back to default latency."); } if (cubeb_stream_init(impl->ctx, &impl->stream, "Citra Microphone", input_device, &input_params, nullptr, nullptr, latency_frames, Impl::DataCallback, Impl::StateCallback, impl.get()) != CUBEB_OK) { - LOG_CRITICAL(Audio, "Error creating cubeb input stream"); - is_sampling = false; + LOG_CRITICAL(Audio, "Error creating cubeb input stream."); return; } if (cubeb_stream_start(impl->stream) != CUBEB_OK) { - LOG_CRITICAL(Audio, "Error starting cubeb input stream"); - is_sampling = false; + LOG_CRITICAL(Audio, "Error starting cubeb input stream."); + cubeb_stream_destroy(impl->stream); + impl->stream = nullptr; return; } + + is_sampling = true; } void CubebInput::StopSampling() { - // TODO(xperia64): Destroy the stream for now to avoid a leak because StartSampling - // reinitializes the stream every time if (impl->stream) { cubeb_stream_stop(impl->stream); cubeb_stream_destroy(impl->stream); @@ -121,8 +118,14 @@ void CubebInput::StopSampling() { } void CubebInput::AdjustSampleRate(u32 sample_rate) { - // TODO This should restart the stream with the new sample rate - LOG_ERROR(Audio, "AdjustSampleRate unimplemented!"); + if (!is_sampling) { + return; + } + + auto new_parameters = parameters; + new_parameters.sample_rate = sample_rate; + StopSampling(); + StartSampling(new_parameters); } Samples CubebInput::Read() { @@ -136,7 +139,7 @@ Samples CubebInput::Read() { long CubebInput::Impl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, void* output_buffer, long num_frames) { - Impl* impl = static_cast(user_data); + auto impl = static_cast(user_data); if (!impl) { return 0; } @@ -177,9 +180,7 @@ std::vector ListCubebInputDevices() { } cubeb_device_collection collection; - if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) { - LOG_WARNING(Audio_Sink, "Audio input device enumeration not supported"); - } else { + if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) == CUBEB_OK) { for (std::size_t i = 0; i < collection.count; i++) { const cubeb_device_info& device = collection.device[i]; if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) { @@ -187,6 +188,8 @@ std::vector ListCubebInputDevices() { } } cubeb_device_collection_destroy(ctx, &collection); + } else { + LOG_WARNING(Audio_Sink, "Audio input device enumeration not supported."); } cubeb_destroy(ctx); diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 16f25f9da..4a42d3277 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp @@ -13,8 +13,6 @@ namespace AudioCore { struct CubebSink::Impl { - unsigned int sample_rate = 0; - cubeb* ctx = nullptr; cubeb_stream* stream = nullptr; @@ -31,28 +29,29 @@ CubebSink::CubebSink(std::string_view target_device_name) : impl(std::make_uniqu LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); return; } - cubeb_set_log_callback(CUBEB_LOG_NORMAL, &Impl::LogCallback); - impl->sample_rate = native_sample_rate; + if (cubeb_set_log_callback(CUBEB_LOG_NORMAL, &Impl::LogCallback) != CUBEB_OK) { + LOG_CRITICAL(Audio_Sink, "cubeb_set_log_callback failed"); + return; + } - cubeb_stream_params params; - params.rate = impl->sample_rate; - params.channels = 2; - params.layout = CUBEB_LAYOUT_STEREO; - params.format = CUBEB_SAMPLE_S16NE; - params.prefs = CUBEB_STREAM_PREF_PERSIST; + cubeb_stream_params params = { + .format = CUBEB_SAMPLE_S16LE, + .rate = native_sample_rate, + .channels = 2, + .layout = CUBEB_LAYOUT_STEREO, + }; - u32 minimum_latency = 100 * impl->sample_rate / 1000; // Firefox default + u32 minimum_latency = 100 * native_sample_rate / 1000; // Firefox default if (cubeb_get_min_latency(impl->ctx, ¶ms, &minimum_latency) != CUBEB_OK) { - LOG_CRITICAL(Audio_Sink, "Error getting minimum latency"); + LOG_WARNING(Audio_Sink, + "Error getting minimum output latency, falling back to default latency."); } cubeb_devid output_device = nullptr; if (target_device_name != auto_device_name && !target_device_name.empty()) { cubeb_device_collection collection; - if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { - LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); - } else { + if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) == CUBEB_OK) { const auto collection_end{collection.device + collection.count}; const auto device{ std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { @@ -63,12 +62,15 @@ CubebSink::CubebSink(std::string_view target_device_name) : impl(std::make_uniqu output_device = device->devid; } cubeb_device_collection_destroy(impl->ctx, &collection); + } else { + LOG_WARNING(Audio_Sink, + "Audio output device enumeration not supported, using default device."); } } - int stream_err = cubeb_stream_init(impl->ctx, &impl->stream, "CitraAudio", nullptr, nullptr, - output_device, ¶ms, std::max(512u, minimum_latency), - &Impl::DataCallback, &Impl::StateCallback, impl.get()); + auto stream_err = cubeb_stream_init(impl->ctx, &impl->stream, "CitraAudio", nullptr, nullptr, + output_device, ¶ms, std::max(512u, minimum_latency), + &Impl::DataCallback, &Impl::StateCallback, impl.get()); if (stream_err != CUBEB_OK) { switch (stream_err) { case CUBEB_ERROR: @@ -92,23 +94,20 @@ CubebSink::CubebSink(std::string_view target_device_name) : impl(std::make_uniqu } CubebSink::~CubebSink() { - if (!impl->ctx) { - return; + if (impl->stream) { + if (cubeb_stream_stop(impl->stream) != CUBEB_OK) { + LOG_ERROR(Audio_Sink, "Error stopping cubeb stream."); + } + cubeb_stream_destroy(impl->stream); } - if (cubeb_stream_stop(impl->stream) != CUBEB_OK) { - LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); + if (impl->ctx) { + cubeb_destroy(impl->ctx); } - - cubeb_stream_destroy(impl->stream); - cubeb_destroy(impl->ctx); } unsigned int CubebSink::GetNativeSampleRate() const { - if (!impl->ctx) - return native_sample_rate; - - return impl->sample_rate; + return native_sample_rate; } void CubebSink::SetCallback(std::function cb) { @@ -121,13 +120,12 @@ long CubebSink::Impl::DataCallback(cubeb_stream* stream, void* user_data, const auto* buffer = static_cast(output_buffer); if (!impl || !impl->cb) { - LOG_DEBUG(Audio_Sink, "Emitting zeros"); + LOG_DEBUG(Audio_Sink, "Missing internal data and/or audio callback, emitting zeroes."); std::memset(output_buffer, 0, num_frames * 2 * sizeof(s16)); - return num_frames; + } else { + impl->cb(buffer, num_frames); } - impl->cb(buffer, num_frames); - return num_frames; } @@ -149,7 +147,7 @@ void CubebSink::Impl::StateCallback(cubeb_stream* stream, void* user_data, cubeb } void CubebSink::Impl::LogCallback(char const* format, ...) { - std::array buffer; + std::array buffer{}; std::va_list args; va_start(args, format); #ifdef _MSC_VER @@ -166,15 +164,13 @@ std::vector ListCubebSinkDevices() { std::vector device_list; cubeb* ctx; - if (cubeb_init(&ctx, "CitraEnumerator", nullptr) != CUBEB_OK) { + if (cubeb_init(&ctx, "Citra Output Device Enumerator", nullptr) != CUBEB_OK) { LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); return {}; } cubeb_device_collection collection; - if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { - LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); - } else { + if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) == CUBEB_OK) { for (std::size_t i = 0; i < collection.count; i++) { const cubeb_device_info& device = collection.device[i]; if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) { @@ -182,6 +178,8 @@ std::vector ListCubebSinkDevices() { } } cubeb_device_collection_destroy(ctx, &collection); + } else { + LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported."); } cubeb_destroy(ctx);