mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	sink_details: Listing available devices should be separate from sink construction
This commit is contained in:
		
							parent
							
								
									ce5a5412ae
								
							
						
					
					
						commit
						d269beab0d
					
				
					 13 changed files with 84 additions and 70 deletions
				
			
		|  | @ -7,7 +7,6 @@ | |||
| #include "audio_core/audio_types.h" | ||||
| #include "audio_core/cubeb_sink.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| 
 | ||||
|  | @ -25,13 +24,12 @@ struct CubebSink::Impl { | |||
|     static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state); | ||||
| }; | ||||
| 
 | ||||
| CubebSink::CubebSink() : impl(std::make_unique<Impl>()) { | ||||
| CubebSink::CubebSink(std::string target_device_name) : impl(std::make_unique<Impl>()) { | ||||
|     if (cubeb_init(&impl->ctx, "Citra", nullptr) != CUBEB_OK) { | ||||
|         LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const char* target_device_name = nullptr; | ||||
|     cubeb_devid output_device = nullptr; | ||||
| 
 | ||||
|     cubeb_stream_params params; | ||||
|  | @ -46,27 +44,21 @@ CubebSink::CubebSink() : impl(std::make_unique<Impl>()) { | |||
|     if (cubeb_get_min_latency(impl->ctx, ¶ms, &minimum_latency) != CUBEB_OK) | ||||
|         LOG_CRITICAL(Audio_Sink, "Error getting minimum latency"); | ||||
| 
 | ||||
|     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 (collection.count >= 1 && Settings::values.audio_device_id != "auto" && | ||||
|             !Settings::values.audio_device_id.empty()) { | ||||
|             target_device_name = Settings::values.audio_device_id.c_str(); | ||||
|         } | ||||
| 
 | ||||
|         for (size_t i = 0; i < collection.count; i++) { | ||||
|             const cubeb_device_info& device = collection.device[i]; | ||||
|             if (device.friendly_name) { | ||||
|                 impl->device_list.emplace_back(device.friendly_name); | ||||
| 
 | ||||
|                 if (target_device_name && strcmp(target_device_name, device.friendly_name) == 0) { | ||||
|                     output_device = device.devid; | ||||
|                 } | ||||
|     if (target_device_name != "auto" && !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 { | ||||
|             const auto collection_end = collection.device + collection.count; | ||||
|             const auto device = std::find_if(collection.device, collection_end, | ||||
|                                              [&](const cubeb_device_info& device) { | ||||
|                                                  return target_device_name == device.friendly_name; | ||||
|                                              }); | ||||
|             if (device != collection_end) { | ||||
|                 output_device = device->devid; | ||||
|             } | ||||
|             cubeb_device_collection_destroy(impl->ctx, &collection); | ||||
|         } | ||||
| 
 | ||||
|         cubeb_device_collection_destroy(impl->ctx, &collection); | ||||
|     } | ||||
| 
 | ||||
|     if (cubeb_stream_init(impl->ctx, &impl->stream, "Citra Audio Output", nullptr, nullptr, | ||||
|  | @ -101,10 +93,6 @@ unsigned int CubebSink::GetNativeSampleRate() const { | |||
|     return impl->sample_rate; | ||||
| } | ||||
| 
 | ||||
| std::vector<std::string> CubebSink::GetDeviceList() const { | ||||
|     return impl->device_list; | ||||
| } | ||||
| 
 | ||||
| void CubebSink::EnqueueSamples(const s16* samples, size_t sample_count) { | ||||
|     if (!impl->ctx) | ||||
|         return; | ||||
|  | @ -144,4 +132,30 @@ long CubebSink::Impl::DataCallback(cubeb_stream* stream, void* user_data, const | |||
| 
 | ||||
| void CubebSink::Impl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {} | ||||
| 
 | ||||
| std::vector<std::string> ListCubebSinkDevices() { | ||||
|     std::vector<std::string> device_list; | ||||
|     cubeb* ctx; | ||||
| 
 | ||||
|     if (cubeb_init(&ctx, "Citra 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 { | ||||
|         for (size_t i = 0; i < collection.count; i++) { | ||||
|             const cubeb_device_info& device = collection.device[i]; | ||||
|             if (device.friendly_name) { | ||||
|                 device_list.emplace_back(device.friendly_name); | ||||
|             } | ||||
|         } | ||||
|         cubeb_device_collection_destroy(ctx, &collection); | ||||
|     } | ||||
| 
 | ||||
|     cubeb_destroy(ctx); | ||||
|     return device_list; | ||||
| } | ||||
| 
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ namespace AudioCore { | |||
| 
 | ||||
| class CubebSink final : public Sink { | ||||
| public: | ||||
|     CubebSink(); | ||||
|     explicit CubebSink(std::string device_id); | ||||
|     ~CubebSink() override; | ||||
| 
 | ||||
|     unsigned int GetNativeSampleRate() const override; | ||||
|  | @ -21,11 +21,11 @@ public: | |||
| 
 | ||||
|     size_t SamplesInQueue() const override; | ||||
| 
 | ||||
|     std::vector<std::string> GetDeviceList() const override; | ||||
| 
 | ||||
| private: | ||||
|     struct Impl; | ||||
|     std::unique_ptr<Impl> impl; | ||||
| }; | ||||
| 
 | ||||
| std::vector<std::string> ListCubebSinkDevices(); | ||||
| 
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -18,9 +18,9 @@ DspInterface::~DspInterface() { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void DspInterface::SetSink(const std::string& sink_id) { | ||||
| void DspInterface::SetSink(const std::string& sink_id, const std::string& audio_device) { | ||||
|     const SinkDetails& sink_details = GetSinkDetails(sink_id); | ||||
|     sink = sink_details.factory(); | ||||
|     sink = sink_details.factory(audio_device); | ||||
|     time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate()); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -61,7 +61,7 @@ public: | |||
|     virtual std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() = 0; | ||||
| 
 | ||||
|     /// Select the sink to use based on sink id.
 | ||||
|     void SetSink(const std::string& sink_id); | ||||
|     void SetSink(const std::string& sink_id, const std::string& audio_device); | ||||
|     /// Get the current sink
 | ||||
|     Sink& GetSink(); | ||||
|     /// Enable/Disable audio stretching.
 | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ namespace AudioCore { | |||
| 
 | ||||
| class NullSink final : public Sink { | ||||
| public: | ||||
|     NullSink(std::string) {} | ||||
|     ~NullSink() override = default; | ||||
| 
 | ||||
|     unsigned int GetNativeSampleRate() const override { | ||||
|  | @ -23,10 +24,6 @@ public: | |||
|     size_t SamplesInQueue() const override { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     std::vector<std::string> GetDeviceList() const override { | ||||
|         return {}; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -9,7 +9,6 @@ | |||
| #include "audio_core/sdl2_sink.h" | ||||
| #include "common/assert.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/settings.h" | ||||
| 
 | ||||
| namespace AudioCore { | ||||
| 
 | ||||
|  | @ -23,7 +22,7 @@ struct SDL2Sink::Impl { | |||
|     static void Callback(void* impl_, u8* buffer, int buffer_size_in_bytes); | ||||
| }; | ||||
| 
 | ||||
| SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { | ||||
| SDL2Sink::SDL2Sink(std::string device_name) : impl(std::make_unique<Impl>()) { | ||||
|     if (SDL_Init(SDL_INIT_AUDIO) < 0) { | ||||
|         LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed with: {}", SDL_GetError()); | ||||
|         impl->audio_device_id = 0; | ||||
|  | @ -42,24 +41,16 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) { | |||
|     SDL_AudioSpec obtained_audiospec; | ||||
|     SDL_zero(obtained_audiospec); | ||||
| 
 | ||||
|     int device_count = SDL_GetNumAudioDevices(0); | ||||
|     device_list.clear(); | ||||
|     for (int i = 0; i < device_count; ++i) { | ||||
|         device_list.push_back(SDL_GetAudioDeviceName(i, 0)); | ||||
|     } | ||||
| 
 | ||||
|     const char* device = nullptr; | ||||
| 
 | ||||
|     if (device_count >= 1 && Settings::values.audio_device_id != "auto" && | ||||
|         !Settings::values.audio_device_id.empty()) { | ||||
|         device = Settings::values.audio_device_id.c_str(); | ||||
|     if (device_name != "auto" && !device_name.empty()) { | ||||
|         device = device_name.c_str(); | ||||
|     } | ||||
| 
 | ||||
|     impl->audio_device_id = SDL_OpenAudioDevice( | ||||
|         device, false, &desired_audiospec, &obtained_audiospec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); | ||||
|     if (impl->audio_device_id <= 0) { | ||||
|         LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed with code {} for device \"{}\"", | ||||
|                      impl->audio_device_id, Settings::values.audio_device_id); | ||||
|                      impl->audio_device_id, device_name); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|  | @ -83,10 +74,6 @@ unsigned int SDL2Sink::GetNativeSampleRate() const { | |||
|     return impl->sample_rate; | ||||
| } | ||||
| 
 | ||||
| std::vector<std::string> SDL2Sink::GetDeviceList() const { | ||||
|     return device_list; | ||||
| } | ||||
| 
 | ||||
| void SDL2Sink::EnqueueSamples(const s16* samples, size_t sample_count) { | ||||
|     if (impl->audio_device_id <= 0) | ||||
|         return; | ||||
|  | @ -140,4 +127,21 @@ void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) | |||
|     } | ||||
| } | ||||
| 
 | ||||
| std::vector<std::string> ListSDL2SinkDevices() { | ||||
|     if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { | ||||
|         LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem failed with: {}", SDL_GetError()); | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     std::vector<std::string> device_list; | ||||
|     const int device_count = SDL_GetNumAudioDevices(0); | ||||
|     for (int i = 0; i < device_count; ++i) { | ||||
|         device_list.push_back(SDL_GetAudioDeviceName(i, 0)); | ||||
|     } | ||||
| 
 | ||||
|     SDL_QuitSubSystem(SDL_INIT_AUDIO); | ||||
| 
 | ||||
|     return device_list; | ||||
| } | ||||
| 
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ namespace AudioCore { | |||
| 
 | ||||
| class SDL2Sink final : public Sink { | ||||
| public: | ||||
|     SDL2Sink(); | ||||
|     explicit SDL2Sink(std::string device_id); | ||||
|     ~SDL2Sink() override; | ||||
| 
 | ||||
|     unsigned int GetNativeSampleRate() const override; | ||||
|  | @ -21,12 +21,11 @@ public: | |||
| 
 | ||||
|     size_t SamplesInQueue() const override; | ||||
| 
 | ||||
|     std::vector<std::string> GetDeviceList() const override; | ||||
| 
 | ||||
| private: | ||||
|     struct Impl; | ||||
|     std::unique_ptr<Impl> impl; | ||||
|     std::vector<std::string> device_list; | ||||
| }; | ||||
| 
 | ||||
| std::vector<std::string> ListSDL2SinkDevices(); | ||||
| 
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -31,9 +31,6 @@ public: | |||
| 
 | ||||
|     /// Samples enqueued that have not been played yet.
 | ||||
|     virtual std::size_t SamplesInQueue() const = 0; | ||||
| 
 | ||||
|     /// Returns the list of available devices.
 | ||||
|     virtual std::vector<std::string> GetDeviceList() const = 0; | ||||
| }; | ||||
| 
 | ||||
| } // namespace AudioCore
 | ||||
|  |  | |||
|  | @ -21,12 +21,13 @@ namespace AudioCore { | |||
| // g_sink_details is ordered in terms of desirability, with the best choice at the top.
 | ||||
| const std::vector<SinkDetails> g_sink_details = { | ||||
| #ifdef HAVE_CUBEB | ||||
|     {"cubeb", &std::make_unique<CubebSink>}, | ||||
|     SinkDetails{"cubeb", &std::make_unique<CubebSink, std::string>, &ListCubebSinkDevices}, | ||||
| #endif | ||||
| #ifdef HAVE_SDL2 | ||||
|     {"sdl2", &std::make_unique<SDL2Sink>}, | ||||
|     SinkDetails{"sdl2", &std::make_unique<SDL2Sink, std::string>, &ListSDL2SinkDevices}, | ||||
| #endif | ||||
|     {"null", &std::make_unique<NullSink>}, | ||||
|     SinkDetails{"null", &std::make_unique<NullSink, std::string>, | ||||
|                 [] { return std::vector<std::string>{"null"}; }}, | ||||
| }; | ||||
| 
 | ||||
| const SinkDetails& GetSinkDetails(std::string sink_id) { | ||||
|  |  | |||
|  | @ -13,13 +13,16 @@ namespace AudioCore { | |||
| class Sink; | ||||
| 
 | ||||
| struct SinkDetails { | ||||
|     SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>()> factory_) | ||||
|         : id(id_), factory(factory_) {} | ||||
|     SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>(std::string)> factory_, | ||||
|                 std::function<std::vector<std::string>()> list_devices_) | ||||
|         : id(id_), factory(factory_), list_devices(list_devices_) {} | ||||
| 
 | ||||
|     /// Name for this sink.
 | ||||
|     const char* id; | ||||
|     /// A method to call to construct an instance of this type of sink.
 | ||||
|     std::function<std::unique_ptr<Sink>()> factory; | ||||
|     std::function<std::unique_ptr<Sink>(std::string device_id)> factory; | ||||
|     /// A method to call to list available devices.
 | ||||
|     std::function<std::vector<std::string>()> list_devices; | ||||
| }; | ||||
| 
 | ||||
| extern const std::vector<SinkDetails> g_sink_details; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue