From 052e57cb1ff04227303ee226a06e69a3dae5355c Mon Sep 17 00:00:00 2001 From: Abramo Bagnara Date: Wed, 21 Jun 2000 14:57:19 +0000 Subject: [PATCH] First incomplete implementation --- src/pcm/pcm_multi.c | 691 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 691 insertions(+) create mode 100644 src/pcm/pcm_multi.c diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c new file mode 100644 index 00000000..87469a29 --- /dev/null +++ b/src/pcm/pcm_multi.c @@ -0,0 +1,691 @@ +/* + * PCM - Multi + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pcm_local.h" + +typedef struct { + snd_pcm_t *handle; + unsigned int channels_total; + int close_slave; + char *buf; + snd_pcm_channel_area_t *areas; + struct iovec *iovec; +} snd_pcm_multi_slave_t; + +typedef struct { + unsigned int client_channel; + unsigned int slave; + unsigned int slave_channel; +} snd_pcm_multi_bind_t; + +typedef struct { + snd_pcm_t *handle; + size_t slaves_count; + snd_pcm_multi_slave_t *slaves; + size_t binds_count; + snd_pcm_multi_bind_t *binds; + size_t channels_count; + int interleave; + int one_to_many; +} snd_pcm_multi_t; + +static int snd_pcm_multi_close(void *private) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + int ret = 0; + for (i = 0; i < multi->slaves_count; ++i) { + int err; + snd_pcm_multi_slave_t *slave = &multi->slaves[i]; + if (slave->close_slave) { + err = snd_pcm_close(slave->handle); + if (err < 0) + ret = err; + } + if (slave->buf) { + free(slave->buf); + free(slave->areas); + } + if (slave->iovec) + free(slave->iovec); + } + free(multi->slaves); + free(multi->binds); + free(private); + return ret; +} + +static int snd_pcm_multi_nonblock(void *private, int nonblock) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + snd_pcm_t *handle = multi->slaves[0].handle; + return snd_pcm_nonblock(handle, nonblock); +} + +static int snd_pcm_multi_info(void *private, snd_pcm_info_t *info) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i, channels; + int err; + snd_pcm_t *handle_0 = multi->slaves[0].handle; + unsigned int channels0 = multi->slaves[0].channels_total; + err = snd_pcm_info(handle_0, info); + if (err < 0) + return err; + info->mmap_size = 0; + info->buffer_size /= channels0; + info->min_fragment_size /= channels0; + info->max_fragment_size /= channels0; + info->fragment_align /= channels0; + for (i = 1; i < multi->slaves_count; ++i) { + snd_pcm_t *handle_i = multi->slaves[i].handle; + snd_pcm_info_t info_i; + err = snd_pcm_info(handle_i, &info_i); + if (err < 0) + return err; + channels0 = multi->slaves[i].channels_total; + info_i.buffer_size /= channels0; + info_i.min_fragment_size /= channels0; + info_i.max_fragment_size /= channels0; + info_i.fragment_align /= channels0; + info->flags &= info_i.flags; + info->formats &= info_i.formats; + info->rates &= info_i.rates; + if (info_i.min_rate > info->min_rate) + info->min_rate = info_i.min_rate; + if (info_i.max_rate < info->max_rate) + info->max_rate = info_i.max_rate; + if (info_i.buffer_size < info->buffer_size) + info->buffer_size = info_i.buffer_size; + if (info_i.min_fragment_size > info->min_fragment_size) + info->min_fragment_size = info_i.min_fragment_size; + if (info_i.max_fragment_size < info->max_fragment_size) + info->max_fragment_size = info_i.max_fragment_size; + if (info_i.min_fragments > info->min_fragments) + info->min_fragments = info_i.min_fragments; + if (info_i.max_fragments < info->max_fragments) + info->max_fragments = info_i.max_fragments; + } + channels = multi->channels_count; + info->buffer_size *= channels; + info->min_fragment_size *= channels; + info->max_fragment_size *= channels; + info->fragment_align *= channels; + info->min_channels = multi->channels_count; + info->max_channels = multi->channels_count; + if (multi->one_to_many) + info->flags &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + return 0; +} + +static int snd_pcm_multi_params(void *private, snd_pcm_params_t *params) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + snd_pcm_params_t p; + if (params->format.channels != multi->channels_count) + return -EINVAL; + p = *params; + multi->interleave = params->format.interleave; + for (i = 0; i < multi->slaves_count; ++i) { + int err; + snd_pcm_t *handle = multi->slaves[i].handle; + snd_pcm_info_t info; + err = snd_pcm_info(handle, &info); + if (err < 0) + return err; + p.format.interleave = params->format.interleave; + if (!(info.flags & SND_PCM_INFO_INTERLEAVE)) + p.format.interleave = 0; + else if (!(info.flags & SND_PCM_INFO_NONINTERLEAVE)) + p.format.interleave = 1; + p.format.channels = multi->slaves[i].channels_total; + err = snd_pcm_params(handle, &p); + if (err < 0) + return err; + if (i == 0 && params->mode == SND_PCM_MODE_FRAGMENT) { + snd_pcm_setup_t s; + err = snd_pcm_setup(handle, &s); + if (err < 0) + return err; + p.frag_size = s.frag_size; + p.buffer_size = s.buffer_size; + } + } + return 0; +} + +static int snd_pcm_multi_setup(void *private, snd_pcm_setup_t *setup) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + int err; + err = snd_pcm_setup(multi->slaves[0].handle, setup); + if (err < 0) + return err; + for (i = 1; i < multi->slaves_count; ++i) { + snd_pcm_setup_t s; + snd_pcm_t *sh = multi->slaves[i].handle; + err = snd_pcm_setup(sh, &s); + if (err < 0) + return err; + if (setup->format.rate != s.format.rate) + return -EINVAL; + if (setup->frames_align % s.frames_align != 0) + return -EINVAL; + } + setup->format.interleave = multi->interleave; + setup->format.channels = multi->channels_count; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_multi_slave_t *s = &multi->slaves[i]; + snd_pcm_t *sh = s->handle; + unsigned int c; + if (s->buf) { + free(s->buf); + s->buf = 0; + free(s->areas); + s->areas = 0; + } + if (s->iovec) + free(s->iovec); + if (!sh->setup.format.interleave) { + s->iovec = calloc(s->channels_total, sizeof(*s->iovec)); + if (!multi->handle->setup.format.interleave) + continue; + } + s->buf = malloc(sh->setup.frag_size * sh->bits_per_frame / 8); + if (!s->buf) + return -ENOMEM; + snd_pcm_format_set_silence(sh->setup.format.format, s->buf, + sh->setup.frag_size * sh->setup.format.channels); + s->areas = calloc(s->channels_total, sizeof(*s->areas)); + if (!s->areas) + return -ENOMEM; + for (c = 0; c < s->channels_total; ++c) { + snd_pcm_channel_area_t *a = &s->areas[c]; + if (sh->setup.format.interleave) { + a->addr = s->buf; + a->first = c * sh->bits_per_sample; + a->step = sh->bits_per_frame; + } else { + a->addr = s->buf + sh->setup.frag_size * sh->bits_per_sample / 8; + a->first = 0; + a->step = sh->bits_per_sample; + } + } + } + return 0; +} + +static int snd_pcm_multi_status(void *private, snd_pcm_status_t *status) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + snd_pcm_t *handle = multi->slaves[0].handle; + return snd_pcm_status(handle, status); +} + +static int snd_pcm_multi_state(void *private) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + snd_pcm_t *handle = multi->slaves[0].handle; + return snd_pcm_state(handle); +} + +static ssize_t snd_pcm_multi_frame_io(void *private, int update) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + snd_pcm_t *handle = multi->slaves[0].handle; + return snd_pcm_frame_io(handle, update); +} + +static int snd_pcm_multi_prepare(void *private) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_prepare(handle); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_multi_go(void *private) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_go(handle); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_multi_sync_go(void *private, snd_pcm_sync_t *sync) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_sync_go(handle, sync); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_multi_drain(void *private) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_drain(handle); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_multi_flush(void *private) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_flush(handle); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_multi_pause(void *private, int enable) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_pause(handle, enable); + if (err < 0) + return err; + } + return 0; +} + +static int snd_pcm_multi_channel_setup(void *private, snd_pcm_channel_setup_t *setup) +{ + int err; + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int channel = setup->channel; + unsigned int i; + for (i = 0; i < multi->binds_count; ++i) { + if (multi->binds[i].client_channel == channel) { + setup->channel = multi->binds[i].slave_channel; + err = snd_pcm_channel_setup(multi->slaves[multi->binds[i].slave].handle, setup); + setup->channel = channel; + return err; + } + } + memset(setup, 0, sizeof(*setup)); + setup->channel = channel; + return 0; +} + +static ssize_t snd_pcm_multi_frame_data(void *private, off_t offset) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + ssize_t pos, newpos; + unsigned int i; + snd_pcm_t *handle_0 = multi->slaves[0].handle; + + pos = snd_pcm_frame_data(handle_0, 0); + newpos = snd_pcm_frame_data(handle_0, offset); + if (newpos < 0) + return newpos; + offset = newpos - pos; + if (offset < 0) + offset += handle_0->setup.frame_boundary; + + for (i = 1; i < multi->slaves_count; ++i) { + snd_pcm_t *handle_i = multi->slaves[i].handle; + ssize_t newpos_i; + newpos_i = snd_pcm_frame_data(handle_i, offset); + if (newpos_i < 0) + return newpos_i; + if (newpos_i != newpos) + return -EBADFD; + } + return newpos; +} + +ssize_t snd_pcm_multi_write(void *private, const void *buf, size_t count) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + snd_pcm_t *handle = multi->handle; + unsigned int i; + snd_pcm_channel_area_t area; + area.addr = (void *) buf; + area.step = handle->bits_per_frame; + for (i = 0; i < multi->binds_count; ++i) { + snd_pcm_multi_bind_t *bind = &multi->binds[i]; + snd_pcm_multi_slave_t *slave = &multi->slaves[bind->slave]; + int err; + assert(slave->buf); + area.first = handle->bits_per_sample * bind->client_channel; + err = snd_pcm_area_copy(&area, 0, &slave->areas[bind->slave_channel], 0, count, handle->setup.format.format); + if (err < 0) + return err; + if (!slave->handle->setup.format.interleave) + slave->iovec[bind->slave_channel].iov_base = slave->areas[bind->slave_channel].addr; + } + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_multi_slave_t *slave = &multi->slaves[i]; + snd_pcm_t *sh = slave->handle; + if (sh->setup.format.interleave) { + count = snd_pcm_write(sh, slave->buf, count); + } else { + int channels = sh->setup.format.channels; + struct iovec vec[channels]; + int c; + for (c = 0; c < channels; ++c) + vec[c].iov_len = count; + count = snd_pcm_writev(sh, vec, channels); + } + if (count <= 0) + break; + } + return count; +} + +ssize_t snd_pcm_multi_read(void *private, void *buf, size_t count) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + return count; +} + +ssize_t snd_pcm_multi_writev(void *private, const struct iovec *vector, unsigned long count) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + return count; +} + +ssize_t snd_pcm_multi_readv(void *private, const struct iovec *vector, unsigned long count) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + return count; +} + +static int snd_pcm_multi_mmap_status(void *private, snd_pcm_mmap_status_t **status) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_mmap_status(handle, status); + if (err < 0) + return err; + } + *status = multi->slaves[0].handle->mmap_status; + return 0; +} + +static int snd_pcm_multi_mmap_control(void *private, snd_pcm_mmap_control_t **control) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + snd_pcm_setup_t *setup_0 = &multi->slaves[0].handle->setup; + unsigned int i; + for (i = 1; i < multi->slaves_count; ++i) { + snd_pcm_setup_t *setup = &multi->slaves[i].handle->setup; + /* Don't permit mmap if frame_data's have + different ranges */ + if (setup->buffer_size != setup_0->buffer_size) + return -EBADFD; + } + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_mmap_control(handle, control); + if (err < 0) + return err; + } + *control = multi->slaves[0].handle->mmap_control; + return 0; +} + +static int snd_pcm_multi_mmap_data(void *private, void **buffer, size_t bsize UNUSED) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_mmap_data(handle, 0); + snd_pcm_setup_t *setup; + if (err < 0) + return err; + setup = &handle->setup; + { + snd_pcm_channel_area_t areas[setup->format.channels]; + err = snd_pcm_mmap_get_areas(handle, areas); + if (err < 0) + return err; + err = snd_pcm_areas_silence(areas, 0, setup->format.channels, setup->buffer_size, setup->format.format); + if (err < 0) + return err; + } + } + *buffer = multi->slaves[0].handle->mmap_data; + return 0; +} + +static int snd_pcm_multi_munmap_status(void *private, snd_pcm_mmap_status_t *status UNUSED) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + int ret = 0; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_munmap_status(handle); + if (err < 0) + ret = err; + } + return ret; +} + +static int snd_pcm_multi_munmap_control(void *private, snd_pcm_mmap_control_t *control UNUSED) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + int ret = 0; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_munmap_control(handle); + if (err < 0) + ret = err; + } + return ret; +} + +static int snd_pcm_multi_munmap_data(void *private, void *buffer UNUSED, size_t size UNUSED) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + int ret = 0; + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + int err = snd_pcm_munmap_data(handle); + if (err < 0) + ret = err; + } + return ret; +} + +static int snd_pcm_multi_channels_mask(void *private, bitset_t *client_vmask) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + unsigned int i; + bitset_t *vmasks[multi->slaves_count]; + int err; + for (i = 0; i < multi->slaves_count; ++i) + vmasks[i] = bitset_alloc(multi->slaves[i].channels_total); + for (i = 0; i < multi->binds_count; ++i) { + snd_pcm_multi_bind_t *b = &multi->binds[i]; + if (bitset_get(client_vmask, b->client_channel)) + bitset_set(vmasks[b->slave], b->slave_channel); + } + for (i = 0; i < multi->slaves_count; ++i) { + snd_pcm_t *handle = multi->slaves[i].handle; + err = snd_pcm_channels_mask(handle, vmasks[i]); + if (err < 0) { + for (i = 0; i <= multi->slaves_count; ++i) + free(vmasks[i]); + return err; + } + } + bitset_zero(client_vmask, multi->handle->setup.format.channels); + for (i = 0; i < multi->binds_count; ++i) { + snd_pcm_multi_bind_t *b = &multi->binds[i]; + if (bitset_get(vmasks[b->slave], b->slave_channel)) + bitset_set(client_vmask, b->client_channel); + } + for (i = 0; i < multi->slaves_count; ++i) + free(vmasks[i]); + return 0; +} + +int snd_pcm_multi_file_descriptor(void *private) +{ + snd_pcm_multi_t *multi = (snd_pcm_multi_t*) private; + snd_pcm_t *handle = multi->slaves[0].handle; + return snd_pcm_file_descriptor(handle); +} + +struct snd_pcm_ops snd_pcm_multi_ops = { + close: snd_pcm_multi_close, + nonblock: snd_pcm_multi_nonblock, + info: snd_pcm_multi_info, + params: snd_pcm_multi_params, + setup: snd_pcm_multi_setup, + channel_setup: snd_pcm_multi_channel_setup, + status: snd_pcm_multi_status, + frame_io: snd_pcm_multi_frame_io, + state: snd_pcm_multi_state, + prepare: snd_pcm_multi_prepare, + go: snd_pcm_multi_go, + sync_go: snd_pcm_multi_sync_go, + drain: snd_pcm_multi_drain, + flush: snd_pcm_multi_flush, + pause: snd_pcm_multi_pause, + write: snd_pcm_multi_write, + writev: snd_pcm_multi_writev, + read: snd_pcm_multi_read, + readv: snd_pcm_multi_readv, + frame_data: snd_pcm_multi_frame_data, + mmap_status: snd_pcm_multi_mmap_status, + mmap_control: snd_pcm_multi_mmap_control, + mmap_data: snd_pcm_multi_mmap_data, + munmap_status: snd_pcm_multi_munmap_status, + munmap_control: snd_pcm_multi_munmap_control, + munmap_data: snd_pcm_multi_munmap_data, + file_descriptor: snd_pcm_multi_file_descriptor, + channels_mask: snd_pcm_multi_channels_mask, +}; + +int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count, + snd_pcm_t **slaves_handle, size_t *slaves_channels_count, + size_t binds_count, unsigned int *binds_client_channel, + unsigned int *binds_slave, unsigned int *binds_slave_channel, + int close_slaves) +{ + snd_pcm_t *handle; + snd_pcm_multi_t *multi; + size_t channels = 0; + unsigned int i; + int stream; + char client_map[32] = { 0 }; + char slave_map[32][32] = { { 0 } }; + + assert(slaves_count > 0 && slaves_handle && slaves_channels_count); + assert(binds_count > 0 && binds_slave && binds_client_channel && binds_slave_channel); + + handle = calloc(1, sizeof(snd_pcm_t)); + if (!handle) + return -ENOMEM; + multi = calloc(1, sizeof(snd_pcm_multi_t)); + if (!multi) { + free(handle); + return -ENOMEM; + } + + stream = slaves_handle[0]->stream; + + multi->handle = handle; + multi->slaves_count = slaves_count; + multi->slaves = calloc(slaves_count, sizeof(*multi->slaves)); + multi->binds_count = binds_count; + multi->binds = calloc(binds_count, sizeof(*multi->binds)); + for (i = 0; i < slaves_count; ++i) { + snd_pcm_multi_slave_t *slave = &multi->slaves[i]; + assert(slaves_handle[i]->stream == stream); + slave->handle = slaves_handle[i]; + slave->channels_total = slaves_channels_count[i]; + slave->close_slave = close_slaves; + } + for (i = 0; i < binds_count; ++i) { + snd_pcm_multi_bind_t *bind = &multi->binds[i]; + assert(binds_slave[i] < slaves_count); + assert(binds_slave_channel[i] < slaves_channels_count[binds_slave[i]]); + bind->client_channel = binds_client_channel[i]; + bind->slave = binds_slave[i]; + bind->slave_channel = binds_slave_channel[i]; + if (slave_map[binds_slave[i]][binds_slave_channel[i]]) { + assert(stream == SND_PCM_STREAM_CAPTURE); + multi->one_to_many = 1; + } + slave_map[binds_slave[i]][binds_slave_channel[i]] = 1; + if (client_map[binds_client_channel[i]]) { + assert(stream == SND_PCM_STREAM_PLAYBACK); + multi->one_to_many = 1; + } + client_map[binds_client_channel[i]] = 1; + if (binds_client_channel[i] >= channels) + channels = binds_client_channel[i] + 1; + } + multi->channels_count = channels; + + handle->type = SND_PCM_TYPE_MULTI; + handle->stream = stream; + handle->mode = multi->slaves[0].handle->mode; + handle->ops = &snd_pcm_multi_ops; + handle->op_arg = multi; + handle->private = multi; + *handlep = handle; + return 0; +} -- 2.11.0