Hi hackers,
The patch below is the first of a series for implementing the pyalsa
(python alsa) binding for the sequencer API of libasound2 that I hope
be included in alsa.
The following features are implemented:
1.- Opening a sequencer
1.1 support for SEQ_OPEN_{INPUT,OUTPUT,DUPLEX}
1.2 support for SEQ_NONBLOCK or blocking mode (SEQ_BLOCK)
1.3 support for alsa hw name and clientname
2.- Creating a simple port
2.1 support for port name
2.2 support for port capabilities bits SEQ_PORT_CAP_*
2.3 support for port types bits SEQ_PORT_TYPE_*
3.- Listing all connected client and their ports
3.1 client id's and names
3.2 port lists
3.3 read/write connections of ports
4.- Getting port info by clientid, portid
4.1 name of port
4.2 port capabilities
4.3 port type
I *encourage to test and stress* the current implementation, which is
a rewrite of what I wrote for the old pyalsaaudio project. The
features presented are sufficient for creating a simple (or
graphically!) aconnect python port, there is a sample 'seqtest1.py'
using all the features.
Planned features for next release:
1. alsaseq.SequencerError -- base exception for all exceptions (right
now all method raises RuntimeError)
2. alsaseq.SequenceEvent -- for receiving and sending events
3. Fixing missing documentation (marked as TODO in alsaseq.c)
Thank you for all your hard and good work.
-----
Signed-off-by: Aldrin Martoq <amartoq(a)dcc.uchile.cl>
diff -r 5080500e6cd3 pyalsa/alsaseq.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pyalsa/alsaseq.c Tue Jan 08 14:32:01 2008 -0300
@@ -0,0 +1,846 @@
+/*
+ * Python binding for the ALSA library - sequencer
+ * Copyright (c) 2007 by Aldrin Martoq <amartoq(a)dcc.uchile.cl>
+ *
+ * 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 <alsa/asoundlib.h>
+#include <stdio.h>
+
+/////////////////////////////////////////////////////////////////////
+// some helper #define go here...
+/////////////////////////////////////////////////////////////////////
+
+/* temporal debug (will be deleted in last patch, promise!) */
+#define ddebug(x, args...) fprintf(stderr, x "\n",##args);
+
+/* check passed object is integer and can't be deleted */
+#define SETCHECKPYINT(attr, val) \
+ if (val == NULL) { \
+ PyErr_SetString(PyExc_AttributeError, "attribute " attr " can't be
deleted!"); \
+ return -1; \
+ } \
+ if (!PyInt_Check(val)) { \
+ PyErr_SetString(PyExc_TypeError, "int value expected for " attr); \
+ return -1; \
+ }
+
+/* check if alsaseq.SEQ* constants is being used */
+/*
+ #define CHECKCONSTANT(attr, obj, type) \
+ if (!PyObject_TypeCheck(obj, type)) { \
+ PyErr_Warn(PyExc_RuntimeWarning, "please use only constants from
alsaseq.SEQ*"); \
+ }
+ */
+#define CHECKCONSTANT(attr, obj, type)
+
+/* check passed object is string and can't be deleted */
+#define SETCHECKPYSTR(attr, val) \
+ if (val == NULL) { \
+ PyErr_SetString(PyExc_AttributeError, "attribute " attr " can't be
deleted!"); \
+ return -1; \
+ } \
+ if (!PyString_Check(val)) { \
+ PyErr_SetString(PyExc_TypeError, "string value expected for " attr); \
+ return -1; \
+ }
+
+/* free only if pointer is not NULL */
+#define FREECHECKED(name, pointer) \
+ if (pointer != NULL) { \
+ free(pointer); \
+ pointer = NULL; \
+ }
+
+/* add constant as integer to module */
+#define CONST(module, name, value) \
+ if (PyModule_AddIntConstant(module, name, (long) value) < 0) { \
+ return; \
+ }
+
+/* define constant dict by type */
+#define TDICT(subtype, name) \
+ PyObject *_dictPYALSASEQ_CONST_##subtype = tmp = PyDict_New(); \
+ if (PyModule_AddObject(module, name, tmp) < 0) { \
+ return; \
+ } \
+
+/* create typed constant and add to module */
+#define TCONST(module, subtype, name, value) \
+ tmp = Constant_create(name, value, PYALSASEQ_CONST_##subtype); \
+ if (PyModule_AddObject(module, name, tmp) < 0) { \
+ return; \
+ } \
+ PyDict_SetItem(_dictPYALSASEQ_CONST_##subtype, PyInt_FromLong(value), tmp);
+
+/* num protocol support for binary Constant operations */
+#define NUMPROTOCOL2(name, oper) \
+static PyObject *Constant_##name (PyObject *v, PyObject *w) { \
+ int type = 0; \
+ long val = 0; \
+ /* both have to be a int */ \
+ if (!PyInt_Check(v) || !PyInt_Check(w)) { \
+ Py_INCREF(Py_NotImplemented); \
+ return Py_NotImplemented; \
+ } \
+ val = PyInt_AS_LONG(v) oper PyInt_AS_LONG(w); \
+ /* always asume left will be the type */ \
+ if (PyObject_TypeCheck(v, &ConstantType)) { \
+ type = ((ConstantObject *) v)->type; \
+ } else if (PyObject_TypeCheck(w, &ConstantType)) { \
+ type = ((ConstantObject *) w)->type; \
+ } \
+ PyObject *self = Constant_create(#oper, val, type); \
+ return self; \
+}
+/* num protocol support for unary Constant operations */
+#define NUMPROTOCOL1(name, oper) \
+static PyObject *Constant_##name (PyObject *v) { \
+ int type = 0; \
+ long val = 0; \
+ if (!PyInt_Check(v)) { \
+ Py_INCREF(Py_NotImplemented); \
+ return Py_NotImplemented; \
+ } \
+ val = oper PyInt_AS_LONG(v); \
+ if (PyObject_TypeCheck(v, &ConstantType)) { \
+ type = ((ConstantObject *) v)->type; \
+ } \
+ PyObject *self = Constant_create(#oper, val, type); \
+ return self; \
+}
+
+//#define ddebug(x) {}
+
+// alsaseq.Constant type definitions
+enum {
+ PYALSASEQ_CONST_STREAMS,
+ PYALSASEQ_CONST_MODE,
+ PYALSASEQ_CONST_PORT_CAP,
+ PYALSASEQ_CONST_PORT_TYPE
+};
+
+/////////////////////////////////////////////////////////////////////
+// alsaseq.Constant implementation
+/////////////////////////////////////////////////////////////////////
+
+/** alsaseq.Constant __doc__ */
+// TODO: write doc
+PyDoc_STRVAR(Constant__doc__,
+ "Constant type: TODO: write doc"
+);
+
+/** alsaseq.Constant object structure type */
+typedef struct {
+ PyObject_HEAD
+ ;
+
+ /* value of constant */
+ long value;
+ /* name of constant */
+ const char *name;
+ /* type of constant */
+ int type;
+} ConstantObject;
+
+/** alsaseq.Constant type (initialized later...) */
+static PyTypeObject ConstantType;
+
+/** alsaseq.Constant internal create */
+static PyObject *Constant_create(const char *name, long value, int type) {
+ ConstantObject *self= PyObject_New(ConstantObject, &ConstantType);
+ if (self == NULL) {
+ return NULL;
+ }
+
+ self->value = value;
+ self->name = name;
+ self->type = type;
+
+ return (PyObject *)self;
+}
+
+/** alsaseq.Constant tp_repr */
+static PyObject *Constant_repr(ConstantObject *self) {
+ return PyString_FromFormat("<alsaseq.Constant %s (%ld)", self->name,
+ self->value);
+}
+
+/** alsaseq.Constant tp_str */
+static PyObject *Constant_str(ConstantObject *self) {
+ return PyString_FromFormat("%s", self->name);
+}
+
+/** alsaseq.Constant Number protocol support (note: not all operations) */
+NUMPROTOCOL2(Add, +)
+NUMPROTOCOL2(Subtract, -)
+NUMPROTOCOL2(Xor, ^)
+NUMPROTOCOL2(Or, |)
+NUMPROTOCOL2(And, &)
+NUMPROTOCOL1(Invert, ~)
+
+static PyNumberMethods Constant_as_number = { nb_add:
(binaryfunc)Constant_Add, nb_subtract: (binaryfunc)Constant_Subtract,
nb_xor: (binaryfunc)Constant_Xor, nb_or: (binaryfunc)Constant_Or,
nb_and: (binaryfunc)Constant_And, nb_invert:
(unaryfunc)Constant_Invert };
+
+/** alsaseq.Constant type */
+static PyTypeObject ConstantType = { PyObject_HEAD_INIT(NULL)
+tp_name: "alsaseq.Constant",
+tp_base: &PyInt_Type,
+tp_basicsize: sizeof(ConstantObject),
+tp_flags: Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_CLASS |
Py_TPFLAGS_CHECKTYPES,
+tp_doc: Constant__doc__,
+//tp_dealloc: (destructor)PyObject_Del,
+tp_as_number: &Constant_as_number,
+tp_free: PyObject_Del, tp_str: (reprfunc)Constant_str,
+tp_repr: (reprfunc)Constant_repr, };
+
+/////////////////////////////////////////////////////////////////////
+// alsaseq.Sequencer implementation
+/////////////////////////////////////////////////////////////////////
+
+/** alsaseq.Sequencer __doc__ */
+// TODO: write doc
+PyDoc_STRVAR(Sequencer__doc__,
+ "Sequencer class: TODO: write doc"
+);
+
+/** alsaseq.Sequencer object structure type */
+typedef struct {
+ PyObject_HEAD
+ ;
+
+ /* name of device */
+ char *name;
+ /* name of client */
+ char *clientname;
+ /* client id */
+ int client_id;
+ /* streams */
+ int streams;
+ /* mode */
+ int mode;
+
+ /* alsa handler */
+ snd_seq_t *handle;
+ /* max fd's */
+ int receive_max;
+ /* receive poll fd's */
+ struct pollfd *receive_fds;
+ /* max events for returning in receive_events() */
+ int receive_max_events;
+} SequencerObject;
+
+/** alsaseq.Sequencer type (initialized later...) */
+static PyTypeObject SequencerType;
+
+/** alsaseq.Sequencer tp_init */
+static int Sequencer_init(SequencerObject *self, PyObject *args,
PyObject *kwds) {
+ int ret;
+ /* defaults */
+ char *name = "default";
+ char *clientname = "pyalsa";
+ self->streams = SND_SEQ_OPEN_DUPLEX;
+ self->mode = SND_SEQ_NONBLOCK;
+ int maxreceiveevents = 10;
+
+ char *kwlist[] = { "name", "clientname", "streams", "mode",
+ "maxreceiveevents", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssiii", kwlist, &name,
+ &clientname, &self->streams, &self->mode,
+ &maxreceiveevents)) {
+ return -1;
+ }
+
+ self->name = strdup(name);
+ self->clientname = strdup(clientname);
+ self->receive_fds = NULL;
+ self->receive_max = 0;
+ self->receive_max_events
+ = (self->mode == SND_SEQ_NONBLOCK ? maxreceiveevents
+ : 0);
+
+ ret = snd_seq_open(&(self->handle), self->name, self->streams,
+ self->mode);
+ if (ret < 0) {
+ // TODO: raise exception
+ return -1;
+ }
+ ret = snd_seq_set_client_name(self->handle, self->clientname);
+ if (ret < 0) {
+ // TODO: raise exception
+ return -1;
+ }
+ self->client_id = snd_seq_client_id(self->handle);
+
+ return 0;
+}
+
+/** alsaseq.Sequencer tp_dealloc */
+static void Sequencer_dealloc(SequencerObject *self) {
+ FREECHECKED("name", self->name)
+ FREECHECKED("clientname", self->clientname)
+ FREECHECKED("receive_fds", self->receive_fds)
+
+ if (self->handle) {
+ snd_seq_close(self->handle);
+ self->handle = NULL;
+ }
+
+ self->ob_type->tp_free(self);
+}
+
+/** alsaseq.Sequencer name attribute: __doc__ */
+PyDoc_STRVAR(Sequencer_name__doc__ ,
+ "name -- (string) the hardware name of this alsaseq.Sequencer
(default: \"default\"). "
+ "Note: read-only attribute.");
+
+/**
+ * alsaseq.Sequencer name attribute: tp_get getter()
+ *
+ * @param self -- the SeqEventObject
+ * @returns the alsa hardware name of this Sequencer.
+ */
+static PyObject *Sequencer_get_name(SequencerObject *self) {
+ return PyString_FromString(self->name);
+}
+
+/** alsaseq.Sequencer clientname attribute: __doc__ */
+PyDoc_STRVAR(Sequencer_clientname__doc__ ,
+ "clientname -- (string) the client name of this alsaseq.Sequencer
(default: \"pyalsa\").");
+
+/**
+ * alsaseq.Sequencer clientname attribute: tp_get getter()
+ *
+ * @param self -- the SeqEventObject
+ * @returns the client name of this Sequencer.
+ */
+static PyObject *Sequencer_get_clientname(SequencerObject *self) {
+ return PyString_FromString(self->clientname);
+}
+
+/**
+ * alsaseq.Sequencer clientname attribute: tp_get setter()
+ *
+ * @param self -- the SeqEventObject
+ * @param val -- the new value to set (must be a string)
+ * @returns 0 on success, -1 on error
+ * @raises TypeError -- if the val parameter is not a String
+ */
+static int Sequencer_set_clientname(SequencerObject *self, PyObject *val) {
+ int ret;
+ char *buff;
+
+ SETCHECKPYSTR("clientname", val)
+
+ buff = PyString_AsString(val);
+
+ ret = snd_seq_set_client_name(self->handle, buff);
+ if (ret == 0) {
+ FREECHECKED("clientname", self->clientname)
+ self->clientname = strdup(buff);
+ } else {
+ // TODO: raise exception
+ return -1;
+ }
+ return 0;
+}
+
+/** alsaseq.Sequencer streams attribute: __doc__ */
+PyDoc_STRVAR(Sequencer_streams__doc__ ,
+ "streams -- (int) the streams of this alsaseq.Sequencer (default:
alsaseq.SEQ_OPEN_DUPLEX). "
+ "Posible values: alsaseq.SEQ_OPEN_OUTPUT, alsaseq.SEQ_OPEN_INPUT,
alsaseq.SEQ_OPEN_DUPLEX. "
+ "Note: read-only attribute.");
+
+/**
+ * alsaseq.Sequencer streams attribute: tp_get getter()
+ *
+ * @param self -- the SeqEventObject
+ * @returns the stremas of this Sequencer.
+ */
+static PyObject *Sequencer_get_streams(SequencerObject *self) {
+ return PyInt_FromLong(self->streams);
+}
+
+/** alsaseq.Sequencer mode attribute: __doc__ */
+PyDoc_STRVAR(Sequencer_mode__doc__ ,
+ "mode -- (int) the blocking mode of this alsaseq.Sequencer
(default: alsaseq.SEQ_NONBLOCK). "
+ "Posible values: alsaseq.SEQ_BLOCK, alsaseq.SEQ_NONBLOCK. ");
+
+/**
+ * alsaseq.Sequencer mode attribute: tp_get getter()
+ *
+ * @param self -- the SeqEventObject
+ * @returns the blocking mode of this Sequencer.
+ */
+static PyObject *Sequencer_get_mode(SequencerObject *self) {
+ return PyInt_FromLong(self->mode);
+}
+
+/**
+ * alsaseq.Sequencer mode attribute: tp_get setter()
+ *
+ * @param self -- the SeqEventObject
+ * @param val -- the new value to set (must be a integer)
+ * @returns 0 on success, -1 on error
+ * @raises TypeError -- if the val parameter is not a integer
+ * @raises ValueError -- if the val parameter is not 0 (SEQ_BLOCK) or
1 (SEQ_NONBLOCK).
+ */
+static int Sequencer_set_mode(SequencerObject *self, PyObject *val) {
+ int ret, mode;
+
+ SETCHECKPYINT("mode", val)
+
+CHECKCONSTANT("mode", val, &ConstantType)
+
+ mode = (int) PyInt_AsLong(val);
+
+ if (mode != 0 && mode != SND_SEQ_NONBLOCK) {
+ PyErr_SetString(PyExc_ValueError, "Invalid value for mode.");
+ return -1;
+ }
+
+ ret = snd_seq_nonblock(self->handle, mode);
+ if (ret == 0) {
+ self->mode = mode;
+ } else {
+ // TODO: raise exception
+ return -1;
+ }
+ return 0;
+}
+
+/** alsaseq.Sequencer client_id attribute: __doc__ */
+PyDoc_STRVAR(Sequencer_client_id__doc__ ,
+ "client_id -- (int) the client id of this alsaseq.Sequencer. "
+ "Note: read-only attribute.");
+
+/**
+ * alsaseq.Sequencer client_id attribute: tp_get getter()
+ *
+ * @param self -- the SeqEventObject
+ * @returns the client id of this Sequencer.
+ */
+static PyObject *Sequencer_get_client_id(SequencerObject *self) {
+ return PyInt_FromLong(self->client_id);
+}
+
+/** alsaseq.Sequencer tp_getset list*/
+static PyGetSetDef Sequencer_getset[] = { { "name", (getter)Sequencer_get_name,
+ NULL, Sequencer_name__doc__, NULL }, { "clientname",
+ (getter)Sequencer_get_clientname,
+ (setter)Sequencer_set_clientname, Sequencer_clientname__doc__,
+ NULL }, { "streams", (getter) Sequencer_get_streams, NULL,
+ Sequencer_streams__doc__, NULL }, { "mode",
+ (getter) Sequencer_get_mode, (setter) Sequencer_set_mode,
+ Sequencer_mode__doc__, NULL }, { "client_id",
+ (getter) Sequencer_get_client_id, NULL,
+ Sequencer_client_id__doc__, NULL }, { NULL } };
+
+/** alsaseq.Sequencer tp_repr */
+static PyObject *Sequencer_repr(SequencerObject *self) {
+ return PyString_FromFormat(
+ "<alsaseq.Sequencer client_id=%d name=%s
clientname=%s streams=%d mode=%d at 0x%p>",
+ self->client_id, self->name, self->clientname,
+ self->streams, self->mode, self);
+}
+
+/** alsaseq.Sequencer create_simple_port(): __doc__ */
+PyDoc_STRVAR(Sequencer_create_simple_port__doc__,
+ "create_simple_port(name, caps, type) -- Creates a port for
receiving or sending events.\n\n"
+ "Parameters:\n"
+ " name -- (string) name of the port\n"
+ " caps -- (int) capabilites of the port (use bitwise
alsaseq.SEQ_PORT_CAP_* constants)\n"
+ " type -- (int) type of port (use one of the
alsaseq.SEQ_PORT_TYPE_* constants)"
+ "Returns:\n"
+ " (int) the port id.\n"
+ "Raises:\n"
+ " TypeError: an invalid type was used in a parameter\n"
+ " ValueError: an invalid value was used in a parameter\n"
+);
+
+/**
+ * alsaseq.Sequencer create_simple_port()
+ */
+static PyObject *Sequencer_create_simple_port(SequencerObject *self,
+ PyObject *args) {
+ char *name;
+ unsigned int caps;
+ unsigned int type;
+ int port;
+
+ if (!PyArg_ParseTuple(args, "sII", &name, &caps, &type)) {
+ return NULL;
+ }
+ port = snd_seq_create_simple_port(self->handle, name, caps, type);
+ if (port < 0) {
+ // TODO: Throw exception
+ return NULL;
+ }
+
+ return PyInt_FromLong(port);
+}
+
+/** alsaseq.Sequencer get_client_ports(): __doc__ */
+PyDoc_STRVAR(Sequencer_get_client_ports__doc__,
+ "get_client_ports() -- List current clients and their ports
connected to alsa.\n\n"
+ "Returns:\n"
+ " (list) a list of tuples: client_name, client_id, port_list.\n"
+ " client_name -- the client's name\n"
+ " client_id -- the client's id\n"
+ " port_list -- a list of tuples: port_name, port_id, connection_list\n"
+ " port_name -- the name of the port\n"
+ " port_id -- the port id\n"
+ " connection_list -- a list of tuples: read_conn, write_conn\n"
+ " read_conn -- a list of tuples with the client_id, port_id
this port is connected to (sends events)\n"
+ " write_conn -- a list of tuples with the client_id, port_id
this port is connected from (receives events)\n"
+);
+
+static PyObject *_query_connections_list(snd_seq_t *handle,
+ snd_seq_query_subscribe_t *query,
+ int type) {
+
+ PyObject *list = PyList_New(0);
+ int index = 0;
+ snd_seq_query_subscribe_set_type(query, type);
+ snd_seq_query_subscribe_set_index(query, index);
+ while (snd_seq_query_port_subscribers(handle, query) >= 0) {
+ const snd_seq_addr_t *addr =
+ snd_seq_query_subscribe_get_addr(query);
+
+ /* create tuple for clientid, portid */
+ PyObject *tuple= PyTuple_New(2);
+ PyTuple_SetItem(tuple, 0, PyInt_FromLong(addr->client));
+ PyTuple_SetItem(tuple, 1, PyInt_FromLong(addr->port));
+
+ PyList_Append(list, tuple);
+ snd_seq_query_subscribe_set_index(query, ++index);
+ }
+ return list;
+}
+
+static PyObject *_query_connections(snd_seq_t *handle,
+ const snd_seq_addr_t *addr) {
+ snd_seq_query_subscribe_t *query;
+ snd_seq_query_subscribe_alloca(&query);
+ snd_seq_query_subscribe_set_root(query, addr);
+
+ /* create tuple for read,write lists */
+ PyObject *tuple = PyTuple_New(2);
+ PyObject *readlist = _query_connections_list(handle, query,
+ SND_SEQ_QUERY_SUBS_READ);
+ PyObject *writelist = _query_connections_list(handle, query,
+ SND_SEQ_QUERY_SUBS_WRITE);
+ PyTuple_SetItem(tuple, 0, readlist);
+ PyTuple_SetItem(tuple, 1, writelist);
+
+ return tuple;
+}
+
+/**
+ * alsaseq.Sequencer get_client_ports()
+ */
+static PyObject *Sequencer_get_client_ports(SequencerObject *self,
+ PyObject *args) {
+ snd_seq_client_info_t *cinfo;
+ snd_seq_port_info_t *pinfo;
+ PyObject *list = PyList_New(0);
+
+ if (list == NULL) {
+ return NULL;
+ }
+
+ snd_seq_client_info_alloca(&cinfo);
+ snd_seq_port_info_alloca(&pinfo);
+ snd_seq_client_info_set_client(cinfo, -1);
+ while (snd_seq_query_next_client(self->handle, cinfo) >= 0) {
+ /* reset query info */
+ snd_seq_port_info_set_client(pinfo,
+ snd_seq_client_info_get_client(cinfo));
+ snd_seq_port_info_set_port(pinfo, -1);
+
+ /* create tuple for client info */
+ PyObject *tuple= PyTuple_New(3);
+ PyObject *portlist = PyList_New(0);
+
+ PyTuple_SetItem(tuple, 0, PyString_FromFormat("%s",
+ snd_seq_client_info_get_name(cinfo)));
+ PyTuple_SetItem(
+ tuple,
+ 1,
+ PyInt_FromLong(snd_seq_client_info_get_client(cinfo)));
+
+ while (snd_seq_query_next_port(self->handle, pinfo) >= 0) {
+ /* create tuple for port info */
+ PyObject *porttuple = PyTuple_New(3);
+ PyTuple_SetItem(porttuple, 0, PyString_FromFormat("%s",
+ snd_seq_port_info_get_name(pinfo)));
+ PyTuple_SetItem(
+ porttuple,
+ 1,
+ PyInt_FromLong(snd_seq_port_info_get_port(pinfo)));
+ /* create tuple for read,write connections */
+ PyObject *conntuple= _query_connections(self->handle,
+ snd_seq_port_info_get_addr(pinfo));
+ PyTuple_SetItem(porttuple, 2, conntuple);
+ PyList_Append(portlist, porttuple);
+ }
+ PyTuple_SetItem(tuple, 2, portlist);
+
+ /* append list of port tuples */
+ PyList_Append(list, tuple);
+ }
+
+ return list;
+}
+
+/** alsaseq.Sequencer get_port_info(): __doc__ */
+PyDoc_STRVAR(Sequencer_get_port_info__doc__,
+ "get_port_info(port_id, client_id = self.client_id) -- Retrieve
info about an existing port of a client.\n\n"
+ "Parameters:\n"
+ " port_id -- (int) the port id\n"
+ " client_id -- (int) the client id (defaults to: self.client_id)\n"
+ "Returns:\n"
+ " (dict) a dictionary with the following values:\n"
+ " name -- (string) the port name\n"
+ " type -- (int) the port type bit flags\n"
+ " capability -- (int) the port capability bit flags\n"
+);
+
+/**
+ * alsaseq.Sequencer get_port_info()
+ */
+static PyObject *Sequencer_get_port_info(SequencerObject *self, PyObject *args,
+ PyObject *kwds) {
+ snd_seq_port_info_t *pinfo;
+ int port_id;
+ int client_id = self->client_id;
+ const char *tmpchar;
+ long tmplong;
+ int ret;
+
+ char *kwlist[] = { "port_id", "client_id", NULL };
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwlist, &port_id,
+ &client_id)) {
+ return NULL;
+ }
+
+ PyObject *dict = PyDict_New();
+ if (dict == NULL) {
+ return NULL;
+ }
+
+ snd_seq_port_info_alloca(&pinfo);
+ /* query info */
+ ret
+ = snd_seq_get_any_port_info(self->handle, client_id,
+ port_id, pinfo);
+ if (ret < 0) {
+ ddebug("FIXME: Throw exception: %s", snd_strerror(ret));
+ // TODO: Throw exception
+ return NULL;
+ }
+ tmpchar = snd_seq_port_info_get_name(pinfo);
+ PyDict_SetItem(dict, PyString_FromString("name"),
+ PyString_FromString(tmpchar));
+
+ tmplong = snd_seq_port_info_get_capability(pinfo);
+ PyDict_SetItem(dict, PyString_FromString("capability"),
+ PyInt_FromLong(tmplong));
+ tmplong = snd_seq_port_info_get_type(pinfo);
+ PyDict_SetItem(dict, PyString_FromString("type"),
+ PyInt_FromLong(tmplong));
+
+ return dict;
+}
+
+/** alsaseq.Sequencer connect_ports(): __doc__ */
+PyDoc_STRVAR(Sequencer_connect_ports__doc__,
+ "connect_ports(srcaddr, dstaddr) -- Connect the ports specified by
srcaddr and dstaddr.\n\n"
+ "Parameters:\n"
+ " srcaddr -- (tuple) the client_id, port_id tuple for the source
port (the port sending events)\n"
+ " dstaddr -- (tuple) the client_id, port_id tuple for the
destination port (the port receiveng events)\n"
+);
+
+/**
+ * alsaseq.Sequencer connect_ports()
+ */
+static PyObject *Sequencer_connect_ports(SequencerObject *self,
PyObject *args) {
+ snd_seq_addr_t sender, dest;
+ snd_seq_port_subscribe_t *sinfo;
+ int ret;
+
+ if (!PyArg_ParseTuple(args, "(BB)(BB)",
+ &(sender.client), &(sender.port), &(dest.client),
&(dest.port))) {
+ return NULL;
+ }
+
+ snd_seq_port_subscribe_alloca(&sinfo);
+ snd_seq_port_subscribe_set_sender(sinfo, &sender);
+ snd_seq_port_subscribe_set_dest(sinfo, &dest);
+
+ ret = snd_seq_subscribe_port(self->handle, sinfo);
+ if (ret < 0) {
+ ddebug("FIXME: Raise Exception: %s", snd_strerror(ret));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+
+ return Py_None;
+}
+
+/** alsaseq.Sequencer disconnect_ports(): __doc__ */
+PyDoc_STRVAR(Sequencer_disconnect_ports__doc__,
+ "disconnect_ports(srcaddr, dstaddr) -- Disconnect the ports
specified by srcaddr and dstaddr.\n\n"
+ "Parameters:\n"
+ " srcaddr -- (tuple) the client_id, port_id tuple for the source
port (the port sending events)\n"
+ " dstaddr -- (tuple) the client_id, port_id tuple for the
destination port (the port receiveng events)\n"
+);
+
+/**
+ * alsaseq.Sequencer disconnect_ports()
+ */
+static PyObject *Sequencer_disconnect_ports(SequencerObject *self,
PyObject *args) {
+ snd_seq_addr_t sender, dest;
+ snd_seq_port_subscribe_t *sinfo;
+ int ret;
+
+ if (!PyArg_ParseTuple(args, "(BB)(BB)",
+ &(sender.client), &(sender.port), &(dest.client),
&(dest.port))) {
+ return NULL;
+ }
+
+ snd_seq_port_subscribe_alloca(&sinfo);
+ snd_seq_port_subscribe_set_sender(sinfo, &sender);
+ snd_seq_port_subscribe_set_dest(sinfo, &dest);
+
+ ret = snd_seq_unsubscribe_port(self->handle, sinfo);
+ if (ret < 0) {
+ ddebug("FIXME: Raise Exception: %s", snd_strerror(ret));
+ return NULL;
+ }
+
+ Py_INCREF(Py_None);
+
+ return Py_None;
+}
+
+/** alsaseq.Sequencer tp_methods */
+static PyMethodDef Sequencer_methods[] = { { "create_simple_port",
+ (PyCFunction) Sequencer_create_simple_port, METH_VARARGS,
+ Sequencer_create_simple_port__doc__ }, { "get_client_ports",
+ (PyCFunction) Sequencer_get_client_ports, METH_VARARGS,
+ Sequencer_get_client_ports__doc__ }, { "get_port_info",
+ (PyCFunction) Sequencer_get_port_info,
+ METH_VARARGS, Sequencer_get_port_info__doc__ },
+ {"connect_ports", (PyCFunction)
Sequencer_connect_ports, METH_VARARGS, Sequencer_connect_ports__doc__
},
+ {"disconnect_ports", (PyCFunction)
Sequencer_disconnect_ports, METH_VARARGS,
Sequencer_disconnect_ports__doc__ },
+ { NULL } };
+
+/** alsaseq.Sequencer type */
+static PyTypeObject SequencerType = { PyObject_HEAD_INIT(NULL)
+tp_name: "alsaseq.Sequencer",
+tp_basicsize: sizeof(SequencerObject),
+tp_dealloc: (destructor)Sequencer_dealloc,
+tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+tp_doc: Sequencer__doc__,
+tp_init: (initproc)Sequencer_init,
+tp_new: PyType_GenericNew,
+tp_alloc: PyType_GenericAlloc,
+tp_free: PyObject_Del, tp_repr: (reprfunc)Sequencer_repr,
+tp_methods: Sequencer_methods, tp_getset: Sequencer_getset
+};
+
+/////////////////////////////////////////////////////////////////////
+// alsaseq module implementation
+/////////////////////////////////////////////////////////////////////
+
+/** alsaseq module: __doc__ */
+// TODO: write documentation
+PyDoc_STRVAR(alsaseq__doc__, "alsaseq: TODO: write documentation");
+
+/** alsaseq module methods */
+static PyMethodDef alsaseq_methods[] = { { NULL },
+};
+
+PyMODINIT_FUNC
+initalsaseq(void) {
+ PyObject *module;
+ PyObject *tmp;
+
+ if (PyType_Ready(&ConstantType) < 0) {
+ return;
+ }
+
+ if (PyType_Ready(&SequencerType) < 0) {
+ return;
+ }
+
+ module = Py_InitModule3("alsaseq", alsaseq_methods, alsaseq__doc__);
+
+ Py_INCREF(&SequencerType);
+ PyModule_AddObject(module, "Sequencer", (PyObject *) &SequencerType);
+ Py_INCREF(&ConstantType);
+ PyModule_AddObject(module, "Constant", (PyObject *) &ConstantType);
+
+ // constants definition
+
+ TDICT(STREAMS, "_const_streams");
+ TDICT(MODE, "_const_mode");
+ TDICT(PORT_CAP, "_const_port_cap");
+ TDICT(PORT_TYPE, "_const_port_type");
+
+ /* streams */
+
+ TCONST(module, STREAMS, "SEQ_OPEN_OUTPUT", SND_SEQ_OPEN_OUTPUT);
+ TCONST(module, STREAMS, "SEQ_OPEN_INPUT", SND_SEQ_OPEN_INPUT);
+ TCONST(module, STREAMS, "SEQ_OPEN_DUPLEX", SND_SEQ_OPEN_DUPLEX);
+
+ /* blocking mode */
+ TCONST(module, MODE, "SEQ_BLOCK", 0);
+ TCONST(module, MODE, "SEQ_NONBLOCK", SND_SEQ_NONBLOCK);
+
+ /* port cap */
+ TCONST(module, PORT_CAP, "SEQ_PORT_CAP_WRITE", SND_SEQ_PORT_CAP_WRITE);
+ TCONST(module, PORT_CAP, "SEQ_PORT_CAP_SYNC_WRITE",
SND_SEQ_PORT_CAP_SYNC_WRITE);
+ TCONST(module, PORT_CAP, "SEQ_PORT_CAP_SYNC_READ",
SND_SEQ_PORT_CAP_SYNC_READ);
+ TCONST(module, PORT_CAP, "SEQ_PORT_CAP_SUBS_WRITE",
SND_SEQ_PORT_CAP_SUBS_WRITE);
+ TCONST(module, PORT_CAP, "SEQ_PORT_CAP_SUBS_READ",
SND_SEQ_PORT_CAP_SUBS_READ);
+ TCONST(module, PORT_CAP, "SEQ_PORT_CAP_READ", SND_SEQ_PORT_CAP_READ);
+ TCONST(module, PORT_CAP, "SEQ_PORT_CAP_NO_EXPORT",
SND_SEQ_PORT_CAP_NO_EXPORT);
+ TCONST(module, PORT_CAP, "SEQ_PORT_CAP_DUPLEX", SND_SEQ_PORT_CAP_DUPLEX);
+
+ /* port type */
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_SYNTHESIZER",
SND_SEQ_PORT_TYPE_SYNTHESIZER);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_SYNTH", SND_SEQ_PORT_TYPE_SYNTH);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_SPECIFIC",
SND_SEQ_PORT_TYPE_SPECIFIC);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_SOFTWARE",
SND_SEQ_PORT_TYPE_SOFTWARE);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_SAMPLE", SND_SEQ_PORT_TYPE_SAMPLE);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_PORT", SND_SEQ_PORT_TYPE_PORT);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_MIDI_XG", SND_SEQ_PORT_TYPE_MIDI_XG);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_MIDI_MT32",
SND_SEQ_PORT_TYPE_MIDI_MT32);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_MIDI_GS", SND_SEQ_PORT_TYPE_MIDI_GS);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_MIDI_GM2",
SND_SEQ_PORT_TYPE_MIDI_GM2);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_MIDI_GM", SND_SEQ_PORT_TYPE_MIDI_GM);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_MIDI_GENERIC",
SND_SEQ_PORT_TYPE_MIDI_GENERIC);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_HARDWARE",
SND_SEQ_PORT_TYPE_HARDWARE);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_DIRECT_SAMPLE",
SND_SEQ_PORT_TYPE_DIRECT_SAMPLE);
+ TCONST(module, PORT_TYPE, "SEQ_PORT_TYPE_APPLICATION",
SND_SEQ_PORT_TYPE_APPLICATION);
+
+}
diff -r 5080500e6cd3 setup.py
--- a/setup.py Sat Nov 03 11:25:08 2007 +0100
+++ b/setup.py Tue Jan 08 14:32:01 2008 -0300
@@ -45,6 +45,11 @@ setup(
include_dirs=[],
library_dirs=[],
libraries=['asound']),
+ Extension('pyalsa.alsaseq',
+ ['pyalsa/alsaseq.c'],
+ include_dirs=[],
+ library_dirs=[],
+ libraries=['asound']),
],
packages=['pyalsa'],
scripts=[]
@@ -52,7 +57,7 @@ setup(
uname = os.uname()
a = 'build/lib.%s-%s-%s' % (uname[0].lower(), uname[4], sys.version[:3])
-for f in ['alsacard.so', 'alsacontrol.so', 'alsahcontrol.so', 'alsamixer.so']:
+for f in ['alsacard.so', 'alsacontrol.so', 'alsahcontrol.so',
'alsamixer.so', 'alsaseq.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)
diff -r 5080500e6cd3 test/alsamemdebug.py
--- a/test/alsamemdebug.py Sat Nov 03 11:25:08 2007 +0100
+++ b/test/alsamemdebug.py Tue Jan 08 14:32:01 2008 -0300
@@ -6,6 +6,7 @@ import gc
def debuginit():
gc.set_debug(gc.DEBUG_LEAK)
+ print "LEAK: init"
def debug(what):
from sys import getrefcount
@@ -13,7 +14,8 @@ def debug(what):
for o in what:
if getrefcount(o) - 3 != 1 or \
len(get_referrers(o)) - 2 != 1:
- print 'LEAK:', hex(id(o)), type(o), getrefcount(o)-3,
len(get_referrers(o))-2
+ print 'LEAK:', o, hex(id(o)), type(o), getrefcount(o)-3,
len(get_referrers(o))-2
def debugdone():
+ print "LEAK: done"
None
diff -r 5080500e6cd3 test/seqtest1.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/seqtest1.py Tue Jan 08 14:32:01 2008 -0300
@@ -0,0 +1,135 @@
+#! /usr/bin/python
+# Sample code for pyalsa Sequencer binding
+# by Aldrin Martoq <amartoq(a)dcc.uchile.cl>
+# version 2008010801 (UTC: 1199812236 secs)
+#
+# This code is in the public domain,
+# use it as base for creating your pyalsa'
+# alsaseq application.
+
+import sys
+sys.path.insert(0, '../pyalsa')
+del sys
+from alsamemdebug import debuginit, debug, debugdone
+import alsaseq
+
+
+def dump_portinfo(dict):
+ def dl(dict, key):
+ if dict.has_key(key):
+ return "'" + str(dict[key]) + "'"
+ return "N/A"
+ def da(dict, key, search):
+ tmp = None
+ if dict.has_key(key):
+ for k in search:
+ if k & dict[key]:
+ if tmp == None:
+ tmp = str(search[k])
+ else:
+ tmp += " " + str(search[k])
+ if tmp == None:
+ tmp = "N/A"
+ return tmp
+
+ return "name=%s capability=%s type=%s" % (dl(dict, 'name'),
da(dict, 'capability', alsaseq._const_port_cap), da(dict, 'type',
alsaseq._const_port_type))
+
+debuginit()
+
+print "01:Creating Sequencer =============================="
+sequencer = alsaseq.Sequencer()
+# other examples:
+# sequencer = alsaseq.Sequencer("hw", "myfancyapplication",
alsaseq.SEQ_OPEN_INPUT, alsaseq.SEQ_BLOCK)
+# sequencer = alsaseq.Sequencer(clientname='python-test',
streams=alsaseq.SEQ_OPEN_OUTPUT)
+print " sequencer: %s" % sequencer
+print " name: %s" % sequencer.name
+print " clientname: %s" % sequencer.clientname
+print " streams: %d (%s)" % (sequencer.streams,
alsaseq._const_streams[sequencer.streams])
+print " mode: %d (%s)" % (sequencer.mode,
alsaseq._const_mode[sequencer.mode])
+print " client_id: %s" % sequencer.client_id
+print
+
+print "02:Changing some parameters ========================"
+sequencer.clientname = 'pepito'
+sequencer.mode = alsaseq.SEQ_BLOCK
+print " clientname: %s" % sequencer.clientname
+print " mode: %d (%s)" % (sequencer.mode,
alsaseq._const_mode[sequencer.mode])
+print
+
+print "03:Creating simple port ============================"
+port_id = sequencer.create_simple_port("myport",
alsaseq.SEQ_PORT_CAP_WRITE | alsaseq.SEQ_PORT_CAP_SUBS_WRITE,
alsaseq.SEQ_PORT_TYPE_APPLICATION)
+print " port_id: %s" % port_id
+print
+
+print "04:Getting port info ==============================="
+port_info = sequencer.get_port_info(port_id)
+print " --> %s" % dump_portinfo(port_info)
+print " --> %s" % port_info
+print
+
+print "05:Listing all client ports ========================"
+connections = sequencer.get_client_ports()
+print " %s" % (connections)
+print
+
+print "06:Listing all client ports (lenghtly version) ========"
+connections = sequencer.get_client_ports()
+print " %s" % (connections)
+for clientports in connections:
+ clientname, clientid, portlist = clientports
+ print " client: %3d %s" % (clientid, clientname)
+ for port in portlist:
+ portname, portid, connections = port
+ portinfo = sequencer.get_port_info(portid, clientid)
+ print " port: %3d:%-2d +-%s\t[%s]" %
(clientid, portid, portname, dump_portinfo(portinfo))
+ readconn, writeconn = connections
+ for c, p in readconn:
+ print " connection to: %d:%d" % (c,p)
+ for c,p in writeconn:
+ print " connection from: %d:%d" % (c,p)
+print
+
+print "07:Connecting 'arbitrary' ports... ================="
+sequencer.connect_ports((0, 1), (sequencer.client_id, port_id))
+
+print "08:Listing all client ports (fancy version) ========"
+connections = sequencer.get_client_ports()
+print " %s" % (connections)
+for clientports in connections:
+ clientname, clientid, portlist = clientports
+ print " client: %3d %s" % (clientid, clientname)
+ for port in portlist:
+ portname, portid, connections = port
+ readconn, writeconn = connections
+ for c, p in readconn:
+ print " connection to: %d:%d" % (c,p)
+ for c,p in writeconn:
+ print " connection from: %d:%d" % (c,p)
+print
+
+print "09:Disconnecting previous 'arbitrary' port ========="
+sequencer.disconnect_ports((0, 1), (sequencer.client_id, port_id))
+
+print "10:Listing all client ports (fancy version) ========"
+connections = sequencer.get_client_ports()
+print " %s" % (connections)
+for clientports in connections:
+ clientname, clientid, portlist = clientports
+ print " client: %3d %s" % (clientid, clientname)
+ for port in portlist:
+ portname, portid, connections = port
+ readconn, writeconn = connections
+ for c, p in readconn:
+ print " connection to: %d:%d" % (c,p)
+ for c,p in writeconn:
+ print " connection from: %d:%d" %init (c,p)
+print
+
+
+print "99:Removing sequencer =============================="
+debug([sequencer])
+del sequencer
+print
+
+debugdone()
+print "All Done."
--
Aldrin Martoq