diff -r 9005d28a1f9e configure.in --- a/configure.in Mon Jun 11 10:52:17 2007 +0200 +++ b/configure.in Thu Jul 19 14:55:51 2007 +0200 @@ -336,6 +336,23 @@ AC_ARG_ENABLE(alisp, AC_ARG_ENABLE(alisp, AS_HELP_STRING([--disable-alisp], [disable the alisp component]), [build_alisp="$enableval"], [build_alisp="yes"]) +AC_ARG_ENABLE(python, + AS_HELP_STRING([--disable-python], [disable the python components]), + [build_python="$enableval"], [build_python="yes"]) +PYTHON_LIBS="" +if test "$build_python" = "yes"; then + AC_ARG_WITH(pythonlibs, + AS_HELP_STRING([--with-pythonlibs=ldflags], + [specify python libraries (-lpthread -lm -ldl -lpython2.4)]), + pythonlibs="$withval", pythonlibs=`python-config --libs`) + if test -z "$pythonlibs" ; then + echo "Unable to determine python libraries! Probably python-config is not" + echo "available on this system. Please, use --with-pythonlibs options." + exit 1 + fi + PYTHON_LIBS="$pythonlibs" +fi +AC_SUBST(PYTHON_LIBS) if test "$build_seq" != "yes"; then build_instr="no" @@ -348,6 +365,7 @@ AM_CONDITIONAL(BUILD_SEQ, test x$build_s AM_CONDITIONAL(BUILD_SEQ, test x$build_seq = xyes) AM_CONDITIONAL(BUILD_INSTR, test x$build_instr = xyes) AM_CONDITIONAL(BUILD_ALISP, test x$build_alisp = xyes) +AM_CONDITIONAL(BUILD_PYTHON, test x$build_python = xyes) if test "$build_mixer" = "yes"; then AC_DEFINE([BUILD_MIXER], "1", [Build mixer component]) @@ -357,6 +375,9 @@ fi fi if test "$build_rawmidi" = "yes"; then AC_DEFINE([BUILD_RAWMIDI], "1", [Build raw MIDI component]) +fi +if test "$build_hwdep" = "yes"; then + AC_DEFINE([BUILD_HWDEP], "1", [Build hwdep component]) fi if test "$build_seq" = "yes"; then AC_DEFINE([BUILD_SEQ], "1", [Build sequencer component]) @@ -376,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" +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" build_pcm_plugin="no" for t in $PCM_PLUGIN_LIST; do @@ -414,7 +435,7 @@ if test "$HAVE_LIBPTHREAD" != "yes"; the build_pcm_share="no" fi -if test "$softfloat" != "yes"; then +if test "$softfloat" = "yes"; then build_pcm_lfloat="no" fi @@ -445,6 +466,7 @@ AM_CONDITIONAL(BUILD_PCM_PLUGIN_SOFTVOL, AM_CONDITIONAL(BUILD_PCM_PLUGIN_SOFTVOL, test x$build_pcm_softvol = 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) dnl Defines for plug plugin if test "$build_pcm_rate" = "yes"; then @@ -532,3 +554,21 @@ AC_OUTPUT(Makefile doc/Makefile doc/pict modules/Makefile modules/mixer/Makefile modules/mixer/simple/Makefile \ alsalisp/Makefile aserver/Makefile test/Makefile utils/Makefile \ utils/alsa-lib.spec utils/alsa.pc) + +dnl Create asoundlib.h dynamically according to configure options +echo "Creating asoundlib.h..." +cp "$srcdir"/include/asoundlib-head.h include/asoundlib.h +test "$build_pcm" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_rawmidi" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_pcm" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_hwdep" = "yes" && echo "#include " >> include/asoundlib.h +echo "#include " >> include/asoundlib.h +test "$build_mixer" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_seq" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_instr" = "yes" && echo "#include " >> include/asoundlib.h +test "$build_instr" = "yes" && echo "#include " >> include/asoundlib.h +cat "$srcdir"/include/asoundlib-tail.h >> include/asoundlib.h + diff -r 9005d28a1f9e doc/doxygen.cfg --- a/doc/doxygen.cfg Mon Jun 11 10:52:17 2007 +0200 +++ b/doc/doxygen.cfg Thu Jul 19 14:55:51 2007 +0200 @@ -45,6 +45,7 @@ INPUT = index.doxygen \ ../src/pcm/pcm_mmap.c \ ../src/pcm/pcm_plugin.c \ ../src/pcm/pcm_hw.c \ + ../src/pcm/pcm_mmap_emul.c \ ../src/pcm/pcm_shm.c \ ../src/pcm/pcm_null.c \ ../src/pcm/pcm_copy.c \ diff -r 9005d28a1f9e include/Makefile.am --- a/include/Makefile.am Mon Jun 11 10:52:17 2007 +0200 +++ b/include/Makefile.am Thu Jul 19 14:55:51 2007 +0200 @@ -5,16 +5,59 @@ alsaincludedir = ${includedir}/alsa alsainclude_HEADERS = asoundlib.h asoundef.h \ version.h global.h input.h output.h error.h \ - conf.h pcm.h pcm_old.h pcm_plugin.h rawmidi.h timer.h \ - hwdep.h control.h mixer.h mixer_abst.h \ - seq_event.h seq.h seqmid.h seq_midi_event.h \ - conv.h instr.h iatomic.h \ - alisp.h pcm_external.h pcm_ioplug.h pcm_extplug.h \ - pcm_rate.h control_external.h + conf.h control.h iatomic.h -noinst_HEADERS = alsa sys.h search.h list.h aserver.h local.h alsa-symbols.h +if BUILD_CTL_PLUGIN_EXT +alsainclude_HEADERS += control_external.h +endif -CLEANFILES = stamp-vh version.h alsa +if BUILD_PCM +alsainclude_HEADERS += pcm.h pcm_old.h timer.h +if BUILD_PCM_PLUGIN +alsainclude_HEADERS += pcm_plugin.h +endif +if BUILD_PCM_PLUGIN_RATE +alsainclude_HEADERS += pcm_rate.h +endif +if BUILD_PCM_PLUGIN_EXTPLUG +alsainclude_HEADERS += pcm_external.h pcm_extplug.h +endif +if BUILD_PCM_PLUGIN_IOPLUG +if !BUILD_PCM_PLUGIN_EXTPLUG +alsainclude_HEADERS += pcm_external.h +endif +alsainclude_HEADERS += pcm_ioplug.h +endif +endif + +if BUILD_RAWMIDI +alsainclude_HEADERS += rawmidi.h +endif + +if BUILD_HWDEP +alsainclude_HEADERS += hwdep.h +endif + +if BUILD_MIXER +alsainclude_HEADERS += mixer.h mixer_abst.h +endif + +if BUILD_SEQ +alsainclude_HEADERS += seq_event.h seq.h seqmid.h seq_midi_event.h +endif + +if BUILD_INSTR +alsainclude_HEADERS += conv.h instr.h +endif + +if BUILD_ALISP +alsainclude_HEADERS += alisp.h +endif + +noinst_HEADERS = alsa sys.h search.h list.h aserver.h local.h alsa-symbols.h \ + asoundlib-head.h asoundlib-tail.h + +DISTCLEANFILES = stamp-vh version.h alsa asoundlib.h alsa: ln -s $(top_srcdir)/include alsa diff -r 9005d28a1f9e include/asoundlib-head.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/asoundlib-head.h Thu Jul 19 14:55:51 2007 +0200 @@ -0,0 +1,48 @@ +/** + * \file include/asoundlib.h + * \brief Application interface library for the ALSA driver + * \author Jaroslav Kysela + * \author Abramo Bagnara + * \author Takashi Iwai + * \date 1998-2001 + * + * Application interface library for the ALSA driver + */ +/* + * 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 + * + */ + +#ifndef __ASOUNDLIB_H +#define __ASOUNDLIB_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include diff -r 9005d28a1f9e include/asoundlib-tail.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/include/asoundlib-tail.h Thu Jul 19 14:55:51 2007 +0200 @@ -0,0 +1,2 @@ + +#endif /* __ASOUNDLIB_H */ diff -r 9005d28a1f9e include/asoundlib.h --- a/include/asoundlib.h Mon Jun 11 10:52:17 2007 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/** - * \file include/asoundlib.h - * \brief Application interface library for the ALSA driver - * \author Jaroslav Kysela - * \author Abramo Bagnara - * \author Takashi Iwai - * \date 1998-2001 - * - * Application interface library for the ALSA driver - */ -/* - * 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 - * - */ - -#ifndef __ASOUNDLIB_H -#define __ASOUNDLIB_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#endif /* __ASOUNDLIB_H */ diff -r 9005d28a1f9e include/pcm.h --- a/include/pcm.h Mon Jun 11 10:52:17 2007 +0200 +++ b/include/pcm.h Thu Jul 19 14:55:51 2007 +0200 @@ -362,7 +362,9 @@ enum _snd_pcm_type { SND_PCM_TYPE_IOPLUG, /** External filter plugin */ SND_PCM_TYPE_EXTPLUG, - SND_PCM_TYPE_LAST = SND_PCM_TYPE_EXTPLUG + /** Mmap-emulation plugin */ + SND_PCM_TYPE_MMAP_EMUL, + SND_PCM_TYPE_LAST = SND_PCM_TYPE_MMAP_EMUL }; /** PCM type */ diff -r 9005d28a1f9e modules/mixer/simple/Makefile.am --- a/modules/mixer/simple/Makefile.am Mon Jun 11 10:52:17 2007 +0200 +++ b/modules/mixer/simple/Makefile.am Thu Jul 19 14:55:51 2007 +0200 @@ -1,4 +1,5 @@ pkglibdir = @ALSA_PLUGIN_DIR@/smixer pkglibdir = @ALSA_PLUGIN_DIR@/smixer +pythonlibs = @PYTHON_LIBS@ AM_CFLAGS = -g -O2 -W -Wall @@ -7,6 +8,10 @@ pkglib_LTLIBRARIES = smixer-sbase.la \ pkglib_LTLIBRARIES = smixer-sbase.la \ smixer-ac97.la \ smixer-hda.la + +if BUILD_PYTHON +pkglib_LTLIBRARIES += smixer-python.la +endif noinst_HEADERS = sbase.h @@ -21,3 +26,9 @@ smixer_hda_la_SOURCES = hda.c sbasedl.c smixer_hda_la_SOURCES = hda.c sbasedl.c smixer_hda_la_LDFLAGS = -module -avoid-version smixer_hda_la_LIBADD = ../../../src/libasound.la + +if BUILD_PYTHON +smixer_python_la_SOURCES = python.c +smixer_python_la_LDFLAGS = -module -avoid-version $(pythonlibs) +smixer_python_la_LIBADD = ../../../src/libasound.la +endif diff -r 9005d28a1f9e modules/mixer/simple/python.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/mixer/simple/python.c Thu Jul 19 14:55:51 2007 +0200 @@ -0,0 +1,1002 @@ +/* + * Mixer Interface - python binding simple abstact module + * Copyright (c) 2007 by Jaroslav Kysela + * + * + * 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 +#include "config.h" +#include "asoundlib.h" +#include "mixer_abst.h" +#include + +struct python_priv { + int py_initialized; + PyObject *py_event_func; + PyObject *py_mdict; + PyObject *py_mixer; +}; + +#define SCRIPT ALSA_PLUGIN_DIR "/smixer/python/main.py" + +struct pymelem { + PyObject_HEAD + sm_selem_t selem; + PyObject *py_mixer; + snd_mixer_elem_t *melem; +}; + +struct pymixer { + PyObject_HEAD + snd_mixer_class_t *class; + snd_mixer_t *mixer; + PyObject *mdict; + int hctl_count; + void **hctl; + int helem_count; + void **helem; + int melem_count; + void **melem; +}; + +static PyInterpreterState *main_interpreter; + +static void *get_C_ptr(PyObject *obj, const char *attr) +{ + PyObject *o; + + o = PyObject_GetAttr(obj, PyString_InternFromString(attr)); + if (!o) { + PyErr_Format(PyExc_TypeError, "missing '%s' attribute", attr); + return NULL; + } + if (!PyInt_Check(o)) { + PyErr_Format(PyExc_TypeError, "'%s' attribute is not integer", attr); + return NULL; + } + return (void *)PyInt_AsLong(o); +} + +static struct pymelem *melem_to_pymelem(snd_mixer_elem_t *elem) +{ + return (struct pymelem *)((char *)snd_mixer_elem_get_private(elem) - offsetof(struct pymelem, selem)); +} + +static int pcall(struct pymelem *pymelem, const char *attr, PyObject *args, PyObject **_res) +{ + PyObject *obj = (PyObject *)pymelem, *res; + int xres = 0; + + if (_res) + *_res = NULL; + obj = PyObject_GetAttr(obj, PyString_InternFromString(attr)); + if (!obj) { + PyErr_Format(PyExc_TypeError, "missing '%s' attribute", attr); + PyErr_Print(); + PyErr_Clear(); + Py_DECREF(args); + return -EIO; + } + res = PyObject_CallObject(obj, args); + Py_XDECREF(args); + if (res == NULL) { + PyErr_Print(); + PyErr_Clear(); + return -EIO; + } + if (_res && PyTuple_Check(res)) { + *_res = res; + res = PyTuple_GetItem(res, 0); + } + if (PyInt_Check(res)) { + xres = PyInt_AsLong(res); + } else if (res == Py_None) { + xres = 0; + } else if (PyBool_Check(res)) { + xres = res == Py_True; + } else { + PyErr_Format(PyExc_TypeError, "wrong result from '%s'!", attr); + PyErr_Print(); + PyErr_Clear(); + Py_DECREF(res); + if (_res) + *_res = NULL; + return -EIO; + } + if (_res && *_res) + return xres; + Py_DECREF(res); + return xres; +} + +static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + char *s, fcn[32] = "opsIs"; + int res, xdir = 1, xval = 0; + + switch (cmd) { + case SM_OPS_IS_ACTIVE: s = "Active"; xdir = 0; break; + case SM_OPS_IS_MONO: s = "Mono"; break; + case SM_OPS_IS_CHANNEL: s = "Channel"; xval = 1; break; + case SM_OPS_IS_ENUMERATED: s = "Enumerated"; xdir = val == 1; break; + case SM_OPS_IS_ENUMCNT: s = "EnumCnt"; break; + default: + return 1; + } + strcat(fcn, s); + + obj1 = PyTuple_New(xdir + xval); + if (xdir) { + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + if (xval) + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(val)); + } + res = pcall(pymelem, fcn, obj1, NULL); + return res < 0 ? 0 : res; +} + +static int get_x_range_ops(snd_mixer_elem_t *elem, int dir, + long *min, long *max, const char *attr) +{ + PyObject *obj1, *res; + struct pymelem *pymelem = melem_to_pymelem(elem); + int err; + + obj1 = PyTuple_New(1); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + err = pcall(pymelem, attr, obj1, &res); + if (err >= 0) { + err = !PyInt_Check(PyTuple_GetItem(res, 1)) || !PyInt_Check(PyTuple_GetItem(res, 2)); + if (err) { + err = !PyLong_Check(PyTuple_GetItem(res, 1)) || !PyLong_Check(PyTuple_GetItem(res, 2)); + if (err) { + PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); + PyErr_Print(); + PyErr_Clear(); + err = -EIO; + } else { + *min = PyLong_AsLong(PyTuple_GetItem(res, 1)); + *max = PyLong_AsLong(PyTuple_GetItem(res, 2)); + } + } else { + *min = PyInt_AsLong(PyTuple_GetItem(res, 1)); + *max = PyInt_AsLong(PyTuple_GetItem(res, 2)); + } + } + Py_XDECREF(res); + return err; +} + +static int get_range_ops(snd_mixer_elem_t *elem, int dir, + long *min, long *max) +{ + return get_x_range_ops(elem, dir, min, max, "opsGetRange"); +} + +static int set_range_ops(snd_mixer_elem_t *elem, int dir, + long min, long max) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(3); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(min)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(max)); + return pcall(pymelem, "opsGetRange", obj1, NULL); +} + +static int get_x_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, long *value, + const char *attr) +{ + PyObject *obj1, *res; + struct pymelem *pymelem = melem_to_pymelem(elem); + int err; + + obj1 = PyTuple_New(2); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); + err = pcall(pymelem, attr, obj1, &res); + if (err >= 0) { + err = !PyInt_Check(PyTuple_GetItem(res, 1)); + if (err) { + err = !PyLong_Check(PyTuple_GetItem(res, 1)); + if (err) { + PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); + PyErr_Print(); + PyErr_Clear(); + err = -EIO; + } else { + *value = PyLong_AsLong(PyTuple_GetItem(res, 1)); + } + } else { + *value = PyInt_AsLong(PyTuple_GetItem(res, 1)); + } + } + Py_XDECREF(res); + return err; +} + +static int get_volume_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, long *value) +{ + return get_x_ops(elem, dir, channel, value, "opsGetVolume"); +} + +static int get_switch_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, int *value) +{ + long value1; + int res; + res = get_x_ops(elem, dir, channel, &value1, "opsGetSwitch"); + *value = value1; + return res; +} + +static int get_dB_ops(snd_mixer_elem_t *elem, + int dir, + snd_mixer_selem_channel_id_t channel, + long *value) +{ + return get_x_ops(elem, dir, channel, value, "opsGetDB"); +} + +static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir, + long *min, long *max) +{ + return get_x_range_ops(elem, dir, min, max, "opsGetDBRange"); +} + +static int set_volume_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, long value) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(3); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(value)); + return pcall(pymelem, "opsSetVolume", obj1, NULL); +} + + +static int set_switch_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, int value) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(3); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(value)); + return pcall(pymelem, "opsSetSwitch", obj1, NULL); +} + +static int set_dB_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, + long db_gain, int xdir) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(4); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(db_gain)); + PyTuple_SET_ITEM(obj1, 3, PyInt_FromLong(xdir)); + return pcall(pymelem, "opsSetDB", obj1, NULL); +} + +static int enum_item_name_ops(snd_mixer_elem_t *elem, + unsigned int item, + size_t maxlen, char *buf) +{ + PyObject *obj1, *res; + struct pymelem *pymelem = melem_to_pymelem(elem); + int err; + unsigned int len; + char *s; + + obj1 = PyTuple_New(1); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(item)); + err = pcall(pymelem, "opsGetEnumItemName", obj1, &res); + if (err >= 0) { + err = !PyString_Check(PyTuple_GetItem(res, 1)); + if (err) { + PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); + PyErr_Print(); + PyErr_Clear(); + err = -EIO; + } else { + s = PyString_AsString(PyTuple_GetItem(res, 1)); + len = strlen(s); + if (maxlen - 1 > len) + len = maxlen - 1; + memcpy(buf, s, len); + buf[len] = '\0'; + } + } + Py_XDECREF(res); + return err; +} + +static int get_enum_item_ops(snd_mixer_elem_t *elem, + snd_mixer_selem_channel_id_t channel, + unsigned int *itemp) +{ + PyObject *obj1, *res; + struct pymelem *pymelem = melem_to_pymelem(elem); + int err; + + obj1 = PyTuple_New(1); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(channel)); + err = pcall(pymelem, "opsGetEnumItem", obj1, &res); + if (err >= 0) { + err = !PyInt_Check(PyTuple_GetItem(res, 1)); + if (err) { + PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); + PyErr_Print(); + PyErr_Clear(); + err = -EIO; + } else { + *itemp = PyInt_AsLong(PyTuple_GetItem(res, 1)); + } + } + Py_XDECREF(res); + return err; +} + +static int set_enum_item_ops(snd_mixer_elem_t *elem, + snd_mixer_selem_channel_id_t channel, + unsigned int item) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(2); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(channel)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(item)); + return pcall(pymelem, "opsSetEnumItem", obj1, NULL); +} + +static struct sm_elem_ops simple_python_ops = { + .is = is_ops, + .get_range = get_range_ops, + .get_dB_range = get_dB_range_ops, + .set_range = set_range_ops, + .get_volume = get_volume_ops, + .get_dB = get_dB_ops, + .set_volume = set_volume_ops, + .set_dB = set_dB_ops, + .get_switch = get_switch_ops, + .set_switch = set_switch_ops, + .enum_item_name = enum_item_name_ops, + .get_enum_item = get_enum_item_ops, + .set_enum_item = set_enum_item_ops +}; + +static void selem_free(snd_mixer_elem_t *elem) +{ + sm_selem_t *simple = snd_mixer_elem_get_private(elem); + + if (simple->id) { + snd_mixer_selem_id_free(simple->id); + simple->id = NULL; + } +} + +static PyObject * +pymelem_cap(struct pymelem *pymelem ATTRIBUTE_UNUSED, void *priv) +{ + return PyInt_FromLong((long)priv); +} + +static PyObject * +pymelem_get_caps(struct pymelem *pymelem, void *priv ATTRIBUTE_UNUSED) +{ + return PyInt_FromLong(pymelem->selem.caps); +} + +static PyObject * +pymelem_get_name(struct pymelem *pymelem, void *priv ATTRIBUTE_UNUSED) +{ + return PyString_FromString(snd_mixer_selem_id_get_name(pymelem->selem.id)); +} + +static PyObject * +pymelem_get_index(struct pymelem *pymelem, void *priv ATTRIBUTE_UNUSED) +{ + return PyInt_FromLong(snd_mixer_selem_id_get_index(pymelem->selem.id)); +} + +static int +pymelem_set_caps(struct pymelem *pymelem, PyObject *val, void *priv ATTRIBUTE_UNUSED) +{ + if (!PyInt_Check(val)) { + PyErr_SetString(PyExc_TypeError, "The last attribute value must be an integer"); + return -1; + } + pymelem->selem.caps = PyInt_AsLong(val); + return 0; +} + +static PyObject * +pymelem_ignore(struct pymelem *pymelem ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) +{ + Py_RETURN_NONE; +} + +static PyObject * +pymelem_ignore1(struct pymelem *pymelem ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) +{ + Py_RETURN_TRUE; +} + +static PyObject * +pymelem_error(struct pymelem *pymelem ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) +{ + return PyInt_FromLong(-EIO); +} + +static PyObject * +pymelem_attach(struct pymelem *pymelem, PyObject *args) +{ + PyObject *obj; + snd_hctl_elem_t *helem; + int err; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + helem = (snd_hctl_elem_t *)get_C_ptr(obj, "get_C_helem"); + if (helem == NULL) + return NULL; + err = snd_mixer_elem_attach(pymelem->melem, helem); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot attach hcontrol element to mixer element: %s", snd_strerror(err)); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +pymelem_detach(struct pymelem *pymelem, PyObject *args) +{ + PyObject *obj; + snd_hctl_elem_t *helem; + int err; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + helem = (snd_hctl_elem_t *)get_C_ptr(obj, "get_C_helem"); + if (helem == NULL) + return NULL; + err = snd_mixer_elem_detach(pymelem->melem, helem); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot detach hcontrol element to mixer element: %s", snd_strerror(err)); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +pymelem_event_info(struct pymelem *pymelem, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + return PyInt_FromLong(snd_mixer_elem_info(pymelem->melem)); +} + +static PyObject * +pymelem_event_value(struct pymelem *pymelem, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + return PyInt_FromLong(snd_mixer_elem_value(pymelem->melem)); +} + +static int +pymelem_init(struct pymelem *pymelem, PyObject *args, PyObject *kwds ATTRIBUTE_UNUSED) +{ + char *name; + long index, weight; + snd_mixer_selem_id_t *id; + int err; + + if (!PyArg_ParseTuple(args, "Osii", &pymelem->py_mixer, &name, &index, &weight)) + return -1; + memset(&pymelem->selem, 0, sizeof(pymelem->selem)); + if (snd_mixer_selem_id_malloc(&id)) + return -1; + snd_mixer_selem_id_set_name(id, name); + snd_mixer_selem_id_set_index(id, index); + pymelem->selem.id = id; + pymelem->selem.ops = &simple_python_ops; + err = snd_mixer_elem_new(&pymelem->melem, SND_MIXER_ELEM_SIMPLE, + weight, &pymelem->selem, selem_free); + if (err < 0) { + snd_mixer_selem_id_free(id); + return -1; + } + return 0; +} + +static void +pymelem_dealloc(struct pymelem *self) +{ + selem_free(self->melem); + self->ob_type->tp_free(self); +} + +static PyGetSetDef pymelem_getseters[] = { + {"CAP_GVOLUME", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_GVOLUME}, + {"CAP_GSWITCH", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_GSWITCH}, + {"CAP_PVOLUME", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PVOLUME}, + {"CAP_PVOLUME_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PVOLUME_JOIN}, + {"CAP_PSWITCH", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PSWITCH}, + {"CAP_PSWITCH_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PSWITCH_JOIN}, + {"CAP_CVOLUME", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CVOLUME}, + {"CAP_CVOLUME_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CVOLUME_JOIN}, + {"CAP_CSWITCH", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CSWITCH}, + {"CAP_CSWITCH_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CSWITCH_JOIN}, + {"CAP_CSWITCH_EXCL", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CSWITCH_EXCL}, + {"CAP_PENUM", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PENUM}, + {"CAP_CENUM", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CENUM}, + + {"caps", (getter)pymelem_get_caps, (setter)pymelem_set_caps, NULL, NULL}, + + {"name", (getter)pymelem_get_name, NULL, NULL, NULL}, + {"index", (getter)pymelem_get_index, NULL, NULL, NULL}, + + {NULL,NULL,NULL,NULL,NULL} +}; + +static PyMethodDef pymelem_methods[] = { + {"attach", (PyCFunction)pymelem_attach, METH_VARARGS, NULL}, + {"detach", (PyCFunction)pymelem_detach, METH_VARARGS, NULL}, + + /* "default" functions - no functionality */ + {"opsIsActive", (PyCFunction)pymelem_ignore1, METH_VARARGS, NULL}, + {"opsIsMono", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, + {"opsIsChannel", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, + {"opsIsEnumerated", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, + {"opsIsEnumCnt", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, + + {"opsGetDB", (PyCFunction)pymelem_error, METH_VARARGS, NULL}, + + {"eventInfo", (PyCFunction)pymelem_event_info, METH_VARARGS, NULL}, + {"eventValue", (PyCFunction)pymelem_event_value, METH_VARARGS, NULL}, + + {NULL,NULL,0,NULL} +}; + +static PyTypeObject pymelem_type = { + PyObject_HEAD_INIT(0) + tp_name: "smixer_python.InternalMElement", + tp_basicsize: sizeof(struct pymelem), + tp_dealloc: (destructor)pymelem_dealloc, + tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + tp_doc: NULL /* mixerinit__doc__ */, + tp_getset: pymelem_getseters, + tp_init: (initproc)pymelem_init, + tp_alloc: PyType_GenericAlloc, + tp_new: PyType_GenericNew, + tp_free: PyObject_Del, + tp_methods: pymelem_methods, +}; + +static PyObject * +pymixer_attach_hctl(struct pymixer *pymixer, PyObject *args) +{ + PyObject *obj; + snd_hctl_t *hctl; + void **hctls; + int err; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + hctl = (snd_hctl_t *)get_C_ptr(obj, "get_C_hctl"); + if (hctl == NULL) + return NULL; + err = snd_mixer_attach_hctl(pymixer->mixer, hctl); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot attach hctl: %s", snd_strerror(err)); + return NULL; + } + hctls = realloc(pymixer->hctl, sizeof(void *) * (pymixer->hctl_count+1) * 2); + if (hctls == NULL) { + PyErr_SetString(PyExc_RuntimeError, "No enough memory"); + return NULL; + } + pymixer->hctl = hctls; + pymixer->hctl[pymixer->hctl_count*2] = (void *)hctl; + pymixer->hctl[pymixer->hctl_count*2+1] = (void *)obj; + pymixer->hctl_count++; + Py_INCREF(obj); + Py_RETURN_NONE; +} + +static PyObject * +pymixer_register(struct pymixer *pymixer, PyObject *args) +{ + int err; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + err = snd_mixer_class_register(pymixer->class, pymixer->mixer); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot register mixer: %s", snd_strerror(err)); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +pymixer_melement_new(struct pymixer *pymixer, PyObject *args) +{ + PyObject *obj, *obj1, *obj2; + char *class, *name; + long index, weight; + + if (!PyArg_ParseTuple(args, "ssii", &class, &name, &index, &weight)) + return NULL; + obj = PyDict_GetItemString(pymixer->mdict, class); + if (obj) { + obj1 = PyTuple_New(4); + if (PyTuple_SET_ITEM(obj1, 0, (PyObject *)pymixer)) + Py_INCREF((PyObject *)pymixer); + PyTuple_SET_ITEM(obj1, 1, PyString_FromString(name)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(index)); + PyTuple_SET_ITEM(obj1, 3, PyInt_FromLong(weight)); + obj2 = PyObject_CallObject(obj, obj1); + Py_XDECREF(obj1); + if (obj2) { + struct pymelem *pymelem = (struct pymelem *)obj2; + void **melems = realloc(pymixer->melem, sizeof(void *) * (pymixer->melem_count + 1) * 2); + if (melems == NULL) { + Py_DECREF(obj2); + return NULL; + } + melems[pymixer->melem_count*2] = pymelem->melem; + melems[pymixer->melem_count*2+1] = obj2; + Py_INCREF(obj2); + pymixer->melem = melems; + pymixer->melem_count++; + } + } else { + PyErr_Format(PyExc_RuntimeError, "Cannot find class '%s'", class); + return NULL; + } + return obj2; +} + +static PyObject * +pymixer_melement_add(struct pymixer *pymixer, PyObject *args) +{ + PyObject *obj; + struct pymelem *pymelem; + int err; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + pymelem = (struct pymelem *)obj; + err = snd_mixer_elem_add(pymelem->melem, pymixer->class); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot add mixer element: %s", snd_strerror(err)); + return NULL; + } + Py_RETURN_NONE; +} + +static int +pymixer_init(struct pymixer *pymixer, PyObject *args, PyObject *kwds ATTRIBUTE_UNUSED) +{ + long class, mixer; + + if (!PyArg_ParseTuple(args, "iiO", &class, &mixer, &pymixer->mdict)) + return -1; + pymixer->class = (snd_mixer_class_t *)class; + pymixer->mixer = (snd_mixer_t *)mixer; + pymixer->hctl_count = 0; + pymixer->hctl = NULL; + pymixer->helem_count = 0; + pymixer->helem = NULL; + pymixer->melem_count = 0; + pymixer->melem = NULL; + return 0; +} + +static void +pymixer_free(struct pymixer *self) +{ + int idx; + + for (idx = 0; idx < self->hctl_count; idx++) { + snd_mixer_detach_hctl(self->mixer, self->hctl[idx*2]); + Py_DECREF((PyObject *)self->hctl[idx*2+1]); + } + if (self->hctl) + free(self->hctl); + self->hctl_count = 0; + self->hctl = NULL; + for (idx = 0; idx < self->helem_count; idx++) + Py_DECREF((PyObject *)self->helem[idx*2+1]); + if (self->helem) + free(self->helem); + self->helem_count = 0; + self->helem = NULL; + for (idx = 0; idx < self->melem_count; idx++) + Py_DECREF((PyObject *)self->melem[idx*2+1]); + if (self->melem) + free(self->melem); + self->melem_count = 0; + self->melem = NULL; +} + +static void +pymixer_dealloc(struct pymixer *self) +{ + pymixer_free(self); + self->ob_type->tp_free(self); +} + +static PyGetSetDef pymixer_getseters[] = { + {NULL,NULL,NULL,NULL,NULL} +}; + +static PyMethodDef pymixer_methods[] = { + {"attachHCtl", (PyCFunction)pymixer_attach_hctl, METH_VARARGS, NULL}, + {"register", (PyCFunction)pymixer_register, METH_VARARGS, NULL}, + {"newMElement", (PyCFunction)pymixer_melement_new, METH_VARARGS, NULL}, + {"addMElement", (PyCFunction)pymixer_melement_add, METH_VARARGS, NULL}, + {NULL,NULL,0,NULL} +}; + +static PyTypeObject pymixer_type = { + PyObject_HEAD_INIT(0) + tp_name: "smixer_python.InternalMixer", + tp_basicsize: sizeof(struct pymixer), + tp_dealloc: (destructor)pymixer_dealloc, + tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + tp_doc: NULL /* mixerinit__doc__ */, + tp_getset: pymixer_getseters, + tp_init: (initproc)pymixer_init, + tp_alloc: PyType_GenericAlloc, + tp_new: PyType_GenericNew, + tp_free: PyObject_Del, + tp_methods: pymixer_methods, +}; + +static PyMethodDef python_methods[] = { + {NULL, NULL, 0, NULL} +}; + +static PyObject *new_helem(struct python_priv *priv, snd_hctl_elem_t *helem) +{ + PyObject *obj, *py_hctl = NULL, *obj1, *obj2; + snd_hctl_t *hctl = snd_hctl_elem_get_hctl(helem); + struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; + int idx; + + for (idx = 0; idx < pymixer->hctl_count; idx++) { + if (pymixer->hctl[idx] == hctl) { + py_hctl = pymixer->hctl[idx*2+1]; + break; + } + } + if (py_hctl == NULL) + return NULL; + obj = PyDict_GetItemString(priv->py_mdict, "HElement"); + if (obj) { + obj1 = PyTuple_New(3); + if (PyTuple_SET_ITEM(obj1, 0, py_hctl)) + Py_INCREF(py_hctl); + PyTuple_SET_ITEM(obj1, 1, PyFloat_FromDouble(1)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong((long)helem)); + obj2 = PyObject_CallObject(obj, obj1); + if (obj2 == NULL) { + PyErr_Print(); + PyErr_Clear(); + } + Py_XDECREF(obj1); + } else { + SNDERR("Unable to create InternalMixer object"); + return NULL; + } + if (obj2) { + struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; + void **helems = realloc(pymixer->helem, sizeof(void *) * (pymixer->helem_count + 1) * 2); + if (helems == NULL) { + Py_DECREF(obj2); + return NULL; + } + helems[pymixer->helem_count*2] = helem; + helems[pymixer->helem_count*2+1] = obj2; + Py_INCREF(obj2); + pymixer->helem = helems; + pymixer->helem_count++; + } + return obj2; +} + +static PyObject *find_helem(struct python_priv *priv, snd_hctl_elem_t *helem) +{ + struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; + int idx; + + for (idx = 0; idx < pymixer->helem_count; idx++) { + if (pymixer->helem[idx*2] == helem) + return (PyObject *)pymixer->helem[idx*2+1]; + } + return NULL; +} + +static PyObject *find_melem(struct python_priv *priv, snd_mixer_elem_t *melem) +{ + struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; + int idx; + + for (idx = 0; idx < pymixer->melem_count; idx++) { + if (pymixer->melem[idx*2] == melem) + return (PyObject *)pymixer->melem[idx*2+1]; + } + return NULL; +} + +int alsa_mixer_simple_event(snd_mixer_class_t *class, unsigned int mask, + snd_hctl_elem_t *helem, snd_mixer_elem_t *melem) +{ + struct python_priv *priv = snd_mixer_sbasic_get_private(class); + PyThreadState *tstate, *origstate; + PyObject *t, *o, *r; + int res = -ENOMEM; + + tstate = PyThreadState_New(main_interpreter); + origstate = PyThreadState_Swap(tstate); + + t = PyTuple_New(3); + if (t) { + PyTuple_SET_ITEM(t, 0, (PyObject *)PyInt_FromLong(mask)); + o = find_helem(priv, helem); + if (mask & SND_CTL_EVENT_MASK_ADD) { + if (o == NULL) + o = new_helem(priv, helem); + } + if (o == NULL) + return 0; + if (PyTuple_SET_ITEM(t, 1, o)) + Py_INCREF(o); + o = melem ? find_melem(priv, melem) : Py_None; + if (PyTuple_SET_ITEM(t, 2, o)) + Py_INCREF(o); + r = PyObject_CallObject(priv->py_event_func, t); + Py_DECREF(t); + if (r) { + if (PyInt_Check(r)) { + res = PyInt_AsLong(r); + } else if (r == Py_None) { + res = 0; + } + Py_DECREF(r); + } else { + PyErr_Print(); + PyErr_Clear(); + res = -EIO; + } + } + + return res; +} + +static void alsa_mixer_simple_free(snd_mixer_class_t *class) +{ + struct python_priv *priv = snd_mixer_sbasic_get_private(class); + + if (priv->py_mixer) { + pymixer_free((struct pymixer *)priv->py_mixer); + Py_DECREF(priv->py_mixer); + } + if (priv->py_initialized) { + Py_XDECREF(priv->py_event_func); + Py_Finalize(); + } + free(priv); +} + +int alsa_mixer_simple_finit(snd_mixer_class_t *class, + snd_mixer_t *mixer, + const char *device) +{ + struct python_priv *priv; + FILE *fp; + const char *file; + PyObject *obj, *obj1, *obj2, *py_mod, *mdict; + + priv = calloc(1, sizeof(*priv)); + if (priv == NULL) + return -ENOMEM; + + snd_mixer_sbasic_set_private(class, priv); + snd_mixer_sbasic_set_private_free(class, alsa_mixer_simple_free); + + file = getenv("ALSA_MIXER_SIMPLE_MPYTHON"); + if (file == NULL) + file = SCRIPT; + + fp = fopen(file, "r"); + if (fp == NULL) { + SNDERR("Unable to find python module '%s'", file); + return -ENODEV; + } + + Py_Initialize(); + if (PyType_Ready(&pymelem_type) < 0) + return -EIO; + if (PyType_Ready(&pymixer_type) < 0) + return -EIO; + Py_InitModule("smixer_python", python_methods); + priv->py_initialized = 1; + main_interpreter = PyThreadState_Get()->interp; + obj = PyImport_GetModuleDict(); + py_mod = PyDict_GetItemString(obj, "__main__"); + if (py_mod) { + mdict = priv->py_mdict = PyModule_GetDict(py_mod); + obj = PyString_FromString(file); + if (obj) + PyDict_SetItemString(mdict, "__file__", obj); + Py_XDECREF(obj); + obj = PyString_FromString(device); + if (obj) + PyDict_SetItemString(mdict, "device", obj); + Py_XDECREF(obj); + Py_INCREF(&pymixer_type); + PyModule_AddObject(py_mod, "InternalMElement", (PyObject *)&pymelem_type); + PyModule_AddObject(py_mod, "InternalMixer", (PyObject *)&pymixer_type); + obj = PyDict_GetItemString(mdict, "InternalMixer"); + if (obj) { + obj1 = PyTuple_New(3); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong((long)class)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong((long)mixer)); + if (PyTuple_SET_ITEM(obj1, 2, mdict)) + Py_INCREF(mdict); + obj2 = PyObject_CallObject(obj, obj1); + Py_XDECREF(obj1); + PyDict_SetItemString(mdict, "mixer", obj2); + priv->py_mixer = obj2; + } else { + SNDERR("Unable to create InternalMixer object"); + return -EIO; + } + + + obj = PyRun_FileEx(fp, file, Py_file_input, mdict, mdict, 1); + if (obj == NULL) + PyErr_Print(); + Py_XDECREF(obj); + priv->py_event_func = PyDict_GetItemString(mdict, "event"); + if (priv->py_event_func == NULL) { + SNDERR("Unable to find python function 'event'"); + return -EIO; + } + } + return 0; +} diff -r 9005d28a1f9e modules/mixer/simple/python/common.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/mixer/simple/python/common.py Thu Jul 19 14:55:51 2007 +0200 @@ -0,0 +1,228 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# -*- Python -*- + +from pyalsa.alsahcontrol import HControl, Element as HElement, \ + Info as HInfo, Value as HValue, InterfaceId, \ + EventMask, EventMaskRemove + +MIXER = InterfaceId['Mixer'] +MIXERS = str(MIXER) + +class BaseElement(InternalMElement): + + def __init__(self, mixer, name, index, weight): + InternalMElement.__init__(self, mixer, name, index, weight) + self.channels = 0 + self.min = [0, 0] + self.max = [0, 0] + + def opsIsChannel(self, dir, chn): + return chn >= 0 and chn < self.channels + + def opsGetRange(self, dir): + return (0, self.min[dir], self.max[dir]) + + def opsSetRange(self, dir, min, max): + self.min[dir] = min + self.max[dir] = max + + def volumeToUser(self, info, dir, value): + min = info.min + max = info.max + if min == max: + return self.min[dir] + n = (value - min) * (self.max[dir] - self.min[dir]) + return self.min[dir] + (n + (max - min) / 2) / (max - min) + + def volumeFromUser(self, info, dir, value): + min = info.min + max = info.max + if self.max[dir] == self.min[dir]: + return min + n = (value - self.min[dir]) * (max - min) + return min + (n + (self.max[dir] - self.min[dir]) / 2) / (self.max[dir] - self.min[dir]) + +class StandardElement(BaseElement): + + def __init__(self, mixer, name, index, weight): + BaseElement.__init__(self, mixer, name, index, weight) + self.channels = 1 + self.volume = [None, None] + self.volumeinfo = [None, None] + self.volumetuple = [None, None] + self.switch = [None, None] + self.switchinfo = [None, None] + self.switchtuple = [None, None] + + def decideChannels(self): + max = 0 + for i in [0, 1]: + if self.volume[i]: + count = self.volumeinfo[i].count + if count > max: + max = count + if self.switch[i]: + count = self.switchinfo[i].count + if count > max: + max = count + self.channels = max + + def attachVolume(self, helem, dir): + self.volume[dir] = helem + self.volumeinfo[dir] = HInfo(helem) + self.min[dir] = self.volumeinfo[dir].min + self.max[dir] = self.volumeinfo[dir].max + self.volumetuple[dir] = HValue(helem).getTuple(self.volumeinfo[dir].type, self.volumeinfo[dir].count) + + def attachSwitch(self, helem, dir): + self.switch[dir] = helem + self.switchinfo[dir] = HInfo(helem) + self.switchtuple[dir] = HValue(helem).getTuple(self.switchinfo[dir].type, self.switchinfo[dir].count) + + def attach(self, helem): + BaseElement.attach(self, helem) + if helem.name.endswith('Playback Volume'): + self.attachVolume(helem, 0) + self.caps |= self.CAP_PVOLUME + elif helem.name.endswith('Capture Volume'): + self.attachVolume(helem, 1) + self.caps |= self.CAP_CVOLUME + elif helem.name.endswith('Playback Switch'): + self.attachSwitch(helem, 0) + self.caps |= self.CAP_PSWITCH + elif helem.name.endswith('Capture Switch'): + self.attachSwitch(helem, 1) + self.caps |= self.CAP_CSWITCH + self.decideChannels() + self.eventInfo() + + def opsGetVolume(self, dir, chn): + return (0, self.volumeToUser(self.volumeinfo[dir], dir, self.volumetuple[dir][chn])) + + def opsSetVolume(self, dir, chn, value): + val = self.volumeFromUser(self.volumeinfo[dir], dir, value) + if self.volumetuple[dir][chn] == val: + return + a = list(self.volumetuple[dir]) + a[chn] = val + self.volumetuple[dir] = tuple(a) + hv = HValue(self.volume) + hv.setTuple(self.volumeinfo[dir].type, self.volumetuple[dir]) + hv.write() + + def opsGetSwitch(self, dir, chn): + return (0, self.switchtuple[dir][chn]) + + def opsSetSwitch(self, dir, chn, value): + if self.switchtuple[dir][chn] and value: + return + if not self.switchtuple[dir][chn] and not value: + return + a = list(self.switchtuple[dir]) + a[chn] = int(value) + self.switchtuple[dir] = tuple(a) + hv = HValue(self.switch[dir]) + hv.setTuple(self.switchinfo[dir].type, self.switchtuple[dir]) + hv.write() + + def update(self, helem): + for i in [0, 1]: + if helem == self.volume[i]: + self.volumetuple[i] = HValue(helem).getTuple(self.volumeinfo[i].type, self.volumeinfo[i].count) + elif helem == self.switch[i]: + self.switchtuple[i] = HValue(helem).getTuple(self.switchinfo[i].type, self.switchinfo[i].count) + self.eventValue() + +class EnumElement(BaseElement): + + def __init__(self, mixer, name, index, weight): + BaseElement.__init__(self, mixer, name, index, weight) + self.mycaps = 0 + + def attach(self, helem): + BaseElement.attach(self, helem) + self.enum = helem + self.enuminfo = HInfo(helem) + self.enumtuple = HValue(helem).getTuple(self.enuminfo.type, self.enuminfo.count) + self.channels = self.enuminfo.count + self.texts = self.enuminfo.itemNames + self.caps |= self.mycaps + + def opsIsEnumerated(self, dir=-1): + if dir < 0: + return 1 + if dir == 0 and self.mycaps & self.CAP_PENUM: + return 1 + if dir == 1 and self.mycaps & self.CAP_CENUM: + return 1 + + def opsIsEnumCnt(self, dir): + return self.enuminfo.items + + def opsGetEnumItemName(self, item): + return (0, self.texts[item]) + + def opsGetEnumItem(self, chn): + if chn >= self.channels: + return -1 + return (0, self.enumtuple[chn]) + + def opsSetEnumItem(self, chn, value): + if chn >= self.channels: + return -1 + if self.enumtuple[chn] == value: + return + a = list(self.enumtuple) + a[chn] = int(value) + self.enumtuple = tuple(a) + hv = HValue(self.enum) + hv.setTuple(self.enuminfo.type, self.enumtuple) + hv.write() + + def update(self, helem): + self.enumtuple = HValue(helem).getTuple(self.enuminfo.type, self.enuminfo.count) + self.eventValue() + +class EnumElementPlayback(EnumElement): + + def __init__(self, mixer, name, index, weight): + EnumElement.__init__(self, mixer, name, index, weight) + self.mycaps = self.CAP_PENUM + +class EnumElementCapture(EnumElement): + + def __init__(self, mixer, name, index, weight): + EnumElement.__init__(self, mixer, name, index, weight) + self.mycaps = self.CAP_CENUM + +ELEMS = [] + +def element_add(helem): + key = helem.name+'//'+str(helem.index)+'//'+str(helem.interface) + if not CONTROLS.has_key(key): + return + val = CONTROLS[key] + felem = None + for elem in ELEMS: + if elem.name == val[0] and elem.index == val[1]: + felem = elem + break + if not felem: + felem = mixer.newMElement(val[3], val[0], val[1], val[2]) + mixer.addMElement(felem) + ELEMS.append(felem) + felem.attach(helem) + +def eventHandler(evmask, helem, melem): + if evmask == EventMaskRemove: + return + if evmask & EventMask['Add']: + element_add(helem) + if evmask & EventMask['Value']: + melem.update(helem) + +def init(): + hctl = HControl(device, load=False) + mixer.attachHCtl(hctl) + mixer.register() diff -r 9005d28a1f9e modules/mixer/simple/python/hda.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/mixer/simple/python/hda.py Thu Jul 19 14:55:51 2007 +0200 @@ -0,0 +1,42 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# -*- Python -*- + +alsacode('common') + +CONTROLS = { +'Headphone Playback Switch//0//'+MIXERS:["Headphone", 0, 1, "StandardElement"], +'IEC958 Playback Switch//0//'+MIXERS:["IEC958", 0, 2, "StandardElement"], +'Front Playback Volume//0//'+MIXERS:["Front", 0, 3, "StandardElement"], +'Front Playback Switch//0//'+MIXERS:["Front", 0, 3, "StandardElement"], +'Surround Playback Volume//0//'+MIXERS:["Surround", 0, 4, "StandardElement"], +'Surround Playback Switch//0//'+MIXERS:["Surround", 0, 4, "StandardElement"], +'Center Playback Volume//0//'+MIXERS:["Center", 0, 5, "StandardElement"], +'Center Playback Switch//0//'+MIXERS:["Center", 0, 5, "StandardElement"], +'LFE Playback Volume//0//'+MIXERS:["LFE", 0, 6, "StandardElement"], +'LFE Playback Switch//0//'+MIXERS:["LFE", 0, 6, "StandardElement"], +'Line Playback Volume//0//'+MIXERS:["Line", 0, 7, "StandardElement"], +'Line Playback Switch//0//'+MIXERS:["Line", 0, 7, "StandardElement"], +'CD Playback Volume//0//'+MIXERS:["CD", 0, 8, "StandardElement"], +'CD Playback Switch//0//'+MIXERS:["CD", 0, 8, "StandardElement"], +'Mic Playback Volume//0//'+MIXERS:["Mic", 0, 9, "StandardElement"], +'Mic Playback Switch//0//'+MIXERS:["Mic", 0, 9, "StandardElement"], +'PC Speaker Playback Volume//0//'+MIXERS:["PC Speaker", 0, 10, "StandardElement"], +'PC Speaker Playback Switch//0//'+MIXERS:["PC Speaker", 0, 10, "StandardElement"], +'Front Mic Playback Volume//0//'+MIXERS:["Front Mic", 0, 11, "StandardElement"], +'Front Mic Playback Switch//0//'+MIXERS:["Front Mic", 0, 11, "StandardElement"], +'Capture Switch//0//'+MIXERS:["Capture", 0, 12, "StandardElement"], +'Capture Volume//0//'+MIXERS:["Capture", 0, 12, "StandardElement"], +'Capture Switch//1//'+MIXERS:["Capture", 1, 13, "StandardElement"], +'Capture Volume//1//'+MIXERS:["Capture", 1, 13, "StandardElement"], +'Capture Switch//2//'+MIXERS:["Capture", 2, 14, "StandardElement"], +'Capture Volume//2//'+MIXERS:["Capture", 2, 14, "StandardElement"], +'Input Source//0//'+MIXERS:["Input Source", 0, 15, "EnumElementCapture"], +'Input Source//1//'+MIXERS:["Input Source", 1, 16, "EnumElementCapture"], +'Input Source//2//'+MIXERS:["Input Source", 2, 17, "EnumElementCapture"], +} + +def event(evmask, helem, melem): + return eventHandler(evmask, helem, melem) + +init() diff -r 9005d28a1f9e modules/mixer/simple/python/main.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/modules/mixer/simple/python/main.py Thu Jul 19 14:55:51 2007 +0200 @@ -0,0 +1,24 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# -*- Python -*- + +from os.path import dirname +from pyalsa.alsacontrol import Control +from sys import path +path.insert(0, dirname(__file__)) + +def alsacode(module): + execfile(dirname(__file__)+'/'+module+'.py', globals()) + +ctl = Control(device) +info = ctl.cardInfo() +#mixername = info['mixername'] +components = info['components'] +del ctl + +if components.find('HDA:') >= 0: + module = 'hda' +else: + raise ValueError, "Mixer for this hardware is not implemented in python" + +alsacode(module) diff -r 9005d28a1f9e src/conf/cards/Makefile.am --- a/src/conf/cards/Makefile.am Mon Jun 11 10:52:17 2007 +0200 +++ b/src/conf/cards/Makefile.am Thu Jul 19 14:55:51 2007 +0200 @@ -35,6 +35,7 @@ cfg_files = aliases.conf \ PC-Speaker.conf \ PMac.conf \ PMacToonie.conf \ + PS3.conf \ RME9636.conf \ RME9652.conf \ SI7018.conf \ diff -r 9005d28a1f9e src/conf/cards/PMac.conf --- a/src/conf/cards/PMac.conf Mon Jun 11 10:52:17 2007 +0200 +++ b/src/conf/cards/PMac.conf Thu Jul 19 14:55:51 2007 +0200 @@ -24,14 +24,14 @@ PMac.pcm.default { type plug slave.pcm { @func concat - strings [ "dmix:CARD=" $CARD ",FORMAT=S16" ] + strings [ "dmix:CARD=" $CARD ",FORMAT=S16_BE" ] } } capture.pcm { type plug slave.pcm { @func concat - strings [ "dsnoop:CARD=" $CARD ",FORMAT=S16" ] + strings [ "dsnoop:CARD=" $CARD ",FORMAT=S16_BE" ] } } } diff -r 9005d28a1f9e src/conf/cards/PMacToonie.conf --- a/src/conf/cards/PMacToonie.conf Mon Jun 11 10:52:17 2007 +0200 +++ b/src/conf/cards/PMacToonie.conf Thu Jul 19 14:55:51 2007 +0200 @@ -33,7 +33,7 @@ PMacToonie.pcm.default { type softvol slave.pcm { @func concat - strings [ "dmix:CARD=" $CARD ",FORMAT=S16" ] + strings [ "dmix:CARD=" $CARD ",FORMAT=S16_BE" ] } control { name "PCM Playback Volume" @@ -45,7 +45,7 @@ PMacToonie.pcm.default { type plug slave.pcm { @func concat - strings [ "dsnoop:CARD=" $CARD ",FORMAT=S16" ] + strings [ "dsnoop:CARD=" $CARD ",FORMAT=S16_BE" ] } } } diff -r 9005d28a1f9e src/conf/cards/PS3.conf --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/conf/cards/PS3.conf Thu Jul 19 14:55:51 2007 +0200 @@ -0,0 +1,46 @@ +# +# Configuration for PS3 +# + + + +PS3.pcm.front.0 { + @args [ CARD ] + @args.CARD { + type string + } + type softvol + slave.pcm { + type hw + card $CARD + device 0 + } + control { + name "PCM Playback Volume" + card $CARD + } +} + +# default with dmix+softvol +PS3.pcm.default { + @args [ CARD ] + @args.CARD { + type string + } + type asym + playback.pcm { + type plug + slave.pcm { + type softvol + slave.pcm { + @func concat + #strings [ "dmix:CARD=" $CARD ] + strings [ "dmix:CARD=" $CARD ",FORMAT=S16" ] + } + control { + name "PCM Playback Volume" + card $CARD + } + } + } +} diff -r 9005d28a1f9e src/conf/smixer.conf --- a/src/conf/smixer.conf Mon Jun 11 10:52:17 2007 +0200 +++ b/src/conf/smixer.conf Thu Jul 19 14:55:51 2007 +0200 @@ -1,3 +1,4 @@ usb { +_full smixer-python.so usb { searchl "USB" lib smixer-usb.so diff -r 9005d28a1f9e src/control/namehint.c --- a/src/control/namehint.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/control/namehint.c Thu Jul 19 14:55:51 2007 +0200 @@ -90,6 +90,7 @@ static int get_dev_name1(struct hint_lis if (list->device < 0) return 0; switch (list->iface) { +#ifdef BUILD_HWDEP case SND_CTL_ELEM_IFACE_HWDEP: { snd_hwdep_info_t *info; @@ -100,6 +101,8 @@ static int get_dev_name1(struct hint_lis *res = strdup(snd_hwdep_info_get_name(info)); return 0; } +#endif +#ifdef BUILD_PCM case SND_CTL_ELEM_IFACE_PCM: { snd_pcm_info_t *info; @@ -118,6 +121,8 @@ static int get_dev_name1(struct hint_lis *res = strdup(snd_pcm_info_get_name(info)); return 0; } +#endif +#ifdef BUILD_RAWMIDI case SND_CTL_ELEM_IFACE_RAWMIDI: { snd_rawmidi_info_t *info; @@ -129,6 +134,7 @@ static int get_dev_name1(struct hint_lis *res = strdup(snd_rawmidi_info_get_name(info)); return 0; } +#endif default: return 0; } diff -r 9005d28a1f9e src/mixer/simple.c --- a/src/mixer/simple.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/mixer/simple.c Thu Jul 19 14:55:51 2007 +0200 @@ -38,7 +38,6 @@ #include #include "mixer_local.h" #include "mixer_simple.h" -#include "alisp.h" /** * \brief Register mixer simple element class diff -r 9005d28a1f9e src/mixer/simple_abst.c --- a/src/mixer/simple_abst.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/mixer/simple_abst.c Thu Jul 19 14:55:51 2007 +0200 @@ -55,6 +55,9 @@ typedef struct _class_priv { } class_priv_t; typedef int (*snd_mixer_sbasic_init_t)(snd_mixer_class_t *class); +typedef int (*snd_mixer_sfbasic_init_t)(snd_mixer_class_t *class, + snd_mixer_t *mixer, + const char *device); #endif /* !DOC_HIDDEN */ @@ -62,10 +65,10 @@ static int try_open(snd_mixer_class_t *c { class_priv_t *priv = snd_mixer_class_get_private(class); snd_mixer_event_t event_func; - snd_mixer_sbasic_init_t init_func; + snd_mixer_sbasic_init_t init_func = NULL; char *xlib, *path; void *h; - int err; + int err = 0; path = getenv("ALSA_MIXER_SIMPLE_MODULES"); if (!path) @@ -82,28 +85,71 @@ static int try_open(snd_mixer_class_t *c free(xlib); return -ENXIO; } + priv->dlhandle = h; event_func = snd_dlsym(h, "alsa_mixer_simple_event", NULL); if (event_func == NULL) { SNDERR("Symbol 'alsa_mixer_simple_event' was not found in '%s'", xlib); - snd_dlclose(h); + err = -ENXIO; + } + if (err == 0) { + init_func = snd_dlsym(h, "alsa_mixer_simple_init", NULL); + if (init_func == NULL) { + SNDERR("Symbol 'alsa_mixer_simple_init' was not found in '%s'", xlib); + err = -ENXIO; + } + } + free(xlib); + err = err == 0 ? init_func(class) : err; + if (err < 0) + return err; + snd_mixer_class_set_event(class, event_func); + return 1; +} + +static int try_open_full(snd_mixer_class_t *class, snd_mixer_t *mixer, + const char *lib, const char *device) +{ + class_priv_t *priv = snd_mixer_class_get_private(class); + snd_mixer_event_t event_func; + snd_mixer_sfbasic_init_t init_func = NULL; + char *xlib, *path; + void *h; + int err = 0; + + path = getenv("ALSA_MIXER_SIMPLE_MODULES"); + if (!path) + path = SO_PATH; + xlib = malloc(strlen(lib) + strlen(path) + 1 + 1); + if (xlib == NULL) + return -ENOMEM; + strcpy(xlib, path); + strcat(xlib, "/"); + strcat(xlib, lib); + /* note python modules requires RTLD_GLOBAL */ + h = snd_dlopen(xlib, RTLD_NOW|RTLD_GLOBAL); + if (h == NULL) { + SNDERR("Unable to open library '%s'", xlib); free(xlib); return -ENXIO; } - init_func = snd_dlsym(h, "alsa_mixer_simple_init", NULL); - if (init_func == NULL) { - SNDERR("Symbol 'alsa_mixer_simple_init' was not found in '%s'", xlib); - snd_dlclose(h); - free(xlib); - return -ENXIO; + priv->dlhandle = h; + event_func = snd_dlsym(h, "alsa_mixer_simple_event", NULL); + if (event_func == NULL) { + SNDERR("Symbol 'alsa_mixer_simple_event' was not found in '%s'", xlib); + err = -ENXIO; + } + if (err == 0) { + init_func = snd_dlsym(h, "alsa_mixer_simple_finit", NULL); + if (init_func == NULL) { + SNDERR("Symbol 'alsa_mixer_simple_finit' was not found in '%s'", xlib); + err = -ENXIO; + } } free(xlib); - err = init_func(class); - if (err < 0) { - snd_dlclose(h); + err = err == 0 ? init_func(class, mixer, device) : err; + if (err < 0) return err; - } snd_mixer_class_set_event(class, event_func); - priv->dlhandle = h; return 1; } @@ -126,6 +172,31 @@ static int match(snd_mixer_class_t *clas return 0; } +static int find_full(snd_mixer_class_t *class, snd_mixer_t *mixer, + snd_config_t *top, const char *device) +{ + snd_config_iterator_t i, next; + char *lib; + const char *id; + int err; + + snd_config_for_each(i, next, top) { + snd_config_t *n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + if (strcmp(id, "_full")) + continue; + err = snd_config_get_string(n, (const char **)&lib); + if (err < 0) + return err; + err = try_open_full(class, mixer, lib, device); + if (err < 0) + return err; + return 0; + } + return -ENOENT; +} + static int find_module(snd_mixer_class_t *class, snd_config_t *top) { snd_config_iterator_t i, next; @@ -137,6 +208,8 @@ static int find_module(snd_mixer_class_t snd_config_for_each(i, next, top) { snd_config_t *n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) + continue; + if (*id == '_') continue; searchl = NULL; lib = NULL; @@ -223,20 +296,6 @@ int snd_mixer_simple_basic_register(snd_ snd_mixer_class_set_compare(class, snd_mixer_selem_compare); snd_mixer_class_set_private(class, priv); snd_mixer_class_set_private_free(class, private_free); - err = snd_ctl_open(&priv->ctl, priv->device, 0); - if (err < 0) { - SNDERR("unable to open control device '%s': %s", priv->device, snd_strerror(err)); - goto __error; - } - err = snd_hctl_open_ctl(&priv->hctl, priv->ctl); - if (err < 0) - goto __error; - err = snd_ctl_card_info_malloc(&priv->info); - if (err < 0) - goto __error; - err = snd_ctl_card_info(priv->ctl, priv->info); - if (err < 0) - goto __error; file = getenv("ALSA_MIXER_SIMPLE"); if (!file) file = ALSA_CONFIG_DIR "/smixer.conf"; @@ -253,16 +312,35 @@ int snd_mixer_simple_basic_register(snd_ SNDERR("%s may be old or corrupted: consider to remove or fix it", file); goto __error; } + err = find_full(class, mixer, top, priv->device); + if (err >= 0) + goto __full; + } + if (err >= 0) { + err = snd_ctl_open(&priv->ctl, priv->device, 0); + if (err < 0) { + SNDERR("unable to open control device '%s': %s", priv->device, snd_strerror(err)); + goto __error; + } + err = snd_hctl_open_ctl(&priv->hctl, priv->ctl); + if (err < 0) + goto __error; + err = snd_ctl_card_info_malloc(&priv->info); + if (err < 0) + goto __error; + err = snd_ctl_card_info(priv->ctl, priv->info); + if (err < 0) + goto __error; + } + if (err >= 0) err = find_module(class, top); - snd_config_delete(top); - top = NULL; - } if (err >= 0) err = snd_mixer_attach_hctl(mixer, priv->hctl); if (err >= 0) { priv->attach_flag = 1; err = snd_mixer_class_register(class, mixer); } + __full: if (err < 0) { __error: if (top) diff -r 9005d28a1f9e src/pcm/Makefile.am --- a/src/pcm/Makefile.am Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/Makefile.am Thu Jul 19 14:55:51 2007 +0200 @@ -99,6 +99,9 @@ if BUILD_PCM_PLUGIN_IOPLUG if BUILD_PCM_PLUGIN_IOPLUG libpcm_la_SOURCES += pcm_ioplug.c endif +if BUILD_PCM_PLUGIN_MMAP_EMUL +libpcm_la_SOURCES += pcm_mmap_emul.c +endif EXTRA_DIST = pcm_dmix_i386.c pcm_dmix_x86_64.c pcm_dmix_generic.c diff -r 9005d28a1f9e src/pcm/pcm.c --- a/src/pcm/pcm.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm.c Thu Jul 19 14:55:51 2007 +0200 @@ -1983,7 +1983,8 @@ 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", NULL + "shm", "dsnoop", "dshare", "asym", "iec958", "softvol", "mmap_emul", + NULL }; static int snd_pcm_open_conf(snd_pcm_t **pcmp, const char *name, diff -r 9005d28a1f9e src/pcm/pcm_dmix.c --- a/src/pcm/pcm_dmix.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm_dmix.c Thu Jul 19 14:55:51 2007 +0200 @@ -139,12 +139,14 @@ static void dmix_server_free(snd_pcm_dir * FIXME: optimize it for different architectures */ +#include "pcm_dmix_generic.c" #if defined(__i386__) #include "pcm_dmix_i386.c" #elif defined(__x86_64__) #include "pcm_dmix_x86_64.c" #else -#include "pcm_dmix_generic.c" +#define mix_select_callbacks(x) generic_mix_select_callbacks(x) +#define dmix_supported_format generic_dmix_supported_format #endif static void mix_areas(snd_pcm_direct_t *dmix, diff -r 9005d28a1f9e src/pcm/pcm_dmix_generic.c --- a/src/pcm/pcm_dmix_generic.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm_dmix_generic.c Thu Jul 19 14:55:51 2007 +0200 @@ -119,14 +119,14 @@ static void mix_select_callbacks(snd_pcm #else /* non-concurrent version, supporting both endians */ -static unsigned long long dmix_supported_format = - (1ULL << SND_PCM_FORMAT_S16_LE) | (1ULL << SND_PCM_FORMAT_S32_LE) | - (1ULL << SND_PCM_FORMAT_S16_BE) | (1ULL << SND_PCM_FORMAT_S32_BE) | - (1ULL << SND_PCM_FORMAT_S24_3LE); +#define generic_dmix_supported_format \ + ((1ULL << SND_PCM_FORMAT_S16_LE) | (1ULL << SND_PCM_FORMAT_S32_LE) |\ + (1ULL << SND_PCM_FORMAT_S16_BE) | (1ULL << SND_PCM_FORMAT_S32_BE) |\ + (1ULL << SND_PCM_FORMAT_S24_3LE)) #include -static void mix_areas1_native(unsigned int size, +static void generic_mix_areas1_native(unsigned int size, volatile signed short *dst, signed short *src, volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) @@ -155,7 +155,7 @@ static void mix_areas1_native(unsigned i } } -static void mix_areas2_native(unsigned int size, +static void generic_mix_areas2_native(unsigned int size, volatile signed int *dst, signed int *src, volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) @@ -186,7 +186,7 @@ static void mix_areas2_native(unsigned i } } -static void mix_areas1_swap(unsigned int size, +static void generic_mix_areas1_swap(unsigned int size, volatile signed short *dst, signed short *src, volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) @@ -215,7 +215,7 @@ static void mix_areas1_swap(unsigned int } } -static void mix_areas2_swap(unsigned int size, +static void generic_mix_areas2_swap(unsigned int size, volatile signed int *dst, signed int *src, volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) @@ -247,7 +247,7 @@ static void mix_areas2_swap(unsigned int } /* always little endian */ -static void mix_areas3(unsigned int size, +static void generic_mix_areas3(unsigned int size, volatile unsigned char *dst, unsigned char *src, volatile signed int *sum, size_t dst_step, size_t src_step, size_t sum_step) @@ -278,16 +278,16 @@ static void mix_areas3(unsigned int size } -static void mix_select_callbacks(snd_pcm_direct_t *dmix) +static void generic_mix_select_callbacks(snd_pcm_direct_t *dmix) { if (snd_pcm_format_cpu_endian(dmix->shmptr->s.format)) { - dmix->u.dmix.mix_areas1 = mix_areas1_native; - dmix->u.dmix.mix_areas2 = mix_areas2_native; + dmix->u.dmix.mix_areas1 = generic_mix_areas1_native; + dmix->u.dmix.mix_areas2 = generic_mix_areas2_native; } else { - dmix->u.dmix.mix_areas1 = mix_areas1_swap; - dmix->u.dmix.mix_areas2 = mix_areas2_swap; - } - dmix->u.dmix.mix_areas3 = mix_areas3; -} - -#endif + dmix->u.dmix.mix_areas1 = generic_mix_areas1_swap; + dmix->u.dmix.mix_areas2 = generic_mix_areas2_swap; + } + dmix->u.dmix.mix_areas3 = generic_mix_areas3; +} + +#endif diff -r 9005d28a1f9e src/pcm/pcm_dmix_i386.c --- a/src/pcm/pcm_dmix_i386.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm_dmix_i386.c Thu Jul 19 14:55:51 2007 +0200 @@ -30,33 +30,45 @@ #undef MIX_AREAS3_CMOV #undef LOCK_PREFIX -static unsigned long long dmix_supported_format = - (1ULL << SND_PCM_FORMAT_S16_LE) | - (1ULL << SND_PCM_FORMAT_S32_LE) | - (1ULL << SND_PCM_FORMAT_S24_3LE); +#define i386_dmix_supported_format \ + ((1ULL << SND_PCM_FORMAT_S16_LE) |\ + (1ULL << SND_PCM_FORMAT_S32_LE) |\ + (1ULL << SND_PCM_FORMAT_S24_3LE)) + +#define dmix_supported_format \ + (i386_dmix_supported_format | generic_dmix_supported_format) static void mix_select_callbacks(snd_pcm_direct_t *dmix) { - FILE *in; - char line[255]; - int smp = 0, mmx = 0, cmov = 0; + static int smp = 0, mmx = 0, cmov = 0; + + if (!((1ULL<< dmix->shmptr->s.format) & i386_dmix_supported_format)) { + generic_mix_select_callbacks(dmix); + return; + } + + if (!smp) { + FILE *in; + char line[255]; - /* try to determine the capabilities of the CPU */ - in = fopen("/proc/cpuinfo", "r"); - if (in) { - while (!feof(in)) { - fgets(line, sizeof(line), in); - if (!strncmp(line, "processor", 9)) - smp++; - else if (!strncmp(line, "flags", 5)) { - if (strstr(line, " mmx")) - mmx = 1; - if (strstr(line, " cmov")) - cmov = 1; + /* try to determine the capabilities of the CPU */ + in = fopen("/proc/cpuinfo", "r"); + if (in) { + while (!feof(in)) { + fgets(line, sizeof(line), in); + if (!strncmp(line, "processor", 9)) + smp++; + else if (!strncmp(line, "flags", 5)) { + if (strstr(line, " mmx")) + mmx = 1; + if (strstr(line, " cmov")) + cmov = 1; + } } + fclose(in); } - fclose(in); } + if (mmx) { dmix->u.dmix.mix_areas1 = smp > 1 ? mix_areas1_smp_mmx : mix_areas1_mmx; } else { diff -r 9005d28a1f9e src/pcm/pcm_dmix_x86_64.c --- a/src/pcm/pcm_dmix_x86_64.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm_dmix_x86_64.c Thu Jul 19 14:55:51 2007 +0200 @@ -22,26 +22,37 @@ #undef MIX_AREAS3 #undef LOCK_PREFIX -static unsigned long long dmix_supported_format = - (1ULL << SND_PCM_FORMAT_S16_LE) | - (1ULL << SND_PCM_FORMAT_S32_LE) | - (1ULL << SND_PCM_FORMAT_S24_3LE); +#define x86_64_dmix_supported_format \ + ((1ULL << SND_PCM_FORMAT_S16_LE) |\ + (1ULL << SND_PCM_FORMAT_S32_LE) |\ + (1ULL << SND_PCM_FORMAT_S24_3LE)) + +#define dmix_supported_format \ + (x86_64_dmix_supported_format | generic_dmix_supported_format) static void mix_select_callbacks(snd_pcm_direct_t *dmix) { - FILE *in; - char line[255]; - int smp = 0; + static int smp = 0; - /* try to determine, if we have SMP */ - in = fopen("/proc/cpuinfo", "r"); - if (in) { - while (!feof(in)) { - fgets(line, sizeof(line), in); - if (!strncmp(line, "processor", 9)) - smp++; + if (!((1ULL<< dmix->shmptr->s.format) & x86_64_dmix_supported_format)) { + generic_mix_select_callbacks(dmix); + return; + } + + if (!smp) { + FILE *in; + char line[255]; + + /* try to determine, if we have SMP */ + in = fopen("/proc/cpuinfo", "r"); + if (in) { + while (!feof(in)) { + fgets(line, sizeof(line), in); + if (!strncmp(line, "processor", 9)) + smp++; + } + fclose(in); } - fclose(in); } // printf("SMP: %i\n", smp); dmix->u.dmix.mix_areas1 = smp > 1 ? mix_areas1_smp : mix_areas1; diff -r 9005d28a1f9e src/pcm/pcm_hw.c --- a/src/pcm/pcm_hw.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm_hw.c Thu Jul 19 14:55:51 2007 +0200 @@ -88,14 +88,11 @@ typedef struct { int version; int fd; int card, device, subdevice; - int mmap_emulation; int sync_ptr_ioctl; volatile struct sndrv_pcm_mmap_status * mmap_status; struct sndrv_pcm_mmap_control *mmap_control; struct sndrv_pcm_sync_ptr *sync_ptr; - int shadow_appl_ptr: 1, - avail_update_flag: 1, - mmap_shm: 1; + snd_pcm_uframes_t hw_ptr; snd_pcm_uframes_t appl_ptr; /* restricted parameters */ snd_pcm_format_t format; @@ -108,9 +105,6 @@ typedef struct { #define SNDRV_PCM_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 5) /* update appl_ptr with driver */ -#define UPDATE_SHADOW_PTR(hw) \ - do { if (hw->shadow_appl_ptr && !hw->avail_update_flag) \ - hw->appl_ptr = hw->mmap_control->appl_ptr; } while (0) #define FAST_PCM_STATE(hw) \ ((enum sndrv_pcm_state) (hw)->mmap_status->state) #define FAST_PCM_TSTAMP(hw) \ @@ -246,73 +240,10 @@ static int snd_pcm_hw_hw_refine(snd_pcm_ return err; } - if (hw->mmap_emulation) { - int err = 0; - snd_pcm_access_mask_t oldmask = *snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); - snd_pcm_access_mask_t mask; - const snd_mask_t *pmask; - - snd_mask_empty(&mask); - if (hw_refine_call(hw, params) < 0) - err = -errno; - if (err < 0) { - snd_pcm_hw_params_t new = *params; - - if (!(params->rmask & (1<cmask |= 1<cmask |= 1<cmask |= 1<cmask |= 1<private_data; int err; - if (hw->mmap_emulation) { - snd_pcm_hw_params_t old = *params; - if (hw_params_call(hw, params) < 0) { - snd_pcm_access_t access; - snd_pcm_access_mask_t oldmask; - const snd_mask_t *pmask; - - *params = old; - pmask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); - oldmask = *(snd_pcm_access_mask_t *)pmask; - if (INTERNAL(snd_pcm_hw_params_get_access)(params, &access) < 0) - goto _err; - switch (access) { - case SND_PCM_ACCESS_MMAP_INTERLEAVED: - snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED); - snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_RW_INTERLEAVED); - break; - case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: - snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); - snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_RW_NONINTERLEAVED); - break; - default: - goto _err; - } - if (hw_params_call(hw, params) < 0) - goto _err; - hw->mmap_shm = 1; - *(snd_pcm_access_mask_t *)pmask = oldmask; - } - } else { - if (hw_params_call(hw, params) < 0) { - _err: - err = -errno; - SYSMSG("SNDRV_PCM_IOCTL_HW_PARAMS failed"); - return err; - } + if (hw_params_call(hw, params) < 0) { + err = -errno; + SYSMSG("SNDRV_PCM_IOCTL_HW_PARAMS failed"); + return err; } err = sync_ptr(hw, 0); if (err < 0) return err; if (pcm->stream == SND_PCM_STREAM_CAPTURE) { - if (hw->mmap_shm) { - hw->shadow_appl_ptr = 1; - hw->appl_ptr = 0; - snd_pcm_set_appl_ptr(pcm, &hw->appl_ptr, -1, 0); - } else { - hw->shadow_appl_ptr = 0; - snd_pcm_set_appl_ptr(pcm, &hw->mmap_control->appl_ptr, hw->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL); - } + snd_pcm_set_appl_ptr(pcm, &hw->mmap_control->appl_ptr, hw->fd, + SNDRV_PCM_MMAP_OFFSET_CONTROL); } return 0; } @@ -431,16 +324,13 @@ static int snd_pcm_hw_channel_info(snd_p return err; } info->channel = i.channel; - if (!hw->mmap_shm) { - info->addr = 0; - info->first = i.first; - info->step = i.step; - info->type = SND_PCM_AREA_MMAP; - info->u.mmap.fd = fd; - info->u.mmap.offset = i.offset; - return 0; - } - return snd_pcm_channel_info_shm(pcm, info, -1); + info->addr = 0; + info->first = i.first; + info->step = i.step; + info->type = SND_PCM_AREA_MMAP; + info->u.mmap.fd = fd; + info->u.mmap.offset = i.offset; + return 0; } static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status) @@ -781,7 +671,6 @@ static snd_pcm_sframes_t snd_pcm_hw_read #endif if (err < 0) return snd_pcm_check_error(pcm, err); - UPDATE_SHADOW_PTR(hw); return xferi.result; } @@ -801,7 +690,6 @@ static snd_pcm_sframes_t snd_pcm_hw_read #endif if (err < 0) return snd_pcm_check_error(pcm, err); - UPDATE_SHADOW_PTR(hw); return xfern.result; } @@ -925,22 +813,6 @@ static snd_pcm_sframes_t snd_pcm_hw_mmap { snd_pcm_hw_t *hw = pcm->private_data; - if (hw->mmap_shm) { - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - snd_pcm_sframes_t result = 0, res; - - do { - res = snd_pcm_write_mmap(pcm, size); - if (res < 0) - return result > 0 ? result : res; - size -= res; - result += res; - } while (size > 0); - return result; - } else { - assert(hw->shadow_appl_ptr); - } - } snd_pcm_mmap_appl_forward(pcm, size); sync_ptr(hw, 0); #ifdef DEBUG_MMAP @@ -955,23 +827,7 @@ static snd_pcm_sframes_t snd_pcm_hw_avai snd_pcm_uframes_t avail; sync_ptr(hw, 0); - if (pcm->stream == SND_PCM_STREAM_PLAYBACK) { - avail = snd_pcm_mmap_playback_avail(pcm); - } else { - avail = snd_pcm_mmap_capture_avail(pcm); - if (avail > 0 && hw->mmap_shm) { - snd_pcm_sframes_t err; - snd_pcm_hw_t *hw = pcm->private_data; - hw->avail_update_flag = 1; - err = snd_pcm_read_mmap(pcm, avail); - hw->avail_update_flag = 0; - if (err < 0) - return err; - if ((snd_pcm_uframes_t)err != avail) - SNDMSG("short read %ld for avail %ld", err, avail); - return err; - } - } + avail = snd_pcm_mmap_avail(pcm); switch (FAST_PCM_STATE(hw)) { case SNDRV_PCM_STATE_RUNNING: if (avail >= pcm->stop_threshold) { @@ -1058,7 +914,7 @@ static snd_pcm_fast_ops_t snd_pcm_hw_fas * \param pcmp Returns created PCM handle * \param name Name of PCM * \param fd File descriptor - * \param mmap_emulation Boolean flag for mmap emulation mode + * \param mmap_emulation Obsoleted parameter * \param sync_ptr_ioctl Boolean flag for sync_ptr ioctl * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense @@ -1066,7 +922,8 @@ static snd_pcm_fast_ops_t snd_pcm_hw_fas * changed in future. */ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, const char *name, - int fd, int mmap_emulation, int sync_ptr_ioctl) + int fd, int mmap_emulation ATTRIBUTE_UNUSED, + int sync_ptr_ioctl) { int ver; long fmode; @@ -1139,7 +996,6 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, hw->device = info.device; hw->subdevice = info.subdevice; hw->fd = fd; - hw->mmap_emulation = mmap_emulation; hw->sync_ptr_ioctl = sync_ptr_ioctl; /* no restriction */ hw->format = SND_PCM_FORMAT_UNKNOWN; @@ -1183,7 +1039,7 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp, * \param subdevice Number of subdevice * \param stream PCM Stream * \param mode PCM Mode - * \param mmap_emulation Emulate mmap access using standard r/w access + * \param mmap_emulation Obsoleted parameter * \param sync_ptr_ioctl Use SYNC_PTR ioctl rather than mmap for control structures * \retval zero on success otherwise a negative error code * \warning Using of this function might be dangerous in the sense @@ -1193,7 +1049,8 @@ int snd_pcm_hw_open(snd_pcm_t **pcmp, co int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name, int card, int device, int subdevice, snd_pcm_stream_t stream, int mode, - int mmap_emulation, int sync_ptr_ioctl) + int mmap_emulation ATTRIBUTE_UNUSED, + int sync_ptr_ioctl) { char filename[sizeof(SNDRV_FILE_PCM_STREAM_PLAYBACK) + 20]; const char *filefmt; @@ -1255,7 +1112,7 @@ int snd_pcm_hw_open(snd_pcm_t **pcmp, co } } snd_ctl_close(ctl); - return snd_pcm_hw_open_fd(pcmp, name, fd, mmap_emulation, sync_ptr_ioctl); + return snd_pcm_hw_open_fd(pcmp, name, fd, 0, sync_ptr_ioctl); _err: snd_ctl_close(ctl); return ret; @@ -1281,7 +1138,6 @@ pcm.name { card INT/STR # Card name (string) or number (integer) [device INT] # Device number (default 0) [subdevice INT] # Subdevice number (default -1: first available) - [mmap_emulation BOOL] # Enable mmap emulation for ro/wo devices [sync_ptr_ioctl BOOL] # Use SYNC_PTR ioctl rather than the direct mmap access for control structures [nonblock BOOL] # Force non-blocking open mode [format STR] # Restrict only to the given format @@ -1318,7 +1174,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, c snd_config_iterator_t i, next; long card = -1, device = 0, subdevice = -1; const char *str; - int err, mmap_emulation = 0, sync_ptr_ioctl = 0; + int err, sync_ptr_ioctl = 0; int rate = 0, channels = 0; snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; snd_config_t *n; @@ -1370,13 +1226,6 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, c } continue; } - if (strcmp(id, "mmap_emulation") == 0) { - err = snd_config_get_bool(n); - if (err < 0) - continue; - mmap_emulation = err; - continue; - } if (strcmp(id, "sync_ptr_ioctl") == 0) { err = snd_config_get_bool(n); if (err < 0) @@ -1429,7 +1278,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, c } err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream, mode | (nonblock ? SND_PCM_NONBLOCK : 0), - mmap_emulation, sync_ptr_ioctl); + 0, sync_ptr_ioctl); if (err < 0) return err; if (nonblock && ! (mode & SND_PCM_NONBLOCK)) { diff -r 9005d28a1f9e src/pcm/pcm_local.h --- a/src/pcm/pcm_local.h Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm_local.h Thu Jul 19 14:55:51 2007 +0200 @@ -266,8 +266,10 @@ snd_pcm_sframes_t snd_pcm_write_areas(sn snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, snd_pcm_uframes_t size, snd_pcm_xfer_areas_func_t func); -snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size); -snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size); +snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size); +snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size); static inline int snd_pcm_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info) { return pcm->ops->channel_info(pcm, info); diff -r 9005d28a1f9e src/pcm/pcm_mmap.c --- a/src/pcm/pcm_mmap.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm_mmap.c Thu Jul 19 14:55:51 2007 +0200 @@ -531,7 +531,8 @@ int snd_pcm_munmap(snd_pcm_t *pcm) return 0; } -snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) +snd_pcm_sframes_t snd_pcm_write_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { snd_pcm_uframes_t xfer = 0; snd_pcm_sframes_t err = 0; @@ -539,7 +540,6 @@ snd_pcm_sframes_t snd_pcm_write_mmap(snd return 0; while (xfer < size) { snd_pcm_uframes_t frames = size - xfer; - snd_pcm_uframes_t offset = snd_pcm_mmap_hw_offset(pcm); snd_pcm_uframes_t cont = pcm->buffer_size - offset; if (cont < frames) frames = cont; @@ -575,13 +575,15 @@ snd_pcm_sframes_t snd_pcm_write_mmap(snd if (err < 0) break; xfer += frames; + offset += frames; } if (xfer > 0) return xfer; return err; } -snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t size) +snd_pcm_sframes_t snd_pcm_read_mmap(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) { snd_pcm_uframes_t xfer = 0; snd_pcm_sframes_t err = 0; @@ -589,7 +591,6 @@ snd_pcm_sframes_t snd_pcm_read_mmap(snd_ return 0; while (xfer < size) { snd_pcm_uframes_t frames = size - xfer; - snd_pcm_uframes_t offset = snd_pcm_mmap_hw_offset(pcm); snd_pcm_uframes_t cont = pcm->buffer_size - offset; if (cont < frames) frames = cont; @@ -624,6 +625,7 @@ snd_pcm_sframes_t snd_pcm_read_mmap(snd_ if (err < 0) break; xfer += frames; + offset += frames; } if (xfer > 0) return xfer; diff -r 9005d28a1f9e src/pcm/pcm_mmap_emul.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pcm/pcm_mmap_emul.c Thu Jul 19 14:55:51 2007 +0200 @@ -0,0 +1,485 @@ +/** + * \file pcm/pcm_mmap_emul.c + * \ingroup PCM_Plugins + * \brief PCM Mmap-Emulation Plugin Interface + * \author Takashi Iwai + * \date 2007 + */ +/* + * PCM - Mmap-Emulation + * Copyright (c) 2007 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 "pcm_local.h" +#include "pcm_generic.h" + +#ifndef PIC +/* entry for static linking */ +const char *_snd_module_pcm_mmap_emul = ""; +#endif + +/* + * + */ + +typedef struct { + snd_pcm_generic_t gen; + unsigned int mmap_emul :1; + snd_pcm_uframes_t hw_ptr; + snd_pcm_uframes_t appl_ptr; +} mmap_emul_t; + +/* + * here goes a really tricky part; hw_refine falls back to ACCESS_RW_* type + * when ACCESS_MMAP_* isn't supported by the hardware. + */ +static int snd_pcm_mmap_emul_hw_refine(snd_pcm_t *pcm, + snd_pcm_hw_params_t *params) +{ + mmap_emul_t *map = pcm->private_data; + int err = 0; + snd_pcm_access_mask_t oldmask = + *snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); + snd_pcm_access_mask_t mask; + const snd_mask_t *pmask; + + snd_mask_none(&mask); + err = snd_pcm_hw_refine(map->gen.slave, params); + if (err < 0) { + /* try to use RW_* */ + snd_pcm_hw_params_t new = *params; + + if (!(params->rmask & (1<gen.slave, &new); + if (err < 0) + return err; + *params = new; + } + + pmask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); + if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED) || + snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) || + snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_COMPLEX)) + return 0; + if (snd_pcm_access_mask_test(&mask, SND_PCM_ACCESS_RW_INTERLEAVED)) { + if (snd_pcm_access_mask_test(pmask, + SND_PCM_ACCESS_RW_INTERLEAVED)) + snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_MMAP_INTERLEAVED); + snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_RW_INTERLEAVED); + params->cmask |= 1<cmask |= 1<cmask |= 1<cmask |= 1<private_data; + snd_pcm_hw_params_t old = *params; + snd_pcm_access_t access; + snd_pcm_access_mask_t oldmask; + const snd_mask_t *pmask; + int err; + + err = _snd_pcm_hw_params(map->gen.slave, params); + if (err >= 0) { + map->mmap_emul = 0; + return err; + } + + *params = old; + pmask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS); + oldmask = *(snd_pcm_access_mask_t *)pmask; + if (INTERNAL(snd_pcm_hw_params_get_access)(params, &access) < 0) + goto _err; + switch (access) { + case SND_PCM_ACCESS_MMAP_INTERLEAVED: + snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_MMAP_INTERLEAVED); + snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_RW_INTERLEAVED); + break; + case SND_PCM_ACCESS_MMAP_NONINTERLEAVED: + snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_MMAP_NONINTERLEAVED); + snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, + SND_PCM_ACCESS_RW_NONINTERLEAVED); + break; + default: + goto _err; + } + err = _snd_pcm_hw_params(map->gen.slave, params); + if (err < 0) + goto _err; + + /* need to back the access type to relieve apps */ + *(snd_pcm_access_mask_t *)pmask = oldmask; + + /* OK, we do fake */ + map->mmap_emul = 1; + map->appl_ptr = 0; + map->hw_ptr = 0; + snd_pcm_set_hw_ptr(pcm, &map->hw_ptr, -1, 0); + snd_pcm_set_appl_ptr(pcm, &map->appl_ptr, -1, 0); + return 0; + + _err: + err = -errno; + return err; +} + +static int snd_pcm_mmap_emul_prepare(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + int err; + + err = snd_pcm_generic_prepare(pcm); + if (err < 0) + return err; + map->hw_ptr = map->appl_ptr = 0; + return err; +} + +static int snd_pcm_mmap_emul_reset(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + int err; + + err = snd_pcm_generic_reset(pcm); + if (err < 0) + return err; + map->hw_ptr = map->appl_ptr = 0; + return err; +} + +static snd_pcm_sframes_t +snd_pcm_mmap_emul_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames) +{ + frames = snd_pcm_generic_rewind(pcm, frames); + if (frames > 0) + snd_pcm_mmap_appl_backward(pcm, frames); + return frames; +} + +static snd_pcm_sframes_t +snd_pcm_mmap_emul_forward(snd_pcm_t *pcm, snd_pcm_uframes_t frames) +{ + frames = snd_pcm_generic_forward(pcm, frames); + if (frames > 0) + snd_pcm_mmap_appl_forward(pcm, frames); + return frames; +} + +/* write out the uncommitted chunk on mmap buffer to the slave PCM */ +static snd_pcm_sframes_t +sync_slave_write(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + snd_pcm_t *slave = map->gen.slave; + snd_pcm_uframes_t offset; + snd_pcm_sframes_t size; + + size = map->appl_ptr - *slave->appl.ptr; + if (size < 0) + size += pcm->boundary; + size -= size % pcm->xfer_align; + if (!size) + return 0; + offset = *slave->appl.ptr % pcm->buffer_size; + return snd_pcm_write_mmap(pcm, offset, size); +} + +/* read the available chunk on the slave PCM to mmap buffer */ +static snd_pcm_sframes_t +sync_slave_read(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + snd_pcm_t *slave = map->gen.slave; + snd_pcm_uframes_t offset; + snd_pcm_sframes_t size; + + size = *slave->hw.ptr - map->hw_ptr; + if (size < 0) + size += pcm->boundary; + size -= size % pcm->xfer_align; + if (!size) + return 0; + offset = map->hw_ptr % pcm->buffer_size; + size = snd_pcm_read_mmap(pcm, offset, size); + if (size > 0) + snd_pcm_mmap_hw_forward(pcm, size); + return 0; +} + +static snd_pcm_sframes_t +snd_pcm_mmap_emul_mmap_commit(snd_pcm_t *pcm, snd_pcm_uframes_t offset, + snd_pcm_uframes_t size) +{ + mmap_emul_t *map = pcm->private_data; + snd_pcm_t *slave = map->gen.slave; + + if (!map->mmap_emul) + return snd_pcm_mmap_commit(slave, offset, size); + snd_pcm_mmap_appl_forward(pcm, size); + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + sync_slave_write(pcm); + return size; +} + +static snd_pcm_sframes_t snd_pcm_mmap_emul_avail_update(snd_pcm_t *pcm) +{ + mmap_emul_t *map = pcm->private_data; + snd_pcm_t *slave = map->gen.slave; + snd_pcm_sframes_t avail; + + avail = snd_pcm_avail_update(slave); + if (!map->mmap_emul) + return avail; + + if (pcm->stream == SND_PCM_STREAM_PLAYBACK) + map->hw_ptr = *slave->hw.ptr; + else + sync_slave_read(pcm); + return snd_pcm_mmap_avail(pcm); +} + +static void snd_pcm_mmap_emul_dump(snd_pcm_t *pcm, snd_output_t *out) +{ + mmap_emul_t *map = pcm->private_data; + + snd_output_printf(out, "Mmap emulation PCM\n"); + 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(map->gen.slave, out); +} + +static snd_pcm_ops_t snd_pcm_mmap_emul_ops = { + .close = snd_pcm_generic_close, + .info = snd_pcm_generic_info, + .hw_refine = snd_pcm_mmap_emul_hw_refine, + .hw_params = snd_pcm_mmap_emul_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_mmap_emul_dump, + .nonblock = snd_pcm_generic_nonblock, + .async = snd_pcm_generic_async, + .mmap = snd_pcm_generic_mmap, + .munmap = snd_pcm_generic_munmap, +}; + +static snd_pcm_fast_ops_t snd_pcm_mmap_emul_fast_ops = { + .status = snd_pcm_generic_status, + .state = snd_pcm_generic_state, + .hwsync = snd_pcm_generic_hwsync, + .delay = snd_pcm_generic_delay, + .prepare = snd_pcm_mmap_emul_prepare, + .reset = snd_pcm_mmap_emul_reset, + .start = snd_pcm_generic_start, + .drop = snd_pcm_generic_drop, + .drain = snd_pcm_generic_drain, + .pause = snd_pcm_generic_pause, + .rewind = snd_pcm_mmap_emul_rewind, + .forward = snd_pcm_mmap_emul_forward, + .resume = snd_pcm_generic_resume, + .link = snd_pcm_generic_link, + .link_slaves = snd_pcm_generic_link_slaves, + .unlink = snd_pcm_generic_unlink, + .writei = snd_pcm_generic_writei, + .writen = snd_pcm_generic_writen, + .readi = snd_pcm_generic_readi, + .readn = snd_pcm_generic_readn, + .avail_update = snd_pcm_mmap_emul_avail_update, + .mmap_commit = snd_pcm_mmap_emul_mmap_commit, + .poll_descriptors = snd_pcm_generic_poll_descriptors, + .poll_descriptors_count = snd_pcm_generic_poll_descriptors_count, + .poll_revents = snd_pcm_generic_poll_revents, +}; + +static int snd_pcm_mmap_emul_open(snd_pcm_t **pcmp, const char *name, + snd_pcm_t *slave, int close_slave) +{ + snd_pcm_t *pcm; + mmap_emul_t *map; + int err; + + map = calloc(1, sizeof(*map)); + if (!map) + return -ENOMEM; + map->gen.slave = slave; + map->gen.close_slave = close_slave; + + err = snd_pcm_new(&pcm, SND_PCM_TYPE_MMAP_EMUL, name, + slave->stream, slave->mode); + if (err < 0) { + free(map); + return err; + } + pcm->ops = &snd_pcm_mmap_emul_ops; + pcm->fast_ops = &snd_pcm_mmap_emul_fast_ops; + pcm->private_data = map; + pcm->poll_fd = slave->poll_fd; + pcm->poll_events = slave->poll_events; + snd_pcm_set_hw_ptr(pcm, &map->hw_ptr, -1, 0); + snd_pcm_set_appl_ptr(pcm, &map->appl_ptr, -1, 0); + *pcmp = pcm; + + return 0; +} + +/*! \page pcm_plugins + +\section pcm_plugins_mmap_emul Plugin: mmap_emul + +\code +pcm.name { + type mmap_emul + slave PCM +} +\endcode + +\subsection pcm_plugins_mmap_emul_funcref Function reference + +
    +
  • _snd_pcm_hw_open() +
+ +*/ + +/** + * \brief Creates a new mmap_emul PCM + * \param pcmp Returns created PCM handle + * \param name Name of PCM + * \param root Root configuration node + * \param conf Configuration node with hw PCM description + * \param stream PCM Stream + * \param mode PCM Mode + * \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_mmap_emul_open(snd_pcm_t **pcmp, const char *name, + snd_config_t *root ATTRIBUTE_UNUSED, + 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_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; + } + SNDERR("Unknown field %s", id); + return -EINVAL; + } + if (!slave) { + SNDERR("slave is not defined"); + return -EINVAL; + } + err = snd_pcm_slave_conf(root, slave, &sconf, 0); + 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; + err = snd_pcm_mmap_emul_open(pcmp, name, spcm, 1); + if (err < 0) + snd_pcm_close(spcm); + return err; +} + +#ifndef DOC_HIDDEN +SND_DLSYM_BUILD_VERSION(_snd_pcm_mmap_emul_open, SND_PCM_DLSYM_VERSION); +#endif diff -r 9005d28a1f9e src/pcm/pcm_softvol.c --- a/src/pcm/pcm_softvol.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm_softvol.c Thu Jul 19 14:55:51 2007 +0200 @@ -101,7 +101,7 @@ typedef union { int i; short s[2]; } val_t; -static inline int MULTI_DIV_32x16(int a, unsigned short b, int swap) +static inline int MULTI_DIV_32x16(int a, unsigned short b) { val_t v, x, y; v.i = a; @@ -123,7 +123,7 @@ static inline int MULTI_DIV_int(int a, u unsigned int gain = (b >> VOL_SCALE_SHIFT); int fraction; a = swap ? (int)bswap_32(a) : a; - fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK, swap); + fraction = MULTI_DIV_32x16(a, b & VOL_SCALE_MASK); if (gain) { long long amp = (long long)a * gain + fraction; if (amp > (int)0x7fffffff) diff -r 9005d28a1f9e src/pcm/pcm_symbols.c --- a/src/pcm/pcm_symbols.c Mon Jun 11 10:52:17 2007 +0200 +++ b/src/pcm/pcm_symbols.c Thu Jul 19 14:55:51 2007 +0200 @@ -49,6 +49,7 @@ extern const char *_snd_module_pcm_softv extern const char *_snd_module_pcm_softvol; extern const char *_snd_module_pcm_extplug; extern const char *_snd_module_pcm_ioplug; +extern const char *_snd_module_pcm_mmap_emul; static const char **snd_pcm_open_objects[] = { &_snd_module_pcm_hw,