alsa-lib/src/pcm/pcm_multi.c

984 lines
27 KiB
C
Raw Normal View History

2002-01-16 16:42:40 +00:00
/**
* \file pcm/pcm_multi.c
* \ingroup PCM_Plugins
* \brief PCM Multi Streams to One Conversion Plugin Interface
* \author Abramo Bagnara <abramo@alsa-project.org>
* \date 2000-2001
*/
2000-06-21 14:57:19 +00:00
/*
* 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 Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
2000-06-21 14:57:19 +00:00
* 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 Lesser General Public License for more details.
2000-06-21 14:57:19 +00:00
*
* You should have received a copy of the GNU Lesser General Public
2000-06-21 14:57:19 +00:00
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2000-06-21 14:57:19 +00:00
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <math.h>
#include "pcm_local.h"
2001-10-24 14:14:11 +00:00
#ifndef PIC
/* entry for static linking */
const char *_snd_module_pcm_multi = "";
#endif
2002-01-16 16:42:40 +00:00
#ifndef DOC_HIDDEN
2000-06-21 14:57:19 +00:00
typedef struct {
2000-10-02 06:58:38 +00:00
snd_pcm_t *pcm;
unsigned int channels_count;
2000-06-21 14:57:19 +00:00
int close_slave;
2001-04-13 15:40:53 +00:00
int linked;
2000-06-21 14:57:19 +00:00
} snd_pcm_multi_slave_t;
typedef struct {
2000-10-02 06:58:38 +00:00
int slave_idx;
2000-06-21 14:57:19 +00:00
unsigned int slave_channel;
2000-10-02 06:58:38 +00:00
} snd_pcm_multi_channel_t;
2000-06-21 14:57:19 +00:00
typedef struct {
unsigned int slaves_count;
unsigned int master_slave;
2000-06-21 14:57:19 +00:00
snd_pcm_multi_slave_t *slaves;
unsigned int channels_count;
2000-10-02 06:58:38 +00:00
snd_pcm_multi_channel_t *channels;
2000-06-21 14:57:19 +00:00
} snd_pcm_multi_t;
2002-01-16 16:42:40 +00:00
#endif
static int snd_pcm_multi_close(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int i;
2000-06-21 14:57:19 +00:00
int ret = 0;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_multi_slave_t *slave = &multi->slaves[i];
2001-04-13 15:40:53 +00:00
if (slave->close_slave) {
int err = snd_pcm_close(slave->pcm);
if (err < 0)
ret = err;
}
2000-06-21 14:57:19 +00:00
}
free(multi->slaves);
2000-10-02 06:58:38 +00:00
free(multi->channels);
free(multi);
2000-06-21 14:57:19 +00:00
return ret;
}
2000-10-02 06:58:38 +00:00
static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
2000-06-21 14:57:19 +00:00
{
2000-10-02 06:58:38 +00:00
return 0;
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
return snd_pcm_async(slave_0, sig, pid);
}
static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-06-06 11:49:52 +00:00
int err, n;
assert(info->subdevice < multi->slaves_count);
n = info->subdevice;
info->subdevice = 0;
err = snd_pcm_info(multi->slaves[n].pcm, info);
if (err < 0)
return err;
info->subdevices_count = multi->slaves_count;
return 0;
}
static int snd_pcm_multi_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_access_mask_t access_mask;
int err;
snd_pcm_access_mask_any(&access_mask);
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
&access_mask);
if (err < 0)
return err;
err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
multi->channels_count, 0);
2000-12-21 20:44:10 +00:00
if (err < 0)
return err;
2001-03-29 17:50:28 +00:00
params->info = ~0U;
return 0;
}
2001-03-29 17:50:28 +00:00
static int snd_pcm_multi_hw_refine_sprepare(snd_pcm_t *pcm, unsigned int slave_idx,
snd_pcm_hw_params_t *sparams)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_multi_slave_t *slave = &multi->slaves[slave_idx];
snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
_snd_pcm_hw_params_any(sparams);
_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
&saccess_mask);
_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
slave->channels_count, 0);
return 0;
}
static int snd_pcm_multi_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
2001-03-29 17:50:28 +00:00
unsigned int slave_idx ATTRIBUTE_UNUSED,
snd_pcm_hw_params_t *params,
snd_pcm_hw_params_t *sparams)
{
int err;
unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
SND_PCM_HW_PARBIT_SUBFORMAT |
SND_PCM_HW_PARBIT_RATE |
SND_PCM_HW_PARBIT_PERIOD_SIZE |
SND_PCM_HW_PARBIT_PERIOD_TIME |
SND_PCM_HW_PARBIT_PERIODS |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
SND_PCM_HW_PARBIT_BUFFER_TIME |
SND_PCM_HW_PARBIT_TICK_TIME);
const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) &&
!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
snd_pcm_access_mask_t saccess_mask;
snd_pcm_access_mask_any(&saccess_mask);
snd_pcm_access_mask_reset(&saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
&saccess_mask);
if (err < 0)
return err;
}
err = _snd_pcm_hw_params_refine(sparams, links, params);
if (err < 0)
return err;
return 0;
}
static int snd_pcm_multi_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
2001-03-29 17:50:28 +00:00
unsigned int slave_idx ATTRIBUTE_UNUSED,
snd_pcm_hw_params_t *params,
snd_pcm_hw_params_t *sparams)
{
int err;
unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
SND_PCM_HW_PARBIT_SUBFORMAT |
SND_PCM_HW_PARBIT_RATE |
SND_PCM_HW_PARBIT_PERIOD_SIZE |
SND_PCM_HW_PARBIT_PERIOD_TIME |
SND_PCM_HW_PARBIT_PERIODS |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
SND_PCM_HW_PARBIT_BUFFER_TIME |
SND_PCM_HW_PARBIT_TICK_TIME);
snd_pcm_access_mask_t access_mask;
const snd_pcm_access_mask_t *saccess_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS);
snd_pcm_access_mask_any(&access_mask);
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX) &&
!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
&access_mask);
if (err < 0)
return err;
err = _snd_pcm_hw_params_refine(params, links, sparams);
if (err < 0)
return err;
params->info &= sparams->info;
return 0;
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_hw_refine_slave(snd_pcm_t *pcm,
2001-03-29 17:50:28 +00:00
unsigned int slave_idx,
snd_pcm_hw_params_t *sparams)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
return snd_pcm_hw_refine(slave, sparams);
}
static int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private_data;
2000-12-21 20:44:10 +00:00
unsigned int k;
snd_pcm_hw_params_t sparams[multi->slaves_count];
2000-11-20 20:10:46 +00:00
int err;
2001-01-21 17:03:53 +00:00
unsigned int cmask, changed;
err = snd_pcm_multi_hw_refine_cprepare(pcm, params);
if (err < 0)
return err;
for (k = 0; k < multi->slaves_count; ++k) {
err = snd_pcm_multi_hw_refine_sprepare(pcm, k, &sparams[k]);
if (err < 0) {
SNDERR("Slave PCM #%d not usable", k);
return err;
2000-12-21 20:44:10 +00:00
}
}
2001-01-21 17:03:53 +00:00
do {
cmask = params->cmask;
params->cmask = 0;
for (k = 0; k < multi->slaves_count; ++k) {
err = snd_pcm_multi_hw_refine_schange(pcm, k, params, &sparams[k]);
if (err >= 0)
err = snd_pcm_multi_hw_refine_slave(pcm, k, &sparams[k]);
if (err < 0) {
snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
return err;
}
err = snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
if (err < 0)
return err;
}
2001-01-21 17:03:53 +00:00
err = snd_pcm_hw_refine_soft(pcm, params);
changed = params->cmask;
params->cmask |= cmask;
2000-12-21 20:44:10 +00:00
if (err < 0)
2000-11-20 20:10:46 +00:00
return err;
2001-01-21 17:03:53 +00:00
} while (changed);
return 0;
}
static int snd_pcm_multi_hw_params_slave(snd_pcm_t *pcm,
2001-03-29 17:50:28 +00:00
unsigned int slave_idx,
snd_pcm_hw_params_t *sparams)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
2001-04-13 15:40:53 +00:00
int err = snd_pcm_hw_params(slave, sparams);
if (err < 0)
return err;
err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
if (err < 0)
return err;
if (slave->stopped_areas) {
err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format);
if (err < 0)
return err;
}
return 0;
}
static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-04-13 15:40:53 +00:00
unsigned int i;
snd_pcm_hw_params_t sparams[multi->slaves_count];
int err;
2001-04-13 15:40:53 +00:00
for (i = 0; i < multi->slaves_count; ++i) {
err = snd_pcm_multi_hw_refine_sprepare(pcm, i, &sparams[i]);
assert(err >= 0);
2001-04-13 15:40:53 +00:00
err = snd_pcm_multi_hw_refine_schange(pcm, i, params, &sparams[i]);
assert(err >= 0);
2001-04-13 15:40:53 +00:00
err = snd_pcm_multi_hw_params_slave(pcm, i, &sparams[i]);
if (err < 0) {
2001-04-13 15:40:53 +00:00
snd_pcm_multi_hw_refine_cchange(pcm, i, params, &sparams[i]);
return err;
}
}
2001-04-13 15:40:53 +00:00
multi->slaves[0].linked = 0;
for (i = 1; i < multi->slaves_count; ++i) {
err = snd_pcm_link(multi->slaves[0].pcm, multi->slaves[i].pcm);
multi->slaves[i].linked = (err >= 0);
}
return 0;
}
2001-01-19 13:10:50 +00:00
static int snd_pcm_multi_hw_free(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-01-19 13:10:50 +00:00
unsigned int i;
int err = 0;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
int e = snd_pcm_hw_free(slave);
if (e < 0)
err = e;
2001-04-13 15:40:53 +00:00
if (!multi->slaves[i].linked)
continue;
2001-04-17 09:58:06 +00:00
multi->slaves[i].linked = 0;
2001-04-13 15:40:53 +00:00
e = snd_pcm_unlink(slave);
if (e < 0)
err = e;
2001-01-19 13:10:50 +00:00
}
return err;
}
2000-11-20 20:10:46 +00:00
static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int i;
2000-11-20 20:10:46 +00:00
int err;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_t *slave = multi->slaves[i].pcm;
2000-11-20 20:10:46 +00:00
err = snd_pcm_sw_params(slave, params);
if (err < 0)
return err;
}
return 0;
}
2000-11-20 20:10:46 +00:00
static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
2000-10-02 06:58:38 +00:00
return snd_pcm_status(slave, status);
2000-06-21 14:57:19 +00:00
}
static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
2000-10-02 06:58:38 +00:00
return snd_pcm_state(slave);
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_hwsync(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
return snd_pcm_hwsync(slave);
}
static int snd_pcm_multi_hwptr(snd_pcm_t *pcm, snd_pcm_uframes_t *hwptr)
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
return INTERNAL(snd_pcm_hwptr)(slave, hwptr);
}
static int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
2000-10-02 06:58:38 +00:00
return snd_pcm_delay(slave, delayp);
2000-09-24 09:57:26 +00:00
}
static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
2000-09-24 09:57:26 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-04-13 15:40:53 +00:00
snd_pcm_sframes_t ret = LONG_MAX;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
snd_pcm_sframes_t avail;
avail = snd_pcm_avail_update(multi->slaves[i].pcm);
if (avail < 0)
return avail;
if (ret > avail)
ret = avail;
}
return ret;
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-04-13 15:40:53 +00:00
int err = 0;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
if (multi->slaves[i].linked)
continue;
err = snd_pcm_prepare(multi->slaves[i].pcm);
if (err < 0)
return err;
}
return err;
2000-06-21 14:57:19 +00:00
}
2000-11-24 17:08:03 +00:00
static int snd_pcm_multi_reset(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-04-13 15:40:53 +00:00
int err = 0;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
if (multi->slaves[i].linked)
continue;
err = snd_pcm_reset(multi->slaves[i].pcm);
if (err < 0)
return err;
}
return err;
2000-11-24 17:08:03 +00:00
}
2000-09-24 09:57:26 +00:00
static int snd_pcm_multi_start(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-04-13 15:40:53 +00:00
int err = 0;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
if (multi->slaves[i].linked)
continue;
err = snd_pcm_start(multi->slaves[i].pcm);
if (err < 0)
return err;
}
return err;
2000-06-21 14:57:19 +00:00
}
2000-10-02 06:58:38 +00:00
static int snd_pcm_multi_drop(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-04-13 15:40:53 +00:00
int err = 0;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
if (multi->slaves[i].linked)
continue;
err = snd_pcm_drop(multi->slaves[i].pcm);
if (err < 0)
return err;
}
return err;
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_drain(snd_pcm_t *pcm)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-04-13 15:40:53 +00:00
int err = 0;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
if (multi->slaves[i].linked)
continue;
err = snd_pcm_drain(multi->slaves[i].pcm);
if (err < 0)
return err;
}
return err;
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
2001-04-13 15:40:53 +00:00
int err = 0;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
if (multi->slaves[i].linked)
continue;
err = snd_pcm_pause(multi->slaves[i].pcm, enable);
if (err < 0)
return err;
}
return err;
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int channel = info->channel;
2000-10-02 06:58:38 +00:00
snd_pcm_multi_channel_t *c = &multi->channels[channel];
int err;
if (c->slave_idx < 0)
return -ENXIO;
info->channel = c->slave_channel;
err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info);
info->channel = channel;
2000-10-02 06:58:38 +00:00
return err;
}
static snd_pcm_sframes_t snd_pcm_multi_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
2000-06-21 14:57:19 +00:00
unsigned int i;
snd_pcm_uframes_t pos[multi->slaves_count];
memset(pos, 0, sizeof(pos));
for (i = 0; i < multi->slaves_count; ++i) {
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave_i = multi->slaves[i].pcm;
snd_pcm_sframes_t f = snd_pcm_rewind(slave_i, frames);
if (f < 0)
return f;
pos[i] = f;
frames = f;
}
/* Realign the pointers */
for (i = 0; i < multi->slaves_count; ++i) {
2000-10-02 06:58:38 +00:00
snd_pcm_t *slave_i = multi->slaves[i].pcm;
snd_pcm_uframes_t f = pos[i] - frames;
snd_pcm_sframes_t result;
if (f > 0) {
result = snd_pcm_mmap_commit(slave_i, snd_pcm_mmap_offset(slave_i), f);
if (result < 0)
return result;
if ((snd_pcm_uframes_t)result != f)
return -EIO;
}
2000-06-21 14:57:19 +00:00
}
return frames;
2000-06-21 14:57:19 +00:00
}
static int snd_pcm_multi_resume(snd_pcm_t *pcm)
{
snd_pcm_multi_t *multi = pcm->private_data;
int err = 0;
unsigned int i;
for (i = 0; i < multi->slaves_count; ++i) {
if (multi->slaves[i].linked)
continue;
err = snd_pcm_resume(multi->slaves[i].pcm);
if (err < 0)
return err;
}
return err;
}
static snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm,
snd_pcm_uframes_t offset,
snd_pcm_uframes_t size)
2000-06-21 14:57:19 +00:00
{
snd_pcm_multi_t *multi = pcm->private_data;
snd_pcm_t *slave;
2000-06-21 14:57:19 +00:00
unsigned int i;
snd_pcm_sframes_t result;
2000-09-24 09:57:26 +00:00
2000-06-21 14:57:19 +00:00
for (i = 0; i < multi->slaves_count; ++i) {
slave = multi->slaves[i].pcm;
result = snd_pcm_mmap_commit(slave, offset, size);
if (result < 0)
return result;
if ((snd_pcm_uframes_t)result != size)
return -EIO;
2000-06-21 14:57:19 +00:00
}
2002-02-22 08:21:18 +00:00
return size;
2000-06-21 14:57:19 +00:00
}
2000-09-24 09:57:26 +00:00
2000-11-20 20:10:46 +00:00
static int snd_pcm_multi_mmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
static int snd_pcm_multi_munmap(snd_pcm_t *pcm ATTRIBUTE_UNUSED)
{
return 0;
}
2001-01-17 11:00:32 +00:00
static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out)
{
snd_pcm_multi_t *multi = pcm->private_data;
unsigned int k;
2001-01-17 11:00:32 +00:00
snd_output_printf(out, "Multi PCM\n");
snd_output_printf(out, "\nChannel bindings:\n");
2000-10-10 15:39:09 +00:00
for (k = 0; k < multi->channels_count; ++k) {
snd_pcm_multi_channel_t *c = &multi->channels[k];
if (c->slave_idx < 0)
continue;
2001-01-17 11:00:32 +00:00
snd_output_printf(out, "%d: slave %d, channel %d\n",
2000-10-10 15:39:09 +00:00
k, c->slave_idx, c->slave_channel);
}
2000-11-20 20:10:46 +00:00
if (pcm->setup) {
2001-01-17 11:00:32 +00:00
snd_output_printf(out, "\nIts setup is:\n");
snd_pcm_dump_setup(pcm, out);
}
for (k = 0; k < multi->slaves_count; ++k) {
2001-01-17 11:00:32 +00:00
snd_output_printf(out, "\nSlave #%d: ", k);
snd_pcm_dump(multi->slaves[k].pcm, out);
}
}
2002-01-16 16:42:40 +00:00
static snd_pcm_ops_t snd_pcm_multi_ops = {
2000-06-21 14:57:19 +00:00
close: snd_pcm_multi_close,
info: snd_pcm_multi_info,
2000-12-21 20:44:10 +00:00
hw_refine: snd_pcm_multi_hw_refine,
2000-11-20 20:10:46 +00:00
hw_params: snd_pcm_multi_hw_params,
2001-01-19 13:10:50 +00:00
hw_free: snd_pcm_multi_hw_free,
2000-11-20 20:10:46 +00:00
sw_params: snd_pcm_multi_sw_params,
2000-09-24 09:57:26 +00:00
channel_info: snd_pcm_multi_channel_info,
dump: snd_pcm_multi_dump,
2000-09-24 09:57:26 +00:00
nonblock: snd_pcm_multi_nonblock,
async: snd_pcm_multi_async,
mmap: snd_pcm_multi_mmap,
munmap: snd_pcm_multi_munmap,
};
2002-01-16 16:42:40 +00:00
static snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = {
2000-06-21 14:57:19 +00:00
status: snd_pcm_multi_status,
state: snd_pcm_multi_state,
hwsync: snd_pcm_multi_hwsync,
hwptr: snd_pcm_multi_hwptr,
2000-09-24 09:57:26 +00:00
delay: snd_pcm_multi_delay,
2000-06-21 14:57:19 +00:00
prepare: snd_pcm_multi_prepare,
2000-11-24 17:08:03 +00:00
reset: snd_pcm_multi_reset,
2000-09-24 09:57:26 +00:00
start: snd_pcm_multi_start,
2000-10-02 06:58:38 +00:00
drop: snd_pcm_multi_drop,
drain: snd_pcm_multi_drain,
2000-06-21 14:57:19 +00:00
pause: snd_pcm_multi_pause,
2000-09-24 09:57:26 +00:00
writei: snd_pcm_mmap_writei,
writen: snd_pcm_mmap_writen,
readi: snd_pcm_mmap_readi,
readn: snd_pcm_mmap_readn,
rewind: snd_pcm_multi_rewind,
resume: snd_pcm_multi_resume,
2000-09-24 09:57:26 +00:00
avail_update: snd_pcm_multi_avail_update,
2001-04-13 15:40:53 +00:00
mmap_commit: snd_pcm_multi_mmap_commit,
2000-06-21 14:57:19 +00:00
};
2002-01-16 16:42:40 +00:00
/**
* \brief Creates a new Multi PCM
* \param pcmp Returns created PCM handle
* \param name Name of PCM
* \param slaves_count Count of slaves
* \param master_slave Master slave number
* \param slaves_pcm Array with slave PCMs
* \param schannels_count Array with slave channel counts
* \param channels_count Count of channels
* \param sidxs Array with channels indexes to slaves
* \param schannels Array with slave channels
* \param close_slaves When set, the slave PCM handle is closed
* \retval zero on success otherwise a negative error code
* \warning Using of this function might be dangerous in the sense
* of compatibility reasons. The prototype might be freely
* changed in future.
*/
int snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
unsigned int slaves_count, unsigned int master_slave,
snd_pcm_t **slaves_pcm, unsigned int *schannels_count,
unsigned int channels_count,
2000-10-10 09:11:07 +00:00
int *sidxs, unsigned int *schannels,
int close_slaves)
2000-06-21 14:57:19 +00:00
{
snd_pcm_t *pcm;
2000-06-21 14:57:19 +00:00
snd_pcm_multi_t *multi;
unsigned int i;
snd_pcm_stream_t stream;
2000-06-21 14:57:19 +00:00
char slave_map[32][32] = { { 0 } };
int err;
2000-06-21 14:57:19 +00:00
assert(pcmp);
assert(slaves_count > 0 && slaves_pcm && schannels_count);
2000-10-02 06:58:38 +00:00
assert(channels_count > 0 && sidxs && schannels);
assert(master_slave < slaves_count);
2000-06-21 14:57:19 +00:00
multi = calloc(1, sizeof(snd_pcm_multi_t));
if (!multi) {
return -ENOMEM;
}
stream = slaves_pcm[0]->stream;
2000-06-21 14:57:19 +00:00
multi->slaves_count = slaves_count;
multi->master_slave = master_slave;
2000-06-21 14:57:19 +00:00
multi->slaves = calloc(slaves_count, sizeof(*multi->slaves));
2000-10-02 06:58:38 +00:00
multi->channels_count = channels_count;
multi->channels = calloc(channels_count, sizeof(*multi->channels));
2000-06-21 14:57:19 +00:00
for (i = 0; i < slaves_count; ++i) {
snd_pcm_multi_slave_t *slave = &multi->slaves[i];
assert(slaves_pcm[i]->stream == stream);
slave->pcm = slaves_pcm[i];
2000-10-02 06:58:38 +00:00
slave->channels_count = schannels_count[i];
2000-06-21 14:57:19 +00:00
slave->close_slave = close_slaves;
}
2000-10-02 06:58:38 +00:00
for (i = 0; i < channels_count; ++i) {
snd_pcm_multi_channel_t *bind = &multi->channels[i];
assert(sidxs[i] < (int)slaves_count);
assert(schannels[i] < schannels_count[sidxs[i]]);
bind->slave_idx = sidxs[i];
bind->slave_channel = schannels[i];
if (sidxs[i] < 0)
continue;
assert(!slave_map[sidxs[i]][schannels[i]]);
slave_map[sidxs[i]][schannels[i]] = 1;
2000-06-21 14:57:19 +00:00
}
2000-10-02 06:58:38 +00:00
multi->channels_count = channels_count;
2000-06-21 14:57:19 +00:00
err = snd_pcm_new(&pcm, SND_PCM_TYPE_MULTI, name, stream,
multi->slaves[0].pcm->mode);
if (err < 0) {
2000-09-24 09:57:26 +00:00
free(multi);
return err;
2000-09-24 09:57:26 +00:00
}
2000-11-20 20:10:46 +00:00
pcm->mmap_rw = 1;
pcm->ops = &snd_pcm_multi_ops;
pcm->fast_ops = &snd_pcm_multi_fast_ops;
pcm->private_data = multi;
pcm->poll_fd = multi->slaves[master_slave].pcm->poll_fd;
pcm->poll_events = multi->slaves[master_slave].pcm->poll_events;
snd_pcm_link_hw_ptr(pcm, multi->slaves[master_slave].pcm);
snd_pcm_link_appl_ptr(pcm, multi->slaves[master_slave].pcm);
*pcmp = pcm;
2000-06-21 14:57:19 +00:00
return 0;
}
2000-09-24 09:57:26 +00:00
2002-01-16 16:42:40 +00:00
/*! \page pcm_plugins
\section pcm_plugins_multi Plugin: Multiple streams to One
This plugin converts multiple streams to one.
\code
pcm.name {
type multi # Multiple streams conversion PCM
slaves { # Slaves definition
ID STR # Slave PCM name
# or
ID {
pcm STR # Slave PCM name
# or
pcm { } # Slave PCM definition
channels INT # Slave channels
}
}
bindings { # Bindings table
N {
slave STR # Slave key
channel INT # Slave channel
}
}
[master INT] # Define the master slave
}
\endcode
\subsection pcm_plugins_multi_funcref Function reference
<UL>
<LI>snd_pcm_multi_open()
<LI>_snd_pcm_multi_open()
</UL>
*/
/**
* \brief Creates a new Multi PCM
* \param pcmp Returns created PCM handle
* \param name Name of PCM
* \param root Root configuration node
* \param conf Configuration node with Multi PCM description
* \param stream Stream type
* \param mode Stream mode
* \retval zero on success otherwise a negative error code
* \warning Using of this function might be dangerous in the sense
* of compatibility reasons. The prototype might be freely
* changed in future.
*/
int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
snd_config_t *root, snd_config_t *conf,
snd_pcm_stream_t stream, int mode)
2000-09-24 09:57:26 +00:00
{
snd_config_iterator_t i, inext, j, jnext;
2001-03-17 16:34:43 +00:00
snd_config_t *slaves = NULL;
snd_config_t *bindings = NULL;
2000-09-24 09:57:26 +00:00
int err;
unsigned int idx;
const char **slaves_id = NULL;
snd_config_t **slaves_conf = NULL;
2000-09-24 09:57:26 +00:00
snd_pcm_t **slaves_pcm = NULL;
unsigned int *slaves_channels = NULL;
2001-03-29 17:50:28 +00:00
int *channels_sidx = NULL;
2000-10-02 06:58:38 +00:00
unsigned int *channels_schannel = NULL;
unsigned int slaves_count = 0;
long master_slave = 0;
unsigned int channels_count = 0;
snd_config_for_each(i, inext, conf) {
2001-02-07 11:34:33 +00:00
snd_config_t *n = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
if (snd_pcm_conf_generic_id(id))
2000-09-24 09:57:26 +00:00
continue;
2001-03-17 16:34:43 +00:00
if (strcmp(id, "slaves") == 0) {
2001-02-07 11:34:33 +00:00
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid type for %s", id);
2000-09-24 09:57:26 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
2001-03-17 16:34:43 +00:00
slaves = n;
2000-09-24 09:57:26 +00:00
continue;
}
2001-03-17 16:34:43 +00:00
if (strcmp(id, "bindings") == 0) {
2001-02-07 11:34:33 +00:00
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
SNDERR("Invalid type for %s", id);
2000-09-24 09:57:26 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
2001-03-17 16:34:43 +00:00
bindings = n;
2000-09-24 09:57:26 +00:00
continue;
}
if (strcmp(id, "master") == 0) {
if (snd_config_get_integer(n, &((long)master_slave)) < 0) {
SNDERR("Invalid type for %s", id);
return -EINVAL;
}
continue;
}
SNDERR("Unknown field %s", id);
2000-11-30 09:40:50 +00:00
return -EINVAL;
}
2001-03-17 16:34:43 +00:00
if (!slaves) {
SNDERR("slaves is not defined");
2000-09-24 09:57:26 +00:00
return -EINVAL;
}
2001-03-17 16:34:43 +00:00
if (!bindings) {
SNDERR("bindings is not defined");
2000-09-24 09:57:26 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
2001-03-17 16:34:43 +00:00
snd_config_for_each(i, inext, slaves) {
2000-09-24 09:57:26 +00:00
++slaves_count;
}
if (master_slave < 0 || master_slave >= (long)slaves_count) {
SNDERR("Master slave is out of range (0-%u)\n", slaves_count-1);
return -EINVAL;
}
2001-03-17 16:34:43 +00:00
snd_config_for_each(i, inext, bindings) {
2001-06-06 11:49:52 +00:00
long cchannel;
2001-02-07 11:34:33 +00:00
snd_config_t *m = snd_config_iterator_entry(i);
const char *id;
if (snd_config_get_id(m, &id) < 0)
continue;
2001-06-06 11:49:52 +00:00
err = safe_strtol(id, &cchannel);
if (err < 0 || cchannel < 0) {
SNDERR("Invalid channel number: %s", id);
2000-10-02 06:58:38 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
2001-06-06 11:49:52 +00:00
if ((unsigned long)cchannel >= channels_count)
2000-10-02 06:58:38 +00:00
channels_count = cchannel + 1;
2000-09-24 09:57:26 +00:00
}
2000-11-30 09:40:50 +00:00
if (channels_count == 0) {
SNDERR("No channels defined");
2000-10-02 06:58:38 +00:00
return -EINVAL;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
slaves_id = calloc(slaves_count, sizeof(*slaves_id));
slaves_conf = calloc(slaves_count, sizeof(*slaves_conf));
2000-09-24 09:57:26 +00:00
slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm));
slaves_channels = calloc(slaves_count, sizeof(*slaves_channels));
2000-10-02 06:58:38 +00:00
channels_sidx = calloc(channels_count, sizeof(*channels_sidx));
channels_schannel = calloc(channels_count, sizeof(*channels_schannel));
2000-09-24 09:57:26 +00:00
idx = 0;
2000-10-02 06:58:38 +00:00
for (idx = 0; idx < channels_count; ++idx)
channels_sidx[idx] = -1;
2000-11-30 09:40:50 +00:00
idx = 0;
2001-03-17 16:34:43 +00:00
snd_config_for_each(i, inext, slaves) {
2001-02-07 11:34:33 +00:00
snd_config_t *m = snd_config_iterator_entry(i);
const char *id;
2001-03-17 16:34:43 +00:00
int channels;
if (snd_config_get_id(m, &id) < 0)
continue;
slaves_id[idx] = id;
err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1,
SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
2001-03-17 16:34:43 +00:00
if (err < 0)
2000-09-24 09:57:26 +00:00
goto _free;
slaves_channels[idx] = channels;
++idx;
}
2001-03-17 16:34:43 +00:00
snd_config_for_each(i, inext, bindings) {
2001-02-07 11:34:33 +00:00
snd_config_t *m = snd_config_iterator_entry(i);
2000-10-02 06:58:38 +00:00
long cchannel = -1;
long schannel = -1;
2000-09-24 09:57:26 +00:00
int slave = -1;
long val;
const char *str;
const char *id;
if (snd_config_get_id(m, &id) < 0)
continue;
2001-06-06 11:49:52 +00:00
err = safe_strtol(id, &cchannel);
if (err < 0 || cchannel < 0) {
SNDERR("Invalid channel number: %s", id);
2000-11-30 09:40:50 +00:00
err = -EINVAL;
goto _free;
}
snd_config_for_each(j, jnext, m) {
2001-02-07 11:34:33 +00:00
snd_config_t *n = snd_config_iterator_entry(j);
const char *id;
if (snd_config_get_id(n, &id) < 0)
continue;
2001-02-07 11:34:33 +00:00
if (strcmp(id, "comment") == 0)
2000-09-24 09:57:26 +00:00
continue;
2001-03-17 16:34:43 +00:00
if (strcmp(id, "slave") == 0) {
2000-09-24 09:57:26 +00:00
char buf[32];
unsigned int k;
2001-02-07 11:34:33 +00:00
err = snd_config_get_string(n, &str);
2000-09-24 09:57:26 +00:00
if (err < 0) {
2001-02-07 11:34:33 +00:00
err = snd_config_get_integer(n, &val);
2000-11-30 09:40:50 +00:00
if (err < 0) {
SNDERR("Invalid value for %s", id);
2000-09-24 09:57:26 +00:00
goto _free;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
sprintf(buf, "%ld", val);
str = buf;
}
for (k = 0; k < slaves_count; ++k) {
if (strcmp(slaves_id[k], str) == 0)
slave = k;
}
continue;
}
2001-03-17 16:34:43 +00:00
if (strcmp(id, "channel") == 0) {
2001-02-07 11:34:33 +00:00
err = snd_config_get_integer(n, &schannel);
2000-11-30 09:40:50 +00:00
if (err < 0) {
SNDERR("Invalid type for %s", id);
2000-09-24 09:57:26 +00:00
goto _free;
2000-11-30 09:40:50 +00:00
}
2000-09-24 09:57:26 +00:00
continue;
}
SNDERR("Unknown field %s", id);
2000-09-24 09:57:26 +00:00
err = -EINVAL;
goto _free;
}
if (slave < 0 || (unsigned int)slave >= slaves_count) {
SNDERR("Invalid or missing sidx for channel %s", id);
2000-09-24 09:57:26 +00:00
err = -EINVAL;
goto _free;
}
2000-11-30 09:40:50 +00:00
if (schannel < 0 ||
(unsigned int) schannel >= slaves_channels[slave]) {
SNDERR("Invalid or missing schannel for channel %s", id);
2000-09-24 09:57:26 +00:00
err = -EINVAL;
goto _free;
}
2000-10-02 06:58:38 +00:00
channels_sidx[cchannel] = slave;
channels_schannel[cchannel] = schannel;
2000-09-24 09:57:26 +00:00
}
for (idx = 0; idx < slaves_count; ++idx) {
err = snd_pcm_open_slave(&slaves_pcm[idx], root, slaves_conf[idx], stream, mode);
2000-09-24 09:57:26 +00:00
if (err < 0)
goto _free;
snd_config_delete(slaves_conf[idx]);
slaves_conf[idx] = NULL;
2000-09-24 09:57:26 +00:00
}
err = snd_pcm_multi_open(pcmp, name, slaves_count, master_slave,
slaves_pcm, slaves_channels,
2000-10-10 09:11:07 +00:00
channels_count,
channels_sidx, channels_schannel,
1);
2000-09-24 09:57:26 +00:00
_free:
if (err < 0) {
for (idx = 0; idx < slaves_count; ++idx) {
if (slaves_pcm[idx])
snd_pcm_close(slaves_pcm[idx]);
}
}
if (slaves_conf) {
for (idx = 0; idx < slaves_count; ++idx) {
if (slaves_conf[idx])
snd_config_delete(slaves_conf[idx]);
}
free(slaves_conf);
}
2000-09-24 09:57:26 +00:00
if (slaves_pcm)
free(slaves_pcm);
if (slaves_channels)
free(slaves_channels);
2000-10-02 06:58:38 +00:00
if (channels_sidx)
free(channels_sidx);
if (channels_schannel)
free(channels_schannel);
2000-09-24 09:57:26 +00:00
return err;
}
2002-01-16 16:42:40 +00:00
#ifndef DOC_HIDDEN
2001-10-24 14:14:11 +00:00
SND_DLSYM_BUILD_VERSION(_snd_pcm_multi_open, SND_PCM_DLSYM_VERSION);
2002-01-16 16:42:40 +00:00
#endif