* [patch] Add a software amplifier plugin
@ 2007-08-01 11:58 Stas Sergeev
2007-08-01 12:03 ` Takashi Iwai
0 siblings, 1 reply; 3+ messages in thread
From: Stas Sergeev @ 2007-08-01 11:58 UTC (permalink / raw)
To: alsa-devel
[-- Attachment #1: Type: text/plain, Size: 511 bytes --]
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@aknet.ru>
[-- Attachment #2: a.diff --]
[-- Type: text/x-patch, Size: 22782 bytes --]
# HG changeset patch
# User Stas Sergeev <stsp@users.sourceforge.net>
# 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@users.sourceforge.net>
+ * \date 2007
+ */
+/*
+ * PCM software amplification plugin
+ * Copyright (c) 2007 Stas Sergeev <stsp@users.sourceforge.net>
+ * 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;
[-- Attachment #3: Type: text/plain, Size: 160 bytes --]
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [patch] Add a software amplifier plugin
2007-08-01 11:58 [patch] Add a software amplifier plugin Stas Sergeev
@ 2007-08-01 12:03 ` Takashi Iwai
2007-08-01 12:26 ` Stas Sergeev
0 siblings, 1 reply; 3+ messages in thread
From: Takashi Iwai @ 2007-08-01 12:03 UTC (permalink / raw)
To: Stas Sergeev; +Cc: alsa-devel
At Wed, 01 Aug 2007 15:58:56 +0400,
Stas Sergeev wrote:
>
> 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@aknet.ru>
Did you try the recent softvol plugin?
It supports already amplifier (> 0dB), not only attenuation.
thanks,
Takashi
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [patch] Add a software amplifier plugin
2007-08-01 12:03 ` Takashi Iwai
@ 2007-08-01 12:26 ` Stas Sergeev
0 siblings, 0 replies; 3+ messages in thread
From: Stas Sergeev @ 2007-08-01 12:26 UTC (permalink / raw)
To: Takashi Iwai; +Cc: alsa-devel
Hello.
Takashi Iwai wrote:
> Did you try the recent softvol plugin?
Yep, I tried it for the whole day with
alsa-lib-1.0.14, trying to feed it with
the max_dB value to no avail.
> It supports already amplifier (> 0dB), not only attenuation.
Oh, it does so only in hg, not in 1.0.14,
now I see.
Well, it seems to me that my plugin gives
a way better quality (doing sine amplification,
while yours probably does linear), but then
yes, there is no point for the new plugin
indeed.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2007-08-01 12:28 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-08-01 11:58 [patch] Add a software amplifier plugin Stas Sergeev
2007-08-01 12:03 ` Takashi Iwai
2007-08-01 12:26 ` Stas Sergeev
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.