File alsa-lib-hg-fixes.diff of Package alsa

diff -r 9005d28a1f9e configure.in
--- a/configure.in	Mon Jun 11 10:52:17 2007 +0200
+++ b/configure.in	Tue Aug 14 16:14:08 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 <alsa/pcm.h>" >> include/asoundlib.h
+test "$build_rawmidi" = "yes" && echo "#include <alsa/rawmidi.h>" >> include/asoundlib.h
+test "$build_pcm" = "yes" && echo "#include <alsa/timer.h>" >> include/asoundlib.h
+test "$build_hwdep" = "yes" && echo "#include <alsa/hwdep.h>" >> include/asoundlib.h
+echo "#include <alsa/control.h>" >> include/asoundlib.h
+test "$build_mixer" = "yes" && echo "#include <alsa/mixer.h>" >> include/asoundlib.h
+test "$build_seq" = "yes" && echo "#include <alsa/seq_event.h>" >> include/asoundlib.h
+test "$build_seq" = "yes" && echo "#include <alsa/seq.h>" >> include/asoundlib.h
+test "$build_seq" = "yes" && echo "#include <alsa/seqmid.h>" >> include/asoundlib.h
+test "$build_seq" = "yes" && echo "#include <alsa/seq_midi_event.h>" >> include/asoundlib.h
+test "$build_instr" = "yes" && echo "#include <alsa/conv.h>" >> include/asoundlib.h
+test "$build_instr" = "yes" && echo "#include <alsa/instr.h>" >> 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	Tue Aug 14 16:14:08 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 \
@@ -70,6 +71,7 @@ INPUT            = index.doxygen \
 		   ../src/pcm/pcm_softvol.c \
 		   ../src/pcm/pcm_extplug.c \
 		   ../src/pcm/pcm_ioplug.c \
+		   ../src/pcm/pcm_empty.c \
 		   ../src/pcm/pcm_misc.c \
 		   ../src/pcm/pcm_simple.c \
 		   ../src/rawmidi \
diff -r 9005d28a1f9e include/Makefile.am
--- a/include/Makefile.am	Mon Jun 11 10:52:17 2007 +0200
+++ b/include/Makefile.am	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 2007 +0200
@@ -0,0 +1,48 @@
+/**
+ * \file include/asoundlib.h
+ * \brief Application interface library for the ALSA driver
+ * \author Jaroslav Kysela <perex@suse.cz>
+ * \author Abramo Bagnara <abramo@alsa-project.org>
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \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 <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <endian.h>
+#include <sys/poll.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <alsa/asoundef.h>
+#include <alsa/version.h>
+#include <alsa/global.h>
+#include <alsa/input.h>
+#include <alsa/output.h>
+#include <alsa/error.h>
+#include <alsa/conf.h>
diff -r 9005d28a1f9e include/asoundlib-tail.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/include/asoundlib-tail.h	Tue Aug 14 16:14:08 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 <perex@suse.cz>
- * \author Abramo Bagnara <abramo@alsa-project.org>
- * \author Takashi Iwai <tiwai@suse.de>
- * \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 <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <endian.h>
-#include <sys/poll.h>
-#include <errno.h>
-#include <stdarg.h>
-
-#include <alsa/asoundef.h>
-#include <alsa/version.h>
-#include <alsa/global.h>
-#include <alsa/input.h>
-#include <alsa/output.h>
-#include <alsa/error.h>
-#include <alsa/conf.h>
-#include <alsa/pcm.h>
-#include <alsa/rawmidi.h>
-#include <alsa/timer.h>
-#include <alsa/hwdep.h>
-#include <alsa/control.h>
-#include <alsa/mixer.h>
-#include <alsa/seq_event.h>
-#include <alsa/seq.h>
-#include <alsa/seqmid.h>
-#include <alsa/seq_midi_event.h>
-#include <alsa/conv.h>
-#include <alsa/instr.h>
-
-#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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 2007 +0200
@@ -0,0 +1,1002 @@
+/*
+ *  Mixer Interface - python binding simple abstact module
+ *  Copyright (c) 2007 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   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 <stddef.h>
+#include "config.h"
+#include "asoundlib.h"
+#include "mixer_abst.h"
+#include <python/Python.h>
+
+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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 2007 +0200
@@ -0,0 +1,46 @@
+#
+# Configuration for PS3
+#
+
+<confdir:pcm/front.conf>
+
+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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 2007 +0200
@@ -38,7 +38,6 @@
 #include <math.h>
 #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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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 <byteswap.h>
 
-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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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<<SND_PCM_HW_PARAM_ACCESS)))
-				return err;
-			if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_INTERLEAVED) &&
-			    !snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_RW_INTERLEAVED))
-				snd_pcm_access_mask_set(&mask, SND_PCM_ACCESS_RW_INTERLEAVED);
-			if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) &&
-			    !snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
-				snd_pcm_access_mask_set(&mask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
-			if (snd_pcm_access_mask_empty(&mask))
-				return err;
-			pmask = snd_pcm_hw_param_get_mask(&new, SND_PCM_HW_PARAM_ACCESS);
-			*(snd_mask_t *)pmask = mask;
-			if (hw_refine_call(hw, &new) < 0)
-				return -errno;
-			*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<<SND_PCM_HW_PARAM_ACCESS;
-		}
-		if (snd_pcm_access_mask_test(&mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
-			if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
-				snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
-			snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
-			params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
-		}
-		if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
-			if (snd_pcm_access_mask_test(&oldmask, 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);
-					params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
-				}
-			}
-		}
-		if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
-			if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
-				if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
-					snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
-					params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
-				}
-			}
-		}
-	} else {
-		if (hw_refine_call(hw, params) < 0) {
-			err = -errno;
-			// SYSMSG("SNDRV_PCM_IOCTL_HW_REFINE failed");
-			return err;
-		}
+	if (hw_refine_call(hw, params) < 0) {
+		err = -errno;
+		// SYSMSG("SNDRV_PCM_IOCTL_HW_REFINE failed");
+		return err;
 	}
 	
 	return 0;
@@ -330,55 +261,17 @@ static int snd_pcm_hw_hw_params(snd_pcm_
 {
 	snd_pcm_hw_t *hw = pcm->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;
@@ -1159,8 +1015,6 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp,
 	pcm->poll_fd = fd;
 	pcm->poll_events = info.stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
 
-	*pcmp = pcm;
-
 	ret = snd_pcm_hw_mmap_status(pcm);
 	if (ret < 0) {
 		snd_pcm_close(pcm);
@@ -1171,6 +1025,8 @@ int snd_pcm_hw_open_fd(snd_pcm_t **pcmp,
 		snd_pcm_close(pcm);
 		return ret;
 	}
+
+	*pcmp = pcm;
 	return 0;
 }
 
@@ -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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 2007 +0200
@@ -0,0 +1,485 @@
+/**
+ * \file pcm/pcm_mmap_emul.c
+ * \ingroup PCM_Plugins
+ * \brief PCM Mmap-Emulation Plugin Interface
+ * \author Takashi Iwai <tiwai@suse.de>
+ * \date 2007
+ */
+/*
+ *  PCM - Mmap-Emulation
+ *  Copyright (c) 2007 by Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   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<<SND_PCM_HW_PARAM_ACCESS)))
+			return err;
+		if (snd_pcm_access_mask_test(&oldmask,
+					     SND_PCM_ACCESS_MMAP_INTERLEAVED) &&
+		    !snd_pcm_access_mask_test(&oldmask,
+					      SND_PCM_ACCESS_RW_INTERLEAVED))
+			snd_pcm_access_mask_set(&mask,
+						SND_PCM_ACCESS_RW_INTERLEAVED);
+		if (snd_pcm_access_mask_test(&oldmask,
+					     SND_PCM_ACCESS_MMAP_NONINTERLEAVED) &&
+		    !snd_pcm_access_mask_test(&oldmask,
+					      SND_PCM_ACCESS_RW_NONINTERLEAVED))
+			snd_pcm_access_mask_set(&mask,
+						SND_PCM_ACCESS_RW_NONINTERLEAVED);
+		if (snd_pcm_access_mask_empty(&mask))
+			return err;
+		pmask = snd_pcm_hw_param_get_mask(&new,
+						  SND_PCM_HW_PARAM_ACCESS);
+		*(snd_mask_t *)pmask = mask;
+		err = snd_pcm_hw_refine(map->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<<SND_PCM_HW_PARAM_ACCESS;
+	}
+	if (snd_pcm_access_mask_test(&mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
+		if (snd_pcm_access_mask_test(pmask,
+					     SND_PCM_ACCESS_RW_NONINTERLEAVED))
+			snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask,
+						SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
+		snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask,
+					  SND_PCM_ACCESS_RW_NONINTERLEAVED);
+		params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
+	}
+	if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
+		if (snd_pcm_access_mask_test(&oldmask,
+					     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);
+				params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
+			}
+		}
+	}
+	if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
+		if (snd_pcm_access_mask_test(&oldmask,
+					     SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
+			if (snd_pcm_access_mask_test(pmask,
+						     SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
+				snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask,
+							SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
+				params->cmask |= 1<<SND_PCM_HW_PARAM_ACCESS;
+			}
+		}
+	}
+	return 0;
+}
+
+/*
+ * hw_params needs a similar hack like hw_refine, but it's much simpler
+ * because now snd_pcm_hw_params_t takes only one choice for each item.
+ *
+ * Here, when the normal hw_params call fails, it turns on the mmap_emul
+ * flag and tries to use ACCESS_RW_* mode.
+ *
+ * In mmap_emul mode, the appl_ptr and hw_ptr are handled individually
+ * from the layering slave PCM, and they are sync'ed appropriately in
+ * each read/write or avail_update/commit call.
+ */
+static int snd_pcm_mmap_emul_hw_params(snd_pcm_t *pcm,
+				       snd_pcm_hw_params_t *params)
+{
+	mmap_emul_t *map = pcm->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
+
+<UL>
+  <LI>_snd_pcm_hw_open()
+</UL>
+
+*/
+
+/**
+ * \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	Tue Aug 14 16:14:08 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	Tue Aug 14 16:14:08 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 -r 9005d28a1f9e src/seq/seq_midi_event.c
--- a/src/seq/seq_midi_event.c	Mon Jun 11 10:52:17 2007 +0200
+++ b/src/seq/seq_midi_event.c	Tue Aug 14 16:14:08 2007 +0200
@@ -45,10 +45,9 @@ struct snd_midi_event {
 };
 
 
-/* queue type */
-/* from 0 to 7 are normal commands (note off, on, etc.) */
-#define ST_NOTEOFF	0
-#define ST_NOTEON	1
+/* event type, index into status_event[] */
+/* from 0 to 6 are normal commands (note off, on, etc.) for 0x8?-0xe? */
+#define ST_INVALID	7
 #define ST_SPECIAL	8
 #define ST_SYSEX	ST_SPECIAL
 /* from 8 to 15 are events for 0xf0-0xf7 */
@@ -85,32 +84,33 @@ static struct status_event_list_t {
 	event_encode_t encode;
 	event_decode_t decode;
 } status_event[] = {
-	/* 0x80 - 0xf0 */
-	{SND_SEQ_EVENT_NOTEOFF,		2, note_event, note_decode},
-	{SND_SEQ_EVENT_NOTEON,		2, note_event, note_decode},
-	{SND_SEQ_EVENT_KEYPRESS,	2, note_event, note_decode},
-	{SND_SEQ_EVENT_CONTROLLER,	2, two_param_ctrl_event, two_param_decode},
-	{SND_SEQ_EVENT_PGMCHANGE,	1, one_param_ctrl_event, one_param_decode},
-	{SND_SEQ_EVENT_CHANPRESS,	1, one_param_ctrl_event, one_param_decode},
-	{SND_SEQ_EVENT_PITCHBEND,	2, pitchbend_ctrl_event, pitchbend_decode},
-	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf0 */
+	/* 0x80 - 0xef */
+	{SND_SEQ_EVENT_NOTEOFF,		 2, note_event, note_decode},
+	{SND_SEQ_EVENT_NOTEON,		 2, note_event, note_decode},
+	{SND_SEQ_EVENT_KEYPRESS,	 2, note_event, note_decode},
+	{SND_SEQ_EVENT_CONTROLLER,	 2, two_param_ctrl_event, two_param_decode},
+	{SND_SEQ_EVENT_PGMCHANGE,	 1, one_param_ctrl_event, one_param_decode},
+	{SND_SEQ_EVENT_CHANPRESS,	 1, one_param_ctrl_event, one_param_decode},
+	{SND_SEQ_EVENT_PITCHBEND,	 2, pitchbend_ctrl_event, pitchbend_decode},
+	/* invalid */
+	{SND_SEQ_EVENT_NONE,		-1, NULL, NULL},
 	/* 0xf0 - 0xff */
-	{SND_SEQ_EVENT_SYSEX,		1, NULL, NULL}, /* sysex: 0xf0 */
-	{SND_SEQ_EVENT_QFRAME,		1, one_param_event, one_param_decode}, /* 0xf1 */
-	{SND_SEQ_EVENT_SONGPOS,		2, songpos_event, songpos_decode}, /* 0xf2 */
-	{SND_SEQ_EVENT_SONGSEL,		1, one_param_event, one_param_decode}, /* 0xf3 */
-	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf4 */
-	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf5 */
-	{SND_SEQ_EVENT_TUNE_REQUEST,	0, NULL, NULL},	/* 0xf6 */
-	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf7 */
-	{SND_SEQ_EVENT_CLOCK,		0, NULL, NULL}, /* 0xf8 */
-	{SND_SEQ_EVENT_NONE,		0, NULL, NULL}, /* 0xf9 */
-	{SND_SEQ_EVENT_START,		0, NULL, NULL}, /* 0xfa */
-	{SND_SEQ_EVENT_CONTINUE,	0, NULL, NULL}, /* 0xfb */
-	{SND_SEQ_EVENT_STOP, 		0, NULL, NULL}, /* 0xfc */
-	{SND_SEQ_EVENT_NONE, 		0, NULL, NULL}, /* 0xfd */
-	{SND_SEQ_EVENT_SENSING, 	0, NULL, NULL}, /* 0xfe */
-	{SND_SEQ_EVENT_RESET, 		0, NULL, NULL}, /* 0xff */
+	{SND_SEQ_EVENT_SYSEX,		 1, NULL, NULL}, /* sysex: 0xf0 */
+	{SND_SEQ_EVENT_QFRAME,		 1, one_param_event, one_param_decode}, /* 0xf1 */
+	{SND_SEQ_EVENT_SONGPOS,		 2, songpos_event, songpos_decode}, /* 0xf2 */
+	{SND_SEQ_EVENT_SONGSEL,		 1, one_param_event, one_param_decode}, /* 0xf3 */
+	{SND_SEQ_EVENT_NONE,		-1, NULL, NULL}, /* 0xf4 */
+	{SND_SEQ_EVENT_NONE,		-1, NULL, NULL}, /* 0xf5 */
+	{SND_SEQ_EVENT_TUNE_REQUEST,	 0, NULL, NULL}, /* 0xf6 */
+	{SND_SEQ_EVENT_NONE,		-1, NULL, NULL}, /* 0xf7 */
+	{SND_SEQ_EVENT_CLOCK,		 0, NULL, NULL}, /* 0xf8 */
+	{SND_SEQ_EVENT_NONE,		-1, NULL, NULL}, /* 0xf9 */
+	{SND_SEQ_EVENT_START,		 0, NULL, NULL}, /* 0xfa */
+	{SND_SEQ_EVENT_CONTINUE,	 0, NULL, NULL}, /* 0xfb */
+	{SND_SEQ_EVENT_STOP, 		 0, NULL, NULL}, /* 0xfc */
+	{SND_SEQ_EVENT_NONE, 		-1, NULL, NULL}, /* 0xfd */
+	{SND_SEQ_EVENT_SENSING, 	 0, NULL, NULL}, /* 0xfe */
+	{SND_SEQ_EVENT_RESET, 		 0, NULL, NULL}, /* 0xff */
 };
 
 static int extra_decode_ctrl14(snd_midi_event_t *dev, unsigned char *buf, int len, const snd_seq_event_t *ev);
@@ -153,6 +153,7 @@ int snd_midi_event_new(size_t bufsize, s
 	}
 	dev->bufsize = bufsize;
 	dev->lastcmd = 0xff;
+	dev->type = ST_INVALID;
 	*rdev = dev;
 	return 0;
 }
@@ -191,7 +192,7 @@ inline static void reset_encode(snd_midi
 {
 	dev->read = 0;
 	dev->qlen = 0;
-	dev->type = 0;
+	dev->type = ST_INVALID;
 }
 
 /**
@@ -307,28 +308,30 @@ int snd_midi_event_encode_byte(snd_midi_
 		ev->type = status_event[ST_SPECIAL + c - 0xf0].event;
 		ev->flags &= ~SND_SEQ_EVENT_LENGTH_MASK;
 		ev->flags |= SND_SEQ_EVENT_LENGTH_FIXED;
-		return 1;
-	}
-
-	if (dev->qlen > 0) {
-		/* rest of command */
-		dev->buf[dev->read++] = c;
-		if (dev->type != ST_SYSEX)
-			dev->qlen--;
+		return ev->type != SND_SEQ_EVENT_NONE;
+	}
+
+	if ((c & 0x80) &&
+	    (c != MIDI_CMD_COMMON_SYSEX_END || dev->type != ST_SYSEX)) {
+		/* new command */
+		dev->buf[0] = c;
+		if ((c & 0xf0) == 0xf0) /* system message */
+			dev->type = (c & 0x0f) + ST_SPECIAL;
+		else
+			dev->type = (c >> 4) & 0x07;
+		dev->read = 1;
+		dev->qlen = status_event[dev->type].qlen;
 	} else {
-		/* new command */
-		dev->read = 1;
-		if (c & 0x80) {
-			dev->buf[0] = c;
-			if ((c & 0xf0) == 0xf0) /* special events */
-				dev->type = (c & 0x0f) + ST_SPECIAL;
-			else
-				dev->type = (c >> 4) & 0x07;
-			dev->qlen = status_event[dev->type].qlen;
+		if (dev->qlen > 0) {
+			/* rest of command */
+			dev->buf[dev->read++] = c;
+			if (dev->type != ST_SYSEX)
+				dev->qlen--;
 		} else {
-			/* process this byte as argument */
-			dev->buf[dev->read++] = c;
+			/* running status */
+			dev->buf[1] = c;
 			dev->qlen = status_event[dev->type].qlen - 1;
+			dev->read = 2;
 		}
 	}
 	if (dev->qlen == 0) {
@@ -337,6 +340,8 @@ int snd_midi_event_encode_byte(snd_midi_
 		ev->flags |= SND_SEQ_EVENT_LENGTH_FIXED;
 		if (status_event[dev->type].encode) /* set data values */
 			status_event[dev->type].encode(dev, ev);
+		if (dev->type >= ST_SPECIAL)
+			dev->type = ST_INVALID;
 		rc = 1;
 	} else 	if (dev->type == ST_SYSEX) {
 		if (c == MIDI_CMD_COMMON_SYSEX_END ||
openSUSE Build Service is sponsored by