From: Jaroslav Kysela Date: Wed, 11 Jul 2007 08:10:12 +0000 (+0200) Subject: mixer simple basic abstraction - added python binding X-Git-Tag: v1.0.15rc1~18 X-Git-Url: https://git.alsa-project.org/?a=commitdiff_plain;h=e0d7bfcea69950c5ac1e35b48365fa5b5970f6d2;p=alsa-lib.git mixer simple basic abstraction - added python binding reasons: - rapid development - class-like code structure - more readable code features: - hcontrol binding is managed from python (opportunity to create virtual mixer without driver or join multiple cards to behave as one) --- diff --git a/configure.in b/configure.in index db5894da..9e0c57e5 100644 --- a/configure.in +++ b/configure.in @@ -336,6 +336,23 @@ AC_ARG_ENABLE(instr, 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_HWDEP, test x$build_hwdep = xyes) 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]) diff --git a/modules/mixer/simple/Makefile.am b/modules/mixer/simple/Makefile.am index 95b6a51b..d287c7e5 100644 --- a/modules/mixer/simple/Makefile.am +++ b/modules/mixer/simple/Makefile.am @@ -1,4 +1,5 @@ pkglibdir = @ALSA_PLUGIN_DIR@/smixer +pythonlibs = @PYTHON_LIBS@ AM_CFLAGS = -g -O2 -W -Wall @@ -8,6 +9,10 @@ pkglib_LTLIBRARIES = smixer-sbase.la \ smixer-ac97.la \ smixer-hda.la +if BUILD_PYTHON +pkglib_LTLIBRARIES += smixer-python.la +endif + noinst_HEADERS = sbase.h smixer_sbase_la_SOURCES = sbase.c @@ -21,3 +26,9 @@ smixer_ac97_la_LIBADD = ../../../src/libasound.la 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 --git a/modules/mixer/simple/python.c b/modules/mixer/simple/python.c new file mode 100644 index 00000000..ce9c8fd4 --- /dev/null +++ b/modules/mixer/simple/python.c @@ -0,0 +1,1002 @@ +/* + * Mixer Interface - python binding simple abstact module + * Copyright (c) 2007 by Jaroslav Kysela + * + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include "config.h" +#include "asoundlib.h" +#include "mixer_abst.h" +#include + +struct python_priv { + int py_initialized; + PyObject *py_event_func; + PyObject *py_mdict; + PyObject *py_mixer; +}; + +#define SCRIPT ALSA_PLUGIN_DIR "/smixer/python/main.py" + +struct pymelem { + PyObject_HEAD + sm_selem_t selem; + PyObject *py_mixer; + snd_mixer_elem_t *melem; +}; + +struct pymixer { + PyObject_HEAD + snd_mixer_class_t *class; + snd_mixer_t *mixer; + PyObject *mdict; + int hctl_count; + void **hctl; + int helem_count; + void **helem; + int melem_count; + void **melem; +}; + +static PyInterpreterState *main_interpreter; + +static void *get_C_ptr(PyObject *obj, const char *attr) +{ + PyObject *o; + + o = PyObject_GetAttr(obj, PyString_InternFromString(attr)); + if (!o) { + PyErr_Format(PyExc_TypeError, "missing '%s' attribute", attr); + return NULL; + } + if (!PyInt_Check(o)) { + PyErr_Format(PyExc_TypeError, "'%s' attribute is not integer", attr); + return NULL; + } + return (void *)PyInt_AsLong(o); +} + +static struct pymelem *melem_to_pymelem(snd_mixer_elem_t *elem) +{ + return (struct pymelem *)((char *)snd_mixer_elem_get_private(elem) - offsetof(struct pymelem, selem)); +} + +static int pcall(struct pymelem *pymelem, const char *attr, PyObject *args, PyObject **_res) +{ + PyObject *obj = (PyObject *)pymelem, *res; + int xres = 0; + + if (_res) + *_res = NULL; + obj = PyObject_GetAttr(obj, PyString_InternFromString(attr)); + if (!obj) { + PyErr_Format(PyExc_TypeError, "missing '%s' attribute", attr); + PyErr_Print(); + PyErr_Clear(); + Py_DECREF(args); + return -EIO; + } + res = PyObject_CallObject(obj, args); + Py_XDECREF(args); + if (res == NULL) { + PyErr_Print(); + PyErr_Clear(); + return -EIO; + } + if (_res && PyTuple_Check(res)) { + *_res = res; + res = PyTuple_GetItem(res, 0); + } + if (PyInt_Check(res)) { + xres = PyInt_AsLong(res); + } else if (res == Py_None) { + xres = 0; + } else if (PyBool_Check(res)) { + xres = res == Py_True; + } else { + PyErr_Format(PyExc_TypeError, "wrong result from '%s'!", attr); + PyErr_Print(); + PyErr_Clear(); + Py_DECREF(res); + if (_res) + *_res = NULL; + return -EIO; + } + if (_res && *_res) + return xres; + Py_DECREF(res); + return xres; +} + +static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + char *s, fcn[32] = "opsIs"; + int res, xdir = 1, xval = 0; + + switch (cmd) { + case SM_OPS_IS_ACTIVE: s = "Active"; xdir = 0; break; + case SM_OPS_IS_MONO: s = "Mono"; break; + case SM_OPS_IS_CHANNEL: s = "Channel"; xval = 1; break; + case SM_OPS_IS_ENUMERATED: s = "Enumerated"; xdir = val == 1; break; + case SM_OPS_IS_ENUMCNT: s = "EnumCnt"; break; + default: + return 1; + } + strcat(fcn, s); + + obj1 = PyTuple_New(xdir + xval); + if (xdir) { + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + if (xval) + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(val)); + } + res = pcall(pymelem, fcn, obj1, NULL); + return res < 0 ? 0 : res; +} + +static int get_x_range_ops(snd_mixer_elem_t *elem, int dir, + long *min, long *max, const char *attr) +{ + PyObject *obj1, *res; + struct pymelem *pymelem = melem_to_pymelem(elem); + int err; + + obj1 = PyTuple_New(1); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + err = pcall(pymelem, attr, obj1, &res); + if (err >= 0) { + err = !PyInt_Check(PyTuple_GetItem(res, 1)) || !PyInt_Check(PyTuple_GetItem(res, 2)); + if (err) { + err = !PyLong_Check(PyTuple_GetItem(res, 1)) || !PyLong_Check(PyTuple_GetItem(res, 2)); + if (err) { + PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); + PyErr_Print(); + PyErr_Clear(); + err = -EIO; + } else { + *min = PyLong_AsLong(PyTuple_GetItem(res, 1)); + *max = PyLong_AsLong(PyTuple_GetItem(res, 2)); + } + } else { + *min = PyInt_AsLong(PyTuple_GetItem(res, 1)); + *max = PyInt_AsLong(PyTuple_GetItem(res, 2)); + } + } + Py_XDECREF(res); + return err; +} + +static int get_range_ops(snd_mixer_elem_t *elem, int dir, + long *min, long *max) +{ + return get_x_range_ops(elem, dir, min, max, "opsGetRange"); +} + +static int set_range_ops(snd_mixer_elem_t *elem, int dir, + long min, long max) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(3); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(min)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(max)); + return pcall(pymelem, "opsGetRange", obj1, NULL); +} + +static int get_x_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, long *value, + const char *attr) +{ + PyObject *obj1, *res; + struct pymelem *pymelem = melem_to_pymelem(elem); + int err; + + obj1 = PyTuple_New(2); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); + err = pcall(pymelem, attr, obj1, &res); + if (err >= 0) { + err = !PyInt_Check(PyTuple_GetItem(res, 1)); + if (err) { + err = !PyLong_Check(PyTuple_GetItem(res, 1)); + if (err) { + PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); + PyErr_Print(); + PyErr_Clear(); + err = -EIO; + } else { + *value = PyLong_AsLong(PyTuple_GetItem(res, 1)); + } + } else { + *value = PyInt_AsLong(PyTuple_GetItem(res, 1)); + } + } + Py_XDECREF(res); + return err; +} + +static int get_volume_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, long *value) +{ + return get_x_ops(elem, dir, channel, value, "opsGetVolume"); +} + +static int get_switch_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, int *value) +{ + long value1; + int res; + res = get_x_ops(elem, dir, channel, &value1, "opsGetSwitch"); + *value = value1; + return res; +} + +static int get_dB_ops(snd_mixer_elem_t *elem, + int dir, + snd_mixer_selem_channel_id_t channel, + long *value) +{ + return get_x_ops(elem, dir, channel, value, "opsGetDB"); +} + +static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir, + long *min, long *max) +{ + return get_x_range_ops(elem, dir, min, max, "opsGetDBRange"); +} + +static int set_volume_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, long value) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(3); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(value)); + return pcall(pymelem, "opsSetVolume", obj1, NULL); +} + + +static int set_switch_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, int value) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(3); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(value)); + return pcall(pymelem, "opsSetSwitch", obj1, NULL); +} + +static int set_dB_ops(snd_mixer_elem_t *elem, int dir, + snd_mixer_selem_channel_id_t channel, + long db_gain, int xdir) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(4); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(db_gain)); + PyTuple_SET_ITEM(obj1, 3, PyInt_FromLong(xdir)); + return pcall(pymelem, "opsSetDB", obj1, NULL); +} + +static int enum_item_name_ops(snd_mixer_elem_t *elem, + unsigned int item, + size_t maxlen, char *buf) +{ + PyObject *obj1, *res; + struct pymelem *pymelem = melem_to_pymelem(elem); + int err; + unsigned int len; + char *s; + + obj1 = PyTuple_New(1); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(item)); + err = pcall(pymelem, "opsGetEnumItemName", obj1, &res); + if (err >= 0) { + err = !PyString_Check(PyTuple_GetItem(res, 1)); + if (err) { + PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); + PyErr_Print(); + PyErr_Clear(); + err = -EIO; + } else { + s = PyString_AsString(PyTuple_GetItem(res, 1)); + len = strlen(s); + if (maxlen - 1 > len) + len = maxlen - 1; + memcpy(buf, s, len); + buf[len] = '\0'; + } + } + Py_XDECREF(res); + return err; +} + +static int get_enum_item_ops(snd_mixer_elem_t *elem, + snd_mixer_selem_channel_id_t channel, + unsigned int *itemp) +{ + PyObject *obj1, *res; + struct pymelem *pymelem = melem_to_pymelem(elem); + int err; + + obj1 = PyTuple_New(1); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(channel)); + err = pcall(pymelem, "opsGetEnumItem", obj1, &res); + if (err >= 0) { + err = !PyInt_Check(PyTuple_GetItem(res, 1)); + if (err) { + PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); + PyErr_Print(); + PyErr_Clear(); + err = -EIO; + } else { + *itemp = PyInt_AsLong(PyTuple_GetItem(res, 1)); + } + } + Py_XDECREF(res); + return err; +} + +static int set_enum_item_ops(snd_mixer_elem_t *elem, + snd_mixer_selem_channel_id_t channel, + unsigned int item) +{ + PyObject *obj1; + struct pymelem *pymelem = melem_to_pymelem(elem); + + obj1 = PyTuple_New(2); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(channel)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(item)); + return pcall(pymelem, "opsSetEnumItem", obj1, NULL); +} + +static struct sm_elem_ops simple_python_ops = { + .is = is_ops, + .get_range = get_range_ops, + .get_dB_range = get_dB_range_ops, + .set_range = set_range_ops, + .get_volume = get_volume_ops, + .get_dB = get_dB_ops, + .set_volume = set_volume_ops, + .set_dB = set_dB_ops, + .get_switch = get_switch_ops, + .set_switch = set_switch_ops, + .enum_item_name = enum_item_name_ops, + .get_enum_item = get_enum_item_ops, + .set_enum_item = set_enum_item_ops +}; + +static void selem_free(snd_mixer_elem_t *elem) +{ + sm_selem_t *simple = snd_mixer_elem_get_private(elem); + + if (simple->id) { + snd_mixer_selem_id_free(simple->id); + simple->id = NULL; + } +} + +static PyObject * +pymelem_cap(struct pymelem *pymelem ATTRIBUTE_UNUSED, void *priv) +{ + return PyInt_FromLong((long)priv); +} + +static PyObject * +pymelem_get_caps(struct pymelem *pymelem, void *priv ATTRIBUTE_UNUSED) +{ + return PyInt_FromLong(pymelem->selem.caps); +} + +static PyObject * +pymelem_get_name(struct pymelem *pymelem, void *priv ATTRIBUTE_UNUSED) +{ + return PyString_FromString(snd_mixer_selem_id_get_name(pymelem->selem.id)); +} + +static PyObject * +pymelem_get_index(struct pymelem *pymelem, void *priv ATTRIBUTE_UNUSED) +{ + return PyInt_FromLong(snd_mixer_selem_id_get_index(pymelem->selem.id)); +} + +static int +pymelem_set_caps(struct pymelem *pymelem, PyObject *val, void *priv ATTRIBUTE_UNUSED) +{ + if (!PyInt_Check(val)) { + PyErr_SetString(PyExc_TypeError, "The last attribute value must be an integer"); + return -1; + } + pymelem->selem.caps = PyInt_AsLong(val); + return 0; +} + +static PyObject * +pymelem_ignore(struct pymelem *pymelem ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) +{ + Py_RETURN_NONE; +} + +static PyObject * +pymelem_ignore1(struct pymelem *pymelem ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) +{ + Py_RETURN_TRUE; +} + +static PyObject * +pymelem_error(struct pymelem *pymelem ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) +{ + return PyInt_FromLong(-EIO); +} + +static PyObject * +pymelem_attach(struct pymelem *pymelem, PyObject *args) +{ + PyObject *obj; + snd_hctl_elem_t *helem; + int err; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + helem = (snd_hctl_elem_t *)get_C_ptr(obj, "get_C_helem"); + if (helem == NULL) + return NULL; + err = snd_mixer_elem_attach(pymelem->melem, helem); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot attach hcontrol element to mixer element: %s", snd_strerror(err)); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +pymelem_detach(struct pymelem *pymelem, PyObject *args) +{ + PyObject *obj; + snd_hctl_elem_t *helem; + int err; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + helem = (snd_hctl_elem_t *)get_C_ptr(obj, "get_C_helem"); + if (helem == NULL) + return NULL; + err = snd_mixer_elem_detach(pymelem->melem, helem); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot detach hcontrol element to mixer element: %s", snd_strerror(err)); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +pymelem_event_info(struct pymelem *pymelem, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + return PyInt_FromLong(snd_mixer_elem_info(pymelem->melem)); +} + +static PyObject * +pymelem_event_value(struct pymelem *pymelem, PyObject *args) +{ + if (!PyArg_ParseTuple(args, "")) + return NULL; + return PyInt_FromLong(snd_mixer_elem_value(pymelem->melem)); +} + +static int +pymelem_init(struct pymelem *pymelem, PyObject *args, PyObject *kwds ATTRIBUTE_UNUSED) +{ + char *name; + long index, weight; + snd_mixer_selem_id_t *id; + int err; + + if (!PyArg_ParseTuple(args, "Osii", &pymelem->py_mixer, &name, &index, &weight)) + return -1; + memset(&pymelem->selem, 0, sizeof(pymelem->selem)); + if (snd_mixer_selem_id_malloc(&id)) + return -1; + snd_mixer_selem_id_set_name(id, name); + snd_mixer_selem_id_set_index(id, index); + pymelem->selem.id = id; + pymelem->selem.ops = &simple_python_ops; + err = snd_mixer_elem_new(&pymelem->melem, SND_MIXER_ELEM_SIMPLE, + weight, &pymelem->selem, selem_free); + if (err < 0) { + snd_mixer_selem_id_free(id); + return -1; + } + return 0; +} + +static void +pymelem_dealloc(struct pymelem *self) +{ + selem_free(self->melem); + self->ob_type->tp_free(self); +} + +static PyGetSetDef pymelem_getseters[] = { + {"CAP_GVOLUME", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_GVOLUME}, + {"CAP_GSWITCH", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_GSWITCH}, + {"CAP_PVOLUME", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PVOLUME}, + {"CAP_PVOLUME_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PVOLUME_JOIN}, + {"CAP_PSWITCH", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PSWITCH}, + {"CAP_PSWITCH_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PSWITCH_JOIN}, + {"CAP_CVOLUME", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CVOLUME}, + {"CAP_CVOLUME_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CVOLUME_JOIN}, + {"CAP_CSWITCH", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CSWITCH}, + {"CAP_CSWITCH_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CSWITCH_JOIN}, + {"CAP_CSWITCH_EXCL", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CSWITCH_EXCL}, + {"CAP_PENUM", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PENUM}, + {"CAP_CENUM", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CENUM}, + + {"caps", (getter)pymelem_get_caps, (setter)pymelem_set_caps, NULL, NULL}, + + {"name", (getter)pymelem_get_name, NULL, NULL, NULL}, + {"index", (getter)pymelem_get_index, NULL, NULL, NULL}, + + {NULL,NULL,NULL,NULL,NULL} +}; + +static PyMethodDef pymelem_methods[] = { + {"attach", (PyCFunction)pymelem_attach, METH_VARARGS, NULL}, + {"detach", (PyCFunction)pymelem_detach, METH_VARARGS, NULL}, + + /* "default" functions - no functionality */ + {"opsIsActive", (PyCFunction)pymelem_ignore1, METH_VARARGS, NULL}, + {"opsIsMono", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, + {"opsIsChannel", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, + {"opsIsEnumerated", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, + {"opsIsEnumCnt", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, + + {"opsGetDB", (PyCFunction)pymelem_error, METH_VARARGS, NULL}, + + {"eventInfo", (PyCFunction)pymelem_event_info, METH_VARARGS, NULL}, + {"eventValue", (PyCFunction)pymelem_event_value, METH_VARARGS, NULL}, + + {NULL,NULL,0,NULL} +}; + +static PyTypeObject pymelem_type = { + PyObject_HEAD_INIT(0) + tp_name: "smixer_python.InternalMElement", + tp_basicsize: sizeof(struct pymelem), + tp_dealloc: (destructor)pymelem_dealloc, + tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + tp_doc: NULL /* mixerinit__doc__ */, + tp_getset: pymelem_getseters, + tp_init: (initproc)pymelem_init, + tp_alloc: PyType_GenericAlloc, + tp_new: PyType_GenericNew, + tp_free: PyObject_Del, + tp_methods: pymelem_methods, +}; + +static PyObject * +pymixer_attach_hctl(struct pymixer *pymixer, PyObject *args) +{ + PyObject *obj; + snd_hctl_t *hctl; + void **hctls; + int err; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + hctl = (snd_hctl_t *)get_C_ptr(obj, "get_C_hctl"); + if (hctl == NULL) + return NULL; + err = snd_mixer_attach_hctl(pymixer->mixer, hctl); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot attach hctl: %s", snd_strerror(err)); + return NULL; + } + hctls = realloc(pymixer->hctl, sizeof(void *) * (pymixer->hctl_count+1) * 2); + if (hctls == NULL) { + PyErr_SetString(PyExc_RuntimeError, "No enough memory"); + return NULL; + } + pymixer->hctl = hctls; + pymixer->hctl[pymixer->hctl_count*2] = (void *)hctl; + pymixer->hctl[pymixer->hctl_count*2+1] = (void *)obj; + pymixer->hctl_count++; + Py_INCREF(obj); + Py_RETURN_NONE; +} + +static PyObject * +pymixer_register(struct pymixer *pymixer, PyObject *args) +{ + int err; + + if (!PyArg_ParseTuple(args, "")) + return NULL; + err = snd_mixer_class_register(pymixer->class, pymixer->mixer); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot register mixer: %s", snd_strerror(err)); + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +pymixer_melement_new(struct pymixer *pymixer, PyObject *args) +{ + PyObject *obj, *obj1, *obj2; + char *class, *name; + long index, weight; + + if (!PyArg_ParseTuple(args, "ssii", &class, &name, &index, &weight)) + return NULL; + obj = PyDict_GetItemString(pymixer->mdict, class); + if (obj) { + obj1 = PyTuple_New(4); + if (PyTuple_SET_ITEM(obj1, 0, (PyObject *)pymixer)) + Py_INCREF((PyObject *)pymixer); + PyTuple_SET_ITEM(obj1, 1, PyString_FromString(name)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(index)); + PyTuple_SET_ITEM(obj1, 3, PyInt_FromLong(weight)); + obj2 = PyObject_CallObject(obj, obj1); + Py_XDECREF(obj1); + if (obj2) { + struct pymelem *pymelem = (struct pymelem *)obj2; + void **melems = realloc(pymixer->melem, sizeof(void *) * (pymixer->melem_count + 1) * 2); + if (melems == NULL) { + Py_DECREF(obj2); + return NULL; + } + melems[pymixer->melem_count*2] = pymelem->melem; + melems[pymixer->melem_count*2+1] = obj2; + Py_INCREF(obj2); + pymixer->melem = melems; + pymixer->melem_count++; + } + } else { + PyErr_Format(PyExc_RuntimeError, "Cannot find class '%s'", class); + return NULL; + } + return obj2; +} + +static PyObject * +pymixer_melement_add(struct pymixer *pymixer, PyObject *args) +{ + PyObject *obj; + struct pymelem *pymelem; + int err; + + if (!PyArg_ParseTuple(args, "O", &obj)) + return NULL; + pymelem = (struct pymelem *)obj; + err = snd_mixer_elem_add(pymelem->melem, pymixer->class); + if (err < 0) { + PyErr_Format(PyExc_RuntimeError, "Cannot add mixer element: %s", snd_strerror(err)); + return NULL; + } + Py_RETURN_NONE; +} + +static int +pymixer_init(struct pymixer *pymixer, PyObject *args, PyObject *kwds ATTRIBUTE_UNUSED) +{ + long class, mixer; + + if (!PyArg_ParseTuple(args, "iiO", &class, &mixer, &pymixer->mdict)) + return -1; + pymixer->class = (snd_mixer_class_t *)class; + pymixer->mixer = (snd_mixer_t *)mixer; + pymixer->hctl_count = 0; + pymixer->hctl = NULL; + pymixer->helem_count = 0; + pymixer->helem = NULL; + pymixer->melem_count = 0; + pymixer->melem = NULL; + return 0; +} + +static void +pymixer_free(struct pymixer *self) +{ + int idx; + + for (idx = 0; idx < self->hctl_count; idx++) { + snd_mixer_detach_hctl(self->mixer, self->hctl[idx*2]); + Py_DECREF((PyObject *)self->hctl[idx*2+1]); + } + if (self->hctl) + free(self->hctl); + self->hctl_count = 0; + self->hctl = NULL; + for (idx = 0; idx < self->helem_count; idx++) + Py_DECREF((PyObject *)self->helem[idx*2+1]); + if (self->helem) + free(self->helem); + self->helem_count = 0; + self->helem = NULL; + for (idx = 0; idx < self->melem_count; idx++) + Py_DECREF((PyObject *)self->melem[idx*2+1]); + if (self->melem) + free(self->melem); + self->melem_count = 0; + self->melem = NULL; +} + +static void +pymixer_dealloc(struct pymixer *self) +{ + pymixer_free(self); + self->ob_type->tp_free(self); +} + +static PyGetSetDef pymixer_getseters[] = { + {NULL,NULL,NULL,NULL,NULL} +}; + +static PyMethodDef pymixer_methods[] = { + {"attachHCtl", (PyCFunction)pymixer_attach_hctl, METH_VARARGS, NULL}, + {"register", (PyCFunction)pymixer_register, METH_VARARGS, NULL}, + {"newMElement", (PyCFunction)pymixer_melement_new, METH_VARARGS, NULL}, + {"addMElement", (PyCFunction)pymixer_melement_add, METH_VARARGS, NULL}, + {NULL,NULL,0,NULL} +}; + +static PyTypeObject pymixer_type = { + PyObject_HEAD_INIT(0) + tp_name: "smixer_python.InternalMixer", + tp_basicsize: sizeof(struct pymixer), + tp_dealloc: (destructor)pymixer_dealloc, + tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + tp_doc: NULL /* mixerinit__doc__ */, + tp_getset: pymixer_getseters, + tp_init: (initproc)pymixer_init, + tp_alloc: PyType_GenericAlloc, + tp_new: PyType_GenericNew, + tp_free: PyObject_Del, + tp_methods: pymixer_methods, +}; + +static PyMethodDef python_methods[] = { + {NULL, NULL, 0, NULL} +}; + +static PyObject *new_helem(struct python_priv *priv, snd_hctl_elem_t *helem) +{ + PyObject *obj, *py_hctl = NULL, *obj1, *obj2; + snd_hctl_t *hctl = snd_hctl_elem_get_hctl(helem); + struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; + int idx; + + for (idx = 0; idx < pymixer->hctl_count; idx++) { + if (pymixer->hctl[idx] == hctl) { + py_hctl = pymixer->hctl[idx*2+1]; + break; + } + } + if (py_hctl == NULL) + return NULL; + obj = PyDict_GetItemString(priv->py_mdict, "HElement"); + if (obj) { + obj1 = PyTuple_New(3); + if (PyTuple_SET_ITEM(obj1, 0, py_hctl)) + Py_INCREF(py_hctl); + PyTuple_SET_ITEM(obj1, 1, PyFloat_FromDouble(1)); + PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong((long)helem)); + obj2 = PyObject_CallObject(obj, obj1); + if (obj2 == NULL) { + PyErr_Print(); + PyErr_Clear(); + } + Py_XDECREF(obj1); + } else { + SNDERR("Unable to create InternalMixer object"); + return NULL; + } + if (obj2) { + struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; + void **helems = realloc(pymixer->helem, sizeof(void *) * (pymixer->helem_count + 1) * 2); + if (helems == NULL) { + Py_DECREF(obj2); + return NULL; + } + helems[pymixer->helem_count*2] = helem; + helems[pymixer->helem_count*2+1] = obj2; + Py_INCREF(obj2); + pymixer->helem = helems; + pymixer->helem_count++; + } + return obj2; +} + +static PyObject *find_helem(struct python_priv *priv, snd_hctl_elem_t *helem) +{ + struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; + int idx; + + for (idx = 0; idx < pymixer->helem_count; idx++) { + if (pymixer->helem[idx*2] == helem) + return (PyObject *)pymixer->helem[idx*2+1]; + } + return NULL; +} + +static PyObject *find_melem(struct python_priv *priv, snd_mixer_elem_t *melem) +{ + struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; + int idx; + + for (idx = 0; idx < pymixer->melem_count; idx++) { + if (pymixer->melem[idx*2] == melem) + return (PyObject *)pymixer->melem[idx*2+1]; + } + return NULL; +} + +int alsa_mixer_simple_event(snd_mixer_class_t *class, unsigned int mask, + snd_hctl_elem_t *helem, snd_mixer_elem_t *melem) +{ + struct python_priv *priv = snd_mixer_sbasic_get_private(class); + PyThreadState *tstate, *origstate; + PyObject *t, *o, *r; + int res = -ENOMEM; + + tstate = PyThreadState_New(main_interpreter); + origstate = PyThreadState_Swap(tstate); + + t = PyTuple_New(3); + if (t) { + PyTuple_SET_ITEM(t, 0, (PyObject *)PyInt_FromLong(mask)); + o = find_helem(priv, helem); + if (mask & SND_CTL_EVENT_MASK_ADD) { + if (o == NULL) + o = new_helem(priv, helem); + } + if (o == NULL) + return 0; + if (PyTuple_SET_ITEM(t, 1, o)) + Py_INCREF(o); + o = melem ? find_melem(priv, melem) : Py_None; + if (PyTuple_SET_ITEM(t, 2, o)) + Py_INCREF(o); + r = PyObject_CallObject(priv->py_event_func, t); + Py_DECREF(t); + if (r) { + if (PyInt_Check(r)) { + res = PyInt_AsLong(r); + } else if (r == Py_None) { + res = 0; + } + Py_DECREF(r); + } else { + PyErr_Print(); + PyErr_Clear(); + res = -EIO; + } + } + + return res; +} + +static void alsa_mixer_simple_free(snd_mixer_class_t *class) +{ + struct python_priv *priv = snd_mixer_sbasic_get_private(class); + + if (priv->py_mixer) { + pymixer_free((struct pymixer *)priv->py_mixer); + Py_DECREF(priv->py_mixer); + } + if (priv->py_initialized) { + Py_XDECREF(priv->py_event_func); + Py_Finalize(); + } + free(priv); +} + +int alsa_mixer_simple_finit(snd_mixer_class_t *class, + snd_mixer_t *mixer, + const char *device) +{ + struct python_priv *priv; + FILE *fp; + const char *file; + PyObject *obj, *obj1, *obj2, *py_mod, *mdict; + + priv = calloc(1, sizeof(*priv)); + if (priv == NULL) + return -ENOMEM; + + snd_mixer_sbasic_set_private(class, priv); + snd_mixer_sbasic_set_private_free(class, alsa_mixer_simple_free); + + file = getenv("ALSA_MIXER_SIMPLE_MPYTHON"); + if (file == NULL) + file = SCRIPT; + + fp = fopen(file, "r"); + if (fp == NULL) { + SNDERR("Unable to find python module '%s'", file); + return -ENODEV; + } + + Py_Initialize(); + if (PyType_Ready(&pymelem_type) < 0) + return -EIO; + if (PyType_Ready(&pymixer_type) < 0) + return -EIO; + Py_InitModule("smixer_python", python_methods); + priv->py_initialized = 1; + main_interpreter = PyThreadState_Get()->interp; + obj = PyImport_GetModuleDict(); + py_mod = PyDict_GetItemString(obj, "__main__"); + if (py_mod) { + mdict = priv->py_mdict = PyModule_GetDict(py_mod); + obj = PyString_FromString(file); + if (obj) + PyDict_SetItemString(mdict, "__file__", obj); + Py_XDECREF(obj); + obj = PyString_FromString(device); + if (obj) + PyDict_SetItemString(mdict, "device", obj); + Py_XDECREF(obj); + Py_INCREF(&pymixer_type); + PyModule_AddObject(py_mod, "InternalMElement", (PyObject *)&pymelem_type); + PyModule_AddObject(py_mod, "InternalMixer", (PyObject *)&pymixer_type); + obj = PyDict_GetItemString(mdict, "InternalMixer"); + if (obj) { + obj1 = PyTuple_New(3); + PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong((long)class)); + PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong((long)mixer)); + if (PyTuple_SET_ITEM(obj1, 2, mdict)) + Py_INCREF(mdict); + obj2 = PyObject_CallObject(obj, obj1); + Py_XDECREF(obj1); + PyDict_SetItemString(mdict, "mixer", obj2); + priv->py_mixer = obj2; + } else { + SNDERR("Unable to create InternalMixer object"); + return -EIO; + } + + + obj = PyRun_FileEx(fp, file, Py_file_input, mdict, mdict, 1); + if (obj == NULL) + PyErr_Print(); + Py_XDECREF(obj); + priv->py_event_func = PyDict_GetItemString(mdict, "event"); + if (priv->py_event_func == NULL) { + SNDERR("Unable to find python function 'event'"); + return -EIO; + } + } + return 0; +} diff --git a/modules/mixer/simple/python/common.py b/modules/mixer/simple/python/common.py new file mode 100644 index 00000000..d2a842a3 --- /dev/null +++ b/modules/mixer/simple/python/common.py @@ -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 --git a/modules/mixer/simple/python/hda.py b/modules/mixer/simple/python/hda.py new file mode 100644 index 00000000..b11c076f --- /dev/null +++ b/modules/mixer/simple/python/hda.py @@ -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 --git a/modules/mixer/simple/python/main.py b/modules/mixer/simple/python/main.py new file mode 100644 index 00000000..d3e69fb2 --- /dev/null +++ b/modules/mixer/simple/python/main.py @@ -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 --git a/src/conf/smixer.conf b/src/conf/smixer.conf index 53c44b62..f215661e 100644 --- a/src/conf/smixer.conf +++ b/src/conf/smixer.conf @@ -1,3 +1,4 @@ +_full smixer-python.so usb { searchl "USB" lib smixer-usb.so diff --git a/src/mixer/simple.c b/src/mixer/simple.c index 2d90dfd4..ca180542 100644 --- a/src/mixer/simple.c +++ b/src/mixer/simple.c @@ -38,7 +38,6 @@ #include #include "mixer_local.h" #include "mixer_simple.h" -#include "alisp.h" /** * \brief Register mixer simple element class diff --git a/src/mixer/simple_abst.c b/src/mixer/simple_abst.c index 5de2018b..22c90673 100644 --- a/src/mixer/simple_abst.c +++ b/src/mixer/simple_abst.c @@ -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 *class, const char *lib) { 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 *class, const char *lib) 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); - free(xlib); - return -ENXIO; + 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; + } } - 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); + 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; } + 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 *class, const char *lib, const char *searchl) 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; @@ -138,6 +209,8 @@ static int find_module(snd_mixer_class_t *class, snd_config_t *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; snd_config_for_each(j, jnext, n) { @@ -223,20 +296,6 @@ int snd_mixer_simple_basic_register(snd_mixer_t *mixer, 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_mixer_t *mixer, SNDERR("%s may be old or corrupted: consider to remove or fix it", file); goto __error; } - err = find_module(class, top); - snd_config_delete(top); - top = NULL; + 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); 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)