Hi. Most of the modern built-in cards do not have an internal amplifier. This makes it difficult to use them with those cheap small passive speakers that were in a heavy use 15 years ago when the cards had an amplifier builtin. I wrote a simple software amplifier plugin, it is attached. Of course you trade a sound quality, but whoever uses the passive speakers, have already made his choice anyway. Would it be possible to apply such a patch to the repository? Signed-off-by: Stas Sergeev <stsp@xxxxxxxx>
# HG changeset patch # User Stas Sergeev <stsp@xxxxxxxxxxxxxxxxxxxxx> # Date 1185966660 -14400 # Node ID 1164919ed31213ef9ae463e3af7a04cdd47fe019 # Parent dc32d9f00649b5c89589e1dc5eeba6924568bf41 Add a software amplifier plugin. diff -r dc32d9f00649 -r 1164919ed312 configure.in --- a/configure.in Fri Jul 13 12:44:43 2007 +0200 +++ b/configure.in Wed Aug 01 15:11:00 2007 +0400 @@ -397,7 +397,7 @@ pcm_plugins="" pcm_plugins="" fi -PCM_PLUGIN_LIST="copy linear route mulaw alaw adpcm rate plug multi shm file null empty share meter hooks lfloat ladspa dmix dshare dsnoop asym iec958 softvol extplug ioplug mmap_emul" +PCM_PLUGIN_LIST="copy linear route mulaw alaw adpcm rate plug multi shm file null empty share meter hooks lfloat ladspa dmix dshare dsnoop asym iec958 softvol softamp extplug ioplug mmap_emul" build_pcm_plugin="no" for t in $PCM_PLUGIN_LIST; do @@ -464,6 +464,7 @@ AM_CONDITIONAL(BUILD_PCM_PLUGIN_ASYM, te AM_CONDITIONAL(BUILD_PCM_PLUGIN_ASYM, test x$build_pcm_asym = xyes) AM_CONDITIONAL(BUILD_PCM_PLUGIN_IEC958, test x$build_pcm_iec958 = xyes) AM_CONDITIONAL(BUILD_PCM_PLUGIN_SOFTVOL, test x$build_pcm_softvol = xyes) +AM_CONDITIONAL(BUILD_PCM_PLUGIN_SOFTAMP, test x$build_pcm_softamp = xyes) AM_CONDITIONAL(BUILD_PCM_PLUGIN_EXTPLUG, test x$build_pcm_extplug = xyes) AM_CONDITIONAL(BUILD_PCM_PLUGIN_IOPLUG, test x$build_pcm_ioplug = xyes) AM_CONDITIONAL(BUILD_PCM_PLUGIN_MMAP_EMUL, test x$build_pcm_mmap_emul = xyes) diff -r dc32d9f00649 -r 1164919ed312 doc/doxygen.cfg --- a/doc/doxygen.cfg Fri Jul 13 12:44:43 2007 +0200 +++ b/doc/doxygen.cfg Wed Aug 01 15:11:00 2007 +0400 @@ -69,6 +69,7 @@ INPUT = index.doxygen \ ../src/pcm/pcm_asym.c \ ../src/pcm/pcm_iec958.c \ ../src/pcm/pcm_softvol.c \ + ../src/pcm/pcm_softamp.c \ ../src/pcm/pcm_extplug.c \ ../src/pcm/pcm_ioplug.c \ ../src/pcm/pcm_misc.c \ diff -r dc32d9f00649 -r 1164919ed312 include/pcm.h --- a/include/pcm.h Fri Jul 13 12:44:43 2007 +0200 +++ b/include/pcm.h Wed Aug 01 15:11:00 2007 +0400 @@ -358,6 +358,8 @@ enum _snd_pcm_type { SND_PCM_TYPE_IEC958, /** Soft volume plugin */ SND_PCM_TYPE_SOFTVOL, + /** Soft amp plugin */ + SND_PCM_TYPE_SOFTAMP, /** External I/O plugin */ SND_PCM_TYPE_IOPLUG, /** External filter plugin */ diff -r dc32d9f00649 -r 1164919ed312 src/pcm/Makefile.am --- a/src/pcm/Makefile.am Fri Jul 13 12:44:43 2007 +0200 +++ b/src/pcm/Makefile.am Wed Aug 01 15:11:00 2007 +0400 @@ -93,6 +93,9 @@ if BUILD_PCM_PLUGIN_SOFTVOL if BUILD_PCM_PLUGIN_SOFTVOL libpcm_la_SOURCES += pcm_softvol.c endif +if BUILD_PCM_PLUGIN_SOFTAMP +libpcm_la_SOURCES += pcm_softamp.c +endif if BUILD_PCM_PLUGIN_EXTPLUG libpcm_la_SOURCES += pcm_extplug.c endif diff -r dc32d9f00649 -r 1164919ed312 src/pcm/pcm.c --- a/src/pcm/pcm.c Fri Jul 13 12:44:43 2007 +0200 +++ b/src/pcm/pcm.c Wed Aug 01 15:11:00 2007 +0400 @@ -1561,7 +1561,8 @@ static const char *snd_pcm_type_names[] PCMTYPE(JACK), PCMTYPE(DSNOOP), PCMTYPE(IEC958), - PCMTYPE(SOFTVOL), + PCMTYPE(SOFTVOL), + PCMTYPE(SOFTAMP), PCMTYPE(IOPLUG), PCMTYPE(EXTPLUG), }; @@ -1983,7 +1984,7 @@ static char *build_in_pcms[] = { static char *build_in_pcms[] = { "adpcm", "alaw", "copy", "dmix", "file", "hooks", "hw", "ladspa", "lfloat", "linear", "meter", "mulaw", "multi", "null", "empty", "plug", "rate", "route", "share", - "shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul", + "shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "softamp", "mmap_emul", NULL }; diff -r dc32d9f00649 -r 1164919ed312 src/pcm/pcm_softamp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pcm/pcm_softamp.c Wed Aug 01 15:11:00 2007 +0400 @@ -0,0 +1,625 @@ +/** + * \file pcm/pcm_softamp.c + * \ingroup PCM_Plugins + * \brief PCM software amplification plugin + * \author Stas Sergeev <stsp@xxxxxxxxxxxxxxxxxxxxx> + * \date 2007 + */ +/* + * PCM software amplification plugin + * Copyright (c) 2007 Stas Sergeev <stsp@xxxxxxxxxxxxxxxxxxxxx> + * Based on softvol plugin by Takashi Iwai + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <byteswap.h> +#include <math.h> +#include "pcm_local.h" +#include "pcm_plugin.h" + +#ifndef PIC +/* entry for static linking */ +const char *_snd_module_pcm_softamp = ""; +#endif + +#ifndef DOC_HIDDEN + +typedef struct { + /* This field need to be the first */ + snd_pcm_plugin_t plug; + unsigned int cchannels; + snd_ctl_t *ctl; + snd_ctl_elem_value_t elem; + unsigned int cur_amp[2]; + unsigned int max_val; /* max index */ +} snd_pcm_softamp_t; + +#endif /* DOC_HIDDEN */ + +#define AMP_MAX 3 + +static void softamp_convert(snd_pcm_softamp_t *swamp, + const snd_pcm_channel_area_t *dst_areas, + snd_pcm_uframes_t dst_offset, + const snd_pcm_channel_area_t *src_areas, + snd_pcm_uframes_t src_offset, + unsigned int channels, + snd_pcm_uframes_t frames) +{ + const snd_pcm_channel_area_t *dst_area, *src_area; + unsigned int src_step, dst_step; + unsigned int ch, fr, am; + float *src, *dst; + + for (ch = 0; ch < channels; ch++) { + src_area = &src_areas[ch]; + dst_area = &dst_areas[ch]; + src = snd_pcm_channel_area_addr(src_area, src_offset); + dst = snd_pcm_channel_area_addr(dst_area, dst_offset); + src_step = snd_pcm_channel_area_step(src_area) / sizeof(float); + dst_step = snd_pcm_channel_area_step(dst_area) / sizeof(float); + fr = frames; + while (fr--) { + *dst = *src; + for (am = 0; am < swamp->cur_amp[ch & 1]; am++) + *dst = sinf(*dst * M_PI_2); + src += src_step; + dst += dst_step; + } + } +} + +/* + * get the current value from driver + * + * TODO: mmap support? + */ +static void get_current_volume(snd_pcm_softamp_t *swamp) +{ + unsigned int val; + unsigned int i; + + if (snd_ctl_elem_read(swamp->ctl, &swamp->elem) < 0) + return; + for (i = 0; i < swamp->cchannels; i++) { + val = swamp->elem.value.integer.value[i]; + if (val > swamp->max_val) + val = swamp->max_val; + swamp->cur_amp[i] = val; + } +} + +static void softamp_free(snd_pcm_softamp_t *swamp) +{ + if (swamp->plug.gen.close_slave) + snd_pcm_close(swamp->plug.gen.slave); + if (swamp->ctl) + snd_ctl_close(swamp->ctl); + free(swamp); +} + +static int snd_pcm_softamp_close(snd_pcm_t *pcm) +{ + snd_pcm_softamp_t *swamp = pcm->private_data; + softamp_free(swamp); + return 0; +} + +static int snd_pcm_softamp_hw_refine_cprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, + snd_pcm_hw_params_t *params) +{ + int err; + snd_pcm_access_mask_t access_mask = { SND_PCM_ACCBIT_SHM }; + snd_pcm_format_mask_t format_mask = { SND_PCM_FMTBIT_FLOAT }; + + err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_ACCESS, + &access_mask); + if (err < 0) + return err; + err = _snd_pcm_hw_param_set_mask(params, SND_PCM_HW_PARAM_FORMAT, + &format_mask); + if (err < 0) + return err; + err = _snd_pcm_hw_params_set_subformat(params, SND_PCM_SUBFORMAT_STD); + if (err < 0) + return err; + err = _snd_pcm_hw_param_set_min(params, SND_PCM_HW_PARAM_CHANNELS, 1, 0); + if (err < 0) + return err; + params->info &= ~(SND_PCM_INFO_MMAP | SND_PCM_INFO_MMAP_VALID); + return 0; +} + +static int snd_pcm_softamp_hw_refine_sprepare(snd_pcm_t *pcm ATTRIBUTE_UNUSED, + snd_pcm_hw_params_t *sparams) +{ + snd_pcm_access_mask_t saccess_mask = { SND_PCM_ACCBIT_MMAP }; + _snd_pcm_hw_params_any(sparams); + _snd_pcm_hw_param_set_mask(sparams, SND_PCM_HW_PARAM_ACCESS, + &saccess_mask); + _snd_pcm_hw_params_set_format(sparams, SND_PCM_FORMAT_FLOAT); + _snd_pcm_hw_params_set_subformat(sparams, SND_PCM_SUBFORMAT_STD); + return 0; +} + +static int snd_pcm_softamp_hw_refine_schange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, + snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *sparams) +{ + int err; + unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_PERIODS | + SND_PCM_HW_PARBIT_PERIOD_SIZE | + SND_PCM_HW_PARBIT_PERIOD_TIME | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_BUFFER_TIME | + SND_PCM_HW_PARBIT_TICK_TIME); + err = _snd_pcm_hw_params_refine(sparams, links, params); + if (err < 0) + return err; + return 0; +} + +static int snd_pcm_softamp_hw_refine_cchange(snd_pcm_t *pcm ATTRIBUTE_UNUSED, + snd_pcm_hw_params_t *params, + snd_pcm_hw_params_t *sparams) +{ + int err; + unsigned int links = (SND_PCM_HW_PARBIT_CHANNELS | + SND_PCM_HW_PARBIT_RATE | + SND_PCM_HW_PARBIT_PERIODS | + SND_PCM_HW_PARBIT_PERIOD_SIZE | + SND_PCM_HW_PARBIT_PERIOD_TIME | + SND_PCM_HW_PARBIT_BUFFER_SIZE | + SND_PCM_HW_PARBIT_BUFFER_TIME | + SND_PCM_HW_PARBIT_TICK_TIME); + err = _snd_pcm_hw_params_refine(params, links, sparams); + if (err < 0) + return err; + return 0; +} + +static int snd_pcm_softamp_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params) +{ + return snd_pcm_hw_refine_slave(pcm, params, + snd_pcm_softamp_hw_refine_cprepare, + snd_pcm_softamp_hw_refine_cchange, + snd_pcm_softamp_hw_refine_sprepare, + snd_pcm_softamp_hw_refine_schange, + snd_pcm_generic_hw_refine); +} + +static int snd_pcm_softamp_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params) +{ + snd_pcm_softamp_t *swamp = pcm->private_data; + snd_pcm_t *slave = swamp->plug.gen.slave; + int err = snd_pcm_hw_params_slave(pcm, params, + snd_pcm_softamp_hw_refine_cchange, + snd_pcm_softamp_hw_refine_sprepare, + snd_pcm_softamp_hw_refine_schange, + snd_pcm_generic_hw_params); + if (err < 0) + return err; + if (!snd_pcm_format_float(slave->format)) { + SNDERR("softamp supports only FLOAT format"); + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t +snd_pcm_softamp_write_areas(snd_pcm_t *pcm, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size, + const snd_pcm_channel_area_t *slave_areas, + snd_pcm_uframes_t slave_offset, + snd_pcm_uframes_t *slave_sizep) +{ + snd_pcm_softamp_t *swamp = pcm->private_data; + if (size > *slave_sizep) + size = *slave_sizep; + get_current_volume(swamp); + softamp_convert(swamp, slave_areas, slave_offset, + areas, offset, pcm->channels, size); + *slave_sizep = size; + return size; +} + +static snd_pcm_uframes_t +snd_pcm_softamp_read_areas(snd_pcm_t *pcm, + const snd_pcm_channel_area_t *areas, + snd_pcm_uframes_t offset, + snd_pcm_uframes_t size, + const snd_pcm_channel_area_t *slave_areas, + snd_pcm_uframes_t slave_offset, + snd_pcm_uframes_t *slave_sizep) +{ + snd_pcm_softamp_t *swamp = pcm->private_data; + if (size > *slave_sizep) + size = *slave_sizep; + get_current_volume(swamp); + softamp_convert(swamp, areas, offset, slave_areas, + slave_offset, pcm->channels, size); + *slave_sizep = size; + return size; +} + +static void snd_pcm_softamp_dump(snd_pcm_t *pcm, snd_output_t *out) +{ + snd_pcm_softamp_t *swamp = pcm->private_data; + snd_output_printf(out, "Soft amp PCM\n"); + snd_output_printf(out, "Control: %s\n", swamp->elem.id.name); + snd_output_printf(out, "max_amp: %d\n", swamp->max_val); + if (pcm->setup) { + snd_output_printf(out, "Its setup is:\n"); + snd_pcm_dump_setup(pcm, out); + } + snd_output_printf(out, "Slave: "); + snd_pcm_dump(swamp->plug.gen.slave, out); +} + +static int add_tlv_info(snd_pcm_softamp_t *swamp, snd_ctl_elem_info_t *cinfo) +{ + unsigned int tlv[4]; + tlv[0] = SND_CTL_TLVT_DB_SCALE; + tlv[1] = 2 * sizeof(int); + tlv[2] = 0; + tlv[3] = 1; + return snd_ctl_elem_tlv_write(swamp->ctl, &cinfo->id, tlv); +} + +static int add_user_ctl(snd_pcm_softamp_t *swamp, snd_ctl_elem_info_t *cinfo, int count) +{ + int err; + int i; + + err = snd_ctl_elem_add_integer(swamp->ctl, &cinfo->id, count, 0, swamp->max_val, 0); + if (err < 0) + return err; + add_tlv_info(swamp, cinfo); + for (i = 0; i < count; i++) + swamp->elem.value.integer.value[i] = 0; + return snd_ctl_elem_write(swamp->ctl, &swamp->elem); +} + +/* + * load and set up user-control + * returns 0 if the user-control is found or created, + * returns 1 if the control is a hw control, + * or a negative error code + */ +static int softamp_load_control(snd_pcm_t *pcm, snd_pcm_softamp_t *swamp, + int ctl_card, snd_ctl_elem_id_t *ctl_id, + int cchannels, int max_amp) +{ + char tmp_name[32]; + snd_pcm_info_t *info; + snd_ctl_elem_info_t *cinfo; + int err; + + if (ctl_card < 0) { + snd_pcm_info_alloca(&info); + err = snd_pcm_info(pcm, info); + if (err < 0) + return err; + ctl_card = snd_pcm_info_get_card(info); + if (ctl_card < 0) { + SNDERR("No card defined for softamp control"); + return -EINVAL; + } + } + sprintf(tmp_name, "hw:%d", ctl_card); + err = snd_ctl_open(&swamp->ctl, tmp_name, 0); + if (err < 0) { + SNDERR("Cannot open CTL %s", tmp_name); + return err; + } + + swamp->elem.id = *ctl_id; + swamp->max_val = max_amp; + + snd_ctl_elem_info_alloca(&cinfo); + snd_ctl_elem_info_set_id(cinfo, ctl_id); + if ((err = snd_ctl_elem_info(swamp->ctl, cinfo)) < 0) { + if (err != -ENOENT) { + SNDERR("Cannot get info for CTL %s", tmp_name); + return err; + } + err = add_user_ctl(swamp, cinfo, cchannels); + if (err < 0) { + SNDERR("Cannot add a control"); + return err; + } + } else { + if (! (cinfo->access & SNDRV_CTL_ELEM_ACCESS_USER)) { + /* hardware control exists */ + return 1; /* notify */ + + } else if (cinfo->type != SND_CTL_ELEM_TYPE_INTEGER || + cinfo->count != (unsigned int)cchannels || + cinfo->value.integer.min != 0 || + cinfo->value.integer.max != swamp->max_val) { + if ((err = snd_ctl_elem_remove(swamp->ctl, &cinfo->id)) < 0) { + SNDERR("Control %s mismatch", tmp_name); + return err; + } + snd_ctl_elem_info_set_id(cinfo, ctl_id); /* reset numid */ + if ((err = add_user_ctl(swamp, cinfo, cchannels)) < 0) { + SNDERR("Cannot add a control"); + return err; + } + } else { + /* check TLV availability */ + unsigned int tlv[4]; + err = snd_ctl_elem_tlv_read(swamp->ctl, &cinfo->id, tlv, sizeof(tlv)); + if (err < 0) + add_tlv_info(swamp, cinfo); + } + } + + return 0; +} + +static snd_pcm_ops_t snd_pcm_softamp_ops = { + .close = snd_pcm_softamp_close, + .info = snd_pcm_generic_info, + .hw_refine = snd_pcm_softamp_hw_refine, + .hw_params = snd_pcm_softamp_hw_params, + .hw_free = snd_pcm_generic_hw_free, + .sw_params = snd_pcm_generic_sw_params, + .channel_info = snd_pcm_generic_channel_info, + .dump = snd_pcm_softamp_dump, + .nonblock = snd_pcm_generic_nonblock, + .async = snd_pcm_generic_async, + .mmap = snd_pcm_generic_mmap, + .munmap = snd_pcm_generic_munmap, +}; + +/** + * \brief Creates a new SoftAmp PCM + * \param pcmp Returns created PCM handle + * \param name Name of PCM + * \param sformat Slave format + * \param ctl_card card index of the control + * \param ctl_id The control element + * \param cchannels PCM channels + * \param max_amp maximum amplification level + * \param slave Slave PCM handle + * \param close_slave When set, the slave PCM handle is closed with copy PCM + * \retval zero on success otherwise a negative error code + * \warning Using of this function might be dangerous in the sense + * of compatibility reasons. The prototype might be freely + * changed in future. + */ +int snd_pcm_softamp_open(snd_pcm_t **pcmp, const char *name, + snd_pcm_format_t sformat, + int ctl_card, snd_ctl_elem_id_t *ctl_id, + int cchannels, int max_amp, + snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *pcm; + snd_pcm_softamp_t *swamp; + int err; + assert(pcmp && slave); + swamp = calloc(1, sizeof(*swamp)); + if (! swamp) + return -ENOMEM; + err = softamp_load_control(slave, swamp, ctl_card, ctl_id, cchannels, + max_amp); + if (err < 0) { + softamp_free(swamp); + return err; + } + if (err > 0) { /* hardware control - no need for softamp! */ + softamp_free(swamp); + *pcmp = slave; /* just pass the slave */ + return 0; + } + + /* do softamp */ + snd_pcm_plugin_init(&swamp->plug); + swamp->cchannels = cchannels; + swamp->plug.read = snd_pcm_softamp_read_areas; + swamp->plug.write = snd_pcm_softamp_write_areas; + swamp->plug.undo_read = snd_pcm_plugin_undo_read_generic; + swamp->plug.undo_write = snd_pcm_plugin_undo_write_generic; + + err = snd_pcm_new(&pcm, SND_PCM_TYPE_SOFTAMP, name, slave->stream, slave->mode); + if (err < 0) { + softamp_free(swamp); + return err; + } + pcm->ops = &snd_pcm_softamp_ops; + pcm->fast_ops = &snd_pcm_plugin_fast_ops; + pcm->private_data = swamp; + pcm->poll_fd = slave->poll_fd; + pcm->poll_events = slave->poll_events; + /* + * Since the softamp converts on the place, and the format/channels + * must be identical between source and destination, we don't need + * an extra buffer. + */ + pcm->mmap_shadow = 1; + snd_pcm_set_hw_ptr(pcm, &swamp->plug.hw_ptr, -1, 0); + snd_pcm_set_appl_ptr(pcm, &swamp->plug.appl_ptr, -1, 0); + + if (!snd_pcm_format_float(sformat)) { +#ifdef BUILD_PCM_PLUGIN_LFLOAT + snd_pcm_t *pre_cnv; + err = snd_pcm_lfloat_open(&pre_cnv, NULL, + SND_PCM_FORMAT_FLOAT, pcm, 1); + if (err < 0) { + snd_pcm_close(pcm); + softamp_free(swamp); + return err; + } + err = snd_pcm_lfloat_open(&swamp->plug.gen.slave, NULL, + sformat, slave, close_slave); + if (err < 0) { + snd_pcm_close(pre_cnv); + snd_pcm_close(pcm); + softamp_free(swamp); + return err; + } + swamp->plug.gen.close_slave = 1; + *pcmp = pre_cnv; +#else + SNDERR("lfloat plugin needed for softamp, is not compiled in"); + return -EINVAL; +#endif + } else { + swamp->plug.gen.slave = slave; + swamp->plug.gen.close_slave = close_slave; + *pcmp = pcm; + } + + return 0; +} + +/* in pcm_misc.c */ +int snd_pcm_parse_control_id(snd_config_t *conf, snd_ctl_elem_id_t *ctl_id, int *cardp, + int *cchannelsp, int *hwctlp); + +/*! \page pcm_plugins + +\section pcm_plugins_softamp Plugin: Soft Volume + +This plugin applies the software volume amplification. +The format, rate and channels must match for both of source and destination. + +\code +pcm.name { + type softamp # Soft Volume conversion PCM + slave STR # Slave name + # or + slave { # Slave definition + pcm STR # Slave PCM name + # or + pcm { } # Slave PCM definition + format STR # Slave format + } + control { + name STR # control element id string + card STR # control card index + [iface STR] # interface of the element + [index INT] # index of the element + [device INT] # device number of the element + [subdevice INT] # subdevice number of the element + [count INT] # control channels 1 or 2 (default: 2) + } + [max_amp INT] # maximum amplification (default: 3) +} +\endcode + +\subsection pcm_plugins_softamp_funcref Function reference + +<UL> + <LI>snd_pcm_softamp_open() + <LI>_snd_pcm_softamp_open() +</UL> + +*/ + +/** + * \brief Creates a new Soft Volume PCM + * \param pcmp Returns created PCM handle + * \param name Name of PCM + * \param root Root configuration node + * \param conf Configuration node with Soft Volume PCM description + * \param stream Stream type + * \param mode Stream mode + * \retval zero on success otherwise a negative error code + * \warning Using of this function might be dangerous in the sense + * of compatibility reasons. The prototype might be freely + * changed in future. + */ +int _snd_pcm_softamp_open(snd_pcm_t **pcmp, const char *name, + snd_config_t *root, snd_config_t *conf, + snd_pcm_stream_t stream, int mode) +{ + snd_config_iterator_t i, next; + int err; + snd_pcm_t *spcm; + snd_config_t *slave = NULL, *sconf; + snd_config_t *control = NULL; + snd_pcm_format_t sformat; + snd_ctl_elem_id_t *ctl_id; + int max_amp = AMP_MAX; + int card = -1, cchannels = 2; + + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; + if (snd_config_get_id(n, &id) < 0) + continue; + if (snd_pcm_conf_generic_id(id)) + continue; + if (strcmp(id, "slave") == 0) { + slave = n; + continue; + } + if (strcmp(id, "control") == 0) { + control = n; + continue; + } + if (strcmp(id, "max_amp") == 0) { + long v; + err = snd_config_get_integer(n, &v); + if (err < 0) { + SNDERR("Invalid max_amp value"); + return err; + } + max_amp = v; + continue; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + if (!slave) { + SNDERR("slave is not defined"); + return -EINVAL; + } + if (!control) { + SNDERR("control is not defined"); + return -EINVAL; + } + err = snd_pcm_slave_conf(root, slave, &sconf, 1, + SND_PCM_HW_PARAM_FORMAT, SCONF_MANDATORY, + &sformat); + if (err < 0) + return err; + err = snd_pcm_open_slave(&spcm, root, sconf, stream, mode, conf); + snd_config_delete(sconf); + if (err < 0) + return err; + snd_ctl_elem_id_alloca(&ctl_id); + if ((err = snd_pcm_parse_control_id(control, ctl_id, &card, &cchannels, NULL)) < 0) { + snd_pcm_close(spcm); + return err; + } + err = snd_pcm_softamp_open(pcmp, name, sformat, card, ctl_id, cchannels, + max_amp, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} +#ifndef DOC_HIDDEN +SND_DLSYM_BUILD_VERSION(_snd_pcm_softamp_open, SND_PCM_DLSYM_VERSION); +#endif diff -r dc32d9f00649 -r 1164919ed312 src/pcm/pcm_symbols.c --- a/src/pcm/pcm_symbols.c Fri Jul 13 12:44:43 2007 +0200 +++ b/src/pcm/pcm_symbols.c Wed Aug 01 15:11:00 2007 +0400 @@ -47,6 +47,7 @@ extern const char *_snd_module_pcm_asym; extern const char *_snd_module_pcm_asym; extern const char *_snd_module_pcm_iec958; extern const char *_snd_module_pcm_softvol; +extern const char *_snd_module_pcm_softamp; extern const char *_snd_module_pcm_extplug; extern const char *_snd_module_pcm_ioplug; extern const char *_snd_module_pcm_mmap_emul;
_______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel