1/* 2 * Mixer Interface - python binding simple abstact module 3 * Copyright (c) 2007 by Jaroslav Kysela <perex@perex.cz> 4 * 5 * 6 * This library is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as 8 * published by the Free Software Foundation; either version 2.1 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 * 20 */ 21 22#include "Python.h" 23#include <stddef.h> 24#include <limits.h> 25#include "config.h" 26#include "asoundlib.h" 27#include "mixer_abst.h" 28 29#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) 30#pragma GCC diagnostic ignored "-Wstrict-aliasing" 31#endif 32 33struct python_priv { 34 int py_initialized; 35 PyObject *py_event_func; 36 PyObject *py_mdict; 37 PyObject *py_mixer; 38}; 39 40#define SCRIPT "smixer/python/main.py" 41 42struct pymelem { 43 PyObject_HEAD 44 sm_selem_t selem; 45 PyObject *py_mixer; 46 snd_mixer_elem_t *melem; 47}; 48 49struct pymixer { 50 PyObject_HEAD 51 snd_mixer_class_t *class; 52 snd_mixer_t *mixer; 53 PyObject *mdict; 54 int hctl_count; 55 void **hctl; 56 int helem_count; 57 void **helem; 58 int melem_count; 59 void **melem; 60}; 61 62static PyInterpreterState *main_interpreter; 63 64#if PY_MAJOR_VERSION >= 3 65 #define PyInt_FromLong PyLong_FromLong 66#endif 67 68static inline int get_long(PyObject *o, long *val) 69{ 70#if PY_MAJOR_VERSION < 3 71 if (PyInt_Check(o)) { 72 *val = PyInt_AsLong(o); 73 return 0; 74 } 75#endif 76 if (PyLong_Check(o)) { 77 *val = PyLong_AsLong(o); 78 return 0; 79 } 80 return 1; 81} 82 83static inline PyObject *InternFromString(const char *name) 84{ 85#if PY_MAJOR_VERSION < 3 86 return PyString_InternFromString(name); 87#else 88 return PyUnicode_InternFromString(name); 89#endif 90} 91 92static void *get_C_ptr(PyObject *obj, const char *attr) 93{ 94 PyObject *o; 95 long val; 96 97 o = PyObject_GetAttr(obj, InternFromString(attr)); 98 if (!o) { 99 PyErr_Format(PyExc_TypeError, "missing '%s' attribute", attr); 100 return NULL; 101 } 102 if (get_long(o, &val)) { 103 PyErr_Format(PyExc_TypeError, "'%s' attribute is not Int or Long", attr); 104 return NULL; 105 } 106 return (void *)val; 107} 108 109static struct pymelem *melem_to_pymelem(snd_mixer_elem_t *elem) 110{ 111 return (struct pymelem *)((char *)snd_mixer_elem_get_private(elem) - offsetof(struct pymelem, selem)); 112} 113 114static int pcall(struct pymelem *pymelem, const char *attr, PyObject *args, PyObject **_res) 115{ 116 PyObject *obj = (PyObject *)pymelem, *res; 117 long xres = 0; 118 119 if (_res) 120 *_res = NULL; 121 obj = PyObject_GetAttr(obj, InternFromString(attr)); 122 if (!obj) { 123 PyErr_Format(PyExc_TypeError, "missing '%s' attribute", attr); 124 PyErr_Print(); 125 PyErr_Clear(); 126 Py_DECREF(args); 127 return -EIO; 128 } 129 res = PyObject_CallObject(obj, args); 130 Py_XDECREF(args); 131 if (res == NULL) { 132 PyErr_Print(); 133 PyErr_Clear(); 134 return -EIO; 135 } 136 if (_res && PyTuple_Check(res)) { 137 *_res = res; 138 res = PyTuple_GetItem(res, 0); 139 } 140 if (PyLong_Check(res)) { 141 xres = PyLong_AsLong(res); 142#if PY_MAJOR_VERSION < 3 143 } else if (PyInt_Check(res)) { 144 xres = PyInt_AsLong(res); 145#endif 146 } else if (res == Py_None) { 147 xres = 0; 148 } else if (PyBool_Check(res)) { 149 xres = res == Py_True; 150 } else { 151 PyErr_Format(PyExc_TypeError, "wrong result from '%s'!", attr); 152 PyErr_Print(); 153 PyErr_Clear(); 154 Py_DECREF(res); 155 if (_res) 156 *_res = NULL; 157 return -EIO; 158 } 159 if (_res && *_res) 160 return xres; 161 Py_DECREF(res); 162 return xres; 163} 164 165static int is_ops(snd_mixer_elem_t *elem, int dir, int cmd, int val) 166{ 167 PyObject *obj1; 168 struct pymelem *pymelem = melem_to_pymelem(elem); 169 char *s, fcn[32] = "opsIs"; 170 int res, xdir = 1, xval = 0; 171 172 switch (cmd) { 173 case SM_OPS_IS_ACTIVE: s = "Active"; xdir = 0; break; 174 case SM_OPS_IS_MONO: s = "Mono"; break; 175 case SM_OPS_IS_CHANNEL: s = "Channel"; xval = 1; break; 176 case SM_OPS_IS_ENUMERATED: s = "Enumerated"; xdir = val == 1; break; 177 case SM_OPS_IS_ENUMCNT: s = "EnumCnt"; break; 178 default: 179 return 1; 180 } 181 strcat(fcn, s); 182 183 obj1 = PyTuple_New(xdir + xval); 184 if (xdir) { 185 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); 186 if (xval) 187 PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(val)); 188 } 189 res = pcall(pymelem, fcn, obj1, NULL); 190 return res < 0 ? 0 : res; 191} 192 193static int get_x_range_ops(snd_mixer_elem_t *elem, int dir, 194 long *min, long *max, const char *attr) 195{ 196 PyObject *obj1, *t1, *t2, *res; 197 struct pymelem *pymelem = melem_to_pymelem(elem); 198 int err; 199 200 obj1 = PyTuple_New(1); 201 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); 202 err = pcall(pymelem, attr, obj1, &res); 203 if (err >= 0) { 204 t1 = PyTuple_GetItem(res, 1); 205 t2 = PyTuple_GetItem(res, 2); 206 if (PyLong_Check(t1) && PyLong_Check(t2)) { 207 *min = PyLong_AsLong(PyTuple_GetItem(res, 1)); 208 *max = PyLong_AsLong(PyTuple_GetItem(res, 2)); 209 err = 0; 210#if PY_MAJOR_VERSION < 3 211 } else if (PyInt_Check(t1) && PyInt_Check(t2)) { 212 *min = PyInt_AsLong(PyTuple_GetItem(res, 1)); 213 *max = PyInt_AsLong(PyTuple_GetItem(res, 2)); 214 err = 0; 215#endif 216 } else { 217 PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); 218 PyErr_Print(); 219 PyErr_Clear(); 220 err = -EIO; 221 } 222 } 223 Py_XDECREF(res); 224 return err; 225} 226 227static int get_range_ops(snd_mixer_elem_t *elem, int dir, 228 long *min, long *max) 229{ 230 return get_x_range_ops(elem, dir, min, max, "opsGetRange"); 231} 232 233static int set_range_ops(snd_mixer_elem_t *elem, int dir, 234 long min, long max) 235{ 236 PyObject *obj1; 237 struct pymelem *pymelem = melem_to_pymelem(elem); 238 239 obj1 = PyTuple_New(3); 240 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); 241 PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(min)); 242 PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(max)); 243 return pcall(pymelem, "opsGetRange", obj1, NULL); 244} 245 246static int get_x_ops(snd_mixer_elem_t *elem, int dir, 247 long channel, long *value, 248 const char *attr) 249{ 250 PyObject *obj1, *t1, *res; 251 struct pymelem *pymelem = melem_to_pymelem(elem); 252 int err; 253 254 obj1 = PyTuple_New(2); 255 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); 256 PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); 257 err = pcall(pymelem, attr, obj1, &res); 258 if (err >= 0) { 259 t1 = PyTuple_GetItem(res, 1); 260 if (PyLong_Check(t1)) { 261 *value = PyLong_AsLong(t1); 262 err = 0; 263#if PY_MAJOR_VERSION < 3 264 } else if (PyInt_Check(t1)) { 265 *value = PyInt_AsLong(t1); 266 err = 0; 267#endif 268 } else { 269 PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); 270 PyErr_Print(); 271 PyErr_Clear(); 272 err = -EIO; 273 } 274 } 275 Py_XDECREF(res); 276 return err; 277} 278 279static int get_volume_ops(snd_mixer_elem_t *elem, int dir, 280 snd_mixer_selem_channel_id_t channel, long *value) 281{ 282 return get_x_ops(elem, dir, channel, value, "opsGetVolume"); 283} 284 285static int get_switch_ops(snd_mixer_elem_t *elem, int dir, 286 snd_mixer_selem_channel_id_t channel, int *value) 287{ 288 long value1; 289 int res; 290 res = get_x_ops(elem, dir, channel, &value1, "opsGetSwitch"); 291 *value = value1; 292 return res; 293} 294 295static int ask_vol_dB_ops(snd_mixer_elem_t *elem, 296 int dir, 297 long value, 298 long *dbValue) 299{ 300 return get_x_ops(elem, dir, value, dbValue, "opsGetVolDB"); 301} 302 303static int ask_dB_vol_ops(snd_mixer_elem_t *elem, 304 int dir, 305 long value, 306 long *dbValue, 307 int xdir) 308{ 309 PyObject *obj1, *t1, *res; 310 struct pymelem *pymelem = melem_to_pymelem(elem); 311 int err; 312 313 obj1 = PyTuple_New(3); 314 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); 315 PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(value)); 316 PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(xdir)); 317 err = pcall(pymelem, "opsGetDBVol", obj1, &res); 318 if (err >= 0) { 319 t1 = PyTuple_GetItem(res, 1); 320 if (PyLong_Check(t1)) { 321 *dbValue = PyLong_AsLong(t1); 322 err = 0; 323#if PY_MAJOR_VERSION < 3 324 } else if (PyInt_Check(t1)) { 325 *dbValue = PyInt_AsLong(t1); 326 err = 0; 327#endif 328 } else { 329 PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); 330 PyErr_Print(); 331 PyErr_Clear(); 332 err = -EIO; 333 } 334 } 335 Py_XDECREF(res); 336 return err; 337} 338 339static int get_dB_ops(snd_mixer_elem_t *elem, 340 int dir, 341 snd_mixer_selem_channel_id_t channel, 342 long *value) 343{ 344 return get_x_ops(elem, dir, channel, value, "opsGetDB"); 345} 346 347static int get_dB_range_ops(snd_mixer_elem_t *elem, int dir, 348 long *min, long *max) 349{ 350 return get_x_range_ops(elem, dir, min, max, "opsGetDBRange"); 351} 352 353static int set_volume_ops(snd_mixer_elem_t *elem, int dir, 354 snd_mixer_selem_channel_id_t channel, long value) 355{ 356 PyObject *obj1; 357 struct pymelem *pymelem = melem_to_pymelem(elem); 358 359 obj1 = PyTuple_New(3); 360 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); 361 PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); 362 PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(value)); 363 return pcall(pymelem, "opsSetVolume", obj1, NULL); 364} 365 366static int set_switch_ops(snd_mixer_elem_t *elem, int dir, 367 snd_mixer_selem_channel_id_t channel, int value) 368{ 369 PyObject *obj1; 370 struct pymelem *pymelem = melem_to_pymelem(elem); 371 372 obj1 = PyTuple_New(3); 373 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); 374 PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); 375 PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(value)); 376 return pcall(pymelem, "opsSetSwitch", obj1, NULL); 377} 378 379static int set_dB_ops(snd_mixer_elem_t *elem, int dir, 380 snd_mixer_selem_channel_id_t channel, 381 long db_gain, int xdir) 382{ 383 PyObject *obj1; 384 struct pymelem *pymelem = melem_to_pymelem(elem); 385 386 obj1 = PyTuple_New(4); 387 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(dir)); 388 PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(channel)); 389 PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(db_gain)); 390 PyTuple_SET_ITEM(obj1, 3, PyInt_FromLong(xdir)); 391 return pcall(pymelem, "opsSetDB", obj1, NULL); 392} 393 394static int enum_item_name_ops(snd_mixer_elem_t *elem, 395 unsigned int item, 396 size_t maxlen, char *buf) 397{ 398 PyObject *obj1, *obj2, *t1, *res; 399 struct pymelem *pymelem = melem_to_pymelem(elem); 400 int err; 401 unsigned int len; 402 char *s; 403 404 obj1 = PyTuple_New(1); 405 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(item)); 406 err = pcall(pymelem, "opsGetEnumItemName", obj1, &res); 407 if (err >= 0) { 408 t1 = PyTuple_GetItem(res, 1); 409 if (PyUnicode_Check(t1)) { 410 obj2 = PyUnicode_AsEncodedString(t1, "utf-8", "strict"); 411 if (obj2) { 412 s = PyBytes_AsString(obj2); 413 len = strlen(s); 414 if (maxlen - 1 > len) 415 len = maxlen - 1; 416 memcpy(buf, s, len); 417 buf[len] = '\0'; 418 Py_DECREF(obj2); 419 } else { 420 goto errlbl; 421 } 422#if PY_MAJOR_VERSION < 3 423 } else if (PyString_Check(t1)) { 424 s = PyString_AsString(t1); 425 len = strlen(s); 426 if (maxlen - 1 > len) 427 len = maxlen - 1; 428 memcpy(buf, s, len); 429 buf[len] = '\0'; 430#endif 431 } else { 432errlbl: 433 PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); 434 PyErr_Print(); 435 PyErr_Clear(); 436 err = -EIO; 437 } 438 } 439 Py_XDECREF(res); 440 return err; 441} 442 443static int get_enum_item_ops(snd_mixer_elem_t *elem, 444 snd_mixer_selem_channel_id_t channel, 445 unsigned int *itemp) 446{ 447 PyObject *obj1, *t1, *res; 448 struct pymelem *pymelem = melem_to_pymelem(elem); 449 int err; 450 451 obj1 = PyTuple_New(1); 452 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(channel)); 453 err = pcall(pymelem, "opsGetEnumItem", obj1, &res); 454 if (err >= 0) { 455 t1 = PyTuple_GetItem(res, 1); 456 if (PyLong_Check(t1)) { 457 *itemp = PyLong_AsLong(t1); 458 err = 0; 459#if PY_MAJOR_VERSION < 3 460 } else if (PyInt_Check(t1)) { 461 *itemp = PyInt_AsLong(t1); 462 err = 0; 463#endif 464 } else { 465 PyErr_Format(PyExc_TypeError, "wrong result (invalid tuple)"); 466 PyErr_Print(); 467 PyErr_Clear(); 468 err = -EIO; 469 } 470 } 471 Py_XDECREF(res); 472 return err; 473} 474 475static int set_enum_item_ops(snd_mixer_elem_t *elem, 476 snd_mixer_selem_channel_id_t channel, 477 unsigned int item) 478{ 479 PyObject *obj1; 480 struct pymelem *pymelem = melem_to_pymelem(elem); 481 482 obj1 = PyTuple_New(2); 483 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong(channel)); 484 PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong(item)); 485 return pcall(pymelem, "opsSetEnumItem", obj1, NULL); 486} 487 488static struct sm_elem_ops simple_python_ops = { 489 .is = is_ops, 490 .get_range = get_range_ops, 491 .get_dB_range = get_dB_range_ops, 492 .set_range = set_range_ops, 493 .ask_vol_dB = ask_vol_dB_ops, 494 .ask_dB_vol = ask_dB_vol_ops, 495 .get_volume = get_volume_ops, 496 .get_dB = get_dB_ops, 497 .set_volume = set_volume_ops, 498 .set_dB = set_dB_ops, 499 .get_switch = get_switch_ops, 500 .set_switch = set_switch_ops, 501 .enum_item_name = enum_item_name_ops, 502 .get_enum_item = get_enum_item_ops, 503 .set_enum_item = set_enum_item_ops 504}; 505 506static void selem_free(snd_mixer_elem_t *elem) 507{ 508 sm_selem_t *simple = snd_mixer_elem_get_private(elem); 509 510 if (simple->id) { 511 snd_mixer_selem_id_free(simple->id); 512 simple->id = NULL; 513 } 514} 515 516static PyObject * 517pymelem_cap(struct pymelem *pymelem ATTRIBUTE_UNUSED, void *priv) 518{ 519 return PyInt_FromLong((long)priv); 520} 521 522static PyObject * 523pymelem_get_caps(struct pymelem *pymelem, void *priv ATTRIBUTE_UNUSED) 524{ 525 return PyInt_FromLong(pymelem->selem.caps); 526} 527 528static PyObject * 529pymelem_get_name(struct pymelem *pymelem, void *priv ATTRIBUTE_UNUSED) 530{ 531 return PyUnicode_FromString(snd_mixer_selem_id_get_name(pymelem->selem.id)); 532} 533 534static PyObject * 535pymelem_get_index(struct pymelem *pymelem, void *priv ATTRIBUTE_UNUSED) 536{ 537 return PyInt_FromLong(snd_mixer_selem_id_get_index(pymelem->selem.id)); 538} 539 540static int 541pymelem_set_caps(struct pymelem *pymelem, PyObject *val, void *priv ATTRIBUTE_UNUSED) 542{ 543 if (PyLong_Check(val)) { 544 pymelem->selem.caps = PyLong_AsLong(val); 545 return 0; 546 } 547#if PY_MAJOR_VERSION < 3 548 if (PyInt_Check(val)) { 549 pymelem->selem.caps = PyInt_AsLong(val); 550 return 0; 551 } 552#endif 553 PyErr_SetString(PyExc_TypeError, "The last attribute value must be an integer"); 554 return -1; 555} 556 557static PyObject * 558pymelem_ignore(struct pymelem *pymelem ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) 559{ 560 Py_RETURN_NONE; 561} 562 563static PyObject * 564pymelem_ignore1(struct pymelem *pymelem ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) 565{ 566 Py_RETURN_TRUE; 567} 568 569static PyObject * 570pymelem_error(struct pymelem *pymelem ATTRIBUTE_UNUSED, PyObject *args ATTRIBUTE_UNUSED) 571{ 572 return PyInt_FromLong(-EIO); 573} 574 575static PyObject * 576pymelem_attach(struct pymelem *pymelem, PyObject *args) 577{ 578 PyObject *obj; 579 snd_hctl_elem_t *helem; 580 int err; 581 582 if (!PyArg_ParseTuple(args, "O", &obj)) 583 return NULL; 584 helem = (snd_hctl_elem_t *)get_C_ptr(obj, "get_C_helem"); 585 if (helem == NULL) 586 return NULL; 587 err = snd_mixer_elem_attach(pymelem->melem, helem); 588 if (err < 0) { 589 PyErr_Format(PyExc_RuntimeError, "Cannot attach hcontrol element to mixer element: %s", snd_strerror(err)); 590 return NULL; 591 } 592 Py_RETURN_NONE; 593} 594 595static PyObject * 596pymelem_detach(struct pymelem *pymelem, PyObject *args) 597{ 598 PyObject *obj; 599 snd_hctl_elem_t *helem; 600 int err; 601 602 if (!PyArg_ParseTuple(args, "O", &obj)) 603 return NULL; 604 helem = (snd_hctl_elem_t *)get_C_ptr(obj, "get_C_helem"); 605 if (helem == NULL) 606 return NULL; 607 err = snd_mixer_elem_detach(pymelem->melem, helem); 608 if (err < 0) { 609 PyErr_Format(PyExc_RuntimeError, "Cannot detach hcontrol element to mixer element: %s", snd_strerror(err)); 610 return NULL; 611 } 612 Py_RETURN_NONE; 613} 614 615static PyObject * 616pymelem_event_info(struct pymelem *pymelem, PyObject *args) 617{ 618 if (!PyArg_ParseTuple(args, "")) 619 return NULL; 620 return PyInt_FromLong(snd_mixer_elem_info(pymelem->melem)); 621} 622 623static PyObject * 624pymelem_event_value(struct pymelem *pymelem, PyObject *args) 625{ 626 if (!PyArg_ParseTuple(args, "")) 627 return NULL; 628 return PyInt_FromLong(snd_mixer_elem_value(pymelem->melem)); 629} 630 631static int 632pymelem_init(struct pymelem *pymelem, PyObject *args, PyObject *kwds ATTRIBUTE_UNUSED) 633{ 634 char *name; 635 long index, weight; 636 snd_mixer_selem_id_t *id; 637 int err; 638 639 if (!PyArg_ParseTuple(args, "Osii", &pymelem->py_mixer, &name, &index, &weight)) 640 return -1; 641 memset(&pymelem->selem, 0, sizeof(pymelem->selem)); 642 if (snd_mixer_selem_id_malloc(&id)) 643 return -1; 644 snd_mixer_selem_id_set_name(id, name); 645 snd_mixer_selem_id_set_index(id, index); 646 pymelem->selem.id = id; 647 pymelem->selem.ops = &simple_python_ops; 648 err = snd_mixer_elem_new(&pymelem->melem, SND_MIXER_ELEM_SIMPLE, 649 weight, &pymelem->selem, selem_free); 650 if (err < 0) { 651 snd_mixer_selem_id_free(id); 652 return -1; 653 } 654 return 0; 655} 656 657static void 658pymelem_dealloc(struct pymelem *self) 659{ 660 selem_free(self->melem); 661} 662 663static PyGetSetDef pymelem_getseters[] = { 664 {"CAP_GVOLUME", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_GVOLUME}, 665 {"CAP_GSWITCH", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_GSWITCH}, 666 {"CAP_PVOLUME", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PVOLUME}, 667 {"CAP_PVOLUME_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PVOLUME_JOIN}, 668 {"CAP_PSWITCH", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PSWITCH}, 669 {"CAP_PSWITCH_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PSWITCH_JOIN}, 670 {"CAP_CVOLUME", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CVOLUME}, 671 {"CAP_CVOLUME_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CVOLUME_JOIN}, 672 {"CAP_CSWITCH", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CSWITCH}, 673 {"CAP_CSWITCH_JOIN", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CSWITCH_JOIN}, 674 {"CAP_CSWITCH_EXCL", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CSWITCH_EXCL}, 675 {"CAP_PENUM", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_PENUM}, 676 {"CAP_CENUM", (getter)pymelem_cap, NULL, NULL, (void *)SM_CAP_CENUM}, 677 678 {"caps", (getter)pymelem_get_caps, (setter)pymelem_set_caps, NULL, NULL}, 679 680 {"name", (getter)pymelem_get_name, NULL, NULL, NULL}, 681 {"index", (getter)pymelem_get_index, NULL, NULL, NULL}, 682 683 {NULL,NULL,NULL,NULL,NULL} 684}; 685 686static PyMethodDef pymelem_methods[] = { 687 {"attach", (PyCFunction)pymelem_attach, METH_VARARGS, NULL}, 688 {"detach", (PyCFunction)pymelem_detach, METH_VARARGS, NULL}, 689 690 /* "default" functions - no functionality */ 691 {"opsIsActive", (PyCFunction)pymelem_ignore1, METH_VARARGS, NULL}, 692 {"opsIsMono", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, 693 {"opsIsChannel", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, 694 {"opsIsEnumerated", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, 695 {"opsIsEnumCnt", (PyCFunction)pymelem_ignore, METH_VARARGS, NULL}, 696 697 {"opsGetDB", (PyCFunction)pymelem_error, METH_VARARGS, NULL}, 698 699 {"eventInfo", (PyCFunction)pymelem_event_info, METH_VARARGS, NULL}, 700 {"eventValue", (PyCFunction)pymelem_event_value, METH_VARARGS, NULL}, 701 702 {NULL,NULL,0,NULL} 703}; 704 705static PyTypeObject pymelem_type = { 706 PyVarObject_HEAD_INIT(NULL, 0) 707 tp_name: "smixer_python.InternalMElement", 708 tp_basicsize: sizeof(struct pymelem), 709 tp_dealloc: (destructor)pymelem_dealloc, 710 tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 711 tp_doc: NULL /* mixerinit__doc__ */, 712 tp_getset: pymelem_getseters, 713 tp_init: (initproc)pymelem_init, 714 tp_alloc: PyType_GenericAlloc, 715 tp_new: PyType_GenericNew, 716 tp_free: PyObject_Del, 717 tp_methods: pymelem_methods, 718}; 719 720static PyObject * 721pymixer_attach_hctl(struct pymixer *pymixer, PyObject *args) 722{ 723 PyObject *obj; 724 snd_hctl_t *hctl; 725 void **hctls; 726 int err; 727 728 if (!PyArg_ParseTuple(args, "O", &obj)) 729 return NULL; 730 hctl = (snd_hctl_t *)get_C_ptr(obj, "get_C_hctl"); 731 if (hctl == NULL) 732 return NULL; 733 err = snd_mixer_attach_hctl(pymixer->mixer, hctl); 734 if (err < 0) { 735 PyErr_Format(PyExc_RuntimeError, "Cannot attach hctl: %s", snd_strerror(err)); 736 return NULL; 737 } 738 hctls = realloc(pymixer->hctl, sizeof(void *) * (pymixer->hctl_count+1) * 2); 739 if (hctls == NULL) { 740 PyErr_SetString(PyExc_RuntimeError, "No enough memory"); 741 return NULL; 742 } 743 pymixer->hctl = hctls; 744 pymixer->hctl[pymixer->hctl_count*2] = (void *)hctl; 745 pymixer->hctl[pymixer->hctl_count*2+1] = (void *)obj; 746 pymixer->hctl_count++; 747 Py_INCREF(obj); 748 Py_RETURN_NONE; 749} 750 751static PyObject * 752pymixer_register(struct pymixer *pymixer, PyObject *args) 753{ 754 int err; 755 756 if (!PyArg_ParseTuple(args, "")) 757 return NULL; 758 err = snd_mixer_class_register(pymixer->class, pymixer->mixer); 759 if (err < 0) { 760 PyErr_Format(PyExc_RuntimeError, "Cannot register mixer: %s", snd_strerror(err)); 761 return NULL; 762 } 763 Py_RETURN_NONE; 764} 765 766static PyObject * 767pymixer_melement_new(struct pymixer *pymixer, PyObject *args) 768{ 769 PyObject *obj, *obj1, *obj2; 770 char *class, *name; 771 long index, weight; 772 773 if (!PyArg_ParseTuple(args, "ssii", &class, &name, &index, &weight)) 774 return NULL; 775 obj = PyDict_GetItemString(pymixer->mdict, class); 776 if (obj) { 777 obj1 = PyTuple_New(4); 778 PyTuple_SET_ITEM(obj1, 0, (PyObject *)pymixer); 779 Py_INCREF((PyObject *)pymixer); 780 PyTuple_SET_ITEM(obj1, 1, PyUnicode_FromString(name)); 781 PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong(index)); 782 PyTuple_SET_ITEM(obj1, 3, PyInt_FromLong(weight)); 783 obj2 = PyObject_CallObject(obj, obj1); 784 Py_XDECREF(obj1); 785 if (obj2) { 786 struct pymelem *pymelem = (struct pymelem *)obj2; 787 void **melems = realloc(pymixer->melem, sizeof(void *) * (pymixer->melem_count + 1) * 2); 788 if (melems == NULL) { 789 Py_DECREF(obj2); 790 return NULL; 791 } 792 melems[pymixer->melem_count*2] = pymelem->melem; 793 melems[pymixer->melem_count*2+1] = obj2; 794 Py_INCREF(obj2); 795 pymixer->melem = melems; 796 pymixer->melem_count++; 797 } 798 } else { 799 PyErr_Format(PyExc_RuntimeError, "Cannot find class '%s'", class); 800 return NULL; 801 } 802 return obj2; 803} 804 805static PyObject * 806pymixer_melement_add(struct pymixer *pymixer, PyObject *args) 807{ 808 PyObject *obj; 809 struct pymelem *pymelem; 810 int err; 811 812 if (!PyArg_ParseTuple(args, "O", &obj)) 813 return NULL; 814 pymelem = (struct pymelem *)obj; 815 err = snd_mixer_elem_add(pymelem->melem, pymixer->class); 816 if (err < 0) { 817 PyErr_Format(PyExc_RuntimeError, "Cannot add mixer element: %s", snd_strerror(err)); 818 return NULL; 819 } 820 Py_RETURN_NONE; 821} 822 823static int 824pymixer_init(struct pymixer *pymixer, PyObject *args, PyObject *kwds ATTRIBUTE_UNUSED) 825{ 826 long class, mixer; 827 828 if (!PyArg_ParseTuple(args, "iiO", &class, &mixer, &pymixer->mdict)) 829 return -1; 830 pymixer->class = (snd_mixer_class_t *)class; 831 pymixer->mixer = (snd_mixer_t *)mixer; 832 pymixer->hctl_count = 0; 833 pymixer->hctl = NULL; 834 pymixer->helem_count = 0; 835 pymixer->helem = NULL; 836 pymixer->melem_count = 0; 837 pymixer->melem = NULL; 838 return 0; 839} 840 841static void 842pymixer_free(struct pymixer *self) 843{ 844 int idx; 845 846 for (idx = 0; idx < self->hctl_count; idx++) { 847 snd_mixer_detach_hctl(self->mixer, self->hctl[idx*2]); 848 Py_DECREF((PyObject *)self->hctl[idx*2+1]); 849 } 850 if (self->hctl) 851 free(self->hctl); 852 self->hctl_count = 0; 853 self->hctl = NULL; 854 for (idx = 0; idx < self->helem_count; idx++) 855 Py_DECREF((PyObject *)self->helem[idx*2+1]); 856 if (self->helem) 857 free(self->helem); 858 self->helem_count = 0; 859 self->helem = NULL; 860 for (idx = 0; idx < self->melem_count; idx++) 861 Py_DECREF((PyObject *)self->melem[idx*2+1]); 862 if (self->melem) 863 free(self->melem); 864 self->melem_count = 0; 865 self->melem = NULL; 866} 867 868static void 869pymixer_dealloc(struct pymixer *self) 870{ 871 pymixer_free(self); 872} 873 874static PyGetSetDef pymixer_getseters[] = { 875 {NULL,NULL,NULL,NULL,NULL} 876}; 877 878static PyMethodDef pymixer_methods[] = { 879 {"attachHCtl", (PyCFunction)pymixer_attach_hctl, METH_VARARGS, NULL}, 880 {"register", (PyCFunction)pymixer_register, METH_VARARGS, NULL}, 881 {"newMElement", (PyCFunction)pymixer_melement_new, METH_VARARGS, NULL}, 882 {"addMElement", (PyCFunction)pymixer_melement_add, METH_VARARGS, NULL}, 883 {NULL,NULL,0,NULL} 884}; 885 886static PyTypeObject pymixer_type = { 887 PyVarObject_HEAD_INIT(NULL, 0) 888 tp_name: "smixer_python.InternalMixer", 889 tp_basicsize: sizeof(struct pymixer), 890 tp_dealloc: (destructor)pymixer_dealloc, 891 tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, 892 tp_doc: NULL /* mixerinit__doc__ */, 893 tp_getset: pymixer_getseters, 894 tp_init: (initproc)pymixer_init, 895 tp_alloc: PyType_GenericAlloc, 896 tp_new: PyType_GenericNew, 897 tp_free: PyObject_Del, 898 tp_methods: pymixer_methods, 899}; 900 901static PyMethodDef python_methods[] = { 902 {NULL, NULL, 0, NULL} 903}; 904 905static PyObject *new_helem(struct python_priv *priv, snd_hctl_elem_t *helem) 906{ 907 PyObject *obj, *py_hctl = NULL, *obj1, *obj2; 908 snd_hctl_t *hctl = snd_hctl_elem_get_hctl(helem); 909 struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; 910 int idx; 911 912 for (idx = 0; idx < pymixer->hctl_count; idx++) { 913 if (pymixer->hctl[idx] == hctl) { 914 py_hctl = pymixer->hctl[idx*2+1]; 915 break; 916 } 917 } 918 if (py_hctl == NULL) 919 return NULL; 920 obj = PyDict_GetItemString(priv->py_mdict, "HElement"); 921 if (obj) { 922 obj1 = PyTuple_New(3); 923 PyTuple_SET_ITEM(obj1, 0, py_hctl); 924 Py_INCREF(py_hctl); 925 PyTuple_SET_ITEM(obj1, 1, PyFloat_FromDouble(1)); 926 PyTuple_SET_ITEM(obj1, 2, PyInt_FromLong((long)helem)); 927 obj2 = PyObject_CallObject(obj, obj1); 928 if (obj2 == NULL) { 929 PyErr_Print(); 930 PyErr_Clear(); 931 } 932 Py_XDECREF(obj1); 933 } else { 934 SNDERR("Unable to create InternalMixer object"); 935 return NULL; 936 } 937 if (obj2) { 938 struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; 939 void **helems = realloc(pymixer->helem, sizeof(void *) * (pymixer->helem_count + 1) * 2); 940 if (helems == NULL) { 941 Py_DECREF(obj2); 942 return NULL; 943 } 944 helems[pymixer->helem_count*2] = helem; 945 helems[pymixer->helem_count*2+1] = obj2; 946 Py_INCREF(obj2); 947 pymixer->helem = helems; 948 pymixer->helem_count++; 949 } 950 return obj2; 951} 952 953static PyObject *find_helem(struct python_priv *priv, snd_hctl_elem_t *helem) 954{ 955 struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; 956 int idx; 957 958 for (idx = 0; idx < pymixer->helem_count; idx++) { 959 if (pymixer->helem[idx*2] == helem) 960 return (PyObject *)pymixer->helem[idx*2+1]; 961 } 962 return NULL; 963} 964 965static PyObject *find_melem(struct python_priv *priv, snd_mixer_elem_t *melem) 966{ 967 struct pymixer *pymixer = (struct pymixer *)priv->py_mixer; 968 int idx; 969 970 for (idx = 0; idx < pymixer->melem_count; idx++) { 971 if (pymixer->melem[idx*2] == melem) 972 return (PyObject *)pymixer->melem[idx*2+1]; 973 } 974 return NULL; 975} 976 977int alsa_mixer_simple_event(snd_mixer_class_t *class, unsigned int mask, 978 snd_hctl_elem_t *helem, snd_mixer_elem_t *melem) 979{ 980 struct python_priv *priv = snd_mixer_sbasic_get_private(class); 981 PyThreadState *tstate; 982 PyObject *t, *o, *r; 983 int res = -ENOMEM; 984 985 tstate = PyThreadState_New(main_interpreter); 986 PyThreadState_Swap(tstate); 987 988 t = PyTuple_New(3); 989 if (t) { 990 PyTuple_SET_ITEM(t, 0, (PyObject *)PyInt_FromLong(mask)); 991 o = find_helem(priv, helem); 992 if (mask & SND_CTL_EVENT_MASK_ADD) { 993 if (o == NULL) 994 o = new_helem(priv, helem); 995 } 996 if (o == NULL) 997 return 0; 998 PyTuple_SET_ITEM(t, 1, o); 999 Py_INCREF(o); 1000 o = melem ? find_melem(priv, melem) : Py_None; 1001 PyTuple_SET_ITEM(t, 2, o); 1002 Py_INCREF(o); 1003 r = PyObject_CallObject(priv->py_event_func, t); 1004 Py_DECREF(t); 1005 if (r) { 1006 if (PyLong_Check(r)) { 1007 res = PyLong_AsLong(r); 1008#if PY_MAJOR_VERSION < 3 1009 } else if (PyInt_Check(r)) { 1010 res = PyInt_AsLong(r); 1011#endif 1012 } else if (r == Py_None) { 1013 res = 0; 1014 } 1015 Py_DECREF(r); 1016 } else { 1017 PyErr_Print(); 1018 PyErr_Clear(); 1019 res = -EIO; 1020 } 1021 } 1022 1023 return res; 1024} 1025 1026static void alsa_mixer_simple_free(snd_mixer_class_t *class) 1027{ 1028 struct python_priv *priv = snd_mixer_sbasic_get_private(class); 1029 1030 if (priv->py_mixer) { 1031 pymixer_free((struct pymixer *)priv->py_mixer); 1032 Py_DECREF(priv->py_mixer); 1033 } 1034 if (priv->py_initialized) { 1035 Py_XDECREF(priv->py_event_func); 1036 Py_Finalize(); 1037 } 1038 free(priv); 1039} 1040 1041static int alsa_mixer_simple_pyinit(struct python_priv *priv, 1042 PyObject *py_mod, 1043 FILE *fp, 1044 const char *file, 1045 snd_mixer_class_t *class, 1046 snd_mixer_t *mixer, 1047 const char *device) 1048{ 1049 PyObject *obj, *obj1, *obj2, *mdict; 1050 1051 mdict = priv->py_mdict = PyModule_GetDict(py_mod); 1052 obj = PyUnicode_FromString(file); 1053 if (obj) 1054 PyDict_SetItemString(mdict, "__file__", obj); 1055 Py_XDECREF(obj); 1056 obj = PyUnicode_FromString(device); 1057 if (obj) 1058 PyDict_SetItemString(mdict, "device", obj); 1059 Py_XDECREF(obj); 1060 Py_INCREF(&pymelem_type); 1061 Py_INCREF(&pymixer_type); 1062 PyModule_AddObject(py_mod, "InternalMElement", (PyObject *)&pymelem_type); 1063 PyModule_AddObject(py_mod, "InternalMixer", (PyObject *)&pymixer_type); 1064 obj = PyDict_GetItemString(mdict, "InternalMixer"); 1065 if (obj) { 1066 obj1 = PyTuple_New(3); 1067 PyTuple_SET_ITEM(obj1, 0, PyInt_FromLong((long)class)); 1068 PyTuple_SET_ITEM(obj1, 1, PyInt_FromLong((long)mixer)); 1069 PyTuple_SET_ITEM(obj1, 2, mdict); 1070 Py_INCREF(mdict); 1071 obj2 = PyObject_CallObject(obj, obj1); 1072 Py_XDECREF(obj1); 1073 PyDict_SetItemString(mdict, "mixer", obj2); 1074 priv->py_mixer = obj2; 1075 } else { 1076 SNDERR("Unable to create InternalMixer object"); 1077 return -EIO; 1078 } 1079 1080 obj = PyRun_FileEx(fp, file, Py_file_input, mdict, mdict, 1); 1081 if (obj == NULL) 1082 PyErr_Print(); 1083 Py_XDECREF(obj); 1084 priv->py_event_func = PyDict_GetItemString(mdict, "event"); 1085 if (priv->py_event_func == NULL) { 1086 SNDERR("Unable to find python function 'event'"); 1087 return -EIO; 1088 } 1089 return 0; 1090} 1091 1092#if PY_MAJOR_VERSION >= 3 1093static struct PyModuleDef smixer_python_module = { 1094 PyModuleDef_HEAD_INIT, 1095 "smixer_python", 1096 NULL, 1097 0, 1098 python_methods, 1099 NULL, 1100 NULL, 1101 NULL, 1102 NULL 1103}; 1104#endif 1105 1106int alsa_mixer_simple_finit(snd_mixer_class_t *class, 1107 snd_mixer_t *mixer, 1108 const char *device) 1109{ 1110 struct python_priv *priv; 1111 FILE *fp; 1112 const char *file; 1113 PyObject *obj, *py_mod; 1114 char path[PATH_MAX]; 1115 1116 priv = calloc(1, sizeof(*priv)); 1117 if (priv == NULL) 1118 return -ENOMEM; 1119 1120 snd_mixer_sbasic_set_private(class, priv); 1121 snd_mixer_sbasic_set_private_free(class, alsa_mixer_simple_free); 1122 1123 file = getenv("ALSA_MIXER_SIMPLE_MPYTHON"); 1124 if (file == NULL) { 1125 snd_dlpath(path, sizeof(path), SCRIPT); 1126 file = path; 1127 } 1128 1129 fp = fopen(file, "r"); 1130 if (fp == NULL) { 1131 SNDERR("Unable to find python module '%s'", file); 1132 return -ENODEV; 1133 } 1134 1135 Py_Initialize(); 1136 if (PyType_Ready(&pymelem_type) < 0 || 1137 PyType_Ready(&pymixer_type) < 0) { 1138 fclose(fp); 1139 return -EIO; 1140 } 1141#if PY_MAJOR_VERSION < 3 1142 Py_InitModule("smixer_python", python_methods); 1143#else 1144 PyModule_Create(&smixer_python_module); 1145#endif 1146 priv->py_initialized = 1; 1147 main_interpreter = PyThreadState_Get()->interp; 1148 obj = PyImport_GetModuleDict(); 1149 py_mod = PyDict_GetItemString(obj, "__main__"); 1150 if (py_mod) 1151 alsa_mixer_simple_pyinit(priv, py_mod, fp, file, class, mixer, device); 1152 return 0; 1153} 1154