]> git.alsa-project.org Git - alsa-python.git/commitdiff
python-alsa module initial code import - only simple mixer interface is available
authorJaroslav Kysela <perex@perex.cz>
Thu, 22 Feb 2007 13:28:53 +0000 (14:28 +0100)
committerJaroslav Kysela <perex@perex.cz>
Thu, 22 Feb 2007 13:28:53 +0000 (14:28 +0100)
.hgignore [new file with mode: 0644]
PKG-INFO [new file with mode: 0644]
pyalsa/__init__.py [new file with mode: 0644]
pyalsa/alsamixer.c [new file with mode: 0644]
pyalsa/mixertest1.py [new file with mode: 0755]
pyalsa/mixertest2.py [new file with mode: 0755]
setup.py [new file with mode: 0755]

diff --git a/.hgignore b/.hgignore
new file mode 100644 (file)
index 0000000..b5a0cbd
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,8 @@
+syntax: glob
+
+build/*
+old/*
+*~
+.*~
+*.orig
+*.rej
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644 (file)
index 0000000..c8950fa
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,10 @@
+Metadata-Version: 1.0
+Name: PyAlsa
+Version: 1.0.14rc2
+Summary: ALSA Python Binding
+Home-page: http://www.alsa-project.org
+Author: The ALSA Team
+Author-email: alsa-devel@alsa-project.org
+License: GPL
+Description: Official ALSA Python Binding
+Platform: Python, Linux
diff --git a/pyalsa/__init__.py b/pyalsa/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/pyalsa/alsamixer.c b/pyalsa/alsamixer.c
new file mode 100644 (file)
index 0000000..e58d8cf
--- /dev/null
@@ -0,0 +1,1227 @@
+/*
+ *  Python binding for the ALSA library - mixer
+ *  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 "Python.h"
+#include "structmember.h"
+#include "frameobject.h"
+#ifndef PY_LONG_LONG
+  #define PY_LONG_LONG LONG_LONG
+#endif
+#include "sys/poll.h"
+#include "stdlib.h"
+#include "alsa/asoundlib.h"
+
+#ifndef Py_RETURN_NONE
+#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
+#endif
+#ifndef Py_RETURN_TRUE
+#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
+#endif
+#ifndef Py_RETURN_FALSE
+#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
+#endif
+
+static int element_callback(snd_mixer_elem_t *elem, unsigned int mask);
+
+static PyObject *module;
+#if 0
+static PyObject *buildin;
+#endif
+static PyInterpreterState *main_interpreter;
+
+/*
+ *
+ */
+
+#define PYALSAMIXER(v) (((v) == Py_None) ? NULL : \
+       ((struct pyalsamixer *)(v)))
+
+struct pyalsamixer {
+       PyObject_HEAD
+       snd_mixer_t *handle;
+};
+
+static inline PyObject *get_bool(int val)
+{
+       if (val) {
+               Py_INCREF(Py_True);
+               return Py_True;
+       } else {
+               Py_INCREF(Py_False);
+               return Py_False;
+       }
+}
+
+static PyObject *
+pyalsamixer_getcount(struct pyalsamixer *self, void *priv)
+{
+       return PyLong_FromLong(snd_mixer_get_count(self->handle));
+}
+
+PyDoc_STRVAR(attach__doc__,
+"attach(card=None, [, abstract=]) -- Attach mixer to a sound card.");
+
+static PyObject *
+pyalsamixer_attach(struct pyalsamixer *self, PyObject *args, PyObject *kwds)
+{
+       char *card = "default";
+       int abstract = -1, res;
+       static char * kwlist[] = { "card", "abstract", NULL };
+       struct snd_mixer_selem_regopt *options = NULL, _options;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|si", kwlist, &card, &abstract))
+               Py_RETURN_NONE;
+
+       if (abstract < 0) {
+               res = snd_mixer_attach(self->handle, card);
+               if (res < 0)
+                       return PyErr_Format(PyExc_RuntimeError, "Cannot attach card '%s': %s", card, snd_strerror(-res));
+               abstract = -1;
+       }
+       if (abstract >= 0) {
+               memset(&_options, 0, sizeof(_options));
+               _options.ver = 1;
+               _options.abstract = abstract;
+               _options.device = card;
+               options = &_options;
+       }
+       res = snd_mixer_selem_register(self->handle, options, NULL);
+       if (res < 0)
+               return PyErr_Format(PyExc_RuntimeError, "Cannot register simple mixer (abstract %i): %s", abstract, snd_strerror(-res));
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(load__doc__,
+"load() -- Load mixer elements.");
+
+static PyObject *
+pyalsamixer_load(struct pyalsamixer *self, PyObject *args)
+{
+       int res = snd_mixer_load(self->handle);
+       if (res < 0)
+               return PyErr_Format(PyExc_RuntimeError, "Cannot load mixer elements: %s", snd_strerror(-res));
+       return Py_BuildValue("i", res);
+}
+
+PyDoc_STRVAR(free__doc__,
+"free() -- Free mixer elements and all related resources.");
+
+static PyObject *
+pyalsamixer_free(struct pyalsamixer *self, PyObject *args)
+{
+       snd_mixer_free(self->handle);
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(handlevents__doc__,
+"handleEvents() -- Process waiting mixer events (and call appropriate callbacks).");
+
+static PyObject *
+pyalsamixer_handleevents(struct pyalsamixer *self, PyObject *args)
+{
+       int err = snd_mixer_handle_events(self->handle);
+       if (err < 0)
+               PyErr_Format(PyExc_IOError,
+                    "Alsamixer handle events error: %s", strerror(-err));
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(registerpoll__doc__,
+"registerPoll(pollObj) -- Register poll file descriptors.");
+
+static PyObject *
+pyalsamixer_registerpoll(struct pyalsamixer *self, PyObject *args)
+{
+       PyObject *pollObj, *reg, *t;
+       struct pollfd *pfd;
+       int i, count;
+
+       if (!PyArg_ParseTuple(args, "O", &pollObj))
+               return NULL;
+
+       count = snd_mixer_poll_descriptors_count(self->handle);
+       if (count <= 0)
+               Py_RETURN_NONE;
+       pfd = malloc(sizeof(struct pollfd) * count);
+       if (pfd == NULL)
+               Py_RETURN_NONE;
+       count = snd_mixer_poll_descriptors(self->handle, pfd, count);
+       if (count <= 0)
+               Py_RETURN_NONE;
+       
+       reg = PyObject_GetAttr(pollObj, PyString_InternFromString("register"));
+
+       for (i = 0; i < count; i++) {
+               t = PyTuple_New(2);
+               if (t) {
+                       PyTuple_SET_ITEM(t, 0, PyInt_FromLong(pfd[i].fd));
+                       PyTuple_SET_ITEM(t, 1, PyInt_FromLong(pfd[i].events));
+                       Py_XDECREF(PyObject_CallObject(reg, t));
+                       Py_DECREF(t);
+               }
+       }       
+
+       Py_XDECREF(reg);
+
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(list__doc__,
+"list() -- Return a list (tuple) of element IDs in (name,index) tuple.");
+
+static PyObject *
+pyalsamixer_list(struct pyalsamixer *self, PyObject *args)
+{
+       PyObject *t, *v;
+       int i, count;
+       snd_mixer_elem_t *elem;
+
+       count = snd_mixer_get_count(self->handle);
+       t = PyTuple_New(count);
+       if (count == 0)
+               return t;
+       elem = snd_mixer_first_elem(self->handle);
+       for (i = 0; i < count; i++) {
+               v = NULL;
+               if (elem) {
+                       v = PyTuple_New(2);
+                       PyTuple_SET_ITEM(v, 0, PyString_FromString(snd_mixer_selem_get_name(elem)));
+                       PyTuple_SET_ITEM(v, 1, PyInt_FromLong(snd_mixer_selem_get_index(elem)));
+               }
+               if (v == NULL || elem == NULL) {
+                       v = Py_None;
+                       Py_INCREF(v);
+               }
+               PyTuple_SET_ITEM(t, i, v);
+               elem = snd_mixer_elem_next(elem);
+       }
+       return t;
+}
+
+PyDoc_STRVAR(alsamixerinit__doc__,
+"Mixer([mode=0])\n"
+"  -- Open an ALSA mixer device.\n");
+
+static int
+pyalsamixer_init(struct pyalsamixer *pymix, PyObject *args, PyObject *kwds)
+{
+       int mode = 0, err;
+
+       static char * kwlist[] = { "mode", NULL };
+
+       pymix->handle = NULL;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &mode))
+               return -1;
+
+       err = snd_mixer_open(&pymix->handle, mode);
+       if (err < 0) {
+               PyErr_Format(PyExc_IOError,
+                            "Alsamixer open error: %s", strerror(-err));
+               return -1;
+       }
+
+       return 0;
+}
+
+static void
+pyalsamixer_dealloc(struct pyalsamixer *self)
+{
+       if (self->handle != NULL)
+               snd_mixer_close(self->handle);
+
+       self->ob_type->tp_free(self);
+}
+
+static PyGetSetDef pyalsamixer_getseters[] = {
+
+       {"count",       (getter)pyalsamixer_getcount,   NULL,   "mixer element count",          NULL},
+
+       {NULL}
+};
+
+static PyMethodDef pyalsamixer_methods[] = {
+
+       {"attach",      (PyCFunction)pyalsamixer_attach, METH_VARARGS|METH_KEYWORDS,    attach__doc__},
+       {"load",        (PyCFunction)pyalsamixer_load,  METH_NOARGS,    load__doc__},
+       {"free",        (PyCFunction)pyalsamixer_free,  METH_NOARGS,    free__doc__},
+       {"list",        (PyCFunction)pyalsamixer_list,  METH_NOARGS,    list__doc__},
+       {"handleEvents",(PyCFunction)pyalsamixer_handleevents,  METH_NOARGS,    handlevents__doc__},
+       {"registerPoll",(PyCFunction)pyalsamixer_registerpoll,  METH_VARARGS,   registerpoll__doc__},
+       {NULL}
+};
+
+static PyTypeObject pyalsamixer_type = {
+       PyObject_HEAD_INIT(0)
+       tp_name:        "alsamixer.Mixer",
+       tp_basicsize:   sizeof(struct pyalsamixer),
+       tp_dealloc:     (destructor)pyalsamixer_dealloc,
+       tp_flags:       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+       tp_doc:         alsamixerinit__doc__,
+       tp_getset:      pyalsamixer_getseters,
+       tp_init:        (initproc)pyalsamixer_init,
+       tp_alloc:       PyType_GenericAlloc,
+       tp_new:         PyType_GenericNew,
+       tp_free:        PyObject_Del,
+       tp_methods:     pyalsamixer_methods,
+};
+
+/*
+ * mixer element section
+ */
+
+#define PYALSAMIXERELEMENT(v) (((v) == Py_None) ? NULL : \
+       ((struct pyalsamixerelement *)(v)))
+
+struct pyalsamixerelement {
+       PyObject_HEAD
+       PyObject *pyhandle;
+       PyObject *callback;
+       snd_mixer_t *handle;
+       snd_mixer_elem_t *elem;
+};
+
+static PyObject *
+pyalsamixerelement_getname(struct pyalsamixerelement *pyelem, void *priv)
+{
+       return PyString_FromString(snd_mixer_selem_get_name(pyelem->elem));
+}
+
+static PyObject *
+pyalsamixerelement_getindex(struct pyalsamixerelement *pyelem, void *priv)
+{
+       return PyInt_FromLong(snd_mixer_selem_get_index(pyelem->elem));
+}
+
+typedef unsigned int (*fcn1)(void *);
+
+static PyObject *
+pyalsamixerelement_bool(struct pyalsamixerelement *pyelem, void *fcn)
+{
+       int res = ((fcn1)fcn)(pyelem->elem);
+       if (res > 0)
+               Py_RETURN_TRUE;
+       else
+               Py_RETURN_FALSE;
+}
+
+static PyObject *
+pyalsamixerelement_getcapgroup(struct pyalsamixerelement *pyelem, void *priv)
+{
+       return PyInt_FromLong(snd_mixer_selem_get_capture_group(pyelem->elem));
+}
+
+PyDoc_STRVAR(ismono__doc__,
+"isMono([capture=False]]) -- Return if this control is mono.");
+
+static PyObject *
+pyalsamixerelement_ismono(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0;
+
+       if (!PyArg_ParseTuple(args, "|I", &dir))
+               return NULL;
+
+       if (dir == 0)
+               res = snd_mixer_selem_is_playback_mono(pyelem->elem);
+       else
+               res = snd_mixer_selem_is_capture_mono(pyelem->elem);
+       if (res > 0)
+               Py_RETURN_TRUE;
+       else
+               Py_RETURN_FALSE;
+}
+
+PyDoc_STRVAR(haschannel__doc__,
+"hasChannel([channel=ChannelId['MONO'], [capture=False]]) -- Return if channel exists.");
+
+static PyObject *
+pyalsamixerelement_haschannel(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, chn = SND_MIXER_SCHN_MONO;
+
+       if (!PyArg_ParseTuple(args, "|II", &chn, &dir))
+               return NULL;
+
+       if (dir == 0)
+               res = snd_mixer_selem_has_playback_channel(pyelem->elem, chn);
+       else
+               res = snd_mixer_selem_has_capture_channel(pyelem->elem, chn);
+       if (res > 0)
+               Py_RETURN_TRUE;
+       else
+               Py_RETURN_FALSE;
+}
+
+PyDoc_STRVAR(hasvolume__doc__,
+"hasVolume([capture=False]]) -- Return if volume exists.\n"
+"Note: String 'Joined' is returned when volume is joined.");
+
+static PyObject *
+pyalsamixerelement_hasvolume(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0;
+
+       if (!PyArg_ParseTuple(args, "|I", &dir))
+               return NULL;
+
+       if (dir == 0)
+               res = snd_mixer_selem_has_playback_volume(pyelem->elem);
+       else
+               res = snd_mixer_selem_has_capture_volume(pyelem->elem);
+       if (res > 0) {
+               if (dir == 0)
+                       res = snd_mixer_selem_has_playback_volume_joined(pyelem->elem);
+               else
+                       res = snd_mixer_selem_has_capture_volume_joined(pyelem->elem);
+               if (res > 0)
+                       return Py_BuildValue("s", "Joined");
+               Py_RETURN_TRUE;
+       } else {
+               Py_RETURN_FALSE;
+       }
+}
+
+PyDoc_STRVAR(hasswitch__doc__,
+"hasSwitch([capture=False]) -- Return if switch exists."
+"Note: String 'Joined' is returned when switch is joined.");
+
+static PyObject *
+pyalsamixerelement_hasswitch(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0;
+
+       if (!PyArg_ParseTuple(args, "|I", &dir))
+               return NULL;
+
+       if (dir == 0)
+               res = snd_mixer_selem_has_playback_switch(pyelem->elem);
+       else
+               res = snd_mixer_selem_has_capture_switch(pyelem->elem);
+       if (res > 0) {
+               if (dir == 0)
+                       res = snd_mixer_selem_has_playback_switch_joined(pyelem->elem);
+               else
+                       res = snd_mixer_selem_has_capture_switch_joined(pyelem->elem);
+               if (res > 0)
+                       return Py_BuildValue("s", "Joined");
+               Py_RETURN_TRUE;
+       } else {
+               Py_RETURN_FALSE;
+       }
+}
+
+PyDoc_STRVAR(getvolume__doc__,
+"getVolume([channel=ChannelId['MONO'], [capture=False]]) -- Get volume.");
+
+static PyObject *
+pyalsamixerelement_getvolume(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, chn = SND_MIXER_SCHN_MONO;
+       long val;
+
+       if (!PyArg_ParseTuple(args, "|II", &chn, &dir))
+               return NULL;
+
+       if (dir == 0)
+               res = snd_mixer_selem_get_playback_volume(pyelem->elem, chn, &val);
+       else
+               res = snd_mixer_selem_get_capture_volume(pyelem->elem, chn, &val);
+       if (res < 0) {
+                PyErr_Format(PyExc_RuntimeError, "Cannot get mixer volume (capture=%s, channel=%i): %s", dir ? "True" : "False", chn, snd_strerror(-res));
+                Py_RETURN_NONE;
+       }
+       return Py_BuildValue("i", res);
+}
+
+PyDoc_STRVAR(getvolumetuple__doc__,
+"getVolumeTuple([capture=False]]) -- Get volume and store result to tuple.");
+
+static PyObject *
+pyalsamixerelement_getvolumetuple(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, i, last;
+       long val;
+       PyObject *t;
+
+       if (!PyArg_ParseTuple(args, "|I", &dir))
+               return NULL;
+
+       if (dir == 0) {
+               if (snd_mixer_selem_is_playback_mono(pyelem->elem)) {
+                       t = PyTuple_New(1);
+                       if (!t)
+                               return NULL;
+                       res = snd_mixer_selem_get_playback_volume(pyelem->elem, SND_MIXER_SCHN_MONO, &val);
+                       if (res >= 0)
+                               PyTuple_SET_ITEM(t, 0, PyInt_FromLong(val));
+               } else {
+                       t = PyTuple_New(SND_MIXER_SCHN_LAST+1);
+                       if (!t)
+                               return NULL;
+                       for (i = last = 0; i <= SND_MIXER_SCHN_LAST; i++) {
+                               res = -1;
+                               if (snd_mixer_selem_has_playback_channel(pyelem->elem, i)) {
+                                       res = snd_mixer_selem_get_playback_volume(pyelem->elem, i, &val);
+                                       if (res >= 0) {
+                                               while (last < i) {
+                                                       Py_INCREF(Py_None);
+                                                       PyTuple_SET_ITEM(t, last, Py_None);
+                                                       last++;
+                                               }
+                                               PyTuple_SET_ITEM(t, i, PyInt_FromLong(val));
+                                               last++;
+                                       }
+                               }
+                       }
+                       _PyTuple_Resize(&t, last);
+               }
+       } else {
+               if (snd_mixer_selem_is_capture_mono(pyelem->elem)) {
+                       t = PyTuple_New(1);
+                       if (!t)
+                               return NULL;
+                       res = snd_mixer_selem_get_capture_volume(pyelem->elem, SND_MIXER_SCHN_MONO, &val);
+                       if (res >= 0)
+                               PyTuple_SET_ITEM(t, 0, PyInt_FromLong(val));
+               } else {
+                       t = PyTuple_New(SND_MIXER_SCHN_LAST+1);
+                       if (!t)
+                               return NULL;
+                       for (i = last = 0; i <= SND_MIXER_SCHN_LAST; i++) {
+                               res = -1;
+                               if (snd_mixer_selem_has_capture_channel(pyelem->elem, i)) {
+                                       res = snd_mixer_selem_get_capture_volume(pyelem->elem, i, &val);
+                                       if (res >= 0) {
+                                               while (last < i) {
+                                                       Py_INCREF(Py_None);
+                                                       PyTuple_SET_ITEM(t, last, Py_None);
+                                                       last++;
+                                               }
+                                               PyTuple_SET_ITEM(t, i, PyInt_FromLong(val));
+                                       }
+                               }
+                       }
+                       _PyTuple_Resize(&t, last);
+               }
+       }
+       return t;
+}
+
+PyDoc_STRVAR(setvolume__doc__,
+"setVolume(value, [channel=ChannelId['MONO'], [capture=False]]) -- Set volume.");
+
+static PyObject *
+pyalsamixerelement_setvolume(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, chn = SND_MIXER_SCHN_MONO;
+       long val;
+
+       if (!PyArg_ParseTuple(args, "L|II", &val, &chn, &dir))
+               return NULL;
+       if (dir == 0)
+               res = snd_mixer_selem_set_playback_volume(pyelem->elem, chn, val);
+       else
+               res = snd_mixer_selem_set_capture_volume(pyelem->elem, chn, val);
+       if (res < 0)
+                PyErr_Format(PyExc_RuntimeError, "Cannot set mixer volume (capture=%s, channel=%i, value=%li): %s", dir ? "True" : "False", chn, val, snd_strerror(-res));
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(setvolumetuple__doc__,
+"setVolumeTuple(value, [capture=False]]) -- Set volume level from tuple.");
+
+static PyObject *
+pyalsamixerelement_setvolumetuple(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       PyObject *t, *o;
+       int i, res, dir = 0;
+       long val;
+
+       if (!PyArg_ParseTuple(args, "O|I", &t, &dir))
+               return NULL;
+       if (!PyTuple_Check(t))
+               return PyErr_Format(PyExc_RuntimeError, "Volume values in tuple are expected!");
+       for (i = 0; i < PyTuple_Size(t); i++) {
+               o = PyTuple_GetItem(t, i);
+               if (o == Py_None)
+                       continue;
+               if (!PyInt_Check(o)) {
+                       PyErr_Format(PyExc_RuntimeError, "Only None or Int types are expected!");
+                       break;
+               }
+               val = PyInt_AsLong(o);
+               if (dir == 0)
+                       res = snd_mixer_selem_set_playback_volume(pyelem->elem, i, val);
+               else
+                       res = snd_mixer_selem_set_capture_volume(pyelem->elem, i, val);
+               if (res < 0)
+                        PyErr_Format(PyExc_RuntimeError, "Cannot set mixer volume (capture=%s, channel=%i, value=%li): %s", dir ? "True" : "False", i, val, snd_strerror(-res));
+       }
+       Py_DECREF(t);
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(setvolumeall__doc__,
+"setVolumeAll(value, [capture=False]]) -- Set volume for all channels.");
+
+static PyObject *
+pyalsamixerelement_setvolumeall(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0;
+       long val;
+
+       if (!PyArg_ParseTuple(args, "L|I", &val, &dir))
+               return NULL;
+       if (dir == 0)
+               res = snd_mixer_selem_set_playback_volume_all(pyelem->elem, val);
+       else
+               res = snd_mixer_selem_set_capture_volume_all(pyelem->elem, val);
+       if (res < 0)
+                PyErr_Format(PyExc_RuntimeError, "Cannot set mixer volume (capture=%s, value=%li): %s", dir ? "True" : "False", val, snd_strerror(-res));
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(getrange__doc__,
+"getVolumeRange([capture=False]]) -- Get volume range in (min,max) tuple.");
+
+static PyObject *
+pyalsamixerelement_getrange(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int dir = 0, res;
+       long min, max;
+       PyObject *t;
+
+       if (!PyArg_ParseTuple(args, "|I", &dir))
+               return NULL;
+       if (dir == 0)
+               res = snd_mixer_selem_get_playback_volume_range(pyelem->elem, &min, &max);
+       else
+               res = snd_mixer_selem_get_capture_volume_range(pyelem->elem, &min, &max);
+       if (res < 0)
+                return PyErr_Format(PyExc_RuntimeError, "Cannot get mixer volume range (capture=%s): %s", dir ? "True" : "False", snd_strerror(-res));
+               t = PyTuple_New(2);
+       if (!t)
+               Py_RETURN_NONE;
+       PyTuple_SET_ITEM(t, 0, PyInt_FromLong(min));
+       PyTuple_SET_ITEM(t, 1, PyInt_FromLong(max));
+       return t;
+}
+
+PyDoc_STRVAR(setrange__doc__,
+"setVolumeRange(min, max, [capture=False]]) -- Set volume range limits.");
+
+static PyObject *
+pyalsamixerelement_setrange(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int dir = 0, res;
+       long min, max;
+
+       if (!PyArg_ParseTuple(args, "LL|I", &min, &max, &dir))
+               return NULL;
+       if (dir == 0)
+               res = snd_mixer_selem_set_playback_volume_range(pyelem->elem, min, max);
+       else
+               res = snd_mixer_selem_set_capture_volume_range(pyelem->elem, min, max);
+       if (res < 0)
+                return PyErr_Format(PyExc_RuntimeError, "Cannot set mixer volume range (min=%li,max=%li,capture=%s): %s", min, max, dir ? "True" : "False", snd_strerror(-res));
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(getswitch__doc__,
+"getSwitch([channel=ChannelId['MONO'], [capture=False]]) -- Get switch state.");
+
+static PyObject *
+pyalsamixerelement_getswitch(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, chn = SND_MIXER_SCHN_MONO, val;
+
+       if (!PyArg_ParseTuple(args, "|II", &chn, &dir))
+               return NULL;
+
+       if (dir == 0)
+               res = snd_mixer_selem_get_playback_switch(pyelem->elem, chn, &val);
+       else
+               res = snd_mixer_selem_get_capture_switch(pyelem->elem, chn, &val);
+       if (res < 0) {
+                PyErr_Format(PyExc_RuntimeError, "Cannot get mixer volume (capture=%s, channel=%i): %s", dir ? "True" : "False", chn, snd_strerror(-res));
+                Py_RETURN_NONE;
+       }
+       if (val) {
+               Py_RETURN_TRUE;
+       } else {
+               Py_RETURN_FALSE;
+       }
+}
+
+PyDoc_STRVAR(getswitchtuple__doc__,
+"getSwitchTuple([capture=False]]) -- Get switch state and store result to tuple.");
+
+static PyObject *
+pyalsamixerelement_getswitchtuple(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, i, last, val;
+       PyObject *t;
+
+       if (!PyArg_ParseTuple(args, "|I", &dir))
+               return NULL;
+
+       if (dir == 0) {
+               if (snd_mixer_selem_is_playback_mono(pyelem->elem)) {
+                       t = PyTuple_New(1);
+                       if (!t)
+                               return NULL;
+                       res = snd_mixer_selem_get_playback_switch(pyelem->elem, SND_MIXER_SCHN_MONO, &val);
+                       if (res >= 0)
+                               PyTuple_SET_ITEM(t, 0, get_bool(val));
+               } else {
+                       t = PyTuple_New(SND_MIXER_SCHN_LAST+1);
+                       if (!t)
+                               return NULL;
+                       for (i = last = 0; i <= SND_MIXER_SCHN_LAST; i++) {
+                               res = -1;
+                               if (snd_mixer_selem_has_playback_channel(pyelem->elem, i)) {
+                                       res = snd_mixer_selem_get_playback_switch(pyelem->elem, i, &val);
+                                       if (res >= 0) {
+                                               while (last < i) {
+                                                       Py_INCREF(Py_None);
+                                                       PyTuple_SET_ITEM(t, last, Py_None);
+                                                       last++;
+                                               }
+                                               PyTuple_SET_ITEM(t, i, get_bool(val));
+                                               last++;
+                                       }
+                               }
+                       }
+                       _PyTuple_Resize(&t, last);
+               }
+       } else {
+               if (snd_mixer_selem_is_capture_mono(pyelem->elem)) {
+                       t = PyTuple_New(1);
+                       if (!t)
+                               return NULL;
+                       res = snd_mixer_selem_get_capture_switch(pyelem->elem, SND_MIXER_SCHN_MONO, &val);
+                       if (res >= 0)
+                               PyTuple_SET_ITEM(t, 0, get_bool(val));
+               } else {
+                       t = PyTuple_New(SND_MIXER_SCHN_LAST+1);
+                       if (!t)
+                               return NULL;
+                       for (i = last = 0; i <= SND_MIXER_SCHN_LAST; i++) {
+                               res = -1;
+                               if (snd_mixer_selem_has_capture_channel(pyelem->elem, i)) {
+                                       res = snd_mixer_selem_get_capture_switch(pyelem->elem, i, &val);
+                                       if (res >= 0) {
+                                               while (last < i) {
+                                                       Py_INCREF(Py_None);
+                                                       PyTuple_SET_ITEM(t, last, Py_None);
+                                                       last++;
+                                               }
+                                               PyTuple_SET_ITEM(t, i, get_bool(val));
+                                       }
+                               }
+                       }
+                       _PyTuple_Resize(&t, last);
+               }
+       }
+       return t;
+}
+
+PyDoc_STRVAR(setswitch__doc__,
+"setSwitch(value, [channel=ChannelId['MONO'], [capture=False]]) -- Set switch state.");
+
+static PyObject *
+pyalsamixerelement_setswitch(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, chn = SND_MIXER_SCHN_MONO, val;
+
+       if (!PyArg_ParseTuple(args, "I|II", &val, &chn, &dir))
+               return NULL;
+       if (dir == 0)
+               res = snd_mixer_selem_set_playback_switch(pyelem->elem, chn, val);
+       else
+               res = snd_mixer_selem_set_capture_switch(pyelem->elem, chn, val);
+       if (res < 0)
+                PyErr_Format(PyExc_RuntimeError, "Cannot set mixer switch (capture=%s, channel=%i, value=%i): %s", dir ? "True" : "False", chn, val, snd_strerror(-res));
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(setswitchtuple__doc__,
+"setSwitchTuple(value, [capture=False]]) -- Set switch state from tuple.");
+
+static PyObject *
+pyalsamixerelement_setswitchtuple(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       PyObject *t, *o;
+       int i, res, dir = 0, val;
+
+       if (!PyArg_ParseTuple(args, "O|I", &t, &dir))
+               return NULL;
+       if (!PyTuple_Check(t))
+               return PyErr_Format(PyExc_RuntimeError, "Switch state values in tuple are expected!");
+       for (i = 0; i < PyTuple_Size(t); i++) {
+               o = PyTuple_GetItem(t, i);
+               if (o == Py_None)
+                       continue;
+               val = PyObject_IsTrue(o);
+               if (dir == 0)
+                       res = snd_mixer_selem_set_playback_switch(pyelem->elem, i, val);
+               else
+                       res = snd_mixer_selem_set_capture_switch(pyelem->elem, i, val);
+               if (res < 0)
+                        PyErr_Format(PyExc_RuntimeError, "Cannot set mixer switch (capture=%s, channel=%i, value=%i): %s", dir ? "True" : "False", i, val, snd_strerror(-res));
+       }
+       Py_DECREF(t);
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(setswitchall__doc__,
+"setSwitchAll(value, [capture=False]]) -- Set switch for all channels.");
+
+static PyObject *
+pyalsamixerelement_setswitchall(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, val;
+
+       if (!PyArg_ParseTuple(args, "I|I", &val, &dir))
+               return NULL;
+       if (dir == 0)
+               res = snd_mixer_selem_set_playback_switch_all(pyelem->elem, val);
+       else
+               res = snd_mixer_selem_set_capture_switch_all(pyelem->elem, val);
+       if (res < 0)
+                PyErr_Format(PyExc_RuntimeError, "Cannot set mixer switch state (capture=%s, value=%i): %s", dir ? "True" : "False", val, snd_strerror(-res));
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(getvolumedb__doc__,
+"getVolume_dB([channel=ChannelId['MONO'], [capture=False]]) -- Get volume in dB.");
+
+static PyObject *
+pyalsamixerelement_getvolumedb(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, chn = SND_MIXER_SCHN_MONO;
+       long val;
+
+       if (!PyArg_ParseTuple(args, "|II", &chn, &dir))
+               return NULL;
+
+       if (dir == 0)
+               res = snd_mixer_selem_get_playback_dB(pyelem->elem, chn, &val);
+       else
+               res = snd_mixer_selem_get_capture_dB(pyelem->elem, chn, &val);
+       if (res < 0) {
+                PyErr_Format(PyExc_RuntimeError, "Cannot get mixer volume in dB (capture=%s, channel=%i): %s", dir ? "True" : "False", chn, snd_strerror(-res));
+                Py_RETURN_NONE;
+       }
+       return Py_BuildValue("i", res);
+}
+
+PyDoc_STRVAR(setvolumedb__doc__,
+"setVolume_dB(value, [channel=ChannelId['MONO'], [capture=False], [dir=0]]) -- Set volume in dB.");
+
+static PyObject *
+pyalsamixerelement_setvolumedb(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, dir1 = 0, chn = SND_MIXER_SCHN_MONO;
+       long val;
+
+       if (!PyArg_ParseTuple(args, "L|III", &val, &chn, &dir, &dir1))
+               return NULL;
+       if (dir == 0)
+               res = snd_mixer_selem_set_playback_dB(pyelem->elem, chn, val, dir1);
+       else
+               res = snd_mixer_selem_set_capture_dB(pyelem->elem, chn, val, dir1);
+       if (res < 0)
+                PyErr_Format(PyExc_RuntimeError, "Cannot set mixer volume in dB (capture=%s, channel=%i, value=%li): %s", dir ? "True" : "False", chn, val, snd_strerror(-res));
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(setvolumealldb__doc__,
+"setVolumeAll_dB(value, [[capture=False], [dir=0]]) -- Set volume for all channels in dB.");
+
+static PyObject *
+pyalsamixerelement_setvolumealldb(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int res, dir = 0, dir1 = 0;
+       long val;
+
+       if (!PyArg_ParseTuple(args, "L|II", &val, &dir, &dir1))
+               return NULL;
+       if (dir == 0)
+               res = snd_mixer_selem_set_playback_dB_all(pyelem->elem, val, dir1);
+       else
+               res = snd_mixer_selem_set_capture_dB_all(pyelem->elem, val, dir1);
+       if (res < 0)
+                PyErr_Format(PyExc_RuntimeError, "Cannot set mixer volume in dB (capture=%s, value=%li): %s", dir ? "True" : "False", val, snd_strerror(-res));
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(getrangedb__doc__,
+"getVolumeRange_dB([capture=False]]) -- Get volume range in dB in (min,max) tuple.");
+
+static PyObject *
+pyalsamixerelement_getrangedb(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       int dir = 0, res;
+       long min, max;
+       PyObject *t;
+
+       if (!PyArg_ParseTuple(args, "|I", &dir))
+               return NULL;
+       if (dir == 0)
+               res = snd_mixer_selem_get_playback_dB_range(pyelem->elem, &min, &max);
+       else
+               res = snd_mixer_selem_get_capture_dB_range(pyelem->elem, &min, &max);
+       if (res < 0)
+                return PyErr_Format(PyExc_RuntimeError, "Cannot get mixer volume range in dB (capture=%s): %s", dir ? "True" : "False", snd_strerror(-res));
+               t = PyTuple_New(2);
+       if (!t)
+               Py_RETURN_NONE;
+       PyTuple_SET_ITEM(t, 0, PyInt_FromLong(min));
+       PyTuple_SET_ITEM(t, 1, PyInt_FromLong(max));
+       return t;
+}
+
+PyDoc_STRVAR(setcallback__doc__,
+"setCallback(callObj) -- Set callback object.\n"
+"Note: callObj might have callObj.callback attribute.\n");
+
+static PyObject *
+pyalsamixerelement_setcallback(struct pyalsamixerelement *pyelem, PyObject *args)
+{
+       PyObject *o;
+
+       if (!PyArg_ParseTuple(args, "O", &o))
+               return NULL;
+       if (o == Py_None) {
+               Py_XDECREF(pyelem->callback);
+               pyelem->callback = NULL;
+               snd_mixer_elem_set_callback(pyelem->elem, NULL);
+       } else {
+               Py_INCREF(o);
+               pyelem->callback = o;
+               snd_mixer_elem_set_callback_private(pyelem->elem, pyelem);
+               snd_mixer_elem_set_callback(pyelem->elem, element_callback);
+       }
+       Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(elementinit__doc__,
+"Element(mixer, name[, index=0])\n"
+"  -- Create a mixer element object.\n"
+"\n"
+"You may specify simple name and index like:\n"
+"element(mixer, \"PCM\", 0)\n");
+
+static int
+pyalsamixerelement_init(struct pyalsamixerelement *pyelem, PyObject *args, PyObject *kwds)
+{
+       PyObject *mixer;
+       char *name;
+       int index = 0;
+       snd_mixer_selem_id_t *id;
+       static char * kwlist[] = { "mixer", "name", "index", NULL };
+
+       snd_mixer_selem_id_alloca(&id);
+       pyelem->pyhandle = NULL;
+       pyelem->handle = NULL;
+       pyelem->elem = NULL;
+
+       if (!PyArg_ParseTupleAndKeywords(args, kwds, "Os|i",
+                                        kwlist, &mixer, &name, &index))
+               return -1;
+
+       if (mixer->ob_type != &pyalsamixer_type) {
+               PyErr_SetString(PyExc_TypeError, "bad type for mixer argument");
+               return -1;
+       }
+
+       pyelem->pyhandle = mixer;
+       Py_INCREF(mixer);
+       pyelem->handle = PYALSAMIXER(mixer)->handle;
+
+       snd_mixer_selem_id_set_name(id, name);
+       snd_mixer_selem_id_set_index(id, index);
+
+       pyelem->elem = snd_mixer_find_selem(pyelem->handle, id);
+       if (pyelem->elem == NULL) {
+               PyErr_Format(PyExc_IOError, "cannot find mixer element '%s',%i", name, index);
+               return -1;
+       }
+       
+       return 0;
+}
+
+static void
+pyalsamixerelement_dealloc(struct pyalsamixerelement *self)
+{
+       if (self->elem) {
+               Py_XDECREF(self->callback);
+               snd_mixer_elem_set_callback(self->elem, NULL);
+       }
+       if (self->pyhandle) {
+               Py_XDECREF(self->pyhandle);
+       }
+
+       self->ob_type->tp_free(self);
+}
+
+static PyGetSetDef pyalsamixerelement_getseters[] = {
+
+       {"name",        (getter)pyalsamixerelement_getname,     NULL,   "mixer element name",           NULL},
+       {"index",       (getter)pyalsamixerelement_getindex,    NULL,   "mixer element index",          NULL},
+
+       {"isActive",    (getter)pyalsamixerelement_bool,        NULL,   "element is in active status",  snd_mixer_selem_is_active},
+       {"isEnumerated",(getter)pyalsamixerelement_bool,        NULL,   "element is enumerated type",   snd_mixer_selem_is_enumerated},
+       {"hasCommonVolume",(getter)pyalsamixerelement_bool,     NULL,   "element has common volume control",    snd_mixer_selem_has_common_volume},
+       {"hasCommonSwitch",(getter)pyalsamixerelement_bool,     NULL,   "element has common switch control",    snd_mixer_selem_has_common_switch},
+       {"hasCaptureSwitchExclusive", (getter)pyalsamixerelement_bool, NULL, "element has exclusive capture switch",    snd_mixer_selem_has_capture_switch_exclusive},
+
+       {"getCaptureGroup",(getter)pyalsamixerelement_getcapgroup,      NULL,   "element get capture group",    NULL},
+       
+       {NULL}
+};
+
+static PyMethodDef pyalsamixerelement_methods[] = {
+
+       {"getVolume",   (PyCFunction)pyalsamixerelement_getvolume,      METH_VARARGS,   getvolume__doc__},
+       {"getVolumeTuple", (PyCFunction)pyalsamixerelement_getvolumetuple, METH_VARARGS,getvolumetuple__doc__},
+       {"setVolume",   (PyCFunction)pyalsamixerelement_setvolume,      METH_VARARGS,   setvolume__doc__},
+       {"setVolumeTuple", (PyCFunction)pyalsamixerelement_setvolumetuple, METH_VARARGS,setvolumetuple__doc__},
+       {"setVolumeAll",(PyCFunction)pyalsamixerelement_setvolumeall,   METH_VARARGS,   setvolumeall__doc__},
+       {"getVolumeRange", (PyCFunction)pyalsamixerelement_getrange,    METH_VARARGS,   getrange__doc__},
+       {"setVolumeRange", (PyCFunction)pyalsamixerelement_setrange,    METH_VARARGS,   setrange__doc__},
+
+       {"getVolume_dB",(PyCFunction)pyalsamixerelement_getvolumedb,    METH_VARARGS,   getvolumedb__doc__},
+       {"setVolume_dB",(PyCFunction)pyalsamixerelement_setvolumedb,    METH_VARARGS,   setvolumedb__doc__},
+       {"setVolumeAll_dB",(PyCFunction)pyalsamixerelement_setvolumealldb,METH_VARARGS, setvolumealldb__doc__},
+       {"getVolumeRange_dB", (PyCFunction)pyalsamixerelement_getrangedb,       METH_VARARGS,   getrangedb__doc__},
+
+       {"getSwitch",   (PyCFunction)pyalsamixerelement_getswitch,      METH_VARARGS,   getswitch__doc__},
+       {"getSwitchTuple", (PyCFunction)pyalsamixerelement_getswitchtuple, METH_VARARGS,getswitchtuple__doc__},
+       {"setSwitch",   (PyCFunction)pyalsamixerelement_setswitch,      METH_VARARGS,   setswitch__doc__},
+       {"setSwitchTuple", (PyCFunction)pyalsamixerelement_setswitchtuple, METH_VARARGS,setswitchtuple__doc__},
+       {"setSwitchAll",(PyCFunction)pyalsamixerelement_setswitchall,   METH_VARARGS,   setswitchall__doc__},
+
+       {"isMono",      (PyCFunction)pyalsamixerelement_ismono,         METH_VARARGS,   ismono__doc__},
+       {"hasChannel",  (PyCFunction)pyalsamixerelement_haschannel,     METH_VARARGS,   haschannel__doc__},
+       {"hasVolume",   (PyCFunction)pyalsamixerelement_hasvolume,      METH_VARARGS,   hasvolume__doc__},
+       {"hasSwitch",   (PyCFunction)pyalsamixerelement_hasswitch,      METH_VARARGS,   hasswitch__doc__},
+
+       {"setCallback", (PyCFunction)pyalsamixerelement_setcallback,    METH_VARARGS,   setcallback__doc__},
+
+       {NULL}
+};
+
+static PyTypeObject pyalsamixerelement_type = {
+       PyObject_HEAD_INIT(0)
+       tp_name:        "alsamixer.Element",
+       tp_basicsize:   sizeof(struct pyalsamixerelement),
+       tp_dealloc:     (destructor)pyalsamixerelement_dealloc,
+       tp_flags:       Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+       tp_doc:         elementinit__doc__,
+       tp_getset:      pyalsamixerelement_getseters,
+       tp_init:        (initproc)pyalsamixerelement_init,
+       tp_alloc:       PyType_GenericAlloc,
+       tp_new:         PyType_GenericNew,
+       tp_free:        PyObject_Del,
+       tp_methods:     pyalsamixerelement_methods,
+};
+
+/*
+ *
+ */
+
+static PyMethodDef pyalsamixerparse_methods[] = {
+       {NULL}
+};
+
+PyMODINIT_FUNC
+initalsamixer(void)
+{
+       PyObject *d, *d1, *l1, *o;
+       int i;
+
+       if (PyType_Ready(&pyalsamixer_type) < 0)
+               return;
+       if (PyType_Ready(&pyalsamixerelement_type) < 0)
+               return;
+
+       module = Py_InitModule3("alsamixer", pyalsamixerparse_methods, "libasound mixer wrapper");
+       if (module == NULL)
+               return;
+
+#if 0
+       buildin = PyImport_AddModule("__buildin__");
+       if (buildin == NULL)
+               return;
+       if (PyObject_SetAttrString(module, "__buildins__", buildin) < 0)
+               return;
+#endif
+
+       Py_INCREF(&pyalsamixer_type);
+       PyModule_AddObject(module, "Mixer", (PyObject *)&pyalsamixer_type);
+
+       Py_INCREF(&pyalsamixerelement_type);
+       PyModule_AddObject(module, "Element", (PyObject *)&pyalsamixerelement_type);
+
+       d = PyModule_GetDict(module);
+
+       /* ---- */
+
+       d1 = PyDict_New();
+
+#define add_space1(pname, name) { \
+       o = PyInt_FromLong(SND_MIXER_SCHN_##name); \
+       PyDict_SetItemString(d1, pname, o); \
+       Py_DECREF(o); }
+       
+       add_space1("Unknown", UNKNOWN);
+       add_space1("FrontLeft", FRONT_LEFT);
+       add_space1("FrontRight", FRONT_RIGHT);
+       add_space1("RearLeft", REAR_LEFT);
+       add_space1("RearRight", REAR_RIGHT);
+       add_space1("FrontCenter", FRONT_CENTER);
+       add_space1("Woofer", WOOFER);
+       add_space1("SideLeft", SIDE_LEFT);
+       add_space1("SideRight", SIDE_RIGHT);
+       add_space1("RearCenter", REAR_CENTER);
+       add_space1("Last", LAST);
+       add_space1("Mono", MONO);
+
+       PyDict_SetItemString(d, "ChannelId", d1);
+       Py_DECREF(d1);
+
+       /* ---- */
+
+       l1 = PyList_New(0);
+
+       for (i = 0; i <= SND_MIXER_SCHN_LAST; i++) {
+               o = PyString_FromString(snd_mixer_selem_channel_name(i));
+               PyList_Append(l1, o);
+               Py_DECREF(o);
+       }
+
+       PyDict_SetItemString(d, "ChannelName", l1);
+       Py_DECREF(l1);
+
+       /* ---- */
+
+       d1 = PyDict_New();
+       
+#define add_space2(pname, name) { \
+       o = PyInt_FromLong(SND_MIXER_SABSTRACT_##name); \
+       PyDict_SetItemString(d1, pname, o); \
+       Py_DECREF(o); }
+       
+       add_space2("None", NONE);
+       add_space2("Basic", BASIC);
+
+       PyDict_SetItemString(d, "RegoptAbstract", d1);
+       Py_DECREF(d1);
+       
+       /* ---- */
+
+       d1 = PyDict_New();
+       
+#define add_space3(pname, name) { \
+       o = PyInt_FromLong(SND_CTL_EVENT_MASK_##name); \
+       PyDict_SetItemString(d1, pname, o); \
+       Py_DECREF(o); }
+       
+       add_space3("Value", VALUE);
+       add_space3("Info", INFO);
+       add_space3("Add", ADD);
+       add_space3("TLV", TLV);
+
+       PyDict_SetItemString(d, "EventMask", d1);
+       Py_DECREF(d1);
+
+       o = PyInt_FromLong(SND_CTL_EVENT_MASK_REMOVE);
+       PyDict_SetItemString(d, "EventMaskRemove", o);
+       Py_DECREF(o);
+       
+       /* ---- */
+
+       main_interpreter = PyThreadState_Get()->interp;
+
+       if (PyErr_Occurred())
+               Py_FatalError("Cannot initialize module alsamixer");
+}
+
+/*
+ *  element event callback
+ */
+
+static int element_callback(snd_mixer_elem_t *elem, unsigned int mask)
+{
+       PyThreadState *tstate, *origstate;
+       struct pyalsamixerelement *pyelem;
+       PyObject *o, *t, *r;
+       int res = 0, inside = 1;
+
+       if (elem == NULL)
+               return -EINVAL;
+       pyelem = snd_mixer_elem_get_callback_private(elem);
+       if (pyelem == NULL || pyelem->callback == NULL)
+               return -EINVAL;
+
+       tstate = PyThreadState_New(main_interpreter);
+       origstate = PyThreadState_Swap(tstate);
+
+       o = PyObject_GetAttr(pyelem->callback, PyString_InternFromString("callback"));
+       if (!o) {
+               PyErr_Clear();
+               o = pyelem->callback;
+               inside = 0;
+       }
+
+       t = PyTuple_New(2);
+       if (t) {
+               if (PyTuple_SET_ITEM(t, 0, (PyObject *)pyelem))
+                       Py_INCREF(pyelem);
+               PyTuple_SET_ITEM(t, 1, PyInt_FromLong(mask));
+               r = PyObject_CallObject(o, t);
+               Py_DECREF(t);
+                       
+               if (r) {
+                       if (PyInt_Check(r)) {
+                               res = PyInt_AsLong(o);
+                       } else if (r == Py_None) {
+                               res = 0;
+                       }
+                       Py_DECREF(r);
+               } else {
+                       PyErr_Print();
+                       PyErr_Clear();
+                       res = -EIO;
+               }
+       }
+       if (inside) {
+               Py_DECREF(o);
+       }
+
+       PyThreadState_Swap(origstate);
+       PyThreadState_Delete(tstate);
+
+       return res;
+}
diff --git a/pyalsa/mixertest1.py b/pyalsa/mixertest1.py
new file mode 100755 (executable)
index 0000000..efe00c9
--- /dev/null
@@ -0,0 +1,55 @@
+#! /usr/bin/python
+# -*- Python -*-
+
+import alsamixer
+
+def print_elem(e):
+       direction = ["Playback", "Capture"]
+
+       print "Mixer Element '%s:%i':" % (e.name, e.index)
+       print '  isActive: %s' % e.isActive
+       print '  isEnumerated: %s' % e.isEnumerated
+       print '  hasCommonVolume: %s' % e.hasCommonVolume
+       print '  hasCommonSwitch: %s' % e.hasCommonSwitch
+       print '  hasCaptureSwitchExclusive: %s' % e.hasCaptureSwitchExclusive
+       if e.hasSwitch(True):
+               print '  getCaptureGroup: %s' % e.getCaptureGroup
+       for capture in [False, True]:
+               print '  is%sMono: %s' % (direction[capture], e.isMono(capture))
+               print '  has%sVolume: %s' % (direction[capture], e.hasVolume(capture))
+               if e.hasVolume(capture):
+                       print '  get%sVolumeRange: %s' % (direction[capture], e.getVolumeRange(capture))
+                       print '  get%sVolumeRange_dB: %s' % (direction[capture], e.getVolumeRange_dB(capture))
+                       print '  get%sVolumeTuple: %s' % (direction[capture], e.getVolumeTuple(capture)) 
+               print '  has%sSwitch: %s' % (direction[capture], e.hasSwitch(capture))
+               if e.hasSwitch(capture):
+                       print '  get%sSwitchTuple: %s' % (direction[capture], e.getSwitchTuple(capture)) 
+               for channel in range(alsamixer.ChannelId['Last']+1):
+                       if e.hasChannel(channel, capture):
+                               print  '  has%sChannel%s: %s' % (direction[capture], channel, alsamixer.ChannelName[channel])
+
+print 'ChannelId:'
+print alsamixer.ChannelId
+
+print 'ChannelName:'
+print alsamixer.ChannelName
+
+print 'RegoptAbstracts:'
+print alsamixer.RegoptAbstract
+
+print 'EventMask:'
+print alsamixer.EventMask
+
+print 'EventMaskRemove:', alsamixer.EventMaskRemove
+
+mixer = alsamixer.Mixer()
+mixer.attach()
+mixer.load()
+print 'Element Count:', mixer.count
+print 'Elements:'
+print mixer.list()
+element = alsamixer.Element(mixer, "PCM")
+element.setVolumeTuple((128, 128))
+print_elem(element)
+print_elem(alsamixer.Element(mixer, "Off-hook"))
+del mixer
diff --git a/pyalsa/mixertest2.py b/pyalsa/mixertest2.py
new file mode 100755 (executable)
index 0000000..414cf36
--- /dev/null
@@ -0,0 +1,44 @@
+#! /usr/bin/python
+# -*- Python -*-
+
+import alsamixer
+import select
+
+def parse_event_mask(events):
+       if events == 0:
+               return 'None'
+       if events == alsamixer.EventMaskRemove:
+               return 'Removed'
+       s = ''
+       for i in alsamixer.EventMask.keys():
+               if events & alsamixer.EventMask[i]:
+                       s += '%s ' % i
+       return s[:-1]
+
+def event_callback(element, events):
+
+       print 'CALLBACK (DEF)! [%s] %s:%i' % (parse_event_mask(events), element.name, element.index)
+       print '  ', element.getVolumeTuple(), element.getSwitchTuple()
+
+
+class MyElementEvent:
+
+       def callback(self, element, events):
+               print 'CALLBACK (CLASS)! [%s] %s:%i' % (parse_event_mask(events), element.name, element.index)
+               print '  ', element.getVolumeTuple(), element.getSwitchTuple()
+
+
+mixer = alsamixer.Mixer()
+mixer.attach()
+mixer.load()
+element1 = alsamixer.Element(mixer, "Front")
+element1.setCallback(event_callback)
+element2 = alsamixer.Element(mixer, "PCM")
+element2.setCallback(MyElementEvent())
+
+poller = select.poll()
+mixer.registerPoll(poller)
+while True:
+       poller.poll()
+       print 'Poll OK!'
+       mixer.handleEvents()
diff --git a/setup.py b/setup.py
new file mode 100755 (executable)
index 0000000..ac8ce9d
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,29 @@
+#! /usr/bin/python
+# -*- Python -*-
+
+import os
+import sys
+from distutils.core import setup, Extension
+
+setup(
+       name='pyalsa',
+        version='0.1',
+        author="The ALSA Team",
+        author_email='alsa-devel@alsa-project.org',
+        ext_modules=[
+          Extension('pyalsa.alsamixer', ['pyalsa/alsamixer.c'],
+                    include_dirs=[],
+                    library_dirs=[],
+                    libraries=['asound'])],
+        packages=['pyalsa'],
+        scripts=[]
+)
+
+uname = os.uname()
+a = 'build/lib.%s-%s-%s' % (uname[0].lower(), uname[4], sys.version[:3])
+for f in ['alsamixer.so']:
+        if not os.path.exists('pyalsa/%s' % f):
+                a = '../build/lib.%s-%s-%s/pyalsa/%s' % (uname[0].lower(),
+                        uname[4], sys.version[:3], f)
+                print a, f
+                os.symlink(a, 'pyalsa/%s' % f)