]> git.alsa-project.org Git - alsa-lib.git/commitdiff
First incomplete implementation
authorAbramo Bagnara <abramo@alsa-project.org>
Wed, 21 Jun 2000 14:57:19 +0000 (14:57 +0000)
committerAbramo Bagnara <abramo@alsa-project.org>
Wed, 21 Jun 2000 14:57:19 +0000 (14:57 +0000)
src/pcm/pcm_multi.c [new file with mode: 0644]

diff --git a/src/pcm/pcm_multi.c b/src/pcm/pcm_multi.c
new file mode 100644 (file)
index 0000000..87469a2
--- /dev/null
@@ -0,0 +1,691 @@
+/*
+ *  PCM - Multi
+ *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
+ *
+ *
+ *   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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <sys/uio.h>
+#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;
+}