diff --git a/README.md b/README.md index 198373e..51c855e 100644 --- a/README.md +++ b/README.md @@ -245,17 +245,10 @@ 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. 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? 0. Verify that JACK xrun callback context is the same as process callback. If not, might need to hav xrun callback set a flag and have process callback call the underflow callback. - 0. In PulseAudio, to get buffer duration and period duration, fill the buffer - with silence before starting, start the stream corked, and have the - 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`. @@ -294,6 +287,13 @@ view `coverage/index.html` in a browser. 0. write detailed docs on buffer underflows explaining when they occur, what state changes are related to them, and how to recover from them. 0. Consider testing on FreeBSD + 0. PulseAudio idea: when prebuf gets set to 0 need to pass `PA_STREAM_START_CORKED`. + In PulseAudio, to get buffer duration and period duration, fill the buffer + with silence before starting, start the stream corked, and have the + 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. In ALSA do we need to wake up the poll when destroying the in or out stream? ## Planned Uses for libsoundio diff --git a/src/alsa.cpp b/src/alsa.cpp index 538c2cb..67cb305 100644 --- a/src/alsa.cpp +++ b/src/alsa.cpp @@ -68,12 +68,12 @@ static char * str_partition_on_char(char *str, char c) { return nullptr; } -static snd_pcm_stream_t purpose_to_stream(SoundIoDevicePurpose purpose) { - switch (purpose) { - case SoundIoDevicePurposeOutput: return SND_PCM_STREAM_PLAYBACK; - case SoundIoDevicePurposeInput: return SND_PCM_STREAM_CAPTURE; +static snd_pcm_stream_t aim_to_stream(SoundIoDeviceAim aim) { + switch (aim) { + case SoundIoDeviceAimOutput: return SND_PCM_STREAM_PLAYBACK; + case SoundIoDeviceAimInput: return SND_PCM_STREAM_CAPTURE; } - assert(0); // Invalid purpose + assert(0); // Invalid aim return SND_PCM_STREAM_PLAYBACK; } @@ -382,7 +382,7 @@ static int probe_device(SoundIoDevice *device, snd_pcm_chmap_query_t **maps) { int err; snd_pcm_t *handle; - snd_pcm_stream_t stream = purpose_to_stream(device->purpose); + snd_pcm_stream_t stream = aim_to_stream(device->aim); if ((err = snd_pcm_open(&handle, device->id, stream, 0)) < 0) { handle_channel_maps(device, maps); @@ -558,13 +558,13 @@ static int refresh_devices(SoundIoPrivate *si) { SoundIoList *device_list; bool is_default = str_has_prefix(name, "default:") || strcmp(name, "default") == 0; if (stream == SND_PCM_STREAM_PLAYBACK) { - device->purpose = SoundIoDevicePurposeOutput; + device->aim = SoundIoDeviceAimOutput; device_list = &devices_info->output_devices; if (devices_info->default_output_index < 0 && is_default) devices_info->default_output_index = device_list->length; } else { assert(stream == SND_PCM_STREAM_CAPTURE); - device->purpose = SoundIoDevicePurposeInput; + device->aim = SoundIoDeviceAimInput; device_list = &devices_info->input_devices; if (devices_info->default_input_index < 0 && is_default) devices_info->default_input_index = device_list->length; @@ -671,11 +671,11 @@ static int refresh_devices(SoundIoPrivate *si) { SoundIoList *device_list; if (stream == SND_PCM_STREAM_PLAYBACK) { - device->purpose = SoundIoDevicePurposeOutput; + device->aim = SoundIoDeviceAimOutput; device_list = &devices_info->output_devices; } else { assert(stream == SND_PCM_STREAM_CAPTURE); - device->purpose = SoundIoDevicePurposeInput; + device->aim = SoundIoDeviceAimInput; device_list = &devices_info->input_devices; } @@ -1122,7 +1122,7 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); - snd_pcm_stream_t stream = purpose_to_stream(outstream->device->purpose); + snd_pcm_stream_t stream = aim_to_stream(outstream->device->aim); if ((err = snd_pcm_open(&osa->handle, outstream->device->id, stream, 0)) < 0) { outstream_destroy_alsa(si, os); @@ -1409,7 +1409,7 @@ static int instream_open_alsa(SoundIoPrivate *si, SoundIoInStreamPrivate *is) { snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams); - snd_pcm_stream_t stream = purpose_to_stream(instream->device->purpose); + snd_pcm_stream_t stream = aim_to_stream(instream->device->aim); if ((err = snd_pcm_open(&isa->handle, instream->device->id, stream, 0)) < 0) { instream_destroy_alsa(si, is); diff --git a/src/dummy.cpp b/src/dummy.cpp index fc63fcf..a11b13b 100644 --- a/src/dummy.cpp +++ b/src/dummy.cpp @@ -448,7 +448,7 @@ int soundio_dummy_init(SoundIoPrivate *si) { device->period_duration_min = 0.01; device->period_duration_max = 2; device->period_duration_current = 0.05; - device->purpose = SoundIoDevicePurposeOutput; + device->aim = SoundIoDeviceAimOutput; if (si->safe_devices_info->output_devices.append(device)) { soundio_device_unref(device); @@ -497,7 +497,7 @@ int soundio_dummy_init(SoundIoPrivate *si) { device->period_duration_min = 0.01; device->period_duration_max = 2; device->period_duration_current = 0.05; - device->purpose = SoundIoDevicePurposeInput; + device->aim = SoundIoDeviceAimInput; if (si->safe_devices_info->input_devices.append(device)) { soundio_device_unref(device); diff --git a/src/jack.cpp b/src/jack.cpp index aa2b275..110bfd8 100644 --- a/src/jack.cpp +++ b/src/jack.cpp @@ -26,7 +26,7 @@ struct SoundIoJackClient { const char *name; int name_len; bool is_physical; - SoundIoDevicePurpose purpose; + SoundIoDeviceAim aim; int port_count; SoundIoJackPort ports[SOUNDIO_MAX_CHANNELS]; }; @@ -47,12 +47,12 @@ static void split_str(const char *input_str, int input_str_len, char c, } static SoundIoJackClient *find_or_create_client(SoundIoList *clients, - SoundIoDevicePurpose purpose, bool is_physical, const char *client_name, int client_name_len) + SoundIoDeviceAim aim, bool is_physical, const char *client_name, int client_name_len) { for (int i = 0; i < clients->length; i += 1) { SoundIoJackClient *client = &clients->at(i); if (client->is_physical == is_physical && - client->purpose == purpose && + client->aim == aim && soundio_streql(client->name, client->name_len, client_name, client_name_len)) { return client; @@ -63,7 +63,7 @@ static SoundIoJackClient *find_or_create_client(SoundIoList * return nullptr; SoundIoJackClient *client = &clients->last(); client->is_physical = is_physical; - client->purpose = purpose; + client->aim = aim; client->name = client_name; client->name_len = client_name_len; client->port_count = 0; @@ -129,8 +129,8 @@ static int refresh_devices_bare(SoundIoPrivate *si) { continue; } - SoundIoDevicePurpose purpose = (flags & JackPortIsInput) ? - SoundIoDevicePurposeOutput : SoundIoDevicePurposeInput; + SoundIoDeviceAim aim = (flags & JackPortIsInput) ? + SoundIoDeviceAimOutput : SoundIoDeviceAimInput; bool is_physical = flags & JackPortIsPhysical; const char *client_name = nullptr; @@ -143,7 +143,7 @@ static int refresh_devices_bare(SoundIoPrivate *si) { // device does not have colon, skip it continue; } - SoundIoJackClient *client = find_or_create_client(&clients, purpose, is_physical, + SoundIoJackClient *client = find_or_create_client(&clients, aim, is_physical, client_name, client_name_len); if (!client) { jack_free(port_names); @@ -161,7 +161,7 @@ static int refresh_devices_bare(SoundIoPrivate *si) { port->name_len = port_name_len; port->channel_id = soundio_parse_channel_id(port_name, port_name_len); - jack_latency_callback_mode_t latency_mode = (purpose == SoundIoDevicePurposeOutput) ? + jack_latency_callback_mode_t latency_mode = (aim == SoundIoDeviceAimOutput) ? JackPlaybackLatency : JackCaptureLatency; jack_port_get_latency_range(jport, latency_mode, &port->latency_range); @@ -193,7 +193,7 @@ static int refresh_devices_bare(SoundIoPrivate *si) { device->ref_count = 1; device->soundio = soundio; device->is_raw = false; - device->purpose = client->purpose; + device->aim = client->aim; device->id = dupe_str(client->name, client->name_len); device->name = allocate(description_len); device->layout_count = 1; @@ -267,12 +267,12 @@ static int refresh_devices_bare(SoundIoPrivate *si) { device->formats[0] = device->current_format; SoundIoList *device_list; - if (device->purpose == SoundIoDevicePurposeOutput) { + if (device->aim == SoundIoDeviceAimOutput) { device_list = &devices_info->output_devices; if (devices_info->default_output_index < 0 && client->is_physical) devices_info->default_output_index = device_list->length; } else { - assert(device->purpose == SoundIoDevicePurposeInput); + assert(device->aim == SoundIoDeviceAimInput); device_list = &devices_info->input_devices; if (devices_info->default_input_index < 0 && client->is_physical) devices_info->default_input_index = device_list->length; diff --git a/src/pulseaudio.cpp b/src/pulseaudio.cpp index 56c53fb..fbc508e 100644 --- a/src/pulseaudio.cpp +++ b/src/pulseaudio.cpp @@ -220,7 +220,7 @@ static void finish_device_query(SoundIoPrivate *si) { sipa->current_devices_info->default_input_index = 0; for (int i = 0; i < sipa->current_devices_info->input_devices.length; i += 1) { SoundIoDevice *device = sipa->current_devices_info->input_devices.at(i); - assert(device->purpose == SoundIoDevicePurposeInput); + assert(device->aim == SoundIoDeviceAimInput); if (strcmp(device->id, sipa->default_source_name) == 0) { sipa->current_devices_info->default_input_index = i; } @@ -231,7 +231,7 @@ static void finish_device_query(SoundIoPrivate *si) { sipa->current_devices_info->default_output_index = 0; for (int i = 0; i < sipa->current_devices_info->output_devices.length; i += 1) { SoundIoDevice *device = sipa->current_devices_info->output_devices.at(i); - assert(device->purpose == SoundIoDevicePurposeOutput); + assert(device->aim == SoundIoDeviceAimOutput); if (strcmp(device->id, sipa->default_sink_name) == 0) { sipa->current_devices_info->default_output_index = i; } @@ -304,7 +304,7 @@ static void sink_info_callback(pa_context *pulse_context, const pa_sink_info *in // "period" is not a recognized concept in PulseAudio. - device->purpose = SoundIoDevicePurposeOutput; + device->aim = SoundIoDeviceAimOutput; if (sipa->current_devices_info->output_devices.append(device)) { soundio_device_unref(device); @@ -374,7 +374,7 @@ static void source_info_callback(pa_context *pulse_context, const pa_source_info // "period" is not a recognized concept in PulseAudio. - device->purpose = SoundIoDevicePurposeInput; + device->aim = SoundIoDeviceAimInput; if (sipa->current_devices_info->input_devices.append(device)) { soundio_device_unref(device); diff --git a/src/soundio.cpp b/src/soundio.cpp index 9248c9b..614898e 100644 --- a/src/soundio.cpp +++ b/src/soundio.cpp @@ -296,10 +296,6 @@ struct SoundIoDevice *soundio_get_output_device(struct SoundIo *soundio, int ind return device; } -enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *device) { - return device->purpose; -} - void soundio_device_unref(struct SoundIoDevice *device) { if (!device) return; diff --git a/src/soundio.h b/src/soundio.h index 75b9306..3fd3dbe 100644 --- a/src/soundio.h +++ b/src/soundio.h @@ -111,9 +111,9 @@ enum SoundIoBackend { SoundIoBackendDummy, }; -enum SoundIoDevicePurpose { - SoundIoDevicePurposeInput, - SoundIoDevicePurposeOutput, +enum SoundIoDeviceAim { + SoundIoDeviceAimInput, + SoundIoDeviceAimOutput, }; enum SoundIoFormat { @@ -249,6 +249,9 @@ struct SoundIoDevice { char *id; char *name; + // Tells whether this device is an input device or an output device. + enum SoundIoDeviceAim aim; + // Channel layouts are handled similarly to sample format; see those docs. // If this information is missing due to a `probe_error`, `layouts` // will be NULL. It's OK to modify this data, for example calling @@ -305,9 +308,6 @@ struct SoundIoDevice { double period_duration_max; double period_duration_current; - // Tells whether this device is an input device or an output device. - enum SoundIoDevicePurpose purpose; - // Raw means that you are directly opening the hardware device and not // going through a proxy such as dmix, PulseAudio, or JACK. When you open a // raw device, other applications on the computer are not able to @@ -609,7 +609,6 @@ void soundio_device_unref(struct SoundIoDevice *device); bool soundio_device_equal( const struct SoundIoDevice *a, const struct SoundIoDevice *b); -enum SoundIoDevicePurpose soundio_device_purpose(const struct SoundIoDevice *device); // Sorts channel layouts by channel count, descending. void soundio_device_sort_channel_layouts(struct SoundIoDevice *device);