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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (impl->ctx) {
|
||||||
cubeb_destroy(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,10 +62,13 @@ 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) {
|
||||||
|
@ -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) {
|
if (cubeb_stream_stop(impl->stream) != CUBEB_OK) {
|
||||||
LOG_CRITICAL(Audio_Sink, "Error stopping cubeb stream");
|
LOG_ERROR(Audio_Sink, "Error stopping cubeb stream.");
|
||||||
|
}
|
||||||
|
cubeb_stream_destroy(impl->stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
cubeb_stream_destroy(impl->stream);
|
if (impl->ctx) {
|
||||||
cubeb_destroy(impl->ctx);
|
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,12 +120,11 @@ 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…
Reference in a new issue