diff --git a/alsa-lib-hg-fixes.diff b/alsa-lib-hg-fixes.diff index dc6135c..9d0be92 100644 --- a/alsa-lib-hg-fixes.diff +++ b/alsa-lib-hg-fixes.diff @@ -1,7 +1,58 @@ diff -r 9005d28a1f9e configure.in --- a/configure.in Mon Jun 11 10:52:17 2007 +0200 -+++ b/configure.in Wed Jul 04 10:19:59 2007 +0200 -@@ -414,7 +414,7 @@ if test "$HAVE_LIBPTHREAD" != "yes"; the ++++ 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 @@ -10,9 +61,1612 @@ diff -r 9005d28a1f9e configure.in 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 Wed Jul 04 10:19:59 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 { @@ -32,7 +1686,7 @@ diff -r 9005d28a1f9e src/conf/cards/PMac.conf } 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 Wed Jul 04 10:19:59 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 { @@ -51,9 +1705,352 @@ diff -r 9005d28a1f9e src/conf/cards/PMacToonie.conf } } } +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 Wed Jul 04 10:19:59 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 */ @@ -72,7 +2069,7 @@ diff -r 9005d28a1f9e src/pcm/pcm_dmix.c 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 Wed Jul 04 10:19:59 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 @@ -158,7 +2155,7 @@ diff -r 9005d28a1f9e src/pcm/pcm_dmix_generic.c +#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 Wed Jul 04 10:19:59 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 @@ -227,7 +2224,7 @@ diff -r 9005d28a1f9e src/pcm/pcm_dmix_i386.c } 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 Wed Jul 04 10:19:59 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 @@ -281,3 +2278,946 @@ diff -r 9005d28a1f9e src/pcm/pcm_dmix_x86_64.c } // 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, diff --git a/alsa.changes b/alsa.changes index 20adee5..e8453b9 100644 --- a/alsa.changes +++ b/alsa.changes @@ -1,3 +1,16 @@ +------------------------------------------------------------------- +Thu Aug 2 16:54:53 CEST 2007 - tiwai@suse.de + +- fix typos in set_default_volume script + +------------------------------------------------------------------- +Mon Jul 30 19:10:16 CEST 2007 - tiwai@suse.de + +- split mmap_emulation plugin from hw layer +- python support (disabled) +- generate asoundlib.h dynamically via configure options +- PS3 configuration + ------------------------------------------------------------------- Wed Jul 4 10:20:38 CEST 2007 - tiwai@suse.de diff --git a/alsa.spec b/alsa.spec index ac19bd0..9288b83 100644 --- a/alsa.spec +++ b/alsa.spec @@ -21,7 +21,7 @@ PreReq: %insserv_prereq %fillup_prereq Autoreqprov: on Summary: Advanced Linux Sound Architecture Version: 1.0.14 -Release: 10 +Release: 17 Source: ftp://ftp.alsa-project.org/pub/lib/alsa-lib-%{package_version}.tar.bz2 Source8: 40-alsa.rules Source11: alsasound @@ -123,7 +123,8 @@ autoreconf -fi --disable-static \ --enable-symbolic-functoins \ --disable-aload \ - --disable-alisp + --disable-alisp \ + --disable-python make %{?jobs:-j %jobs} # run doxygen make -C doc doc @@ -254,6 +255,13 @@ exit 0 %{_datadir}/alsa %changelog +* Thu Aug 02 2007 - tiwai@suse.de +- fix typos in set_default_volume script +* Mon Jul 30 2007 - tiwai@suse.de +- split mmap_emulation plugin from hw layer +- python support (disabled) +- generate asoundlib.h dynamically via configure options +- PS3 configuration * Wed Jul 04 2007 - tiwai@suse.de - fix the build of lfloat plugin - allow big-endian with i386/x86-64 dmix diff --git a/set_default_volume b/set_default_volume index df14253..3fa9f04 100644 --- a/set_default_volume +++ b/set_default_volume @@ -29,10 +29,10 @@ set Front 75% unmute set Front -12dB set PCM 90% unmute set PCM 0dB -mixer Synth 90% unmute -mixer Synth 0dB -mixer CD 90% unmute -mixer CD 0dB +set Synth 90% unmute +set Synth 0dB +set CD 90% unmute +set CD 0dB # mute mic set Mic 0% mute # ESS 1969 chipset has 2 PCM channels