|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \file pcm/pcm_multi.c
|
|
Packit Service |
db8eaa |
* \ingroup PCM_Plugins
|
|
Packit Service |
db8eaa |
* \brief PCM Multi Streams to One Conversion Plugin Interface
|
|
Packit Service |
db8eaa |
* \author Abramo Bagnara <abramo@alsa-project.org>
|
|
Packit Service |
db8eaa |
* \date 2000-2001
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
/*
|
|
Packit Service |
db8eaa |
* PCM - Multi
|
|
Packit Service |
db8eaa |
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* This library is free software; you can redistribute it and/or modify
|
|
Packit Service |
db8eaa |
* it under the terms of the GNU Lesser General Public License as
|
|
Packit Service |
db8eaa |
* published by the Free Software Foundation; either version 2.1 of
|
|
Packit Service |
db8eaa |
* the License, or (at your option) any later version.
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* This program is distributed in the hope that it will be useful,
|
|
Packit Service |
db8eaa |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
db8eaa |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit Service |
db8eaa |
* GNU Lesser General Public License for more details.
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
* You should have received a copy of the GNU Lesser General Public
|
|
Packit Service |
db8eaa |
* License along with this library; if not, write to the Free Software
|
|
Packit Service |
db8eaa |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
Packit Service |
db8eaa |
*
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#include <stdio.h>
|
|
Packit Service |
db8eaa |
#include <stdlib.h>
|
|
Packit Service |
db8eaa |
#include <unistd.h>
|
|
Packit Service |
db8eaa |
#include <string.h>
|
|
Packit Service |
db8eaa |
#include <math.h>
|
|
Packit Service |
db8eaa |
#include "pcm_local.h"
|
|
Packit Service |
db8eaa |
#include "pcm_generic.h"
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#ifndef PIC
|
|
Packit Service |
db8eaa |
/* entry for static linking */
|
|
Packit Service |
db8eaa |
const char *_snd_module_pcm_multi = "";
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
typedef struct {
|
|
Packit Service |
db8eaa |
snd_pcm_t *pcm;
|
|
Packit Service |
db8eaa |
unsigned int channels_count;
|
|
Packit Service |
db8eaa |
int close_slave;
|
|
Packit Service |
db8eaa |
snd_pcm_t *linked;
|
|
Packit Service |
db8eaa |
} snd_pcm_multi_slave_t;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
typedef struct {
|
|
Packit Service |
db8eaa |
int slave_idx;
|
|
Packit Service |
db8eaa |
unsigned int slave_channel;
|
|
Packit Service |
db8eaa |
} snd_pcm_multi_channel_t;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
typedef struct {
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t appl_ptr, hw_ptr;
|
|
Packit Service |
db8eaa |
unsigned int slaves_count;
|
|
Packit Service |
db8eaa |
unsigned int master_slave;
|
|
Packit Service |
db8eaa |
snd_pcm_multi_slave_t *slaves;
|
|
Packit Service |
db8eaa |
unsigned int channels_count;
|
|
Packit Service |
db8eaa |
snd_pcm_multi_channel_t *channels;
|
|
Packit Service |
db8eaa |
} snd_pcm_multi_t;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
#endif
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_close(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
int ret = 0;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_slave_t *slave = &multi->slaves[i];
|
|
Packit Service |
db8eaa |
if (slave->close_slave) {
|
|
Packit Service |
db8eaa |
int err = snd_pcm_close(slave->pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
ret = err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
free(multi->slaves);
|
|
Packit Service |
db8eaa |
free(multi->channels);
|
|
Packit Service |
db8eaa |
free(multi);
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_nonblock(snd_pcm_t *pcm ATTRIBUTE_UNUSED, int nonblock ATTRIBUTE_UNUSED)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_async(snd_pcm_t *pcm, int sig, pid_t pid)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
|
|
Packit Service |
db8eaa |
return snd_pcm_async(slave_0, sig, pid);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_poll_descriptors_count(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
|
|
Packit Service |
db8eaa |
return snd_pcm_poll_descriptors_count(slave_0);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
slave = multi->slaves[i].pcm;
|
|
Packit Service |
db8eaa |
if (slave == slave_0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
err = snd_pcm_poll_descriptors(slave, pfds, space);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
/* finally overwrite with master's pfds */
|
|
Packit Service |
db8eaa |
return snd_pcm_poll_descriptors(slave_0, pfds, space);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_poll_revents(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave_0 = multi->slaves[multi->master_slave].pcm;
|
|
Packit Service |
db8eaa |
return snd_pcm_poll_descriptors_revents(slave_0, pfds, nfds, revents);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err, n;
|
|
Packit Service |
db8eaa |
assert(info->subdevice < multi->slaves_count);
|
|
Packit Service |
db8eaa |
n = info->subdevice;
|
|
Packit Service |
db8eaa |
info->subdevice = 0;
|
|
Packit Service |
db8eaa |
err = snd_pcm_info(multi->slaves[n].pcm, info);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
info->subdevices_count = multi->slaves_count;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hw_refine_cprepare(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_t access_mask;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_any(&access_mask);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
|
|
Packit Service |
db8eaa |
&access_mask);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set(params, SND_PCM_HW_PARAM_CHANNELS,
|
|
Packit Service |
db8eaa |
multi->channels_count, 0);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
params->info = ~0U;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hw_refine_sprepare(snd_pcm_t *pcm, unsigned int slave_idx,
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t *sparams)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_multi_slave_t *slave = &multi->slaves[slave_idx];
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP };
|
|
Packit Service |
db8eaa |
_snd_pcm_hw_params_any(sparams);
|
|
Packit Service |
db8eaa |
_snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
|
|
Packit Service |
db8eaa |
&saccess_mask);
|
|
Packit Service |
db8eaa |
_snd_pcm_hw_param_set(sparams, SND_PCM_HW_PARAM_CHANNELS,
|
|
Packit Service |
db8eaa |
slave->channels_count, 0);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
|
|
Packit Service |
db8eaa |
unsigned int slave_idx ATTRIBUTE_UNUSED,
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t *params,
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t *sparams)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_SUBFORMAT |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_RATE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIOD_SIZE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIOD_TIME |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIODS |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_BUFFER_TIME |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_TICK_TIME);
|
|
Packit Service |
db8eaa |
const snd_pcm_access_mask_t *access_mask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
|
|
Packit Service |
db8eaa |
if (!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_INTERLEAVED) &&
|
|
Packit Service |
db8eaa |
!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_RW_NONINTERLEAVED) &&
|
|
Packit Service |
db8eaa |
!snd_pcm_access_mask_test(access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_t saccess_mask;
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_any(&saccess_mask);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS,
|
|
Packit Service |
db8eaa |
&saccess_mask);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_params_refine(sparams, links, params);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED,
|
|
Packit Service |
db8eaa |
unsigned int slave_idx ATTRIBUTE_UNUSED,
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t *params,
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t *sparams)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
unsigned int links = (SND_PCM_HW_PARBIT_FORMAT |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_SUBFORMAT |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_RATE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIOD_SIZE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIOD_TIME |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_PERIODS |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_BUFFER_SIZE |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_BUFFER_TIME |
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARBIT_TICK_TIME);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_t access_mask;
|
|
Packit Service |
db8eaa |
const snd_pcm_access_mask_t *saccess_mask = snd_pcm_hw_param_get_mask(sparams, SND_PCM_HW_PARAM_ACCESS);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_any(&access_mask);
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
|
|
Packit Service |
db8eaa |
if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED))
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
|
|
Packit Service |
db8eaa |
if (!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_COMPLEX) &&
|
|
Packit Service |
db8eaa |
!snd_pcm_access_mask_test(saccess_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED))
|
|
Packit Service |
db8eaa |
snd_pcm_access_mask_reset(&access_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS,
|
|
Packit Service |
db8eaa |
&access_mask);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
err = _snd_pcm_hw_params_refine(params, links, sparams);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
params->info &= sparams->info;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hw_refine_slave(snd_pcm_t *pcm,
|
|
Packit Service |
db8eaa |
unsigned int slave_idx,
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t *sparams)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
|
|
Packit Service |
db8eaa |
return snd_pcm_hw_refine(slave, sparams);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int k;
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t sparams[multi->slaves_count];
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
unsigned int cmask, changed;
|
|
Packit Service |
db8eaa |
err = snd_pcm_multi_hw_refine_cprepare(pcm, params);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
for (k = 0; k < multi->slaves_count; ++k) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_multi_hw_refine_sprepare(pcm, k, &sparams[k]);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("Slave PCM #%d not usable", k);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
do {
|
|
Packit Service |
db8eaa |
cmask = params->cmask;
|
|
Packit Service |
db8eaa |
params->cmask = 0;
|
|
Packit Service |
db8eaa |
for (k = 0; k < multi->slaves_count; ++k) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_multi_hw_refine_schange(pcm, k, params, &sparams[k]);
|
|
Packit Service |
db8eaa |
if (err >= 0)
|
|
Packit Service |
db8eaa |
err = snd_pcm_multi_hw_refine_slave(pcm, k, &sparams[k]);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = snd_pcm_multi_hw_refine_cchange(pcm, k, params, &sparams[k]);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = snd_pcm_hw_refine_soft(pcm, params);
|
|
Packit Service |
db8eaa |
changed = params->cmask;
|
|
Packit Service |
db8eaa |
params->cmask |= cmask;
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
} while (changed);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hw_params_slave(snd_pcm_t *pcm,
|
|
Packit Service |
db8eaa |
unsigned int slave_idx,
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t *sparams)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave = multi->slaves[slave_idx].pcm;
|
|
Packit Service |
db8eaa |
int err = snd_pcm_hw_params(slave, sparams);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
err = snd_pcm_areas_silence(slave->running_areas, 0, slave->channels, slave->buffer_size, slave->format);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
if (slave->stopped_areas) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_areas_silence(slave->stopped_areas, 0, slave->channels, slave->buffer_size, slave->format);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* reset links to the normal state
|
|
Packit Service |
db8eaa |
* slave #0 = trigger master
|
|
Packit Service |
db8eaa |
* slave #1-(N-1) = trigger slaves, linked is set to #0
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
static void reset_links(snd_pcm_multi_t *multi)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
if (multi->slaves[i].linked)
|
|
Packit Service |
db8eaa |
snd_pcm_unlink(multi->slaves[i].linked);
|
|
Packit Service |
db8eaa |
multi->slaves[0].linked = NULL;
|
|
Packit Service |
db8eaa |
if (! i)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
if (snd_pcm_link(multi->slaves[0].pcm, multi->slaves[i].pcm) >= 0)
|
|
Packit Service |
db8eaa |
multi->slaves[i].linked = multi->slaves[0].pcm;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
snd_pcm_hw_params_t sparams[multi->slaves_count];
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_multi_hw_refine_sprepare(pcm, i, &sparams[i]);
|
|
Packit Service |
db8eaa |
assert(err >= 0);
|
|
Packit Service |
db8eaa |
err = snd_pcm_multi_hw_refine_schange(pcm, i, params, &sparams[i]);
|
|
Packit Service |
db8eaa |
assert(err >= 0);
|
|
Packit Service |
db8eaa |
err = snd_pcm_multi_hw_params_slave(pcm, i, &sparams[i]);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_hw_refine_cchange(pcm, i, params, &sparams[i]);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
reset_links(multi);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hw_free(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave = multi->slaves[i].pcm;
|
|
Packit Service |
db8eaa |
int e = snd_pcm_hw_free(slave);
|
|
Packit Service |
db8eaa |
if (e < 0)
|
|
Packit Service |
db8eaa |
err = e;
|
|
Packit Service |
db8eaa |
if (!multi->slaves[i].linked)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
e = snd_pcm_unlink(slave);
|
|
Packit Service |
db8eaa |
if (e < 0)
|
|
Packit Service |
db8eaa |
err = e;
|
|
Packit Service |
db8eaa |
multi->slaves[i].linked = NULL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave = multi->slaves[i].pcm;
|
|
Packit Service |
db8eaa |
err = snd_pcm_sw_params(slave, params);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
|
|
Packit Service |
db8eaa |
return snd_pcm_status(slave, status);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_state_t snd_pcm_multi_state(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
|
|
Packit Service |
db8eaa |
return snd_pcm_state(slave);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void snd_pcm_multi_hwptr_update(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t hw_ptr = 0, slave_hw_ptr, avail, last_avail;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
/* the logic is really simple, choose the lowest hw_ptr from slaves */
|
|
Packit Service |
db8eaa |
if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
|
|
Packit Service |
db8eaa |
last_avail = 0;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr;
|
|
Packit Service |
db8eaa |
avail = __snd_pcm_playback_avail(pcm, multi->hw_ptr, slave_hw_ptr);
|
|
Packit Service |
db8eaa |
if (avail > last_avail) {
|
|
Packit Service |
db8eaa |
hw_ptr = slave_hw_ptr;
|
|
Packit Service |
db8eaa |
last_avail = avail;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
} else {
|
|
Packit Service |
db8eaa |
last_avail = LONG_MAX;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
slave_hw_ptr = *multi->slaves[i].pcm->hw.ptr;
|
|
Packit Service |
db8eaa |
avail = __snd_pcm_capture_avail(pcm, multi->hw_ptr, slave_hw_ptr);
|
|
Packit Service |
db8eaa |
if (avail < last_avail) {
|
|
Packit Service |
db8eaa |
hw_ptr = slave_hw_ptr;
|
|
Packit Service |
db8eaa |
last_avail = avail;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
multi->hw_ptr = hw_ptr;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_hwsync(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_hwsync(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_pcm_multi_hwptr_update(pcm);
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t d, dr = 0;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_delay(multi->slaves[i].pcm, &d);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
if (dr < d)
|
|
Packit Service |
db8eaa |
dr = d;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
*delayp = dr;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_multi_avail_update(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t ret = LONG_MAX;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t avail;
|
|
Packit Service |
db8eaa |
avail = snd_pcm_avail_update(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (avail < 0)
|
|
Packit Service |
db8eaa |
return avail;
|
|
Packit Service |
db8eaa |
if (ret > avail)
|
|
Packit Service |
db8eaa |
ret = avail;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_pcm_multi_hwptr_update(pcm);
|
|
Packit Service |
db8eaa |
return ret;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_htimestamp(snd_pcm_t *pcm, snd_pcm_uframes_t *avail,
|
|
Packit Service |
db8eaa |
snd_htimestamp_t *tstamp)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave = multi->slaves[multi->master_slave].pcm;
|
|
Packit Service |
db8eaa |
return snd_pcm_htimestamp(slave, avail, tstamp);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_prepare(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
int result = 0, err;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
/* We call prepare to each slave even if it's linked.
|
|
Packit Service |
db8eaa |
* This is to make sure to sync non-mmaped control/status.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
err = snd_pcm_prepare(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
result = err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
multi->hw_ptr = multi->appl_ptr = 0;
|
|
Packit Service |
db8eaa |
return result;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_reset(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
int result = 0, err;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
/* Reset each slave, as well as in prepare */
|
|
Packit Service |
db8eaa |
err = snd_pcm_reset(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
result = err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
multi->hw_ptr = multi->appl_ptr = 0;
|
|
Packit Service |
db8eaa |
return result;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* when the first slave PCM is linked, it means that the whole multi
|
|
Packit Service |
db8eaa |
* plugin instance is linked manually to another PCM. in this case,
|
|
Packit Service |
db8eaa |
* we need to trigger the master.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_start(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
if (multi->slaves[0].linked)
|
|
Packit Service |
db8eaa |
return snd_pcm_start(multi->slaves[0].linked);
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
if (multi->slaves[i].linked)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
err = snd_pcm_start(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_drop(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
if (multi->slaves[0].linked)
|
|
Packit Service |
db8eaa |
return snd_pcm_drop(multi->slaves[0].linked);
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
if (multi->slaves[i].linked)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
err = snd_pcm_drop(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_drain(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
if (multi->slaves[0].linked)
|
|
Packit Service |
db8eaa |
return snd_pcm_drain(multi->slaves[0].linked);
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
if (multi->slaves[i].linked)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
err = snd_pcm_drain(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_pause(snd_pcm_t *pcm, int enable)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
if (multi->slaves[0].linked)
|
|
Packit Service |
db8eaa |
return snd_pcm_pause(multi->slaves[0].linked, enable);
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
if (multi->slaves[i].linked)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
err = snd_pcm_pause(multi->slaves[i].pcm, enable);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int channel = info->channel;
|
|
Packit Service |
db8eaa |
snd_pcm_multi_channel_t *c = &multi->channels[channel];
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
if (c->slave_idx < 0)
|
|
Packit Service |
db8eaa |
return -ENXIO;
|
|
Packit Service |
db8eaa |
info->channel = c->slave_channel;
|
|
Packit Service |
db8eaa |
err = snd_pcm_channel_info(multi->slaves[c->slave_idx].pcm, info);
|
|
Packit Service |
db8eaa |
info->channel = channel;
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_multi_rewindable(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t frames = LONG_MAX;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t f = snd_pcm_rewindable(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (f <= 0)
|
|
Packit Service |
db8eaa |
return f;
|
|
Packit Service |
db8eaa |
if (f < frames)
|
|
Packit Service |
db8eaa |
frames = f;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
return frames;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_multi_forwardable(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t frames = LONG_MAX;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t f = snd_pcm_forwardable(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (f <= 0)
|
|
Packit Service |
db8eaa |
return f;
|
|
Packit Service |
db8eaa |
if (f < frames)
|
|
Packit Service |
db8eaa |
frames = f;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
return frames;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_multi_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t pos[multi->slaves_count];
|
|
Packit Service |
db8eaa |
memset(pos, 0, sizeof(pos));
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave_i = multi->slaves[i].pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t f = snd_pcm_rewind(slave_i, frames);
|
|
Packit Service |
db8eaa |
if (f < 0)
|
|
Packit Service |
db8eaa |
return f;
|
|
Packit Service |
db8eaa |
pos[i] = f;
|
|
Packit Service |
db8eaa |
frames = f;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
/* Realign the pointers */
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave_i = multi->slaves[i].pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t f = pos[i] - frames;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t result;
|
|
Packit Service |
db8eaa |
if (f > 0) {
|
|
Packit Service |
db8eaa |
result = INTERNAL(snd_pcm_forward)(slave_i, f);
|
|
Packit Service |
db8eaa |
if (result < 0)
|
|
Packit Service |
db8eaa |
return result;
|
|
Packit Service |
db8eaa |
if ((snd_pcm_uframes_t)result != f)
|
|
Packit Service |
db8eaa |
return -EIO;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return frames;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_multi_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t pos[multi->slaves_count];
|
|
Packit Service |
db8eaa |
memset(pos, 0, sizeof(pos));
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave_i = multi->slaves[i].pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t f = INTERNAL(snd_pcm_forward)(slave_i, frames);
|
|
Packit Service |
db8eaa |
if (f < 0)
|
|
Packit Service |
db8eaa |
return f;
|
|
Packit Service |
db8eaa |
pos[i] = f;
|
|
Packit Service |
db8eaa |
frames = f;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
/* Realign the pointers */
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave_i = multi->slaves[i].pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t f = pos[i] - frames;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t result;
|
|
Packit Service |
db8eaa |
if (f > 0) {
|
|
Packit Service |
db8eaa |
result = snd_pcm_rewind(slave_i, f);
|
|
Packit Service |
db8eaa |
if (result < 0)
|
|
Packit Service |
db8eaa |
return result;
|
|
Packit Service |
db8eaa |
if ((snd_pcm_uframes_t)result != f)
|
|
Packit Service |
db8eaa |
return -EIO;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return frames;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_resume(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
if (multi->slaves[0].linked)
|
|
Packit Service |
db8eaa |
return snd_pcm_resume(multi->slaves[0].linked);
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
if (multi->slaves[i].linked)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
err = snd_pcm_resume(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* if a multi plugin instance is linked as slaves, every slave PCMs
|
|
Packit Service |
db8eaa |
* including the first one has to be relinked to the given master.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_link_slaves(snd_pcm_t *pcm, snd_pcm_t *master)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_unlink(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
multi->slaves[i].linked = NULL;
|
|
Packit Service |
db8eaa |
err = snd_pcm_link(master, multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
reset_links(multi);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
multi->slaves[i].linked = master;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* linking to a multi as a master is easy - simply link to the first
|
|
Packit Service |
db8eaa |
* slave element as its own slaves are already linked.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm1->private_data;
|
|
Packit Service |
db8eaa |
if (multi->slaves[0].pcm->fast_ops->link)
|
|
Packit Service |
db8eaa |
return multi->slaves[0].pcm->fast_ops->link(multi->slaves[0].pcm, pcm2);
|
|
Packit Service |
db8eaa |
return -ENOSYS;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_unlink(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
if (multi->slaves[i].linked)
|
|
Packit Service |
db8eaa |
snd_pcm_unlink(multi->slaves[i].linked);
|
|
Packit Service |
db8eaa |
multi->slaves[0].linked = NULL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_sframes_t snd_pcm_multi_mmap_commit(snd_pcm_t *pcm,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t offset,
|
|
Packit Service |
db8eaa |
snd_pcm_uframes_t size)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
snd_pcm_sframes_t result;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
slave = multi->slaves[i].pcm;
|
|
Packit Service |
db8eaa |
result = snd_pcm_mmap_commit(slave, offset, size);
|
|
Packit Service |
db8eaa |
if (result < 0)
|
|
Packit Service |
db8eaa |
return result;
|
|
Packit Service |
db8eaa |
if ((snd_pcm_uframes_t)result != size)
|
|
Packit Service |
db8eaa |
return -EIO;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
multi->appl_ptr += size;
|
|
Packit Service |
db8eaa |
multi->appl_ptr %= pcm->boundary;
|
|
Packit Service |
db8eaa |
return size;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_munmap(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
free(pcm->mmap_channels);
|
|
Packit Service |
db8eaa |
free(pcm->running_areas);
|
|
Packit Service |
db8eaa |
pcm->mmap_channels = NULL;
|
|
Packit Service |
db8eaa |
pcm->running_areas = NULL;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_mmap(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int c;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
pcm->mmap_channels = calloc(pcm->channels,
|
|
Packit Service |
db8eaa |
sizeof(pcm->mmap_channels[0]));
|
|
Packit Service |
db8eaa |
pcm->running_areas = calloc(pcm->channels,
|
|
Packit Service |
db8eaa |
sizeof(pcm->running_areas[0]));
|
|
Packit Service |
db8eaa |
if (!pcm->mmap_channels || !pcm->running_areas) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_munmap(pcm);
|
|
Packit Service |
db8eaa |
return -ENOMEM;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/* Copy the slave mmapped buffer data */
|
|
Packit Service |
db8eaa |
for (c = 0; c < pcm->channels; c++) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_channel_t *chan = &multi->channels[c];
|
|
Packit Service |
db8eaa |
snd_pcm_t *slave;
|
|
Packit Service |
db8eaa |
if (chan->slave_idx < 0) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_munmap(pcm);
|
|
Packit Service |
db8eaa |
return -ENXIO;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
slave = multi->slaves[chan->slave_idx].pcm;
|
|
Packit Service |
db8eaa |
pcm->mmap_channels[c] =
|
|
Packit Service |
db8eaa |
slave->mmap_channels[chan->slave_channel];
|
|
Packit Service |
db8eaa |
pcm->mmap_channels[c].channel = c;
|
|
Packit Service |
db8eaa |
pcm->running_areas[c] =
|
|
Packit Service |
db8eaa |
slave->running_areas[chan->slave_channel];
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_may_wait_for_avail_min(snd_pcm_t *pcm, snd_pcm_uframes_t avail)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
if (snd_pcm_may_wait_for_avail_min(multi->slaves[i].pcm, avail))
|
|
Packit Service |
db8eaa |
return 1;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_chmap_query_t **snd_pcm_multi_query_chmaps(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_chmap_query_t **slave_maps[multi->slaves_count];
|
|
Packit Service |
db8eaa |
snd_pcm_chmap_query_t **maps;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
int err = -ENOMEM;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
memset(slave_maps, 0, sizeof(slave_maps));
|
|
Packit Service |
db8eaa |
maps = calloc(2, sizeof(*maps));
|
|
Packit Service |
db8eaa |
if (!maps)
|
|
Packit Service |
db8eaa |
return NULL;
|
|
Packit Service |
db8eaa |
maps[0] = calloc(multi->channels_count + 2, sizeof(int *));
|
|
Packit Service |
db8eaa |
if (!maps[0])
|
|
Packit Service |
db8eaa |
goto error;
|
|
Packit Service |
db8eaa |
maps[0]->type = SND_CHMAP_TYPE_FIXED;
|
|
Packit Service |
db8eaa |
maps[0]->map.channels = multi->channels_count;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; i++) {
|
|
Packit Service |
db8eaa |
slave_maps[i] = snd_pcm_query_chmaps(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (!slave_maps[i])
|
|
Packit Service |
db8eaa |
goto error;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->channels_count; i++) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_channel_t *bind = &multi->channels[i];
|
|
Packit Service |
db8eaa |
unsigned int slave_channels =
|
|
Packit Service |
db8eaa |
multi->slaves[bind->slave_idx].channels_count;
|
|
Packit Service |
db8eaa |
snd_pcm_chmap_query_t **p;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (p = slave_maps[bind->slave_idx]; *p; p++) {
|
|
Packit Service |
db8eaa |
if ((*p)->map.channels == slave_channels) {
|
|
Packit Service |
db8eaa |
maps[0]->map.pos[i] =
|
|
Packit Service |
db8eaa |
(*p)->map.pos[bind->slave_channel];
|
|
Packit Service |
db8eaa |
break;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = 0;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
error:
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; i++) {
|
|
Packit Service |
db8eaa |
if (slave_maps[i])
|
|
Packit Service |
db8eaa |
snd_pcm_free_chmaps(slave_maps[i]);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (err) {
|
|
Packit Service |
db8eaa |
snd_pcm_free_chmaps(maps);
|
|
Packit Service |
db8eaa |
return NULL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
return maps;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static snd_pcm_chmap_t *snd_pcm_multi_get_chmap(snd_pcm_t *pcm)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_chmap_t *map;
|
|
Packit Service |
db8eaa |
snd_pcm_chmap_t *slave_maps[multi->slaves_count];
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
int err = -ENOMEM;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
memset(slave_maps, 0, sizeof(slave_maps));
|
|
Packit Service |
db8eaa |
map = calloc(multi->channels_count + 1, sizeof(int));
|
|
Packit Service |
db8eaa |
if (!map)
|
|
Packit Service |
db8eaa |
return NULL;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; i++) {
|
|
Packit Service |
db8eaa |
slave_maps[i] = snd_pcm_get_chmap(multi->slaves[i].pcm);
|
|
Packit Service |
db8eaa |
if (!slave_maps[i])
|
|
Packit Service |
db8eaa |
goto error;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
map->channels = multi->channels_count;
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->channels_count; i++) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_channel_t *bind = &multi->channels[i];
|
|
Packit Service |
db8eaa |
map->pos[i] = slave_maps[bind->slave_idx]->pos[bind->slave_channel];
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = 0;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
error:
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; i++)
|
|
Packit Service |
db8eaa |
free(slave_maps[i]);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (err) {
|
|
Packit Service |
db8eaa |
free(map);
|
|
Packit Service |
db8eaa |
return NULL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
return map;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
snd_pcm_chmap_t *slave_maps[multi->slaves_count];
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
int err = 0;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
if (map->channels != multi->channels_count)
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; i++) {
|
|
Packit Service |
db8eaa |
slave_maps[i] = calloc(multi->slaves[i].channels_count + 1,
|
|
Packit Service |
db8eaa |
sizeof(int));
|
|
Packit Service |
db8eaa |
if (!slave_maps[i]) {
|
|
Packit Service |
db8eaa |
for (i++; i < multi->slaves_count; i++)
|
|
Packit Service |
db8eaa |
slave_maps[i] = NULL;
|
|
Packit Service |
db8eaa |
err = -ENOMEM;
|
|
Packit Service |
db8eaa |
goto error;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->channels_count; i++) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_channel_t *bind = &multi->channels[i];
|
|
Packit Service |
db8eaa |
slave_maps[bind->slave_idx]->pos[bind->slave_channel] =
|
|
Packit Service |
db8eaa |
map->pos[i];
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; i++) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_maps[i]);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto error;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
error:
|
|
Packit Service |
db8eaa |
for (i = 0; i < multi->slaves_count; i++)
|
|
Packit Service |
db8eaa |
free(slave_maps[i]);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi = pcm->private_data;
|
|
Packit Service |
db8eaa |
unsigned int k;
|
|
Packit Service |
db8eaa |
snd_output_printf(out, "Multi PCM\n");
|
|
Packit Service |
db8eaa |
snd_output_printf(out, " Channel bindings:\n");
|
|
Packit Service |
db8eaa |
for (k = 0; k < multi->channels_count; ++k) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_channel_t *c = &multi->channels[k];
|
|
Packit Service |
db8eaa |
if (c->slave_idx < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
snd_output_printf(out, " %d: slave %d, channel %d\n",
|
|
Packit Service |
db8eaa |
k, c->slave_idx, c->slave_channel);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (pcm->setup) {
|
|
Packit Service |
db8eaa |
snd_output_printf(out, "Its setup is:\n");
|
|
Packit Service |
db8eaa |
snd_pcm_dump_setup(pcm, out);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
for (k = 0; k < multi->slaves_count; ++k) {
|
|
Packit Service |
db8eaa |
snd_output_printf(out, "Slave #%d: ", k);
|
|
Packit Service |
db8eaa |
snd_pcm_dump(multi->slaves[k].pcm, out);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static const snd_pcm_ops_t snd_pcm_multi_ops = {
|
|
Packit Service |
db8eaa |
.close = snd_pcm_multi_close,
|
|
Packit Service |
db8eaa |
.info = snd_pcm_multi_info,
|
|
Packit Service |
db8eaa |
.hw_refine = snd_pcm_multi_hw_refine,
|
|
Packit Service |
db8eaa |
.hw_params = snd_pcm_multi_hw_params,
|
|
Packit Service |
db8eaa |
.hw_free = snd_pcm_multi_hw_free,
|
|
Packit Service |
db8eaa |
.sw_params = snd_pcm_multi_sw_params,
|
|
Packit Service |
db8eaa |
.channel_info = snd_pcm_multi_channel_info,
|
|
Packit Service |
db8eaa |
.dump = snd_pcm_multi_dump,
|
|
Packit Service |
db8eaa |
.nonblock = snd_pcm_multi_nonblock,
|
|
Packit Service |
db8eaa |
.async = snd_pcm_multi_async,
|
|
Packit Service |
db8eaa |
.mmap = snd_pcm_multi_mmap,
|
|
Packit Service |
db8eaa |
.munmap = snd_pcm_multi_munmap,
|
|
Packit Service |
db8eaa |
.query_chmaps = snd_pcm_multi_query_chmaps,
|
|
Packit Service |
db8eaa |
.get_chmap = snd_pcm_multi_get_chmap,
|
|
Packit Service |
db8eaa |
.set_chmap = snd_pcm_multi_set_chmap,
|
|
Packit Service |
db8eaa |
};
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
static const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = {
|
|
Packit Service |
db8eaa |
.status = snd_pcm_multi_status,
|
|
Packit Service |
db8eaa |
.state = snd_pcm_multi_state,
|
|
Packit Service |
db8eaa |
.hwsync = snd_pcm_multi_hwsync,
|
|
Packit Service |
db8eaa |
.delay = snd_pcm_multi_delay,
|
|
Packit Service |
db8eaa |
.prepare = snd_pcm_multi_prepare,
|
|
Packit Service |
db8eaa |
.reset = snd_pcm_multi_reset,
|
|
Packit Service |
db8eaa |
.start = snd_pcm_multi_start,
|
|
Packit Service |
db8eaa |
.drop = snd_pcm_multi_drop,
|
|
Packit Service |
db8eaa |
.drain = snd_pcm_multi_drain,
|
|
Packit Service |
db8eaa |
.pause = snd_pcm_multi_pause,
|
|
Packit Service |
db8eaa |
.writei = snd_pcm_mmap_writei,
|
|
Packit Service |
db8eaa |
.writen = snd_pcm_mmap_writen,
|
|
Packit Service |
db8eaa |
.readi = snd_pcm_mmap_readi,
|
|
Packit Service |
db8eaa |
.readn = snd_pcm_mmap_readn,
|
|
Packit Service |
db8eaa |
.rewindable = snd_pcm_multi_rewindable,
|
|
Packit Service |
db8eaa |
.rewind = snd_pcm_multi_rewind,
|
|
Packit Service |
db8eaa |
.forwardable = snd_pcm_multi_forwardable,
|
|
Packit Service |
db8eaa |
.forward = snd_pcm_multi_forward,
|
|
Packit Service |
db8eaa |
.resume = snd_pcm_multi_resume,
|
|
Packit Service |
db8eaa |
.link = snd_pcm_multi_link,
|
|
Packit Service |
db8eaa |
.link_slaves = snd_pcm_multi_link_slaves,
|
|
Packit Service |
db8eaa |
.unlink = snd_pcm_multi_unlink,
|
|
Packit Service |
db8eaa |
.avail_update = snd_pcm_multi_avail_update,
|
|
Packit Service |
db8eaa |
.mmap_commit = snd_pcm_multi_mmap_commit,
|
|
Packit Service |
db8eaa |
.htimestamp = snd_pcm_multi_htimestamp,
|
|
Packit Service |
db8eaa |
.poll_descriptors_count = snd_pcm_multi_poll_descriptors_count,
|
|
Packit Service |
db8eaa |
.poll_descriptors = snd_pcm_multi_poll_descriptors,
|
|
Packit Service |
db8eaa |
.poll_revents = snd_pcm_multi_poll_revents,
|
|
Packit Service |
db8eaa |
.may_wait_for_avail_min = snd_pcm_multi_may_wait_for_avail_min,
|
|
Packit Service |
db8eaa |
};
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Creates a new Multi PCM
|
|
Packit Service |
db8eaa |
* \param pcmp Returns created PCM handle
|
|
Packit Service |
db8eaa |
* \param name Name of PCM
|
|
Packit Service |
db8eaa |
* \param slaves_count Count of slaves
|
|
Packit Service |
db8eaa |
* \param master_slave Master slave number
|
|
Packit Service |
db8eaa |
* \param slaves_pcm Array with slave PCMs
|
|
Packit Service |
db8eaa |
* \param schannels_count Array with slave channel counts
|
|
Packit Service |
db8eaa |
* \param channels_count Count of channels
|
|
Packit Service |
db8eaa |
* \param sidxs Array with channels indexes to slaves
|
|
Packit Service |
db8eaa |
* \param schannels Array with slave channels
|
|
Packit Service |
db8eaa |
* \param close_slaves When set, the slave PCM handle is closed
|
|
Packit Service |
db8eaa |
* \retval zero on success otherwise a negative error code
|
|
Packit Service |
db8eaa |
* \warning Using of this function might be dangerous in the sense
|
|
Packit Service |
db8eaa |
* of compatibility reasons. The prototype might be freely
|
|
Packit Service |
db8eaa |
* changed in future.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
|
Packit Service |
db8eaa |
unsigned int slaves_count, unsigned int master_slave,
|
|
Packit Service |
db8eaa |
snd_pcm_t **slaves_pcm, unsigned int *schannels_count,
|
|
Packit Service |
db8eaa |
unsigned int channels_count,
|
|
Packit Service |
db8eaa |
int *sidxs, unsigned int *schannels,
|
|
Packit Service |
db8eaa |
int close_slaves)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_pcm_t *pcm;
|
|
Packit Service |
db8eaa |
snd_pcm_multi_t *multi;
|
|
Packit Service |
db8eaa |
unsigned int i;
|
|
Packit Service |
db8eaa |
snd_pcm_stream_t stream;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
assert(pcmp);
|
|
Packit Service |
db8eaa |
assert(slaves_count > 0 && slaves_pcm && schannels_count);
|
|
Packit Service |
db8eaa |
assert(channels_count > 0 && sidxs && schannels);
|
|
Packit Service |
db8eaa |
assert(master_slave < slaves_count);
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
multi = calloc(1, sizeof(snd_pcm_multi_t));
|
|
Packit Service |
db8eaa |
if (!multi) {
|
|
Packit Service |
db8eaa |
return -ENOMEM;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
stream = slaves_pcm[0]->stream;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
multi->slaves_count = slaves_count;
|
|
Packit Service |
db8eaa |
multi->master_slave = master_slave;
|
|
Packit Service |
db8eaa |
multi->slaves = calloc(slaves_count, sizeof(*multi->slaves));
|
|
Packit Service |
db8eaa |
if (!multi->slaves) {
|
|
Packit Service |
db8eaa |
free(multi);
|
|
Packit Service |
db8eaa |
return -ENOMEM;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
multi->channels_count = channels_count;
|
|
Packit Service |
db8eaa |
multi->channels = calloc(channels_count, sizeof(*multi->channels));
|
|
Packit Service |
db8eaa |
if (!multi->channels) {
|
|
Packit Service |
db8eaa |
free(multi->slaves);
|
|
Packit Service |
db8eaa |
free(multi);
|
|
Packit Service |
db8eaa |
return -ENOMEM;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
for (i = 0; i < slaves_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_slave_t *slave = &multi->slaves[i];
|
|
Packit Service |
db8eaa |
assert(slaves_pcm[i]->stream == stream);
|
|
Packit Service |
db8eaa |
slave->pcm = slaves_pcm[i];
|
|
Packit Service |
db8eaa |
slave->channels_count = schannels_count[i];
|
|
Packit Service |
db8eaa |
slave->close_slave = close_slaves;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
for (i = 0; i < channels_count; ++i) {
|
|
Packit Service |
db8eaa |
snd_pcm_multi_channel_t *bind = &multi->channels[i];
|
|
Packit Service |
db8eaa |
assert(sidxs[i] < (int)slaves_count);
|
|
Packit Service |
db8eaa |
assert(schannels[i] < schannels_count[sidxs[i]]);
|
|
Packit Service |
db8eaa |
bind->slave_idx = sidxs[i];
|
|
Packit Service |
db8eaa |
bind->slave_channel = schannels[i];
|
|
Packit Service |
db8eaa |
if (sidxs[i] < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
multi->channels_count = channels_count;
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
err = snd_pcm_new(&pcm, SND_PCM_TYPE_MULTI, name, stream,
|
|
Packit Service |
db8eaa |
multi->slaves[0].pcm->mode);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
free(multi->slaves);
|
|
Packit Service |
db8eaa |
free(multi->channels);
|
|
Packit Service |
db8eaa |
free(multi);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
pcm->mmap_rw = 1;
|
|
Packit Service |
db8eaa |
pcm->mmap_shadow = 1; /* has own mmap method */
|
|
Packit Service |
db8eaa |
pcm->ops = &snd_pcm_multi_ops;
|
|
Packit Service |
db8eaa |
pcm->fast_ops = &snd_pcm_multi_fast_ops;
|
|
Packit Service |
db8eaa |
pcm->private_data = multi;
|
|
Packit Service |
db8eaa |
pcm->poll_fd = multi->slaves[master_slave].pcm->poll_fd;
|
|
Packit Service |
db8eaa |
pcm->poll_events = multi->slaves[master_slave].pcm->poll_events;
|
|
Packit Service |
db8eaa |
pcm->tstamp_type = multi->slaves[master_slave].pcm->tstamp_type;
|
|
Packit Service |
db8eaa |
snd_pcm_set_hw_ptr(pcm, &multi->hw_ptr, -1, 0);
|
|
Packit Service |
db8eaa |
snd_pcm_set_appl_ptr(pcm, &multi->appl_ptr, -1, 0);
|
|
Packit Service |
db8eaa |
*pcmp = pcm;
|
|
Packit Service |
db8eaa |
return 0;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/*! \page pcm_plugins
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
\section pcm_plugins_multi Plugin: Multiple streams to One
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
This plugin converts multiple streams to one.
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
\code
|
|
Packit Service |
db8eaa |
pcm.name {
|
|
Packit Service |
db8eaa |
type multi # Multiple streams conversion PCM
|
|
Packit Service |
db8eaa |
slaves { # Slaves definition
|
|
Packit Service |
db8eaa |
ID STR # Slave PCM name
|
|
Packit Service |
db8eaa |
# or
|
|
Packit Service |
db8eaa |
ID {
|
|
Packit Service |
db8eaa |
pcm STR # Slave PCM name
|
|
Packit Service |
db8eaa |
# or
|
|
Packit Service |
db8eaa |
pcm { } # Slave PCM definition
|
|
Packit Service |
db8eaa |
channels INT # Slave channels
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
bindings { # Bindings table
|
|
Packit Service |
db8eaa |
N {
|
|
Packit Service |
db8eaa |
slave STR # Slave key
|
|
Packit Service |
db8eaa |
channel INT # Slave channel
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
[master INT] # Define the master slave
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
\endcode
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
For example, to bind two PCM streams with two-channel stereo (hw:0,0 and
|
|
Packit Service |
db8eaa |
hw:0,1) as one 4-channel stereo PCM stream, define like this:
|
|
Packit Service |
db8eaa |
\code
|
|
Packit Service |
db8eaa |
pcm.quad {
|
|
Packit Service |
db8eaa |
type multi
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
slaves.a.pcm "hw:0,0"
|
|
Packit Service |
db8eaa |
slaves.a.channels 2
|
|
Packit Service |
db8eaa |
slaves.b.pcm "hw:0,1"
|
|
Packit Service |
db8eaa |
slaves.b.channels 2
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
bindings.0.slave a
|
|
Packit Service |
db8eaa |
bindings.0.channel 0
|
|
Packit Service |
db8eaa |
bindings.1.slave a
|
|
Packit Service |
db8eaa |
bindings.1.channel 1
|
|
Packit Service |
db8eaa |
bindings.2.slave b
|
|
Packit Service |
db8eaa |
bindings.2.channel 0
|
|
Packit Service |
db8eaa |
bindings.3.slave b
|
|
Packit Service |
db8eaa |
bindings.3.channel 1
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
\endcode
|
|
Packit Service |
db8eaa |
Note that the resultant pcm "quad" is not in the interleaved format
|
|
Packit Service |
db8eaa |
but in the "complex" format. Hence, it's not accessible by applications
|
|
Packit Service |
db8eaa |
which can handle only the interleaved (or the non-interleaved) format.
|
|
Packit Service |
db8eaa |
In such a case, wrap this PCM with \ref pcm_plugins_route "route" or
|
|
Packit Service |
db8eaa |
\ref pcm_plugins_plug "plug" plugin.
|
|
Packit Service |
db8eaa |
\code
|
|
Packit Service |
db8eaa |
pcm.quad2 {
|
|
Packit Service |
db8eaa |
type route
|
|
Packit Service |
db8eaa |
slave.pcm "quad"
|
|
Packit Service |
db8eaa |
ttable.0.0 1
|
|
Packit Service |
db8eaa |
ttable.1.1 1
|
|
Packit Service |
db8eaa |
ttable.2.2 1
|
|
Packit Service |
db8eaa |
ttable.3.3 1
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
\endcode
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
\subsection pcm_plugins_multi_funcref Function reference
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_pcm_multi_open()
|
|
Packit Service |
db8eaa |
_snd_pcm_multi_open()
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
/**
|
|
Packit Service |
db8eaa |
* \brief Creates a new Multi PCM
|
|
Packit Service |
db8eaa |
* \param pcmp Returns created PCM handle
|
|
Packit Service |
db8eaa |
* \param name Name of PCM
|
|
Packit Service |
db8eaa |
* \param root Root configuration node
|
|
Packit Service |
db8eaa |
* \param conf Configuration node with Multi PCM description
|
|
Packit Service |
db8eaa |
* \param stream Stream type
|
|
Packit Service |
db8eaa |
* \param mode Stream mode
|
|
Packit Service |
db8eaa |
* \retval zero on success otherwise a negative error code
|
|
Packit Service |
db8eaa |
* \warning Using of this function might be dangerous in the sense
|
|
Packit Service |
db8eaa |
* of compatibility reasons. The prototype might be freely
|
|
Packit Service |
db8eaa |
* changed in future.
|
|
Packit Service |
db8eaa |
*/
|
|
Packit Service |
db8eaa |
int _snd_pcm_multi_open(snd_pcm_t **pcmp, const char *name,
|
|
Packit Service |
db8eaa |
snd_config_t *root, snd_config_t *conf,
|
|
Packit Service |
db8eaa |
snd_pcm_stream_t stream, int mode)
|
|
Packit Service |
db8eaa |
{
|
|
Packit Service |
db8eaa |
snd_config_iterator_t i, inext, j, jnext;
|
|
Packit Service |
db8eaa |
snd_config_t *slaves = NULL;
|
|
Packit Service |
db8eaa |
snd_config_t *bindings = NULL;
|
|
Packit Service |
db8eaa |
int err;
|
|
Packit Service |
db8eaa |
unsigned int idx;
|
|
Packit Service |
db8eaa |
const char **slaves_id = NULL;
|
|
Packit Service |
db8eaa |
snd_config_t **slaves_conf = NULL;
|
|
Packit Service |
db8eaa |
snd_pcm_t **slaves_pcm = NULL;
|
|
Packit Service |
db8eaa |
unsigned int *slaves_channels = NULL;
|
|
Packit Service |
db8eaa |
int *channels_sidx = NULL;
|
|
Packit Service |
db8eaa |
unsigned int *channels_schannel = NULL;
|
|
Packit Service |
db8eaa |
unsigned int slaves_count = 0;
|
|
Packit Service |
db8eaa |
long master_slave = 0;
|
|
Packit Service |
db8eaa |
unsigned int channels_count = 0;
|
|
Packit Service |
db8eaa |
snd_config_for_each(i, inext, conf) {
|
|
Packit Service |
db8eaa |
snd_config_t *n = snd_config_iterator_entry(i);
|
|
Packit Service |
db8eaa |
const char *id;
|
|
Packit Service |
db8eaa |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
if (snd_pcm_conf_generic_id(id))
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
if (strcmp(id, "slaves") == 0) {
|
|
Packit Service |
db8eaa |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid type for %s", id);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
slaves = n;
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (strcmp(id, "bindings") == 0) {
|
|
Packit Service |
db8eaa |
if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid type for %s", id);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
bindings = n;
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (strcmp(id, "master") == 0) {
|
|
Packit Service |
db8eaa |
if (snd_config_get_integer(n, &master_slave) < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid type for %s", id);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
SNDERR("Unknown field %s", id);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (!slaves) {
|
|
Packit Service |
db8eaa |
SNDERR("slaves is not defined");
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (!bindings) {
|
|
Packit Service |
db8eaa |
SNDERR("bindings is not defined");
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_config_for_each(i, inext, slaves) {
|
|
Packit Service |
db8eaa |
++slaves_count;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (master_slave < 0 || master_slave >= (long)slaves_count) {
|
|
Packit Service |
db8eaa |
SNDERR("Master slave is out of range (0-%u)\n", slaves_count-1);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_config_for_each(i, inext, bindings) {
|
|
Packit Service |
db8eaa |
long cchannel;
|
|
Packit Service |
db8eaa |
snd_config_t *m = snd_config_iterator_entry(i);
|
|
Packit Service |
db8eaa |
const char *id;
|
|
Packit Service |
db8eaa |
if (snd_config_get_id(m, &id) < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
err = safe_strtol(id, &cchannel);
|
|
Packit Service |
db8eaa |
if (err < 0 || cchannel < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid channel number: %s", id);
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if ((unsigned long)cchannel >= channels_count)
|
|
Packit Service |
db8eaa |
channels_count = cchannel + 1;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (channels_count == 0) {
|
|
Packit Service |
db8eaa |
SNDERR("No channels defined");
|
|
Packit Service |
db8eaa |
return -EINVAL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
slaves_id = calloc(slaves_count, sizeof(*slaves_id));
|
|
Packit Service |
db8eaa |
slaves_conf = calloc(slaves_count, sizeof(*slaves_conf));
|
|
Packit Service |
db8eaa |
slaves_pcm = calloc(slaves_count, sizeof(*slaves_pcm));
|
|
Packit Service |
db8eaa |
slaves_channels = calloc(slaves_count, sizeof(*slaves_channels));
|
|
Packit Service |
db8eaa |
channels_sidx = calloc(channels_count, sizeof(*channels_sidx));
|
|
Packit Service |
db8eaa |
channels_schannel = calloc(channels_count, sizeof(*channels_schannel));
|
|
Packit Service |
db8eaa |
if (!slaves_id || !slaves_conf || !slaves_pcm || !slaves_channels ||
|
|
Packit Service |
db8eaa |
!channels_sidx || !channels_schannel) {
|
|
Packit Service |
db8eaa |
err = -ENOMEM;
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
45dc4a |
idx = 0;
|
|
Packit Service |
db8eaa |
for (idx = 0; idx < channels_count; ++idx)
|
|
Packit Service |
db8eaa |
channels_sidx[idx] = -1;
|
|
Packit Service |
db8eaa |
idx = 0;
|
|
Packit Service |
db8eaa |
snd_config_for_each(i, inext, slaves) {
|
|
Packit Service |
db8eaa |
snd_config_t *m = snd_config_iterator_entry(i);
|
|
Packit Service |
db8eaa |
const char *id;
|
|
Packit Service |
db8eaa |
int channels;
|
|
Packit Service |
db8eaa |
if (snd_config_get_id(m, &id) < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
slaves_id[idx] = id;
|
|
Packit Service |
db8eaa |
err = snd_pcm_slave_conf(root, m, &slaves_conf[idx], 1,
|
|
Packit Service |
db8eaa |
SND_PCM_HW_PARAM_CHANNELS, SCONF_MANDATORY, &channels);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
slaves_channels[idx] = channels;
|
|
Packit Service |
db8eaa |
++idx;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
snd_config_for_each(i, inext, bindings) {
|
|
Packit Service |
db8eaa |
snd_config_t *m = snd_config_iterator_entry(i);
|
|
Packit Service |
db8eaa |
long cchannel = -1;
|
|
Packit Service |
db8eaa |
long schannel = -1;
|
|
Packit Service |
db8eaa |
int slave = -1;
|
|
Packit Service |
db8eaa |
long val;
|
|
Packit Service |
db8eaa |
const char *str;
|
|
Packit Service |
db8eaa |
const char *id;
|
|
Packit Service |
db8eaa |
if (snd_config_get_id(m, &id) < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
err = safe_strtol(id, &cchannel);
|
|
Packit Service |
db8eaa |
if (err < 0 || cchannel < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid channel number: %s", id);
|
|
Packit Service |
db8eaa |
err = -EINVAL;
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
snd_config_for_each(j, jnext, m) {
|
|
Packit Service |
db8eaa |
snd_config_t *n = snd_config_iterator_entry(j);
|
|
Packit Service |
db8eaa |
const char *id;
|
|
Packit Service |
db8eaa |
if (snd_config_get_id(n, &id) < 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
if (strcmp(id, "comment") == 0)
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
if (strcmp(id, "slave") == 0) {
|
|
Packit Service |
db8eaa |
char buf[32];
|
|
Packit Service |
db8eaa |
unsigned int k;
|
|
Packit Service |
db8eaa |
err = snd_config_get_string(n, &str);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
err = snd_config_get_integer(n, &val;;
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid value for %s", id);
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
sprintf(buf, "%ld", val);
|
|
Packit Service |
db8eaa |
str = buf;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
for (k = 0; k < slaves_count; ++k) {
|
|
Packit Service |
db8eaa |
if (strcmp(slaves_id[k], str) == 0)
|
|
Packit Service |
db8eaa |
slave = k;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (strcmp(id, "channel") == 0) {
|
|
Packit Service |
db8eaa |
err = snd_config_get_integer(n, &schannel);
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid type for %s", id);
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
continue;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
SNDERR("Unknown field %s", id);
|
|
Packit Service |
db8eaa |
err = -EINVAL;
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (slave < 0 || (unsigned int)slave >= slaves_count) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid or missing sidx for channel %s", id);
|
|
Packit Service |
db8eaa |
err = -EINVAL;
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (schannel < 0 ||
|
|
Packit Service |
db8eaa |
(unsigned int) schannel >= slaves_channels[slave]) {
|
|
Packit Service |
db8eaa |
SNDERR("Invalid or missing schannel for channel %s", id);
|
|
Packit Service |
db8eaa |
err = -EINVAL;
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
channels_sidx[cchannel] = slave;
|
|
Packit Service |
db8eaa |
channels_schannel[cchannel] = schannel;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
|
|
Packit Service |
db8eaa |
for (idx = 0; idx < slaves_count; ++idx) {
|
|
Packit Service |
db8eaa |
err = snd_pcm_open_slave(&slaves_pcm[idx], root,
|
|
Packit Service |
db8eaa |
slaves_conf[idx], stream, mode,
|
|
Packit Service |
db8eaa |
conf);
|
|
Packit Service |
db8eaa |
if (err < 0)
|
|
Packit Service |
db8eaa |
goto _free;
|
|
Packit Service |
db8eaa |
snd_config_delete(slaves_conf[idx]);
|
|
Packit Service |
db8eaa |
slaves_conf[idx] = NULL;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
err = snd_pcm_multi_open(pcmp, name, slaves_count, master_slave,
|
|
Packit Service |
db8eaa |
slaves_pcm, slaves_channels,
|
|
Packit Service |
db8eaa |
channels_count,
|
|
Packit Service |
db8eaa |
channels_sidx, channels_schannel,
|
|
Packit Service |
db8eaa |
1);
|
|
Packit Service |
db8eaa |
_free:
|
|
Packit Service |
db8eaa |
if (err < 0) {
|
|
Packit Service |
db8eaa |
for (idx = 0; idx < slaves_count; ++idx) {
|
|
Packit Service |
db8eaa |
if (slaves_pcm[idx])
|
|
Packit Service |
db8eaa |
snd_pcm_close(slaves_pcm[idx]);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
if (slaves_conf) {
|
|
Packit Service |
db8eaa |
for (idx = 0; idx < slaves_count; ++idx) {
|
|
Packit Service |
db8eaa |
if (slaves_conf[idx])
|
|
Packit Service |
db8eaa |
snd_config_delete(slaves_conf[idx]);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
free(slaves_conf);
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
free(slaves_pcm);
|
|
Packit Service |
db8eaa |
free(slaves_channels);
|
|
Packit Service |
db8eaa |
free(channels_sidx);
|
|
Packit Service |
db8eaa |
free(channels_schannel);
|
|
Packit Service |
db8eaa |
free(slaves_id);
|
|
Packit Service |
db8eaa |
return err;
|
|
Packit Service |
db8eaa |
}
|
|
Packit Service |
db8eaa |
#ifndef DOC_HIDDEN
|
|
Packit Service |
db8eaa |
SND_DLSYM_BUILD_VERSION(_snd_pcm_multi_open, SND_PCM_DLSYM_VERSION);
|
|
Packit Service |
db8eaa |
#endif
|