From e72ac5eab424d6ae4bd43d5cc532fdde9b6d5bdf Mon Sep 17 00:00:00 2001 From: Abramo Bagnara Date: Mon, 11 Dec 2000 11:16:07 +0000 Subject: [PATCH] Added pcm_copy and fixed pcm_plug for access change case. --- include/pcm.h | 1 + src/pcm/Makefile.am | 8 +- src/pcm/pcm_copy.c | 246 +++++++++++++++++++++++++++++++++++++++++++ src/pcm/pcm_plug.c | 60 +++++++---- src/pcm/pcm_plugin.h | 1 + 5 files changed, 292 insertions(+), 24 deletions(-) create mode 100644 src/pcm/pcm_copy.c diff --git a/include/pcm.h b/include/pcm.h index c1dfd3c4..9203e4e5 100644 --- a/include/pcm.h +++ b/include/pcm.h @@ -21,6 +21,7 @@ typedef enum _snd_pcm_type { SND_PCM_TYPE_NULL, SND_PCM_TYPE_SHM, SND_PCM_TYPE_INET, + SND_PCM_TYPE_COPY, SND_PCM_TYPE_LINEAR, SND_PCM_TYPE_ALAW, SND_PCM_TYPE_MULAW, diff --git a/src/pcm/Makefile.am b/src/pcm/Makefile.am index f59b6bb2..351a3783 100644 --- a/src/pcm/Makefile.am +++ b/src/pcm/Makefile.am @@ -1,10 +1,10 @@ EXTRA_LTLIBRARIES = libpcm.la -libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plugin.c pcm_linear.c pcm_route.c \ - pcm_mulaw.c pcm_alaw.c pcm_adpcm.c pcm_rate.c pcm_plug.c \ - pcm_misc.c pcm_mmap.c pcm_multi.c pcm_shm.c pcm_file.c \ - pcm_share.c pcm_null.c +libpcm_la_SOURCES = pcm.c pcm_hw.c pcm_plugin.c pcm_copy.c pcm_linear.c \ + pcm_route.c pcm_mulaw.c pcm_alaw.c pcm_adpcm.c \ + pcm_rate.c pcm_plug.c pcm_misc.c pcm_mmap.c pcm_multi.c \ + pcm_shm.c pcm_file.c pcm_share.c pcm_null.c noinst_HEADERS = pcm_local.h pcm_plugin.h all: libpcm.la diff --git a/src/pcm/pcm_copy.c b/src/pcm/pcm_copy.c new file mode 100644 index 00000000..b699883d --- /dev/null +++ b/src/pcm/pcm_copy.c @@ -0,0 +1,246 @@ +/* + * PCM - Copy conversion + * Copyright (c) 2000 by Abramo Bagnara + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include "pcm_local.h" +#include "pcm_plugin.h" + +typedef struct { + /* This field need to be the first */ + snd_pcm_plugin_t plug; +} snd_pcm_copy_t; + +static int snd_pcm_copy_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t * info) +{ + snd_pcm_copy_t *copy = pcm->private; + unsigned int access_mask; + int err; + info->access_mask &= (SND_PCM_ACCBIT_MMAP_INTERLEAVED | + SND_PCM_ACCBIT_RW_INTERLEAVED | + SND_PCM_ACCBIT_MMAP_NONINTERLEAVED | + SND_PCM_ACCBIT_RW_NONINTERLEAVED); + access_mask = info->access_mask; + if (access_mask == 0) + return -EINVAL; + + info->access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_info(copy->plug.slave, info); + info->access_mask = access_mask; + if (err < 0) + return err; + info->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + snd_pcm_hw_info_complete(info); + return 0; +} + +static int snd_pcm_copy_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + snd_pcm_copy_t *copy = pcm->private; + snd_pcm_t *slave = copy->plug.slave; + snd_pcm_hw_info_t sinfo; + snd_pcm_hw_params_t sparams; + int err; + snd_pcm_hw_params_to_info(params, &sinfo); + sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + err = snd_pcm_hw_params_info(slave, &sparams, &sinfo); + params->fail_mask = sparams.fail_mask; + return err; +} + +static ssize_t snd_pcm_copy_write_areas(snd_pcm_t *pcm, + const snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_copy_t *copy = pcm->private; + snd_pcm_t *slave = copy->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_playback_xfer(slave, size - xfer); + + snd_pcm_areas_copy(areas, offset, + snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), + pcm->channels, frames, pcm->format); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static ssize_t snd_pcm_copy_read_areas(snd_pcm_t *pcm, + const snd_pcm_channel_area_t *areas, + size_t offset, + size_t size, + size_t *slave_sizep) +{ + snd_pcm_copy_t *copy = pcm->private; + snd_pcm_t *slave = copy->plug.slave; + size_t xfer = 0; + ssize_t err = 0; + if (slave_sizep && *slave_sizep < size) + size = *slave_sizep; + assert(size > 0); + while (xfer < size) { + size_t frames = snd_pcm_mmap_capture_xfer(slave, size - xfer); + snd_pcm_areas_copy(snd_pcm_mmap_areas(slave), snd_pcm_mmap_offset(slave), + areas, offset, + pcm->channels, frames, pcm->format); + err = snd_pcm_mmap_forward(slave, frames); + if (err < 0) + break; + assert((size_t)err == frames); + offset += err; + xfer += err; + snd_pcm_mmap_hw_forward(pcm, err); + } + if (xfer > 0) { + if (slave_sizep) + *slave_sizep = xfer; + return xfer; + } + return err; +} + +static void snd_pcm_copy_dump(snd_pcm_t *pcm, FILE *fp) +{ + snd_pcm_copy_t *copy = pcm->private; + fprintf(fp, "Copy conversion PCM\n"); + if (pcm->setup) { + fprintf(fp, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, fp); + } + fprintf(fp, "Slave: "); + snd_pcm_dump(copy->plug.slave, fp); +} + +snd_pcm_ops_t snd_pcm_copy_ops = { + close: snd_pcm_plugin_close, + info: snd_pcm_plugin_info, + hw_info: snd_pcm_copy_hw_info, + hw_params: snd_pcm_copy_hw_params, + sw_params: snd_pcm_plugin_sw_params, + dig_info: snd_pcm_plugin_dig_info, + dig_params: snd_pcm_plugin_dig_params, + channel_info: snd_pcm_plugin_channel_info, + dump: snd_pcm_copy_dump, + nonblock: snd_pcm_plugin_nonblock, + async: snd_pcm_plugin_async, + mmap: snd_pcm_plugin_mmap, + munmap: snd_pcm_plugin_munmap, +}; + +int snd_pcm_copy_open(snd_pcm_t **pcmp, char *name, snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *pcm; + snd_pcm_copy_t *copy; + assert(pcmp && slave); + copy = calloc(1, sizeof(snd_pcm_copy_t)); + if (!copy) { + return -ENOMEM; + } + copy->plug.read = snd_pcm_copy_read_areas; + copy->plug.write = snd_pcm_copy_write_areas; + copy->plug.slave = slave; + copy->plug.close_slave = close_slave; + + pcm = calloc(1, sizeof(snd_pcm_t)); + if (!pcm) { + free(copy); + return -ENOMEM; + } + if (name) + pcm->name = strdup(name); + pcm->type = SND_PCM_TYPE_COPY; + pcm->stream = slave->stream; + pcm->mode = slave->mode; + pcm->ops = &snd_pcm_copy_ops; + pcm->op_arg = pcm; + pcm->fast_ops = &snd_pcm_plugin_fast_ops; + pcm->fast_op_arg = pcm; + pcm->private = copy; + pcm->poll_fd = slave->poll_fd; + pcm->hw_ptr = ©->plug.hw_ptr; + pcm->appl_ptr = ©->plug.appl_ptr; + *pcmp = pcm; + + return 0; +} + +int _snd_pcm_copy_open(snd_pcm_t **pcmp, char *name, + snd_config_t *conf, + int stream, int mode) +{ + snd_config_iterator_t i; + char *sname = NULL; + int err; + snd_pcm_t *spcm; + snd_config_foreach(i, conf) { + snd_config_t *n = snd_config_entry(i); + if (strcmp(n->id, "comment") == 0) + continue; + if (strcmp(n->id, "type") == 0) + continue; + if (strcmp(n->id, "stream") == 0) + continue; + if (strcmp(n->id, "sname") == 0) { + err = snd_config_string_get(n, &sname); + if (err < 0) { + ERR("Invalid type for %s", n->id); + return -EINVAL; + } + continue; + } + ERR("Unknown field %s", n->id); + return -EINVAL; + } + if (!sname) { + ERR("sname is not defined"); + return -EINVAL; + } + /* This is needed cause snd_config_update may destroy config */ + sname = strdup(sname); + if (!sname) + return -ENOMEM; + err = snd_pcm_open(&spcm, sname, stream, mode); + free(sname); + if (err < 0) + return err; + err = snd_pcm_copy_open(pcmp, name, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + diff --git a/src/pcm/pcm_plug.c b/src/pcm/pcm_plug.c index c956104f..3dc7cfac 100644 --- a/src/pcm/pcm_plug.c +++ b/src/pcm/pcm_plug.c @@ -319,6 +319,7 @@ static int snd_pcm_plug_change_rate(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_ err = snd_pcm_rate_open(new, NULL, slv->format, slv->rate, plug->slave, plug->slave != plug->req_slave); if (err < 0) return err; + slv->access = clt->access; slv->rate = clt->rate; if (snd_pcm_format_linear(clt->format)) slv->format = clt->format; @@ -384,6 +385,7 @@ static int snd_pcm_plug_change_channels(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm if (err < 0) return err; slv->channels = clt->channels; + slv->access = clt->access; if (snd_pcm_format_linear(clt->format)) slv->format = clt->format; return 1; @@ -400,14 +402,6 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_h clt->rate != slv->rate || clt->channels != slv->channels) return 0; - } else { - /* No conversion is needed */ - if (clt->format == slv->format && - clt->rate == slv->rate && - clt->channels == clt->channels) - return 0; - } - if (snd_pcm_format_linear(slv->format)) { cfmt = clt->format; switch (clt->format) { case SND_PCM_FORMAT_MU_LAW: @@ -425,6 +419,11 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_h break; } } else { + /* No conversion is needed */ + if (clt->format == slv->format && + clt->rate == slv->rate && + clt->channels == clt->channels) + return 0; switch (slv->format) { case SND_PCM_FORMAT_MU_LAW: f = snd_pcm_mulaw_open; @@ -448,6 +447,20 @@ static int snd_pcm_plug_change_format(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_h if (err < 0) return err; slv->format = cfmt; + slv->access = clt->access; + return 1; +} + +static int snd_pcm_plug_change_access(snd_pcm_t *pcm, snd_pcm_t **new, snd_pcm_hw_params_t *clt, snd_pcm_hw_params_t *slv) +{ + snd_pcm_plug_t *plug = pcm->private; + int err; + if (clt->access == slv->access) + return 0; + err = snd_pcm_copy_open(new, NULL, plug->slave, plug->slave != plug->req_slave); + if (err < 0) + return err; + slv->access = clt->access; return 1; } @@ -461,17 +474,17 @@ static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, snd_pcm_plug_change_channels, snd_pcm_plug_change_rate, snd_pcm_plug_change_channels, - snd_pcm_plug_change_format + snd_pcm_plug_change_format, + snd_pcm_plug_change_access }; snd_pcm_hw_params_t p = *slave; unsigned int k = 0; - while (1) { + while (client->format != p.format || + client->channels != p.channels || + client->rate != p.rate || + client->access != p.access) { snd_pcm_t *new; int err; - if (client->format == p.format && - client->channels == p.channels && - client->rate == p.rate) - return 0; assert(k < sizeof(funcs)/sizeof(*funcs)); err = funcs[k](pcm, &new, client, &p); if (err < 0) { @@ -485,7 +498,6 @@ static int snd_pcm_plug_insert_plugins(snd_pcm_t *pcm, } k++; } - assert(0); return 0; } @@ -500,7 +512,7 @@ static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) sparams = *params; snd_pcm_hw_params_to_info(&sparams, &sinfo); - sinfo.access_mask = SND_PCM_ACCBIT_MMAP; + sinfo.access_mask = ~0U; sinfo.format_mask = (SND_PCM_FMTBIT_LINEAR | SND_PCM_FMTBIT_MU_LAW | SND_PCM_FMTBIT_A_LAW | SND_PCM_FMTBIT_IMA_ADPCM); sinfo.subformat_mask = SND_PCM_SUBFMTBIT_STD; @@ -543,11 +555,19 @@ static int snd_pcm_plug_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) sparams.rate = sinfo.rate_min; assert(sinfo.channels_min == sinfo.channels_max); sparams.channels = sinfo.channels_min; - + snd_pcm_plug_clear(pcm); - err = snd_pcm_plug_insert_plugins(pcm, params, &sparams); - if (err < 0) - return err; + if (params->format != sparams.format || + params->channels != sparams.channels || + params->rate != sparams.rate || + !(sinfo.access_mask & (1 << params->access))) { + sinfo.access_mask &= SND_PCM_ACCBIT_MMAP; + assert(sinfo.access_mask); + sparams.access = ffs(sinfo.access_mask) - 1; + err = snd_pcm_plug_insert_plugins(pcm, params, &sparams); + if (err < 0) + return err; + } err = snd_pcm_hw_params(plug->slave, params); if (err < 0) { diff --git a/src/pcm/pcm_plugin.h b/src/pcm/pcm_plugin.h index ec40d807..e5029f2a 100644 --- a/src/pcm/pcm_plugin.h +++ b/src/pcm/pcm_plugin.h @@ -90,6 +90,7 @@ typedef int ttable_entry_t; #define FULL ROUTE_PLUGIN_RESOLUTION #endif +int snd_pcm_copy_open(snd_pcm_t **pcmp, char *name, snd_pcm_t *slave, int close_slave); int snd_pcm_linear_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *slave, int close_slave); int snd_pcm_mulaw_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *slave, int close_slave); int snd_pcm_alaw_open(snd_pcm_t **pcmp, char *name, int sformat, snd_pcm_t *slave, int close_slave); -- 2.47.1