sink_details: Listing available devices should be separate from sink construction
This commit is contained in:
parent
ce5a5412ae
commit
d269beab0d
@ -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,28 +44,22 @@ 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");
|
||||
|
||||
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 {
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cubeb_device_collection_destroy(impl->ctx, &collection);
|
||||
}
|
||||
}
|
||||
|
||||
if (cubeb_stream_init(impl->ctx, &impl->stream, "Citra Audio Output", nullptr, nullptr,
|
||||
output_device, ¶ms, std::max(512u, minimum_latency),
|
||||
@ -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;
|
||||
|
@ -69,8 +69,7 @@ void ConfigureAudio::updateAudioDevices(int sink_index) {
|
||||
ui->audio_device_combo_box->addItem("auto");
|
||||
|
||||
std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
|
||||
std::vector<std::string> device_list =
|
||||
AudioCore::GetSinkDetails(sink_id).factory()->GetDeviceList();
|
||||
std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices();
|
||||
for (const auto& device : device_list) {
|
||||
ui->audio_device_combo_box->addItem(device.c_str());
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||
}
|
||||
|
||||
dsp_core = std::make_unique<AudioCore::DspHle>();
|
||||
dsp_core->SetSink(Settings::values.sink_id);
|
||||
dsp_core->SetSink(Settings::values.sink_id, Settings::values.audio_device_id);
|
||||
dsp_core->EnableStretching(Settings::values.enable_audio_stretching);
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
|
@ -32,7 +32,7 @@ void Apply() {
|
||||
}
|
||||
|
||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
||||
Core::DSP().SetSink(values.sink_id);
|
||||
Core::DSP().SetSink(values.sink_id, values.audio_device_id);
|
||||
Core::DSP().EnableStretching(values.enable_audio_stretching);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user