mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-12-22 18:25:35 +00:00
ALSA: poll instead of async handler
This commit is contained in:
parent
f6684a0585
commit
eca4bc6074
|
@ -261,10 +261,13 @@ view `coverage/index.html` in a browser.
|
||||||
specify how much to peek() and if you don't peek all of it, save the
|
specify how much to peek() and if you don't peek all of it, save the
|
||||||
unused to a buffer for you.
|
unused to a buffer for you.
|
||||||
0. add len arguments to APIs that have char *
|
0. add len arguments to APIs that have char *
|
||||||
0. custom allocator support
|
|
||||||
0. Test in an app that needs to synchronize video to test the
|
0. Test in an app that needs to synchronize video to test the
|
||||||
latency/synchronization API.
|
latency/synchronization API.
|
||||||
0. Support PulseAudio proplist properties for main context and streams
|
0. Support PulseAudio proplist properties for main context and streams
|
||||||
|
0. custom allocator support
|
||||||
|
0. mlock memory which is accessed in the real time path
|
||||||
|
0. instead of `void *backend_data` use a union for better cache locality
|
||||||
|
and smaller mlock requirements
|
||||||
|
|
||||||
## Planned Uses for libsoundio
|
## Planned Uses for libsoundio
|
||||||
|
|
||||||
|
|
168
src/alsa.cpp
168
src/alsa.cpp
|
@ -16,6 +16,15 @@
|
||||||
|
|
||||||
static snd_pcm_stream_t stream_types[] = {SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_CAPTURE};
|
static snd_pcm_stream_t stream_types[] = {SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_CAPTURE};
|
||||||
|
|
||||||
|
static snd_pcm_access_t prioritized_access_types[] = {
|
||||||
|
SND_PCM_ACCESS_MMAP_INTERLEAVED,
|
||||||
|
SND_PCM_ACCESS_MMAP_NONINTERLEAVED,
|
||||||
|
SND_PCM_ACCESS_MMAP_COMPLEX,
|
||||||
|
SND_PCM_ACCESS_RW_INTERLEAVED,
|
||||||
|
SND_PCM_ACCESS_RW_NONINTERLEAVED,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct SoundIoAlsa {
|
struct SoundIoAlsa {
|
||||||
SoundIoOsMutex *mutex;
|
SoundIoOsMutex *mutex;
|
||||||
SoundIoOsCond *cond;
|
SoundIoOsCond *cond;
|
||||||
|
@ -35,9 +44,17 @@ struct SoundIoOutStreamAlsa {
|
||||||
snd_pcm_t *handle;
|
snd_pcm_t *handle;
|
||||||
snd_pcm_chmap_t *chmap;
|
snd_pcm_chmap_t *chmap;
|
||||||
int chmap_size;
|
int chmap_size;
|
||||||
snd_async_handler_t *ahandler;
|
|
||||||
snd_pcm_uframes_t offset;
|
snd_pcm_uframes_t offset;
|
||||||
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
SoundIoChannelArea areas[SOUNDIO_MAX_CHANNELS];
|
||||||
|
snd_pcm_access_t access;
|
||||||
|
int sample_buffer_size;
|
||||||
|
char *sample_buffer;
|
||||||
|
int alsa_areas_size;
|
||||||
|
snd_pcm_channel_area_t *alsa_areas;
|
||||||
|
int poll_fd_count;
|
||||||
|
struct pollfd *poll_fds;
|
||||||
|
SoundIoOsThread *thread;
|
||||||
|
atomic_flag thread_exit_flag;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SoundIoInStreamAlsa {
|
struct SoundIoInStreamAlsa {
|
||||||
|
@ -260,6 +277,19 @@ static void test_fmt_mask(SoundIoDevice *device, const snd_pcm_format_mask_t *fm
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int set_access(snd_pcm_t *handle, snd_pcm_hw_params_t *hwparams, snd_pcm_access_t *out_access) {
|
||||||
|
for (int i = 0; i < array_length(prioritized_access_types); i += 1) {
|
||||||
|
snd_pcm_access_t access = prioritized_access_types[i];
|
||||||
|
int err = snd_pcm_hw_params_set_access(handle, hwparams, access);
|
||||||
|
if (err >= 0) {
|
||||||
|
if (out_access)
|
||||||
|
*out_access = access;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
// this function does not override device->formats, so if you want it to, deallocate and set it to NULL
|
// this function does not override device->formats, so if you want it to, deallocate and set it to NULL
|
||||||
static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, int resample) {
|
static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, int resample) {
|
||||||
int err;
|
int err;
|
||||||
|
@ -273,13 +303,8 @@ static int probe_open_device(SoundIoDevice *device, snd_pcm_t *handle, int resam
|
||||||
if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, resample)) < 0)
|
if ((err = snd_pcm_hw_params_set_rate_resample(handle, hwparams, resample)) < 0)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
|
if ((err = set_access(handle, hwparams, nullptr)))
|
||||||
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) < 0) {
|
return err;
|
||||||
if ((err = snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_MMAP_COMPLEX)) < 0) {
|
|
||||||
return SoundIoErrorIncompatibleDevice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int channel_count;
|
unsigned int channel_count;
|
||||||
if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0)
|
if ((err = snd_pcm_hw_params_set_channels_last(handle, hwparams, &channel_count)) < 0)
|
||||||
|
@ -840,11 +865,22 @@ static void outstream_destroy_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *
|
||||||
if (!osa)
|
if (!osa)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
deallocate(osa->chmap, osa->chmap_size);
|
if (osa->thread) {
|
||||||
|
osa->thread_exit_flag.clear();
|
||||||
|
// TODO wake up poll
|
||||||
|
soundio_os_thread_destroy(osa->thread);
|
||||||
|
}
|
||||||
|
|
||||||
if (osa->handle)
|
if (osa->handle)
|
||||||
snd_pcm_close(osa->handle);
|
snd_pcm_close(osa->handle);
|
||||||
|
|
||||||
|
deallocate(osa->poll_fds, osa->poll_fd_count);
|
||||||
|
|
||||||
|
deallocate(osa->chmap, osa->chmap_size);
|
||||||
|
|
||||||
|
deallocate(osa->alsa_areas, osa->alsa_areas_size);
|
||||||
|
deallocate(osa->sample_buffer, osa->sample_buffer_size);
|
||||||
|
|
||||||
destroy(osa);
|
destroy(osa);
|
||||||
os->backend_data = nullptr;
|
os->backend_data = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -867,14 +903,40 @@ static int xrun_recovery(SoundIoOutStreamPrivate *os, int err) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void async_direct_callback(snd_async_handler_t *ahandler) {
|
static int wait_for_poll(SoundIoOutStreamAlsa *osa) {
|
||||||
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *)snd_async_handler_get_callback_private(ahandler);
|
int err;
|
||||||
|
unsigned short revents;
|
||||||
|
for (;;) {
|
||||||
|
if ((err = poll(osa->poll_fds, osa->poll_fd_count, -1)) < 0)
|
||||||
|
return err;
|
||||||
|
if ((err = snd_pcm_poll_descriptors_revents(osa->handle,
|
||||||
|
osa->poll_fds, osa->poll_fd_count, &revents)) < 0)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (revents & POLLERR)
|
||||||
|
return -EIO;
|
||||||
|
if (revents & POLLOUT)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void outstream_thread_run(void *arg) {
|
||||||
|
SoundIoOutStreamPrivate *os = (SoundIoOutStreamPrivate *) arg;
|
||||||
SoundIoOutStream *outstream = &os->pub;
|
SoundIoOutStream *outstream = &os->pub;
|
||||||
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||||
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
if ((err = wait_for_poll(osa)) < 0) {
|
||||||
|
if (!osa->thread_exit_flag.test_and_set())
|
||||||
|
return;
|
||||||
|
outstream->error_callback(outstream, SoundIoErrorStreaming);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!osa->thread_exit_flag.test_and_set())
|
||||||
|
return;
|
||||||
snd_pcm_state_t state = snd_pcm_state(osa->handle);
|
snd_pcm_state_t state = snd_pcm_state(osa->handle);
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case SND_PCM_STATE_OPEN:
|
case SND_PCM_STATE_OPEN:
|
||||||
|
@ -903,7 +965,7 @@ static void async_direct_callback(snd_async_handler_t *ahandler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
outstream->write_callback(outstream, avail);
|
outstream->write_callback(outstream, avail);
|
||||||
return;
|
continue;
|
||||||
}
|
}
|
||||||
case SND_PCM_STATE_XRUN:
|
case SND_PCM_STATE_XRUN:
|
||||||
if ((err = xrun_recovery(os, -EPIPE)) < 0) {
|
if ((err = xrun_recovery(os, -EPIPE)) < 0) {
|
||||||
|
@ -945,7 +1007,9 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
}
|
}
|
||||||
os->backend_data = osa;
|
os->backend_data = osa;
|
||||||
|
|
||||||
osa->chmap_size = sizeof(int) + sizeof(int) * outstream->layout.channel_count;
|
int ch_count = outstream->layout.channel_count;
|
||||||
|
|
||||||
|
osa->chmap_size = sizeof(int) + sizeof(int) * ch_count;
|
||||||
osa->chmap = (snd_pcm_chmap_t *)allocate<char>(osa->chmap_size);
|
osa->chmap = (snd_pcm_chmap_t *)allocate<char>(osa->chmap_size);
|
||||||
if (!osa->chmap) {
|
if (!osa->chmap) {
|
||||||
outstream_destroy_alsa(si, os);
|
outstream_destroy_alsa(si, os);
|
||||||
|
@ -975,16 +1039,12 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_access(osa->handle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) < 0) {
|
if ((err = set_access(osa->handle, hwparams, &osa->access))) {
|
||||||
if ((err = snd_pcm_hw_params_set_access(osa->handle, hwparams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) < 0) {
|
outstream_destroy_alsa(si, os);
|
||||||
if ((err = snd_pcm_hw_params_set_access(osa->handle, hwparams, SND_PCM_ACCESS_MMAP_COMPLEX)) < 0) {
|
return err;
|
||||||
outstream_destroy_alsa(si, os);
|
|
||||||
return SoundIoErrorIncompatibleDevice;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_channels(osa->handle, hwparams, outstream->layout.channel_count)) < 0) {
|
if ((err = snd_pcm_hw_params_set_channels(osa->handle, hwparams, ch_count)) < 0) {
|
||||||
outstream_destroy_alsa(si, os);
|
outstream_destroy_alsa(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
@ -994,7 +1054,14 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_set_format(osa->handle, hwparams, to_alsa_fmt(outstream->format))) < 0) {
|
snd_pcm_format_t format = to_alsa_fmt(outstream->format);
|
||||||
|
int phys_bits_per_sample = snd_pcm_format_physical_width(format);
|
||||||
|
if (phys_bits_per_sample % 8 != 0) {
|
||||||
|
outstream_destroy_alsa(si, os);
|
||||||
|
return SoundIoErrorIncompatibleDevice;
|
||||||
|
}
|
||||||
|
int phys_bytes_per_sample = phys_bits_per_sample / 8;
|
||||||
|
if ((err = snd_pcm_hw_params_set_format(osa->handle, hwparams, format)) < 0) {
|
||||||
outstream_destroy_alsa(si, os);
|
outstream_destroy_alsa(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
@ -1030,8 +1097,8 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set channel map
|
// set channel map
|
||||||
osa->chmap->channels = outstream->layout.channel_count;
|
osa->chmap->channels = ch_count;
|
||||||
for (int i = 0; i < outstream->layout.channel_count; i += 1) {
|
for (int i = 0; i < ch_count; i += 1) {
|
||||||
osa->chmap->pos[i] = to_alsa_chmap_pos(outstream->layout.channels[i]);
|
osa->chmap->pos[i] = to_alsa_chmap_pos(outstream->layout.channels[i]);
|
||||||
}
|
}
|
||||||
if (snd_pcm_set_chmap(osa->handle, osa->chmap) < 0) {
|
if (snd_pcm_set_chmap(osa->handle, osa->chmap) < 0) {
|
||||||
|
@ -1064,7 +1131,49 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice;
|
return (err == -EINVAL) ? SoundIoErrorIncompatibleDevice : SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((err = snd_async_add_pcm_handler(&osa->ahandler, osa->handle, async_direct_callback, os)) < 0) {
|
if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED || osa->access == SND_PCM_ACCESS_RW_NONINTERLEAVED) {
|
||||||
|
osa->sample_buffer_size = ch_count * period_size * phys_bytes_per_sample;
|
||||||
|
osa->sample_buffer = allocate_nonzero<char>(osa->sample_buffer_size);
|
||||||
|
if (!osa->sample_buffer) {
|
||||||
|
outstream_destroy_alsa(si, os);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
osa->alsa_areas_size = ch_count;
|
||||||
|
osa->alsa_areas = allocate<snd_pcm_channel_area_t>(osa->alsa_areas_size);
|
||||||
|
if (!osa->alsa_areas) {
|
||||||
|
outstream_destroy_alsa(si, os);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (osa->access == SND_PCM_ACCESS_RW_INTERLEAVED) {
|
||||||
|
for (int ch = 0; ch < ch_count; ch += 1) {
|
||||||
|
osa->alsa_areas[ch].addr = osa->sample_buffer;
|
||||||
|
osa->alsa_areas[ch].first = ch * phys_bits_per_sample;
|
||||||
|
osa->alsa_areas[ch].step = ch_count * phys_bits_per_sample;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int ch = 0; ch < ch_count; ch += 1) {
|
||||||
|
osa->alsa_areas[ch].addr = osa->sample_buffer;
|
||||||
|
osa->alsa_areas[ch].first = ch * phys_bits_per_sample * period_size;
|
||||||
|
osa->alsa_areas[ch].step = phys_bits_per_sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osa->poll_fd_count = snd_pcm_poll_descriptors_count(osa->handle);
|
||||||
|
if (osa->poll_fd_count <= 0) {
|
||||||
|
outstream_destroy_alsa(si, os);
|
||||||
|
return SoundIoErrorOpeningDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
osa->poll_fds = allocate<struct pollfd>(osa->poll_fd_count);
|
||||||
|
if (!osa->poll_fds) {
|
||||||
|
outstream_destroy_alsa(si, os);
|
||||||
|
return SoundIoErrorNoMem;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_poll_descriptors(osa->handle, osa->poll_fds, osa->poll_fd_count)) < 0) {
|
||||||
outstream_destroy_alsa(si, os);
|
outstream_destroy_alsa(si, os);
|
||||||
return SoundIoErrorOpeningDevice;
|
return SoundIoErrorOpeningDevice;
|
||||||
}
|
}
|
||||||
|
@ -1075,7 +1184,14 @@ static int outstream_open_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os)
|
||||||
static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
static int outstream_start_alsa(SoundIoPrivate *si, SoundIoOutStreamPrivate *os) {
|
||||||
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
SoundIoOutStreamAlsa *osa = (SoundIoOutStreamAlsa *) os->backend_data;
|
||||||
|
|
||||||
async_direct_callback(osa->ahandler);
|
assert(!osa->thread);
|
||||||
|
|
||||||
|
osa->thread_exit_flag.test_and_set();
|
||||||
|
int err;
|
||||||
|
if ((err = soundio_os_thread_create(outstream_thread_run, os, true, &osa->thread))) {
|
||||||
|
outstream_destroy_alsa(si, os);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,3 +111,14 @@
|
||||||
fun:snd_pcm_get_chmap
|
fun:snd_pcm_get_chmap
|
||||||
fun:snd_pcm_set_chmap
|
fun:snd_pcm_set_chmap
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
<insert_a_suppression_name_here>
|
||||||
|
Memcheck:Cond
|
||||||
|
fun:snd_interval_floor
|
||||||
|
fun:snd_pcm_plug_hw_refine_cchange
|
||||||
|
fun:snd1_pcm_hw_refine_slave
|
||||||
|
fun:snd_pcm_plug_hw_refine
|
||||||
|
fun:snd_pcm_hw_refine
|
||||||
|
fun:snd1_pcm_hw_param_set_last
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue