mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	audio_core: Clean up cubeb backend. (#6677)
This commit is contained in:
		
							parent
							
								
									a94af8ea62
								
							
						
					
					
						commit
						f3ac6f054f
					
				
					 2 changed files with 71 additions and 70 deletions
				
			
		|  | @ -37,39 +37,33 @@ CubebInput::CubebInput(std::string device_id) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CubebInput::~CubebInput() { | CubebInput::~CubebInput() { | ||||||
|     if (!impl->ctx) |  | ||||||
|         return; |  | ||||||
| 
 |  | ||||||
|     if (impl->stream) { |     if (impl->stream) { | ||||||
|         if (cubeb_stream_stop(impl->stream) != CUBEB_OK) { |         if (cubeb_stream_stop(impl->stream) != CUBEB_OK) { | ||||||
|             LOG_ERROR(Audio, "Error stopping cubeb input stream."); |             LOG_ERROR(Audio, "Error stopping cubeb input stream."); | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|         cubeb_stream_destroy(impl->stream); |         cubeb_stream_destroy(impl->stream); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     cubeb_destroy(impl->ctx); |     if (impl->ctx) { | ||||||
|  |         cubeb_destroy(impl->ctx); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CubebInput::StartSampling(const InputParameters& params) { | void CubebInput::StartSampling(const InputParameters& params) { | ||||||
|     // Cubeb apparently only supports signed 16 bit PCM (and float32 which the 3ds doesn't support)
 |     // 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) { |     if (params.sign == Signedness::Unsigned) { | ||||||
|         LOG_ERROR(Audio, |         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; |     parameters = params; | ||||||
|     is_sampling = true; |     impl->sample_size_in_bytes = params.sample_size / 8; | ||||||
| 
 | 
 | ||||||
|     cubeb_devid input_device = nullptr; |     cubeb_devid input_device = nullptr; | ||||||
|     if (device_id != auto_device_name && !device_id.empty()) { |     if (device_id != auto_device_name && !device_id.empty()) { | ||||||
|         cubeb_device_collection collection; |         cubeb_device_collection collection; | ||||||
|         if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) { |         if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) == CUBEB_OK) { | ||||||
|             LOG_WARNING(Audio, "Audio input device enumeration not supported"); |  | ||||||
|         } else { |  | ||||||
|             const auto collection_end = collection.device + collection.count; |             const auto collection_end = collection.device + collection.count; | ||||||
|             const auto device = std::find_if( |             const auto device = std::find_if( | ||||||
|                 collection.device, collection_end, [this](const cubeb_device_info& info) { |                 collection.device, collection_end, [this](const cubeb_device_info& info) { | ||||||
|  | @ -79,39 +73,42 @@ void CubebInput::StartSampling(const InputParameters& params) { | ||||||
|                 input_device = device->devid; |                 input_device = device->devid; | ||||||
|             } |             } | ||||||
|             cubeb_device_collection_destroy(impl->ctx, &collection); |             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; |     cubeb_stream_params input_params = { | ||||||
|     input_params.channels = 1; |         .format = CUBEB_SAMPLE_S16LE, | ||||||
|     input_params.layout = CUBEB_LAYOUT_UNDEFINED; |         .rate = params.sample_rate, | ||||||
|     input_params.prefs = CUBEB_STREAM_PREF_NONE; |         .channels = 1, | ||||||
|     input_params.format = CUBEB_SAMPLE_S16LE; |         .layout = CUBEB_LAYOUT_UNDEFINED, | ||||||
|     input_params.rate = params.sample_rate; |     }; | ||||||
| 
 | 
 | ||||||
|     u32 latency_frames = 512; // Firefox default
 |     u32 latency_frames = 512; // Firefox default
 | ||||||
|     if (cubeb_get_min_latency(impl->ctx, &input_params, &latency_frames) != CUBEB_OK) { |     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, |     if (cubeb_stream_init(impl->ctx, &impl->stream, "Citra Microphone", input_device, &input_params, | ||||||
|                           nullptr, nullptr, latency_frames, Impl::DataCallback, Impl::StateCallback, |                           nullptr, nullptr, latency_frames, Impl::DataCallback, Impl::StateCallback, | ||||||
|                           impl.get()) != CUBEB_OK) { |                           impl.get()) != CUBEB_OK) { | ||||||
|         LOG_CRITICAL(Audio, "Error creating cubeb input stream"); |         LOG_CRITICAL(Audio, "Error creating cubeb input stream."); | ||||||
|         is_sampling = false; |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (cubeb_stream_start(impl->stream) != CUBEB_OK) { |     if (cubeb_stream_start(impl->stream) != CUBEB_OK) { | ||||||
|         LOG_CRITICAL(Audio, "Error starting cubeb input stream"); |         LOG_CRITICAL(Audio, "Error starting cubeb input stream."); | ||||||
|         is_sampling = false; |         cubeb_stream_destroy(impl->stream); | ||||||
|  |         impl->stream = nullptr; | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     is_sampling = true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CubebInput::StopSampling() { | void CubebInput::StopSampling() { | ||||||
|     // TODO(xperia64): Destroy the stream for now to avoid a leak because StartSampling
 |  | ||||||
|     // reinitializes the stream every time
 |  | ||||||
|     if (impl->stream) { |     if (impl->stream) { | ||||||
|         cubeb_stream_stop(impl->stream); |         cubeb_stream_stop(impl->stream); | ||||||
|         cubeb_stream_destroy(impl->stream); |         cubeb_stream_destroy(impl->stream); | ||||||
|  | @ -121,8 +118,14 @@ void CubebInput::StopSampling() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CubebInput::AdjustSampleRate(u32 sample_rate) { | void CubebInput::AdjustSampleRate(u32 sample_rate) { | ||||||
|     // TODO This should restart the stream with the new sample rate
 |     if (!is_sampling) { | ||||||
|     LOG_ERROR(Audio, "AdjustSampleRate unimplemented!"); |         return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto new_parameters = parameters; | ||||||
|  |     new_parameters.sample_rate = sample_rate; | ||||||
|  |     StopSampling(); | ||||||
|  |     StartSampling(new_parameters); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Samples CubebInput::Read() { | Samples CubebInput::Read() { | ||||||
|  | @ -136,7 +139,7 @@ Samples CubebInput::Read() { | ||||||
| 
 | 
 | ||||||
| long CubebInput::Impl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, | long CubebInput::Impl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer, | ||||||
|                                     void* output_buffer, long num_frames) { |                                     void* output_buffer, long num_frames) { | ||||||
|     Impl* impl = static_cast<Impl*>(user_data); |     auto impl = static_cast<Impl*>(user_data); | ||||||
|     if (!impl) { |     if (!impl) { | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  | @ -177,9 +180,7 @@ std::vector<std::string> ListCubebInputDevices() { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     cubeb_device_collection collection; |     cubeb_device_collection collection; | ||||||
|     if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) != CUBEB_OK) { |     if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection) == CUBEB_OK) { | ||||||
|         LOG_WARNING(Audio_Sink, "Audio input device enumeration not supported"); |  | ||||||
|     } else { |  | ||||||
|         for (std::size_t i = 0; i < collection.count; i++) { |         for (std::size_t i = 0; i < collection.count; i++) { | ||||||
|             const cubeb_device_info& device = collection.device[i]; |             const cubeb_device_info& device = collection.device[i]; | ||||||
|             if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) { |             if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) { | ||||||
|  | @ -187,6 +188,8 @@ std::vector<std::string> ListCubebInputDevices() { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         cubeb_device_collection_destroy(ctx, &collection); |         cubeb_device_collection_destroy(ctx, &collection); | ||||||
|  |     } else { | ||||||
|  |         LOG_WARNING(Audio_Sink, "Audio input device enumeration not supported."); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     cubeb_destroy(ctx); |     cubeb_destroy(ctx); | ||||||
|  |  | ||||||
|  | @ -13,8 +13,6 @@ | ||||||
| namespace AudioCore { | namespace AudioCore { | ||||||
| 
 | 
 | ||||||
| struct CubebSink::Impl { | struct CubebSink::Impl { | ||||||
|     unsigned int sample_rate = 0; |  | ||||||
| 
 |  | ||||||
|     cubeb* ctx = nullptr; |     cubeb* ctx = nullptr; | ||||||
|     cubeb_stream* stream = 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"); |         LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | ||||||
|         return; |         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; |     cubeb_stream_params params = { | ||||||
|     params.rate = impl->sample_rate; |         .format = CUBEB_SAMPLE_S16LE, | ||||||
|     params.channels = 2; |         .rate = native_sample_rate, | ||||||
|     params.layout = CUBEB_LAYOUT_STEREO; |         .channels = 2, | ||||||
|     params.format = CUBEB_SAMPLE_S16NE; |         .layout = CUBEB_LAYOUT_STEREO, | ||||||
|     params.prefs = CUBEB_STREAM_PREF_PERSIST; |     }; | ||||||
| 
 | 
 | ||||||
|     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) { |     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; |     cubeb_devid output_device = nullptr; | ||||||
|     if (target_device_name != auto_device_name && !target_device_name.empty()) { |     if (target_device_name != auto_device_name && !target_device_name.empty()) { | ||||||
|         cubeb_device_collection collection; |         cubeb_device_collection collection; | ||||||
|         if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { |         if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) == CUBEB_OK) { | ||||||
|             LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); |  | ||||||
|         } else { |  | ||||||
|             const auto collection_end{collection.device + collection.count}; |             const auto collection_end{collection.device + collection.count}; | ||||||
|             const auto device{ |             const auto device{ | ||||||
|                 std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { |                 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; |                 output_device = device->devid; | ||||||
|             } |             } | ||||||
|             cubeb_device_collection_destroy(impl->ctx, &collection); |             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, |     auto stream_err = cubeb_stream_init(impl->ctx, &impl->stream, "CitraAudio", nullptr, nullptr, | ||||||
|                                        output_device, ¶ms, std::max(512u, minimum_latency), |                                         output_device, ¶ms, std::max(512u, minimum_latency), | ||||||
|                                        &Impl::DataCallback, &Impl::StateCallback, impl.get()); |                                         &Impl::DataCallback, &Impl::StateCallback, impl.get()); | ||||||
|     if (stream_err != CUBEB_OK) { |     if (stream_err != CUBEB_OK) { | ||||||
|         switch (stream_err) { |         switch (stream_err) { | ||||||
|         case CUBEB_ERROR: |         case CUBEB_ERROR: | ||||||
|  | @ -92,23 +94,20 @@ CubebSink::CubebSink(std::string_view target_device_name) : impl(std::make_uniqu | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| CubebSink::~CubebSink() { | CubebSink::~CubebSink() { | ||||||
|     if (!impl->ctx) { |     if (impl->stream) { | ||||||
|         return; |         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) { |     if (impl->ctx) { | ||||||
|         LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream"); |         cubeb_destroy(impl->ctx); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     cubeb_stream_destroy(impl->stream); |  | ||||||
|     cubeb_destroy(impl->ctx); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| unsigned int CubebSink::GetNativeSampleRate() const { | unsigned int CubebSink::GetNativeSampleRate() const { | ||||||
|     if (!impl->ctx) |     return native_sample_rate; | ||||||
|         return native_sample_rate; |  | ||||||
| 
 |  | ||||||
|     return impl->sample_rate; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CubebSink::SetCallback(std::function<void(s16*, std::size_t)> cb) { | void CubebSink::SetCallback(std::function<void(s16*, std::size_t)> cb) { | ||||||
|  | @ -121,13 +120,12 @@ long CubebSink::Impl::DataCallback(cubeb_stream* stream, void* user_data, const | ||||||
|     auto* buffer = static_cast<s16*>(output_buffer); |     auto* buffer = static_cast<s16*>(output_buffer); | ||||||
| 
 | 
 | ||||||
|     if (!impl || !impl->cb) { |     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)); |         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; |     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, ...) { | void CubebSink::Impl::LogCallback(char const* format, ...) { | ||||||
|     std::array<char, 512> buffer; |     std::array<char, 512> buffer{}; | ||||||
|     std::va_list args; |     std::va_list args; | ||||||
|     va_start(args, format); |     va_start(args, format); | ||||||
| #ifdef _MSC_VER | #ifdef _MSC_VER | ||||||
|  | @ -166,15 +164,13 @@ std::vector<std::string> ListCubebSinkDevices() { | ||||||
|     std::vector<std::string> device_list; |     std::vector<std::string> device_list; | ||||||
|     cubeb* ctx; |     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"); |         LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | ||||||
|         return {}; |         return {}; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     cubeb_device_collection collection; |     cubeb_device_collection collection; | ||||||
|     if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { |     if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) == CUBEB_OK) { | ||||||
|         LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); |  | ||||||
|     } else { |  | ||||||
|         for (std::size_t i = 0; i < collection.count; i++) { |         for (std::size_t i = 0; i < collection.count; i++) { | ||||||
|             const cubeb_device_info& device = collection.device[i]; |             const cubeb_device_info& device = collection.device[i]; | ||||||
|             if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) { |             if (device.state == CUBEB_DEVICE_STATE_ENABLED && device.friendly_name) { | ||||||
|  | @ -182,6 +178,8 @@ std::vector<std::string> ListCubebSinkDevices() { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         cubeb_device_collection_destroy(ctx, &collection); |         cubeb_device_collection_destroy(ctx, &collection); | ||||||
|  |     } else { | ||||||
|  |         LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported."); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     cubeb_destroy(ctx); |     cubeb_destroy(ctx); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue