Cleaning and fixes

This commit is contained in:
Abramo Bagnara 2000-10-02 06:58:38 +00:00
parent 2ea3fd4bb3
commit ebae7ebeb2

View file

@ -25,28 +25,25 @@
#include <string.h> #include <string.h>
#include <errno.h> #include <errno.h>
#include <math.h> #include <math.h>
#include <sys/uio.h>
#include "pcm_local.h" #include "pcm_local.h"
typedef struct { typedef struct {
snd_pcm_t *handle; snd_pcm_t *pcm;
unsigned int channels_total; unsigned int channels_count;
int close_slave; int close_slave;
} snd_pcm_multi_slave_t; } snd_pcm_multi_slave_t;
typedef struct { typedef struct {
unsigned int client_channel; int slave_idx;
unsigned int slave;
unsigned int slave_channel; unsigned int slave_channel;
} snd_pcm_multi_bind_t; } snd_pcm_multi_channel_t;
typedef struct { typedef struct {
size_t slaves_count; size_t slaves_count;
snd_pcm_multi_slave_t *slaves; snd_pcm_multi_slave_t *slaves;
size_t bindings_count;
snd_pcm_multi_bind_t *bindings;
size_t channels_count; size_t channels_count;
int xfer_mode, mmap_shape; snd_pcm_multi_channel_t *channels;
int xfer_mode;
} snd_pcm_multi_t; } snd_pcm_multi_t;
static int snd_pcm_multi_close(snd_pcm_t *pcm) static int snd_pcm_multi_close(snd_pcm_t *pcm)
@ -58,32 +55,30 @@ static int snd_pcm_multi_close(snd_pcm_t *pcm)
int err; int err;
snd_pcm_multi_slave_t *slave = &multi->slaves[i]; snd_pcm_multi_slave_t *slave = &multi->slaves[i];
if (slave->close_slave) { if (slave->close_slave) {
err = snd_pcm_close(slave->handle); err = snd_pcm_close(slave->pcm);
if (err < 0) if (err < 0)
ret = err; ret = err;
} else } else
snd_pcm_unlink(slave->handle); snd_pcm_unlink(slave->pcm);
} }
free(multi->slaves); free(multi->slaves);
free(multi->bindings); free(multi->channels);
free(multi); free(multi);
return ret; return ret;
} }
static int snd_pcm_multi_nonblock(snd_pcm_t *pcm, int nonblock) static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
{ {
snd_pcm_multi_t *multi = pcm->private; return 0;
snd_pcm_t *handle = multi->slaves[0].handle;
return snd_pcm_nonblock(handle, nonblock);
} }
static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info) static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
int err; int err;
snd_pcm_t *handle_0 = multi->slaves[0].handle; snd_pcm_t *slave_0 = multi->slaves[0].pcm;
/* FIXME */ /* FIXME */
err = snd_pcm_info(handle_0, info); err = snd_pcm_info(slave_0, info);
if (err < 0) if (err < 0)
return err; return err;
return 0; return 0;
@ -94,23 +89,31 @@ static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
unsigned int i; unsigned int i;
int err; int err;
snd_pcm_t *handle_0 = multi->slaves[0].handle; snd_pcm_t *slave_0 = multi->slaves[0].pcm;
unsigned int old_mask = info->req_mask; unsigned int req_mask = info->req_mask;
info->req_mask &= ~(SND_PCM_PARAMS_CHANNELS | unsigned int channels = info->req.format.channels;
SND_PCM_PARAMS_MMAP_SHAPE | if ((req_mask & SND_PCM_PARAMS_CHANNELS) &&
SND_PCM_PARAMS_XFER_MODE); channels != multi->channels_count) {
err = snd_pcm_params_info(handle_0, info); info->req.fail_mask |= SND_PCM_PARAMS_CHANNELS;
info->req.fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL;
}
info->req_mask |= SND_PCM_PARAMS_CHANNELS;
info->req.format.channels = multi->slaves[0].channels_count;
err = snd_pcm_params_info(slave_0, info);
info->req_mask = req_mask;
info->req.format.channels = channels;
if (err < 0) if (err < 0)
return err; return err;
info->min_channels = multi->channels_count; info->min_channels = multi->channels_count;
info->max_channels = multi->channels_count; info->max_channels = multi->channels_count;
for (i = 1; i < multi->slaves_count; ++i) { for (i = 1; i < multi->slaves_count; ++i) {
snd_pcm_t *handle_i = multi->slaves[i].handle; snd_pcm_t *slave_i = multi->slaves[i].pcm;
snd_pcm_params_info_t info_i; snd_pcm_params_info_t info_i;
info_i = *info; info_i = *info;
info_i.req_mask |= SND_PCM_PARAMS_CHANNELS; info_i.req_mask |= SND_PCM_PARAMS_CHANNELS;
info_i.req.format.channels = multi->slaves[i].channels_total; info_i.req.format.channels = multi->slaves[i].channels_count;
err = snd_pcm_params_info(handle_i, &info_i); err = snd_pcm_params_info(slave_i, &info_i);
if (err < 0) if (err < 0)
return err; return err;
info->formats &= info_i.formats; info->formats &= info_i.formats;
@ -131,7 +134,14 @@ static int snd_pcm_multi_params_info(snd_pcm_t *pcm, snd_pcm_params_info_t *info
info->max_fragments = info_i.max_fragments; info->max_fragments = info_i.max_fragments;
info->flags &= info_i.flags; info->flags &= info_i.flags;
} }
info->req_mask = old_mask; if (info->flags & SND_PCM_INFO_INTERLEAVED) {
if (multi->slaves_count > 0) {
info->flags &= ~SND_PCM_INFO_INTERLEAVED;
info->flags |= SND_PCM_INFO_COMPLEX;
}
} else if (!(info->flags & SND_PCM_INFO_NONINTERLEAVED))
info->flags |= SND_PCM_INFO_COMPLEX;
info->req_mask = req_mask;
return 0; return 0;
} }
@ -140,42 +150,42 @@ static int snd_pcm_multi_params(snd_pcm_t *pcm, snd_pcm_params_t *params)
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
unsigned int i; unsigned int i;
snd_pcm_params_t p; snd_pcm_params_t p;
if (params->format.channels != multi->channels_count) int err = 0;
if (params->format.channels != multi->channels_count) {
params->fail_mask = SND_PCM_PARAMS_CHANNELS;
params->fail_reason = SND_PCM_PARAMS_FAIL_INVAL;
return -EINVAL; return -EINVAL;
multi->xfer_mode = params->xfer_mode; }
multi->mmap_shape = params->mmap_shape;
p = *params; p = *params;
for (i = 0; i < multi->slaves_count; ++i) { for (i = 0; i < multi->slaves_count; ++i) {
int err; snd_pcm_t *slave = multi->slaves[i].pcm;
snd_pcm_t *handle = multi->slaves[i].handle; if (slave->mmap_data) {
if (handle->mmap_data) { err = snd_pcm_munmap_data(slave);
err = snd_pcm_munmap_data(handle);
if (err < 0) if (err < 0)
return err; return err;
} }
p.xfer_mode = SND_PCM_XFER_UNSPECIFIED; p.format.channels = multi->slaves[i].channels_count;
p.mmap_shape = SND_PCM_MMAP_UNSPECIFIED;
p.format.channels = multi->slaves[i].channels_total;
#if 1 #if 1
p.xrun_max = ~0; p.xrun_mode = SND_PCM_XRUN_NONE;
#endif #endif
err = snd_pcm_params(handle, &p); err = snd_pcm_params(slave, &p);
if (err < 0) { if (err < 0) {
params->fail_mask = p.fail_mask; params->fail_mask = p.fail_mask;
params->fail_reason = p.fail_reason; params->fail_reason = p.fail_reason;
return err; break;
} }
} }
for (i = 0; i < multi->slaves_count; ++i) { for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *handle = multi->slaves[i].handle; snd_pcm_t *slave = multi->slaves[i].pcm;
int err = snd_pcm_mmap_data(handle, NULL); snd_pcm_mmap_data(slave, NULL);
if (err < 0) if (pcm->stream == SND_PCM_STREAM_PLAYBACK &&
return err; err == 0)
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) snd_pcm_areas_silence(slave->mmap_areas, 0, slave->setup.format.channels,
snd_pcm_areas_silence(handle->mmap_areas, 0, handle->setup.format.channels, slave->setup.buffer_size, slave->setup.format.sfmt);
handle->setup.buffer_size, handle->setup.format.sfmt);
} }
return 0; if (err == 0)
multi->xfer_mode = params->xfer_mode;
return err;
} }
static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup) static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
@ -183,14 +193,12 @@ static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
unsigned int i; unsigned int i;
int err; int err;
size_t frames_alloc; err = snd_pcm_setup(multi->slaves[0].pcm, setup);
err = snd_pcm_setup(multi->slaves[0].handle, setup);
if (err < 0) if (err < 0)
return err; return err;
frames_alloc = multi->slaves[0].handle->setup.frag_size;
for (i = 1; i < multi->slaves_count; ++i) { for (i = 1; i < multi->slaves_count; ++i) {
snd_pcm_setup_t s; snd_pcm_setup_t s;
snd_pcm_t *sh = multi->slaves[i].handle; snd_pcm_t *sh = multi->slaves[i].pcm;
err = snd_pcm_setup(sh, &s); err = snd_pcm_setup(sh, &s);
if (err < 0) if (err < 0)
return err; return err;
@ -207,123 +215,107 @@ static int snd_pcm_multi_setup(snd_pcm_t *pcm, snd_pcm_setup_t *setup)
setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED; setup->xfer_mode = SND_PCM_XFER_NONINTERLEAVED;
else else
setup->xfer_mode = multi->xfer_mode; setup->xfer_mode = multi->xfer_mode;
if (multi->mmap_shape != SND_PCM_MMAP_UNSPECIFIED &&
multi->mmap_shape != setup->mmap_shape)
return -EINVAL;
return 0; return 0;
} }
static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status) static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
snd_pcm_t *handle = multi->slaves[0].handle; snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_status(handle, status); return snd_pcm_status(slave, status);
} }
static int snd_pcm_multi_state(snd_pcm_t *pcm) static int snd_pcm_multi_state(snd_pcm_t *pcm)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
snd_pcm_t *handle = multi->slaves[0].handle; snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_state(handle); return snd_pcm_state(slave);
} }
static int snd_pcm_multi_delay(snd_pcm_t *pcm, ssize_t *delayp) static int snd_pcm_multi_delay(snd_pcm_t *pcm, ssize_t *delayp)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
snd_pcm_t *handle = multi->slaves[0].handle; snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_delay(handle, delayp); return snd_pcm_delay(slave, delayp);
} }
static ssize_t snd_pcm_multi_avail_update(snd_pcm_t *pcm) static ssize_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
snd_pcm_t *handle = multi->slaves[0].handle; snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_avail_update(handle); return snd_pcm_avail_update(slave);
} }
static int snd_pcm_multi_prepare(snd_pcm_t *pcm) static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
return snd_pcm_prepare(multi->slaves[0].handle); return snd_pcm_prepare(multi->slaves[0].pcm);
} }
static int snd_pcm_multi_start(snd_pcm_t *pcm) static int snd_pcm_multi_start(snd_pcm_t *pcm)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
return snd_pcm_start(multi->slaves[0].handle); return snd_pcm_start(multi->slaves[0].pcm);
} }
static int snd_pcm_multi_stop(snd_pcm_t *pcm) static int snd_pcm_multi_drop(snd_pcm_t *pcm)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
return snd_pcm_stop(multi->slaves[0].handle); return snd_pcm_drop(multi->slaves[0].pcm);
} }
static int snd_pcm_multi_drain(snd_pcm_t *pcm) static int snd_pcm_multi_drain(snd_pcm_t *pcm)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
return snd_pcm_drain(multi->slaves[0].handle); return snd_pcm_drain(multi->slaves[0].pcm);
} }
static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable) static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
return snd_pcm_pause(multi->slaves[0].handle, enable); return snd_pcm_pause(multi->slaves[0].pcm, enable);
} }
static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
{ {
int err;
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
unsigned int channel = info->channel; unsigned int channel = info->channel;
unsigned int i; snd_pcm_multi_channel_t *c = &multi->channels[channel];
for (i = 0; i < multi->bindings_count; ++i) { int err;
if (multi->bindings[i].client_channel == channel) { if (c->slave_idx < 0)
info->channel = multi->bindings[i].slave_channel; return -ENXIO;
err = snd_pcm_channel_info(multi->slaves[multi->bindings[i].slave].handle, info); info->channel = c->slave_channel;
info->channel = channel; err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info);
return err;
}
}
info->channel = channel; info->channel = channel;
return -EINVAL; return err;
} }
static int snd_pcm_multi_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params) static int snd_pcm_multi_channel_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
{ {
int err;
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
unsigned int channel = params->channel; unsigned int channel = params->channel;
unsigned int i; snd_pcm_multi_channel_t *c = &multi->channels[channel];
for (i = 0; i < multi->bindings_count; ++i) { int err;
if (multi->bindings[i].client_channel == channel) { if (c->slave_idx < 0)
params->channel = multi->bindings[i].slave_channel; return -ENXIO;
err = snd_pcm_channel_params(multi->slaves[multi->bindings[i].slave].handle, params); params->channel = c->slave_channel;
params->channel = channel; err = snd_pcm_channel_params(multi->slaves[c->slave_idx].pcm, params);
return err;
}
}
params->channel = channel; params->channel = channel;
return -EINVAL; return err;
} }
static int snd_pcm_multi_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup) static int snd_pcm_multi_channel_setup(snd_pcm_t *pcm, snd_pcm_channel_setup_t *setup)
{ {
int err;
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
unsigned int channel = setup->channel; unsigned int channel = setup->channel;
unsigned int i; snd_pcm_multi_channel_t *c = &multi->channels[channel];
for (i = 0; i < multi->bindings_count; ++i) { int err;
if (multi->bindings[i].client_channel == channel) { if (c->slave_idx < 0)
setup->channel = multi->bindings[i].slave_channel; return -ENXIO;
err = snd_pcm_channel_setup(multi->slaves[multi->bindings[i].slave].handle, setup); setup->channel = c->slave_channel;
setup->channel = channel; err = snd_pcm_channel_setup(multi->slaves[c->slave_idx].pcm, setup);
return err;
}
}
memset(setup, 0, sizeof(*setup));
setup->channel = channel; setup->channel = channel;
return 0; return err;
} }
static ssize_t snd_pcm_multi_rewind(snd_pcm_t *pcm, size_t frames) static ssize_t snd_pcm_multi_rewind(snd_pcm_t *pcm, size_t frames)
@ -333,8 +325,8 @@ static ssize_t snd_pcm_multi_rewind(snd_pcm_t *pcm, size_t frames)
size_t pos[multi->slaves_count]; size_t pos[multi->slaves_count];
memset(pos, 0, sizeof(pos)); memset(pos, 0, sizeof(pos));
for (i = 0; i < multi->slaves_count; ++i) { for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *handle_i = multi->slaves[i].handle; snd_pcm_t *slave_i = multi->slaves[i].pcm;
ssize_t f = snd_pcm_rewind(handle_i, frames); ssize_t f = snd_pcm_rewind(slave_i, frames);
if (f < 0) if (f < 0)
return f; return f;
pos[i] = f; pos[i] = f;
@ -342,10 +334,10 @@ static ssize_t snd_pcm_multi_rewind(snd_pcm_t *pcm, size_t frames)
} }
/* Realign the pointers */ /* Realign the pointers */
for (i = 0; i < multi->slaves_count; ++i) { for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *handle_i = multi->slaves[i].handle; snd_pcm_t *slave_i = multi->slaves[i].pcm;
size_t f = pos[i] - frames; size_t f = pos[i] - frames;
if (f > 0) if (f > 0)
snd_pcm_mmap_appl_forward(handle_i, f); snd_pcm_mmap_appl_forward(slave_i, f);
} }
return frames; return frames;
} }
@ -353,14 +345,14 @@ static ssize_t snd_pcm_multi_rewind(snd_pcm_t *pcm, size_t frames)
static int snd_pcm_multi_mmap_status(snd_pcm_t *pcm) static int snd_pcm_multi_mmap_status(snd_pcm_t *pcm)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
pcm->mmap_status = multi->slaves[0].handle->mmap_status; pcm->mmap_status = multi->slaves[0].pcm->mmap_status;
return 0; return 0;
} }
static int snd_pcm_multi_mmap_control(snd_pcm_t *pcm) static int snd_pcm_multi_mmap_control(snd_pcm_t *pcm)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
pcm->mmap_control = multi->slaves[0].handle->mmap_control; pcm->mmap_control = multi->slaves[0].pcm->mmap_control;
return 0; return 0;
} }
@ -369,15 +361,15 @@ static int snd_pcm_multi_mmap_data(snd_pcm_t *pcm)
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
unsigned int i; unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) { for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *handle = multi->slaves[i].handle; snd_pcm_t *slave = multi->slaves[i].pcm;
int err = snd_pcm_mmap_data(handle, 0); int err = snd_pcm_mmap_data(slave, 0);
snd_pcm_setup_t *setup; snd_pcm_setup_t *setup;
if (err < 0) if (err < 0)
return err; return err;
setup = &handle->setup; setup = &slave->setup;
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
snd_pcm_channel_area_t areas[setup->format.channels]; snd_pcm_channel_area_t areas[setup->format.channels];
err = snd_pcm_mmap_get_areas(handle, areas); err = snd_pcm_mmap_get_areas(slave, areas);
if (err < 0) if (err < 0)
return err; return err;
err = snd_pcm_areas_silence(areas, 0, setup->format.channels, setup->buffer_size, setup->format.sfmt); err = snd_pcm_areas_silence(areas, 0, setup->format.channels, setup->buffer_size, setup->format.sfmt);
@ -385,7 +377,7 @@ static int snd_pcm_multi_mmap_data(snd_pcm_t *pcm)
return err; return err;
} }
} }
pcm->mmap_data = multi->slaves[0].handle->mmap_data; pcm->mmap_data = multi->slaves[0].pcm->mmap_data;
return 0; return 0;
} }
@ -405,8 +397,8 @@ static int snd_pcm_multi_munmap_data(snd_pcm_t *pcm)
unsigned int i; unsigned int i;
int ret = 0; int ret = 0;
for (i = 0; i < multi->slaves_count; ++i) { for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *handle = multi->slaves[i].handle; snd_pcm_t *slave = multi->slaves[i].pcm;
int err = snd_pcm_munmap_data(handle); int err = snd_pcm_munmap_data(slave);
if (err < 0) if (err < 0)
ret = err; ret = err;
} }
@ -419,8 +411,8 @@ static ssize_t snd_pcm_multi_mmap_forward(snd_pcm_t *pcm, size_t size)
unsigned int i; unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) { for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *handle = multi->slaves[i].handle; snd_pcm_t *slave = multi->slaves[i].pcm;
ssize_t frames = snd_pcm_mmap_forward(handle, size); ssize_t frames = snd_pcm_mmap_forward(slave, size);
if (frames < 0) if (frames < 0)
return frames; return frames;
if (i == 0) { if (i == 0) {
@ -440,15 +432,17 @@ static int snd_pcm_multi_channels_mask(snd_pcm_t *pcm, bitset_t *cmask)
bitset_t *cmasks[multi->slaves_count]; bitset_t *cmasks[multi->slaves_count];
int err; int err;
for (i = 0; i < multi->slaves_count; ++i) for (i = 0; i < multi->slaves_count; ++i)
cmasks[i] = bitset_alloc(multi->slaves[i].channels_total); cmasks[i] = bitset_alloc(multi->slaves[i].channels_count);
for (i = 0; i < multi->bindings_count; ++i) { for (i = 0; i < multi->channels_count; ++i) {
snd_pcm_multi_bind_t *b = &multi->bindings[i]; snd_pcm_multi_channel_t *b = &multi->channels[i];
if (bitset_get(cmask, b->client_channel)) if (b->slave_idx < 0)
bitset_set(cmasks[b->slave], b->slave_channel); continue;
if (bitset_get(cmask, i))
bitset_set(cmasks[b->slave_idx], b->slave_channel);
} }
for (i = 0; i < multi->slaves_count; ++i) { for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *handle = multi->slaves[i].handle; snd_pcm_t *slave = multi->slaves[i].pcm;
err = snd_pcm_channels_mask(handle, cmasks[i]); err = snd_pcm_channels_mask(slave, cmasks[i]);
if (err < 0) { if (err < 0) {
for (i = 0; i <= multi->slaves_count; ++i) for (i = 0; i <= multi->slaves_count; ++i)
free(cmasks[i]); free(cmasks[i]);
@ -456,10 +450,12 @@ static int snd_pcm_multi_channels_mask(snd_pcm_t *pcm, bitset_t *cmask)
} }
} }
bitset_zero(cmask, pcm->setup.format.channels); bitset_zero(cmask, pcm->setup.format.channels);
for (i = 0; i < multi->bindings_count; ++i) { for (i = 0; i < multi->channels_count; ++i) {
snd_pcm_multi_bind_t *b = &multi->bindings[i]; snd_pcm_multi_channel_t *b = &multi->channels[i];
if (bitset_get(cmasks[b->slave], b->slave_channel)) if (b->slave_idx < 0)
bitset_set(cmask, b->client_channel); continue;
if (bitset_get(cmasks[b->slave_idx], b->slave_channel))
bitset_set(cmask, i);
} }
for (i = 0; i < multi->slaves_count; ++i) for (i = 0; i < multi->slaves_count; ++i)
free(cmasks[i]); free(cmasks[i]);
@ -469,8 +465,8 @@ static int snd_pcm_multi_channels_mask(snd_pcm_t *pcm, bitset_t *cmask)
int snd_pcm_multi_poll_descriptor(snd_pcm_t *pcm) int snd_pcm_multi_poll_descriptor(snd_pcm_t *pcm)
{ {
snd_pcm_multi_t *multi = pcm->private; snd_pcm_multi_t *multi = pcm->private;
snd_pcm_t *handle = multi->slaves[0].handle; snd_pcm_t *slave = multi->slaves[0].pcm;
return snd_pcm_poll_descriptor(handle); return snd_pcm_poll_descriptor(slave);
} }
static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp) static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp)
@ -484,14 +480,15 @@ static void snd_pcm_multi_dump(snd_pcm_t *pcm, FILE *fp)
} }
for (k = 0; k < multi->slaves_count; ++k) { for (k = 0; k < multi->slaves_count; ++k) {
fprintf(fp, "\nSlave #%d: ", k); fprintf(fp, "\nSlave #%d: ", k);
snd_pcm_dump(multi->slaves[k].handle, fp); snd_pcm_dump(multi->slaves[k].pcm, fp);
} }
fprintf(fp, "\nBindings:\n"); fprintf(fp, "\nChannel bindings:\n");
for (k = 0; k < multi->bindings_count; ++k) { for (k = 0; k < multi->channels_count; ++k) {
snd_pcm_multi_channel_t *c = &multi->channels[k];
if (c->slave_idx < 0)
continue;
fprintf(fp, "Channel #%d: slave %d[%d]\n", fprintf(fp, "Channel #%d: slave %d[%d]\n",
multi->bindings[k].client_channel, k, c->slave_idx, c->slave_channel);
multi->bindings[k].slave,
multi->bindings[k].slave_channel);
} }
} }
@ -520,7 +517,7 @@ struct snd_pcm_fast_ops snd_pcm_multi_fast_ops = {
delay: snd_pcm_multi_delay, delay: snd_pcm_multi_delay,
prepare: snd_pcm_multi_prepare, prepare: snd_pcm_multi_prepare,
start: snd_pcm_multi_start, start: snd_pcm_multi_start,
stop: snd_pcm_multi_stop, drop: snd_pcm_multi_drop,
drain: snd_pcm_multi_drain, drain: snd_pcm_multi_drain,
pause: snd_pcm_multi_pause, pause: snd_pcm_multi_pause,
writei: snd_pcm_mmap_writei, writei: snd_pcm_mmap_writei,
@ -536,22 +533,20 @@ struct snd_pcm_fast_ops snd_pcm_multi_fast_ops = {
int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count, int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
snd_pcm_t **slaves_handle, size_t *schannels_count, snd_pcm_t **slaves_handle, size_t *schannels_count,
size_t bindings_count, unsigned int *bindings_cchannel, size_t channels_count,
unsigned int *bindings_slave, unsigned int *bindings_schannel, int *sidxs, unsigned int *schannels,
int close_slaves) int close_slaves)
{ {
snd_pcm_t *handle; snd_pcm_t *handle;
snd_pcm_multi_t *multi; snd_pcm_multi_t *multi;
size_t channels = 0;
unsigned int i; unsigned int i;
int err; int err;
int stream; int stream;
char client_map[32] = { 0 };
char slave_map[32][32] = { { 0 } }; char slave_map[32][32] = { { 0 } };
assert(handlep); assert(handlep);
assert(slaves_count > 0 && slaves_handle && schannels_count); assert(slaves_count > 0 && slaves_handle && schannels_count);
assert(bindings_count > 0 && bindings_slave && bindings_cchannel && bindings_schannel); assert(channels_count > 0 && sidxs && schannels);
multi = calloc(1, sizeof(snd_pcm_multi_t)); multi = calloc(1, sizeof(snd_pcm_multi_t));
if (!multi) { if (!multi) {
@ -562,32 +557,29 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
multi->slaves_count = slaves_count; multi->slaves_count = slaves_count;
multi->slaves = calloc(slaves_count, sizeof(*multi->slaves)); multi->slaves = calloc(slaves_count, sizeof(*multi->slaves));
multi->bindings_count = bindings_count; multi->channels_count = channels_count;
multi->bindings = calloc(bindings_count, sizeof(*multi->bindings)); multi->channels = calloc(channels_count, sizeof(*multi->channels));
for (i = 0; i < slaves_count; ++i) { for (i = 0; i < slaves_count; ++i) {
snd_pcm_multi_slave_t *slave = &multi->slaves[i]; snd_pcm_multi_slave_t *slave = &multi->slaves[i];
assert(slaves_handle[i]->stream == stream); assert(slaves_handle[i]->stream == stream);
slave->handle = slaves_handle[i]; slave->pcm = slaves_handle[i];
slave->channels_total = schannels_count[i]; slave->channels_count = schannels_count[i];
slave->close_slave = close_slaves; slave->close_slave = close_slaves;
if (i != 0) if (i != 0)
snd_pcm_link(slaves_handle[i-1], slaves_handle[i]); snd_pcm_link(slaves_handle[i-1], slaves_handle[i]);
} }
for (i = 0; i < bindings_count; ++i) { for (i = 0; i < channels_count; ++i) {
snd_pcm_multi_bind_t *bind = &multi->bindings[i]; snd_pcm_multi_channel_t *bind = &multi->channels[i];
assert(bindings_slave[i] < slaves_count); assert(sidxs[i] < (int)slaves_count);
assert(bindings_schannel[i] < schannels_count[bindings_slave[i]]); assert(schannels[i] < schannels_count[sidxs[i]]);
bind->client_channel = bindings_cchannel[i]; bind->slave_idx = sidxs[i];
bind->slave = bindings_slave[i]; bind->slave_channel = schannels[i];
bind->slave_channel = bindings_schannel[i]; if (sidxs[i] < 0)
assert(!slave_map[bindings_slave[i]][bindings_schannel[i]]); continue;
slave_map[bindings_slave[i]][bindings_schannel[i]] = 1; assert(!slave_map[sidxs[i]][schannels[i]]);
assert(!client_map[bindings_cchannel[i]]); slave_map[sidxs[i]][schannels[i]] = 1;
client_map[bindings_cchannel[i]] = 1;
if (bindings_cchannel[i] >= channels)
channels = bindings_cchannel[i] + 1;
} }
multi->channels_count = channels; multi->channels_count = channels_count;
handle = calloc(1, sizeof(snd_pcm_t)); handle = calloc(1, sizeof(snd_pcm_t));
if (!handle) { if (!handle) {
@ -596,7 +588,7 @@ int snd_pcm_multi_create(snd_pcm_t **handlep, size_t slaves_count,
} }
handle->type = SND_PCM_TYPE_MULTI; handle->type = SND_PCM_TYPE_MULTI;
handle->stream = stream; handle->stream = stream;
handle->mode = multi->slaves[0].handle->mode; handle->mode = multi->slaves[0].pcm->mode;
handle->ops = &snd_pcm_multi_ops; handle->ops = &snd_pcm_multi_ops;
handle->op_arg = handle; handle->op_arg = handle;
handle->fast_ops = &snd_pcm_multi_fast_ops; handle->fast_ops = &snd_pcm_multi_fast_ops;
@ -623,11 +615,10 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
char **slaves_name = NULL; char **slaves_name = NULL;
snd_pcm_t **slaves_pcm = NULL; snd_pcm_t **slaves_pcm = NULL;
size_t *slaves_channels = NULL; size_t *slaves_channels = NULL;
unsigned int *bindings_cchannel = NULL; unsigned int *channels_sidx = NULL;
unsigned int *bindings_slave = NULL; unsigned int *channels_schannel = NULL;
unsigned int *bindings_schannel = NULL;
size_t slaves_count = 0; size_t slaves_count = 0;
size_t bindings_count = 0; size_t channels_count = 0;
snd_config_foreach(i, conf) { snd_config_foreach(i, conf) {
snd_config_t *n = snd_config_entry(i); snd_config_t *n = snd_config_entry(i);
if (strcmp(n->id, "comment") == 0) if (strcmp(n->id, "comment") == 0)
@ -656,27 +647,38 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
++slaves_count; ++slaves_count;
} }
snd_config_foreach(i, binding) { snd_config_foreach(i, binding) {
++bindings_count; int cchannel = -1;
char *p;
snd_config_t *m = snd_config_entry(i);
errno = 0;
cchannel = strtol(m->id, &p, 10);
if (errno || *p || cchannel < 0)
return -EINVAL;
if ((unsigned)cchannel > channels_count)
channels_count = cchannel + 1;
} }
if (channels_count == 0)
return -EINVAL;
slaves_id = calloc(slaves_count, sizeof(*slaves_id)); slaves_id = calloc(slaves_count, sizeof(*slaves_id));
slaves_name = calloc(slaves_count, sizeof(*slaves_name)); slaves_name = calloc(slaves_count, sizeof(*slaves_name));
slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm)); slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm));
slaves_channels = calloc(slaves_count, sizeof(*slaves_channels)); slaves_channels = calloc(slaves_count, sizeof(*slaves_channels));
bindings_cchannel = calloc(bindings_count, sizeof(*bindings_cchannel)); channels_sidx = calloc(channels_count, sizeof(*channels_sidx));
bindings_slave = calloc(bindings_count, sizeof(*bindings_slave)); channels_schannel = calloc(channels_count, sizeof(*channels_schannel));
bindings_schannel = calloc(bindings_count, sizeof(*bindings_schannel));
idx = 0; idx = 0;
for (idx = 0; idx < channels_count; ++idx)
channels_sidx[idx] = -1;
snd_config_foreach(i, slave) { snd_config_foreach(i, slave) {
snd_config_t *m = snd_config_entry(i); snd_config_t *m = snd_config_entry(i);
char *pcm = NULL; char *name = NULL;
long channels = -1; long channels = -1;
slaves_id[idx] = snd_config_id(m); slaves_id[idx] = snd_config_id(m);
snd_config_foreach(j, m) { snd_config_foreach(j, m) {
snd_config_t *n = snd_config_entry(j); snd_config_t *n = snd_config_entry(j);
if (strcmp(n->id, "comment") == 0) if (strcmp(n->id, "comment") == 0)
continue; continue;
if (strcmp(n->id, "pcm") == 0) { if (strcmp(n->id, "name") == 0) {
err = snd_config_string_get(n, &pcm); err = snd_config_string_get(n, &name);
if (err < 0) if (err < 0)
goto _free; goto _free;
continue; continue;
@ -690,33 +692,28 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
err = -EINVAL; err = -EINVAL;
goto _free; goto _free;
} }
if (!pcm || channels < 0) { if (!name || channels < 0) {
err = -EINVAL; err = -EINVAL;
goto _free; goto _free;
} }
slaves_name[idx] = strdup(pcm); slaves_name[idx] = strdup(name);
slaves_channels[idx] = channels; slaves_channels[idx] = channels;
++idx; ++idx;
} }
idx = 0;
snd_config_foreach(i, binding) { snd_config_foreach(i, binding) {
snd_config_t *m = snd_config_entry(i); snd_config_t *m = snd_config_entry(i);
long cchannel = -1, schannel = -1; long cchannel = -1;
long schannel = -1;
int slave = -1; int slave = -1;
long val; long val;
char *str; char *str;
cchannel = strtol(m->id, 0, 10);
snd_config_foreach(j, m) { snd_config_foreach(j, m) {
snd_config_t *n = snd_config_entry(j); snd_config_t *n = snd_config_entry(j);
if (strcmp(n->id, "comment") == 0) if (strcmp(n->id, "comment") == 0)
continue; continue;
if (strcmp(n->id, "client_channel") == 0) { if (strcmp(n->id, "sidx") == 0) {
err = snd_config_integer_get(n, &cchannel);
if (err < 0)
goto _free;
continue;
}
if (strcmp(n->id, "slave") == 0) {
char buf[32]; char buf[32];
unsigned int k; unsigned int k;
err = snd_config_string_get(n, &str); err = snd_config_string_get(n, &str);
@ -733,7 +730,7 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
} }
continue; continue;
} }
if (strcmp(n->id, "slave_channel") == 0) { if (strcmp(n->id, "schannel") == 0) {
err = snd_config_integer_get(n, &schannel); err = snd_config_integer_get(n, &schannel);
if (err < 0) if (err < 0)
goto _free; goto _free;
@ -754,10 +751,8 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
err = -EINVAL; err = -EINVAL;
goto _free; goto _free;
} }
bindings_cchannel[idx] = cchannel; channels_sidx[cchannel] = slave;
bindings_slave[idx] = slave; channels_schannel[cchannel] = schannel;
bindings_schannel[idx] = schannel;
++idx;
} }
for (idx = 0; idx < slaves_count; ++idx) { for (idx = 0; idx < slaves_count; ++idx) {
@ -767,8 +762,8 @@ int _snd_pcm_multi_open(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
} }
err = snd_pcm_multi_create(pcmp, slaves_count, slaves_pcm, err = snd_pcm_multi_create(pcmp, slaves_count, slaves_pcm,
slaves_channels, slaves_channels,
bindings_count, bindings_cchannel, channels_count,
bindings_slave, bindings_schannel, channels_sidx, channels_schannel,
1); 1);
_free: _free:
if (err < 0) { if (err < 0) {
@ -785,12 +780,10 @@ _free:
free(slaves_pcm); free(slaves_pcm);
if (slaves_channels) if (slaves_channels)
free(slaves_channels); free(slaves_channels);
if (bindings_cchannel) if (channels_sidx)
free(bindings_cchannel); free(channels_sidx);
if (bindings_slave) if (channels_schannel)
free(bindings_slave); free(channels_schannel);
if (bindings_schannel)
free(bindings_schannel);
return err; return err;
} }