mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2024-11-11 04:35:05 +01:00
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/audio_types.h"
|
||||||
#include "audio_core/cubeb_sink.h"
|
#include "audio_core/cubeb_sink.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/settings.h"
|
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
@ -25,13 +24,12 @@ struct CubebSink::Impl {
|
|||||||
static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state);
|
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) {
|
if (cubeb_init(&impl->ctx, "Citra", nullptr) != CUBEB_OK) {
|
||||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* target_device_name = nullptr;
|
|
||||||
cubeb_devid output_device = nullptr;
|
cubeb_devid output_device = nullptr;
|
||||||
|
|
||||||
cubeb_stream_params params;
|
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)
|
if (cubeb_get_min_latency(impl->ctx, ¶ms, &minimum_latency) != CUBEB_OK)
|
||||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
|
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
|
||||||
|
|
||||||
cubeb_device_collection collection;
|
if (target_device_name != "auto" && !target_device_name.empty()) {
|
||||||
if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
|
cubeb_device_collection collection;
|
||||||
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
if (cubeb_enumerate_devices(impl->ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
|
||||||
} else {
|
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
||||||
if (collection.count >= 1 && Settings::values.audio_device_id != "auto" &&
|
} else {
|
||||||
!Settings::values.audio_device_id.empty()) {
|
const auto collection_end = collection.device + collection.count;
|
||||||
target_device_name = Settings::values.audio_device_id.c_str();
|
const auto device = std::find_if(collection.device, collection_end,
|
||||||
}
|
[&](const cubeb_device_info& device) {
|
||||||
|
return target_device_name == device.friendly_name;
|
||||||
for (size_t i = 0; i < collection.count; i++) {
|
});
|
||||||
const cubeb_device_info& device = collection.device[i];
|
if (device != collection_end) {
|
||||||
if (device.friendly_name) {
|
output_device = device->devid;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
cubeb_device_collection_destroy(impl->ctx, &collection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cubeb_stream_init(impl->ctx, &impl->stream, "Citra Audio Output", nullptr, nullptr,
|
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;
|
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) {
|
void CubebSink::EnqueueSamples(const s16* samples, size_t sample_count) {
|
||||||
if (!impl->ctx)
|
if (!impl->ctx)
|
||||||
return;
|
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) {}
|
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
|
} // namespace AudioCore
|
||||||
|
@ -12,7 +12,7 @@ namespace AudioCore {
|
|||||||
|
|
||||||
class CubebSink final : public Sink {
|
class CubebSink final : public Sink {
|
||||||
public:
|
public:
|
||||||
CubebSink();
|
explicit CubebSink(std::string device_id);
|
||||||
~CubebSink() override;
|
~CubebSink() override;
|
||||||
|
|
||||||
unsigned int GetNativeSampleRate() const override;
|
unsigned int GetNativeSampleRate() const override;
|
||||||
@ -21,11 +21,11 @@ public:
|
|||||||
|
|
||||||
size_t SamplesInQueue() const override;
|
size_t SamplesInQueue() const override;
|
||||||
|
|
||||||
std::vector<std::string> GetDeviceList() const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
std::unique_ptr<Impl> impl;
|
std::unique_ptr<Impl> impl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<std::string> ListCubebSinkDevices();
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // 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);
|
const SinkDetails& sink_details = GetSinkDetails(sink_id);
|
||||||
sink = sink_details.factory();
|
sink = sink_details.factory(audio_device);
|
||||||
time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
|
time_stretcher.SetOutputSampleRate(sink->GetNativeSampleRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ public:
|
|||||||
virtual std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() = 0;
|
virtual std::array<u8, Memory::DSP_RAM_SIZE>& GetDspMemory() = 0;
|
||||||
|
|
||||||
/// Select the sink to use based on sink id.
|
/// 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
|
/// Get the current sink
|
||||||
Sink& GetSink();
|
Sink& GetSink();
|
||||||
/// Enable/Disable audio stretching.
|
/// Enable/Disable audio stretching.
|
||||||
|
@ -12,6 +12,7 @@ namespace AudioCore {
|
|||||||
|
|
||||||
class NullSink final : public Sink {
|
class NullSink final : public Sink {
|
||||||
public:
|
public:
|
||||||
|
NullSink(std::string) {}
|
||||||
~NullSink() override = default;
|
~NullSink() override = default;
|
||||||
|
|
||||||
unsigned int GetNativeSampleRate() const override {
|
unsigned int GetNativeSampleRate() const override {
|
||||||
@ -23,10 +24,6 @@ public:
|
|||||||
size_t SamplesInQueue() const override {
|
size_t SamplesInQueue() const override {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> GetDeviceList() const override {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#include "audio_core/sdl2_sink.h"
|
#include "audio_core/sdl2_sink.h"
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/settings.h"
|
|
||||||
|
|
||||||
namespace AudioCore {
|
namespace AudioCore {
|
||||||
|
|
||||||
@ -23,7 +22,7 @@ struct SDL2Sink::Impl {
|
|||||||
static void Callback(void* impl_, u8* buffer, int buffer_size_in_bytes);
|
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) {
|
if (SDL_Init(SDL_INIT_AUDIO) < 0) {
|
||||||
LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed with: {}", SDL_GetError());
|
LOG_CRITICAL(Audio_Sink, "SDL_Init(SDL_INIT_AUDIO) failed with: {}", SDL_GetError());
|
||||||
impl->audio_device_id = 0;
|
impl->audio_device_id = 0;
|
||||||
@ -42,24 +41,16 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
|
|||||||
SDL_AudioSpec obtained_audiospec;
|
SDL_AudioSpec obtained_audiospec;
|
||||||
SDL_zero(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;
|
const char* device = nullptr;
|
||||||
|
if (device_name != "auto" && !device_name.empty()) {
|
||||||
if (device_count >= 1 && Settings::values.audio_device_id != "auto" &&
|
device = device_name.c_str();
|
||||||
!Settings::values.audio_device_id.empty()) {
|
|
||||||
device = Settings::values.audio_device_id.c_str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl->audio_device_id = SDL_OpenAudioDevice(
|
impl->audio_device_id = SDL_OpenAudioDevice(
|
||||||
device, false, &desired_audiospec, &obtained_audiospec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
device, false, &desired_audiospec, &obtained_audiospec, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
||||||
if (impl->audio_device_id <= 0) {
|
if (impl->audio_device_id <= 0) {
|
||||||
LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed with code {} for device \"{}\"",
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +74,6 @@ unsigned int SDL2Sink::GetNativeSampleRate() const {
|
|||||||
return impl->sample_rate;
|
return impl->sample_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> SDL2Sink::GetDeviceList() const {
|
|
||||||
return device_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL2Sink::EnqueueSamples(const s16* samples, size_t sample_count) {
|
void SDL2Sink::EnqueueSamples(const s16* samples, size_t sample_count) {
|
||||||
if (impl->audio_device_id <= 0)
|
if (impl->audio_device_id <= 0)
|
||||||
return;
|
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
|
} // namespace AudioCore
|
||||||
|
@ -12,7 +12,7 @@ namespace AudioCore {
|
|||||||
|
|
||||||
class SDL2Sink final : public Sink {
|
class SDL2Sink final : public Sink {
|
||||||
public:
|
public:
|
||||||
SDL2Sink();
|
explicit SDL2Sink(std::string device_id);
|
||||||
~SDL2Sink() override;
|
~SDL2Sink() override;
|
||||||
|
|
||||||
unsigned int GetNativeSampleRate() const override;
|
unsigned int GetNativeSampleRate() const override;
|
||||||
@ -21,12 +21,11 @@ public:
|
|||||||
|
|
||||||
size_t SamplesInQueue() const override;
|
size_t SamplesInQueue() const override;
|
||||||
|
|
||||||
std::vector<std::string> GetDeviceList() const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
std::unique_ptr<Impl> impl;
|
std::unique_ptr<Impl> impl;
|
||||||
std::vector<std::string> device_list;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<std::string> ListSDL2SinkDevices();
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
@ -31,9 +31,6 @@ public:
|
|||||||
|
|
||||||
/// Samples enqueued that have not been played yet.
|
/// Samples enqueued that have not been played yet.
|
||||||
virtual std::size_t SamplesInQueue() const = 0;
|
virtual std::size_t SamplesInQueue() const = 0;
|
||||||
|
|
||||||
/// Returns the list of available devices.
|
|
||||||
virtual std::vector<std::string> GetDeviceList() const = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace AudioCore
|
} // namespace AudioCore
|
||||||
|
@ -21,12 +21,13 @@ namespace AudioCore {
|
|||||||
// g_sink_details is ordered in terms of desirability, with the best choice at the top.
|
// g_sink_details is ordered in terms of desirability, with the best choice at the top.
|
||||||
const std::vector<SinkDetails> g_sink_details = {
|
const std::vector<SinkDetails> g_sink_details = {
|
||||||
#ifdef HAVE_CUBEB
|
#ifdef HAVE_CUBEB
|
||||||
{"cubeb", &std::make_unique<CubebSink>},
|
SinkDetails{"cubeb", &std::make_unique<CubebSink, std::string>, &ListCubebSinkDevices},
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SDL2
|
#ifdef HAVE_SDL2
|
||||||
{"sdl2", &std::make_unique<SDL2Sink>},
|
SinkDetails{"sdl2", &std::make_unique<SDL2Sink, std::string>, &ListSDL2SinkDevices},
|
||||||
#endif
|
#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) {
|
const SinkDetails& GetSinkDetails(std::string sink_id) {
|
||||||
|
@ -13,13 +13,16 @@ namespace AudioCore {
|
|||||||
class Sink;
|
class Sink;
|
||||||
|
|
||||||
struct SinkDetails {
|
struct SinkDetails {
|
||||||
SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>()> factory_)
|
SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>(std::string)> factory_,
|
||||||
: id(id_), factory(factory_) {}
|
std::function<std::vector<std::string>()> list_devices_)
|
||||||
|
: id(id_), factory(factory_), list_devices(list_devices_) {}
|
||||||
|
|
||||||
/// Name for this sink.
|
/// Name for this sink.
|
||||||
const char* id;
|
const char* id;
|
||||||
/// A method to call to construct an instance of this type of sink.
|
/// 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;
|
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");
|
ui->audio_device_combo_box->addItem("auto");
|
||||||
|
|
||||||
std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
|
std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString();
|
||||||
std::vector<std::string> device_list =
|
std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices();
|
||||||
AudioCore::GetSinkDetails(sink_id).factory()->GetDeviceList();
|
|
||||||
for (const auto& device : device_list) {
|
for (const auto& device : device_list) {
|
||||||
ui->audio_device_combo_box->addItem(device.c_str());
|
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 = 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);
|
dsp_core->EnableStretching(Settings::values.enable_audio_stretching);
|
||||||
|
|
||||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||||
|
@ -32,7 +32,7 @@ void Apply() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Core::System::GetInstance().IsPoweredOn()) {
|
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);
|
Core::DSP().EnableStretching(values.enable_audio_stretching);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user