mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-23 03:45:31 +00:00
remove all calls to soundio_panic
Return codes are the way errors are communicated, not crashing the entire program.
This commit is contained in:
parent
5503072fc8
commit
12db5fd970
|
@ -245,7 +245,6 @@ view `coverage/index.html` in a browser.
|
|||
0. implement WASAPI (Windows) backend, get examples working
|
||||
0. implement ASIO (Windows) backend, get examples working
|
||||
0. Integrate into libgroove and test with Groove Basin
|
||||
0. Avoid calling `soundio_panic` in PulseAudio.
|
||||
0. PulseAudio: when prebuf gets set to 0 need to pass `PA_STREAM_START_CORKED`.
|
||||
0. clear buffer maybe could take an argument to say how many frames to not clear
|
||||
0. In ALSA do we need to wake up the poll when destroying the in or out stream?
|
||||
|
@ -257,6 +256,8 @@ view `coverage/index.html` in a browser.
|
|||
callback be a callback that just provides silence. Once
|
||||
`soundio_outstream_start` is called, switch to the real callback, then call
|
||||
`pa_stream_flush`, then uncork the stream.
|
||||
0. API: devices should reference to their "other" device when the same
|
||||
hardware has input and output. This is important due to clock timing.
|
||||
0. Detect PulseAudio server going offline and emit `on_backend_disconnect`.
|
||||
0. Instead fo open(), start(), pause(), open() starts it and it starts paused.
|
||||
0. Create a test for underflow handling. It just makes a sine wave for 5
|
||||
|
|
37
src/alsa.cpp
37
src/alsa.cpp
|
@ -73,7 +73,8 @@ static snd_pcm_stream_t purpose_to_stream(SoundIoDevicePurpose purpose) {
|
|||
case SoundIoDevicePurposeOutput: return SND_PCM_STREAM_PLAYBACK;
|
||||
case SoundIoDevicePurposeInput: return SND_PCM_STREAM_CAPTURE;
|
||||
}
|
||||
soundio_panic("invalid purpose");
|
||||
assert(0); // Invalid purpose
|
||||
return SND_PCM_STREAM_PLAYBACK;
|
||||
}
|
||||
|
||||
static SoundIoChannelId from_alsa_chmap_pos(unsigned int pos) {
|
||||
|
@ -507,11 +508,10 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
if (strcmp(io, "Input") == 0) {
|
||||
is_playback = false;
|
||||
is_capture = true;
|
||||
} else if (strcmp(io, "Output") == 0) {
|
||||
} else {
|
||||
assert(strcmp(io, "Output") == 0);
|
||||
is_playback = true;
|
||||
is_capture = false;
|
||||
} else {
|
||||
soundio_panic("invalid io hint value");
|
||||
}
|
||||
free(io);
|
||||
} else {
|
||||
|
@ -706,6 +706,15 @@ static int refresh_devices(SoundIoPrivate *si) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void shutdown_backend(SoundIoPrivate *si, int err) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
SoundIoAlsa *sia = &si->backend_data.alsa;
|
||||
soundio_os_mutex_lock(sia->mutex);
|
||||
sia->shutdown_err = err;
|
||||
soundio->on_events_signal(soundio);
|
||||
soundio_os_mutex_unlock(sia->mutex);
|
||||
}
|
||||
|
||||
static void device_thread_run(void *arg) {
|
||||
SoundIoPrivate *si = (SoundIoPrivate *)arg;
|
||||
SoundIoAlsa *sia = &si->backend_data.alsa;
|
||||
|
@ -737,7 +746,9 @@ static void device_thread_run(void *arg) {
|
|||
assert(errno != EFAULT);
|
||||
assert(errno != EINVAL);
|
||||
assert(errno == ENOMEM);
|
||||
soundio_panic("kernel ran out of polling memory");
|
||||
// Kernel ran out of polling memory.
|
||||
shutdown_backend(si, SoundIoErrorSystemResources);
|
||||
return;
|
||||
}
|
||||
if (poll_num <= 0)
|
||||
continue;
|
||||
|
@ -794,8 +805,10 @@ static void device_thread_run(void *arg) {
|
|||
}
|
||||
}
|
||||
if (got_rescan_event) {
|
||||
if ((err = refresh_devices(si)))
|
||||
soundio_panic("error refreshing devices: %s", soundio_strerror(err));
|
||||
if ((err = refresh_devices(si))) {
|
||||
shutdown_backend(si, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -815,11 +828,15 @@ static void flush_events(SoundIoPrivate *si) {
|
|||
block_until_have_devices(sia);
|
||||
|
||||
bool change = false;
|
||||
bool cb_shutdown = false;
|
||||
SoundIoDevicesInfo *old_devices_info = nullptr;
|
||||
|
||||
soundio_os_mutex_lock(sia->mutex);
|
||||
|
||||
if (sia->ready_devices_info) {
|
||||
if (sia->shutdown_err && !sia->emitted_shutdown_cb) {
|
||||
sia->emitted_shutdown_cb = true;
|
||||
cb_shutdown = true;
|
||||
} else if (sia->ready_devices_info) {
|
||||
old_devices_info = si->safe_devices_info;
|
||||
si->safe_devices_info = sia->ready_devices_info;
|
||||
sia->ready_devices_info = nullptr;
|
||||
|
@ -828,7 +845,9 @@ static void flush_events(SoundIoPrivate *si) {
|
|||
|
||||
soundio_os_mutex_unlock(sia->mutex);
|
||||
|
||||
if (change)
|
||||
if (cb_shutdown)
|
||||
soundio->on_backend_disconnect(soundio, sia->shutdown_err);
|
||||
else if (change)
|
||||
soundio->on_devices_change(soundio);
|
||||
|
||||
soundio_destroy_devices_info(old_devices_info);
|
||||
|
|
|
@ -33,6 +33,9 @@ struct SoundIoAlsa {
|
|||
|
||||
// this one is ready to be read with flush_events. protected by mutex
|
||||
struct SoundIoDevicesInfo *ready_devices_info;
|
||||
|
||||
int shutdown_err;
|
||||
bool emitted_shutdown_cb;
|
||||
};
|
||||
|
||||
struct SoundIoOutStreamAlsa {
|
||||
|
|
|
@ -317,7 +317,7 @@ static void flush_events_jack(struct SoundIoPrivate *si) {
|
|||
soundio_os_mutex_unlock(sij->mutex);
|
||||
|
||||
if (cb_shutdown) {
|
||||
soundio->on_backend_disconnect(soundio);
|
||||
soundio->on_backend_disconnect(soundio, SoundIoErrorBackendDisconnected);
|
||||
} else {
|
||||
if (!sij->refresh_devices_flag.test_and_set()) {
|
||||
if ((err = refresh_devices(si))) {
|
||||
|
|
47
src/os.cpp
47
src/os.cpp
|
@ -165,16 +165,6 @@ static DWORD WINAPI run_win32_thread(LPVOID userdata) {
|
|||
thread->run(thread->arg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void win32_panic(const char *str) {
|
||||
DWORD err = GetLastError();
|
||||
LPSTR messageBuffer = nullptr;
|
||||
size_t size = FormatMessageA(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
|
||||
soundio_panic(str, messageBuffer);
|
||||
LocalFree(messageBuffer);
|
||||
}
|
||||
#else
|
||||
static void assert_no_err(int err) {
|
||||
assert(!err);
|
||||
|
@ -433,8 +423,8 @@ void soundio_os_cond_signal(struct SoundIoOsCond *cond,
|
|||
|
||||
if (kevent(kq_id, &kev, 1, NULL, 0, &timeout) == -1) {
|
||||
if (errno == EINTR)
|
||||
return;
|
||||
soundio_panic("kevent signal error: %s", strerror(errno));
|
||||
return 0;
|
||||
assert(0); // kevent signal error
|
||||
}
|
||||
#else
|
||||
if (locked_mutex) {
|
||||
|
@ -479,7 +469,7 @@ void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
|
|||
if (kevent(kq_id, &kev, 1, &out_kev, 1, &timeout) == -1) {
|
||||
if (errno == EINTR)
|
||||
return;
|
||||
soundio_panic("kevent wait error: %s", strerror(errno));
|
||||
assert(0); // kevent wait error
|
||||
}
|
||||
#else
|
||||
pthread_mutex_t *target_mutex;
|
||||
|
@ -531,7 +521,7 @@ void soundio_os_cond_wait(struct SoundIoOsCond *cond,
|
|||
if (kevent(kq_id, &kev, 1, &out_kev, 1, NULL) == -1) {
|
||||
if (errno == EINTR)
|
||||
return;
|
||||
soundio_panic("kevent wait error: %s", strerror(errno));
|
||||
assert(0); // kevent wait error
|
||||
}
|
||||
#else
|
||||
pthread_mutex_t *target_mutex;
|
||||
|
@ -551,11 +541,11 @@ void soundio_os_cond_wait(struct SoundIoOsCond *cond,
|
|||
#endif
|
||||
}
|
||||
|
||||
static void internal_init(void) {
|
||||
static int internal_init(void) {
|
||||
#if defined(SOUNDIO_OS_KQUEUE)
|
||||
kq_id = kqueue();
|
||||
if (kq_id == -1)
|
||||
soundio_panic("unable to create kqueue: %s", strerror(errno));
|
||||
return SoundIoErrorSystemResources;
|
||||
next_notify_ident.store(1);
|
||||
#endif
|
||||
#if defined(SOUNDIO_OS_WINDOWS)
|
||||
|
@ -563,42 +553,49 @@ static void internal_init(void) {
|
|||
if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) {
|
||||
win32_time_resolution = 1.0 / (double) frequency;
|
||||
} else {
|
||||
win32_panic("unable to initialize high precision timer: %s");
|
||||
return SoundIoErrorSystemResources;
|
||||
}
|
||||
GetSystemInfo(&win32_system_info);
|
||||
page_size = win32_system_info.dwAllocationGranularity;
|
||||
#else
|
||||
page_size = getpagesize();
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void soundio_os_init(void) {
|
||||
int soundio_os_init(void) {
|
||||
int err;
|
||||
#if defined(SOUNDIO_OS_WINDOWS)
|
||||
PVOID lpContext;
|
||||
BOOL pending;
|
||||
|
||||
if (!InitOnceBeginInitialize(&win32_init_once, INIT_ONCE_ASYNC, &pending, &lpContext))
|
||||
win32_panic("InitOnceBeginInitialize failed: %s");
|
||||
return SoundIoErrorSystemResources;
|
||||
|
||||
if (!pending)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
internal_init();
|
||||
if ((err = internal_init()))
|
||||
return err;
|
||||
|
||||
if (!InitOnceComplete(&win32_init_once, INIT_ONCE_ASYNC, nullptr))
|
||||
win32_panic("InitOnceComplete failed: %s");
|
||||
return SoundIoErrorSystemResources;
|
||||
#else
|
||||
if (initialized.load())
|
||||
return;
|
||||
return 0;
|
||||
|
||||
assert_no_err(pthread_mutex_lock(&init_mutex));
|
||||
if (initialized.load()) {
|
||||
assert_no_err(pthread_mutex_unlock(&init_mutex));
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
initialized.store(true);
|
||||
internal_init();
|
||||
if ((err = internal_init()))
|
||||
return err;
|
||||
assert_no_err(pthread_mutex_unlock(&init_mutex));
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int soundio_os_page_size(void) {
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
// safe to call from any thread(s) multiple times, but
|
||||
// must be called at least once before calling any other os functions
|
||||
// soundio_create calls this function.
|
||||
void soundio_os_init(void);
|
||||
int soundio_os_init(void);
|
||||
|
||||
double soundio_os_get_time(void);
|
||||
|
||||
|
|
|
@ -22,15 +22,15 @@ static void subscribe_callback(pa_context *context,
|
|||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
}
|
||||
|
||||
static void subscribe_to_events(SoundIoPrivate *si) {
|
||||
static int subscribe_to_events(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
pa_subscription_mask_t events = (pa_subscription_mask_t)(
|
||||
PA_SUBSCRIPTION_MASK_SINK|PA_SUBSCRIPTION_MASK_SOURCE|PA_SUBSCRIPTION_MASK_SERVER);
|
||||
pa_operation *subscribe_op = pa_context_subscribe(sipa->pulse_context,
|
||||
events, nullptr, si);
|
||||
pa_operation *subscribe_op = pa_context_subscribe(sipa->pulse_context, events, nullptr, si);
|
||||
if (!subscribe_op)
|
||||
soundio_panic("pa_context_subscribe failed: %s", pa_strerror(pa_context_errno(sipa->pulse_context)));
|
||||
return SoundIoErrorNoMem;
|
||||
pa_operation_unref(subscribe_op);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void context_state_callback(pa_context *context, void *userdata) {
|
||||
|
@ -47,8 +47,6 @@ static void context_state_callback(pa_context *context, void *userdata) {
|
|||
case PA_CONTEXT_SETTING_NAME: // The client is passing its application name to the daemon.
|
||||
return;
|
||||
case PA_CONTEXT_READY: // The connection is established, the context is ready to execute operations.
|
||||
sipa->device_scan_queued = true;
|
||||
subscribe_to_events(si);
|
||||
sipa->ready_flag = true;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
|
@ -56,16 +54,11 @@ static void context_state_callback(pa_context *context, void *userdata) {
|
|||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
case PA_CONTEXT_FAILED: // The connection failed or was disconnected.
|
||||
{
|
||||
int err_number = pa_context_errno(context);
|
||||
if (err_number == PA_ERR_CONNECTIONREFUSED) {
|
||||
sipa->connection_refused = true;
|
||||
} else {
|
||||
soundio_panic("pulseaudio connect failure: %s", pa_strerror(pa_context_errno(context)));
|
||||
}
|
||||
sipa->connection_err = SoundIoErrorInitAudioBackend;
|
||||
sipa->ready_flag = true;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_pa(SoundIoPrivate *si) {
|
||||
|
@ -135,8 +128,7 @@ static SoundIoChannelId from_pulseaudio_channel_pos(pa_channel_position_t pos) {
|
|||
case PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return SoundIoChannelIdTopBackRight;
|
||||
case PA_CHANNEL_POSITION_TOP_REAR_CENTER: return SoundIoChannelIdTopBackCenter;
|
||||
|
||||
default:
|
||||
soundio_panic("cannot map pulseaudio channel to libsoundio");
|
||||
default: return SoundIoChannelIdInvalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,6 +176,8 @@ static int set_all_device_formats(SoundIoDevice *device) {
|
|||
}
|
||||
|
||||
static int perform_operation(SoundIoPrivate *si, pa_operation *op) {
|
||||
if (!op)
|
||||
return SoundIoErrorNoMem;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
for (;;) {
|
||||
switch (pa_operation_get_state(op)) {
|
||||
|
@ -195,7 +189,7 @@ static int perform_operation(SoundIoPrivate *si, pa_operation *op) {
|
|||
return 0;
|
||||
case PA_OPERATION_CANCELLED:
|
||||
pa_operation_unref(op);
|
||||
return -1;
|
||||
return SoundIoErrorInterrupted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -211,6 +205,11 @@ static void finish_device_query(SoundIoPrivate *si) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (sipa->device_query_err) {
|
||||
sipa->device_scan_queued.store(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// based on the default sink name, figure out the default output index
|
||||
// if the name doesn't match just pick the first one. if there are no
|
||||
// devices then we need to set it to -1.
|
||||
|
@ -255,18 +254,25 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in
|
|||
if (eol) {
|
||||
sipa->have_sink_list = true;
|
||||
finish_device_query(si);
|
||||
} else {
|
||||
} else if (!sipa->device_query_err) {
|
||||
SoundIoDevicePrivate *dev = create<SoundIoDevicePrivate>();
|
||||
if (!dev)
|
||||
soundio_panic("out of memory");
|
||||
if (!dev) {
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
SoundIoDevice *device = &dev->pub;
|
||||
|
||||
device->ref_count = 1;
|
||||
device->soundio = soundio;
|
||||
device->name = strdup(info->name);
|
||||
device->description = strdup(info->description);
|
||||
if (!device->name || !device->description)
|
||||
soundio_panic("out of memory");
|
||||
if (!device->name || !device->description) {
|
||||
soundio_device_unref(device);
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
device->sample_rate_current = info->sample_spec.rate;
|
||||
// PulseAudio performs resampling, so any value is valid. Let's pick
|
||||
|
@ -277,13 +283,21 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in
|
|||
device->current_format = from_pulseaudio_format(info->sample_spec);
|
||||
// PulseAudio performs sample format conversion, so any PulseAudio
|
||||
// value is valid.
|
||||
if ((err = set_all_device_formats(device)))
|
||||
soundio_panic("out of memory");
|
||||
if ((err = set_all_device_formats(device))) {
|
||||
soundio_device_unref(device);
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
set_from_pulseaudio_channel_map(info->channel_map, &device->current_layout);
|
||||
// PulseAudio does channel layout remapping, so any channel layout is valid.
|
||||
if ((err = set_all_device_channel_layouts(device)))
|
||||
soundio_panic("out of memory");
|
||||
if ((err = set_all_device_channel_layouts(device))) {
|
||||
soundio_device_unref(device);
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
device->buffer_duration_min = 0.10;
|
||||
device->buffer_duration_max = 4.0;
|
||||
|
@ -292,8 +306,12 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in
|
|||
|
||||
device->purpose = SoundIoDevicePurposeOutput;
|
||||
|
||||
if (sipa->current_devices_info->output_devices.append(device))
|
||||
soundio_panic("out of memory");
|
||||
if (sipa->current_devices_info->output_devices.append(device)) {
|
||||
soundio_device_unref(device);
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
}
|
||||
|
@ -306,18 +324,25 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info
|
|||
if (eol) {
|
||||
sipa->have_source_list = true;
|
||||
finish_device_query(si);
|
||||
} else {
|
||||
} else if (!sipa->device_query_err) {
|
||||
SoundIoDevicePrivate *dev = create<SoundIoDevicePrivate>();
|
||||
if (!dev)
|
||||
soundio_panic("out of memory");
|
||||
if (!dev) {
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
SoundIoDevice *device = &dev->pub;
|
||||
|
||||
device->ref_count = 1;
|
||||
device->soundio = soundio;
|
||||
device->name = strdup(info->name);
|
||||
device->description = strdup(info->description);
|
||||
if (!device->name || !device->description)
|
||||
soundio_panic("out of memory");
|
||||
if (!device->name || !device->description) {
|
||||
soundio_device_unref(device);
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
device->sample_rate_current = info->sample_spec.rate;
|
||||
// PulseAudio performs resampling, so any value is valid. Let's pick
|
||||
|
@ -328,24 +353,35 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info
|
|||
device->current_format = from_pulseaudio_format(info->sample_spec);
|
||||
// PulseAudio performs sample format conversion, so any PulseAudio
|
||||
// value is valid.
|
||||
if ((err = set_all_device_formats(device)))
|
||||
soundio_panic("out of memory");
|
||||
if ((err = set_all_device_formats(device))) {
|
||||
soundio_device_unref(device);
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
set_from_pulseaudio_channel_map(info->channel_map, &device->current_layout);
|
||||
// PulseAudio does channel layout remapping, so any channel layout is valid.
|
||||
if ((err = set_all_device_channel_layouts(device)))
|
||||
soundio_panic("out of memory");
|
||||
if ((err = set_all_device_channel_layouts(device))) {
|
||||
soundio_device_unref(device);
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
device->buffer_duration_min = 0.10;
|
||||
device->buffer_duration_max = 4.0;
|
||||
device->buffer_duration_current = -1.0;
|
||||
|
||||
// "period" is not a recognized concept in PulseAudio.
|
||||
|
||||
device->purpose = SoundIoDevicePurposeInput;
|
||||
|
||||
if (sipa->current_devices_info->input_devices.append(device))
|
||||
soundio_panic("out of memory");
|
||||
if (sipa->current_devices_info->input_devices.append(device)) {
|
||||
soundio_device_unref(device);
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
}
|
||||
|
@ -361,15 +397,18 @@ static void server_info_callback(pa_context *pulse_context, const pa_server_info
|
|||
sipa->default_sink_name = strdup(info->default_sink_name);
|
||||
sipa->default_source_name = strdup(info->default_source_name);
|
||||
|
||||
if (!sipa->default_sink_name || !sipa->default_source_name)
|
||||
soundio_panic("out of memory");
|
||||
if (!sipa->default_sink_name || !sipa->default_source_name) {
|
||||
free(sipa->default_sink_name);
|
||||
free(sipa->default_source_name);
|
||||
sipa->device_query_err = SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
sipa->have_default_sink = true;
|
||||
finish_device_query(si);
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
}
|
||||
|
||||
static void scan_devices(SoundIoPrivate *si) {
|
||||
static int scan_devices(SoundIoPrivate *si) {
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
sipa->have_sink_list = false;
|
||||
|
@ -379,27 +418,32 @@ static void scan_devices(SoundIoPrivate *si) {
|
|||
soundio_destroy_devices_info(sipa->current_devices_info);
|
||||
sipa->current_devices_info = create<SoundIoDevicesInfo>();
|
||||
if (!sipa->current_devices_info)
|
||||
soundio_panic("out of memory");
|
||||
return SoundIoErrorNoMem;
|
||||
|
||||
pa_threaded_mainloop_lock(sipa->main_loop);
|
||||
|
||||
pa_operation *list_sink_op = pa_context_get_sink_info_list(sipa->pulse_context,
|
||||
sink_info_callback, si);
|
||||
pa_operation *list_source_op = pa_context_get_source_info_list(sipa->pulse_context,
|
||||
source_info_callback, si);
|
||||
pa_operation *server_info_op = pa_context_get_server_info(sipa->pulse_context,
|
||||
server_info_callback, si);
|
||||
pa_operation *list_sink_op = pa_context_get_sink_info_list(sipa->pulse_context, sink_info_callback, si);
|
||||
pa_operation *list_source_op = pa_context_get_source_info_list(sipa->pulse_context, source_info_callback, si);
|
||||
pa_operation *server_info_op = pa_context_get_server_info(sipa->pulse_context, server_info_callback, si);
|
||||
|
||||
if (perform_operation(si, list_sink_op))
|
||||
soundio_panic("list sinks failed");
|
||||
if (perform_operation(si, list_source_op))
|
||||
soundio_panic("list sources failed");
|
||||
if (perform_operation(si, server_info_op))
|
||||
soundio_panic("get server info failed");
|
||||
int err;
|
||||
if ((err = perform_operation(si, list_sink_op))) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
return err;
|
||||
}
|
||||
if ((err = perform_operation(si, list_source_op))) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
return err;
|
||||
}
|
||||
if ((err = perform_operation(si, server_info_op))) {
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
return err;
|
||||
}
|
||||
|
||||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
|
||||
pa_threaded_mainloop_unlock(sipa->main_loop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void block_until_have_devices(SoundIoPrivate *si) {
|
||||
|
@ -426,13 +470,13 @@ static void block_until_ready(SoundIoPrivate *si) {
|
|||
|
||||
static void flush_events(SoundIoPrivate *si) {
|
||||
SoundIo *soundio = &si->pub;
|
||||
block_until_ready(si);
|
||||
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
int err;
|
||||
if (sipa->device_scan_queued) {
|
||||
if (!(err = scan_devices(si)))
|
||||
sipa->device_scan_queued = false;
|
||||
scan_devices(si);
|
||||
}
|
||||
|
||||
SoundIoDevicesInfo *old_devices_info = nullptr;
|
||||
|
@ -544,8 +588,7 @@ static pa_channel_map to_pulseaudio_channel_map(const SoundIoChannelLayout *chan
|
|||
pa_channel_map channel_map;
|
||||
channel_map.channels = channel_layout->channel_count;
|
||||
|
||||
if ((unsigned)channel_layout->channel_count > PA_CHANNELS_MAX)
|
||||
soundio_panic("channel layout greater than pulseaudio max channels");
|
||||
assert((unsigned)channel_layout->channel_count <= PA_CHANNELS_MAX);
|
||||
|
||||
for (int i = 0; i < channel_layout->channel_count; i += 1)
|
||||
channel_map.map[i] = to_pulseaudio_channel_pos(channel_layout->channels[i]);
|
||||
|
@ -570,7 +613,7 @@ static void playback_stream_state_callback(pa_stream *stream, void *userdata) {
|
|||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
break;
|
||||
case PA_STREAM_FAILED:
|
||||
soundio_panic("pulseaudio stream error: %s", pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
|
||||
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -611,6 +654,11 @@ static int outstream_open_pa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
|||
SoundIoOutStreamPulseAudio *ospa = &os->backend_data.pulseaudio;
|
||||
SoundIoOutStream *outstream = &os->pub;
|
||||
|
||||
if (outstream->layout.channel_count > SOUNDIO_MAX_CHANNELS)
|
||||
return SoundIoErrorInvalid;
|
||||
if ((unsigned)outstream->layout.channel_count > PA_CHANNELS_MAX)
|
||||
return SoundIoErrorIncompatibleBackend;
|
||||
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
ospa->stream_ready = false;
|
||||
|
||||
|
@ -771,8 +819,7 @@ static void recording_stream_state_callback(pa_stream *stream, void *userdata) {
|
|||
pa_threaded_mainloop_signal(sipa->main_loop, 0);
|
||||
break;
|
||||
case PA_STREAM_FAILED:
|
||||
soundio_panic("pulseaudio stream error: %s",
|
||||
pa_strerror(pa_context_errno(pa_stream_get_context(stream))));
|
||||
instream->error_callback(instream, SoundIoErrorStreaming);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -808,6 +855,11 @@ static int instream_open_pa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) {
|
|||
SoundIoInStreamPulseAudio *ispa = &is->backend_data.pulseaudio;
|
||||
SoundIoInStream *instream = &is->pub;
|
||||
|
||||
if (instream->layout.channel_count > SOUNDIO_MAX_CHANNELS)
|
||||
return SoundIoErrorInvalid;
|
||||
if ((unsigned)instream->layout.channel_count > PA_CHANNELS_MAX)
|
||||
return SoundIoErrorIncompatibleBackend;
|
||||
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
ispa->stream_ready = false;
|
||||
|
||||
|
@ -949,10 +1001,9 @@ int soundio_pulseaudio_init(SoundIoPrivate *si) {
|
|||
SoundIo *soundio = &si->pub;
|
||||
SoundIoPulseAudio *sipa = &si->backend_data.pulseaudio;
|
||||
|
||||
sipa->connection_refused = false;
|
||||
sipa->device_scan_queued = false;
|
||||
sipa->ready_flag = false;
|
||||
sipa->have_devices_flag = false;
|
||||
sipa->device_scan_queued.store(false);
|
||||
sipa->ready_flag.store(false);
|
||||
sipa->have_devices_flag.store(false);
|
||||
|
||||
sipa->main_loop = pa_threaded_mainloop_new();
|
||||
if (!sipa->main_loop) {
|
||||
|
@ -983,16 +1034,24 @@ int soundio_pulseaudio_init(SoundIoPrivate *si) {
|
|||
return SoundIoErrorInitAudioBackend;
|
||||
}
|
||||
|
||||
if (sipa->connection_refused) {
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorInitAudioBackend;
|
||||
}
|
||||
|
||||
if (pa_threaded_mainloop_start(sipa->main_loop)) {
|
||||
destroy_pa(si);
|
||||
return SoundIoErrorNoMem;
|
||||
}
|
||||
|
||||
block_until_ready(si);
|
||||
|
||||
if (sipa->connection_err) {
|
||||
destroy_pa(si);
|
||||
return sipa->connection_err;
|
||||
}
|
||||
|
||||
sipa->device_scan_queued.store(true);
|
||||
if ((err = subscribe_to_events(si))) {
|
||||
destroy_pa(si);
|
||||
return err;
|
||||
}
|
||||
|
||||
si->destroy = destroy_pa;
|
||||
si->flush_events = flush_events;
|
||||
si->wait_events = wait_events;
|
||||
|
|
|
@ -20,7 +20,7 @@ struct SoundIoDevicePulseAudio {
|
|||
};
|
||||
|
||||
struct SoundIoPulseAudio {
|
||||
bool connection_refused;
|
||||
int connection_err;
|
||||
|
||||
pa_context *pulse_context;
|
||||
atomic_bool device_scan_queued;
|
||||
|
@ -33,6 +33,7 @@ struct SoundIoPulseAudio {
|
|||
// this one is ready to be read with flush_events. protected by mutex
|
||||
struct SoundIoDevicesInfo *ready_devices_info;
|
||||
|
||||
int device_query_err;
|
||||
bool have_sink_list;
|
||||
bool have_source_list;
|
||||
bool have_default_sink;
|
||||
|
|
|
@ -63,7 +63,7 @@ const char *soundio_strerror(int error) {
|
|||
case SoundIoErrorInterrupted: return "interrupted; try again";
|
||||
case SoundIoErrorUnderflow: return "buffer underflow";
|
||||
}
|
||||
soundio_panic("invalid error enum value: %d", error);
|
||||
return "(invalid error)";
|
||||
}
|
||||
|
||||
int soundio_get_bytes_per_sample(enum SoundIoFormat format) {
|
||||
|
@ -87,10 +87,9 @@ int soundio_get_bytes_per_sample(enum SoundIoFormat format) {
|
|||
case SoundIoFormatFloat64LE: return 8;
|
||||
case SoundIoFormatFloat64BE: return 8;
|
||||
|
||||
case SoundIoFormatInvalid:
|
||||
soundio_panic("invalid sample format");
|
||||
case SoundIoFormatInvalid: return -1;
|
||||
}
|
||||
soundio_panic("invalid sample format");
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char * soundio_format_string(enum SoundIoFormat format) {
|
||||
|
@ -129,7 +128,7 @@ const char *soundio_backend_name(enum SoundIoBackend backend) {
|
|||
case SoundIoBackendAlsa: return "ALSA";
|
||||
case SoundIoBackendDummy: return "Dummy";
|
||||
}
|
||||
soundio_panic("invalid backend enum value: %d", (int)backend);
|
||||
return "(invalid backend)";
|
||||
}
|
||||
|
||||
void soundio_destroy(struct SoundIo *soundio) {
|
||||
|
@ -143,16 +142,19 @@ void soundio_destroy(struct SoundIo *soundio) {
|
|||
}
|
||||
|
||||
static void do_nothing_cb(struct SoundIo *) { }
|
||||
static void do_nothing_backend_disconnect_cb(struct SoundIo *, int err) { }
|
||||
static void default_msg_callback(const char *msg) { }
|
||||
|
||||
struct SoundIo * soundio_create(void) {
|
||||
soundio_os_init();
|
||||
int err;
|
||||
if ((err = soundio_os_init()))
|
||||
return nullptr;
|
||||
struct SoundIoPrivate *si = create<SoundIoPrivate>();
|
||||
if (!si)
|
||||
return NULL;
|
||||
return nullptr;
|
||||
SoundIo *soundio = &si->pub;
|
||||
soundio->on_devices_change = do_nothing_cb;
|
||||
soundio->on_backend_disconnect = do_nothing_cb;
|
||||
soundio->on_backend_disconnect = do_nothing_backend_disconnect_cb;
|
||||
soundio->on_events_signal = do_nothing_cb;
|
||||
soundio->app_name = "SoundIo";
|
||||
soundio->jack_info_callback = default_msg_callback;
|
||||
|
|
|
@ -213,7 +213,7 @@ struct SoundIo {
|
|||
// and opening streams will always fail with
|
||||
// SoundIoErrorBackendDisconnected. This callback is only called during a
|
||||
// call to soundio_flush_events or soundio_wait_events.
|
||||
void (*on_backend_disconnect)(struct SoundIo *);
|
||||
void (*on_backend_disconnect)(struct SoundIo *, int err);
|
||||
// Optional callback. Called from an unknown thread that you should not use
|
||||
// to call any soundio functions. You may use this to signal a condition
|
||||
// variable to wake up. Called when soundio_wait_events would be woken up.
|
||||
|
@ -287,9 +287,12 @@ struct SoundIoDevice {
|
|||
int sample_rate_max;
|
||||
int sample_rate_current;
|
||||
|
||||
// Buffer duration in seconds. If any values are unknown, they are set to
|
||||
// 0.0. These values are unknown for PulseAudio. For JACK, buffer duration
|
||||
// and period duration are the same.
|
||||
// Buffer duration in seconds. If `buffer_duration_current` is unknown or
|
||||
// irrelevant, it is set to 0.0.
|
||||
// PulseAudio allows any value and so reasonable min/max of 0.10 and 4.0
|
||||
// are used. You may check that the current backend is PulseAudio and
|
||||
// ignore these min/max values.
|
||||
// For JACK, buffer duration and period duration are the same.
|
||||
double buffer_duration_min;
|
||||
double buffer_duration_max;
|
||||
double buffer_duration_current;
|
||||
|
@ -561,6 +564,7 @@ void soundio_sort_channel_layouts(struct SoundIoChannelLayout *layouts, int layo
|
|||
|
||||
// Sample Formats
|
||||
|
||||
// Returns -1 on invalid format.
|
||||
int soundio_get_bytes_per_sample(enum SoundIoFormat format);
|
||||
|
||||
static inline int soundio_get_bytes_per_frame(enum SoundIoFormat format, int channel_count) {
|
||||
|
|
Loading…
Reference in a new issue