[alsa-devel] [PATCH] pyalsa sequencer binding - 1/3
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@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@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@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."
Hi hackers again,
I'm sending second PATCH for pyalsa sequencer binding. Major new features: - documentation of all python API - more info on subscription listing - subscribe/unsubscribe - events (SeqEvent class) - send/receive/modify events - creating and managing queues
I've already ported some existing applications to python versions: - aconnect.py - aplaymidi.py - aseqdump.py
There are also 3 samples of code seqtest[1-3].py included in the patch.
It's been a long and tedious work (16 days); please tell me anyhow to improve and get this done and finished. Thanks again!!!
------- Signed-off-by: Aldrin Martoq amartoq@dcc.uchile.cl
diff -r 02b11d9f243d pyalsa/alsaseq.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyalsa/alsaseq.c Thu Jan 24 05:18:40 2008 -0300 @@ -0,0 +1,3818 @@ +/* + * Python binding for the ALSA library - sequencer + * Copyright (c) 2008 by Aldrin Martoq amartoq@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!) */ +#if 1 +#define ddebug(x, args...) fprintf(stderr, x "\n",##args); +#else +#define ddebug(x, args...) +#endif + +/* checks if passed argument is an integer and can't be deleted. + returns -1 if fails and raises TypeError or AttributeError. */ +#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, \ + "integer value expected for " attr); \ + return -1; \ + } + +/* checks if passed argument is a string and can't be deleted. + returns -1 if fails and raises TypeError or AttributeError. */ +#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; \ + } + +/* checks if passed argument is a list and can't be deleted. + returns -1 if fails and raises TypeError or AttributeError. */ +#define SETCHECKPYLIST(attr, val) \ + if (val == NULL) { \ + PyErr_SetString(PyExc_AttributeError, \ + "attribute " attr " can't be deleted!"); \ + return -1; \ + } \ + if (!PyList_Check(val)) { \ + PyErr_SetString(PyExc_TypeError, \ + "list value expected for " attr); \ + return -1; \ + } + +/* frees only if pointer is not NULL. */ +#define FREECHECKED(name, pointer) \ + if (pointer != NULL) { \ + free(pointer); \ + pointer = NULL; \ + } + +/* raises SequencerError with the specified string */ +#define RAISESTR(str, args...) \ + PyErr_Format(SequencerError, str,##args); + +/* raises SequencerError with the specified string and appends + the alsaaudio error as string */ +#define RAISESND(ret, str, args...) \ + PyErr_Format(SequencerError, str ": %s",##args, snd_strerror(ret)); + +/* the C variable of a constant dict */ +#define TDICT(subtype) _dictPYALSASEQ_CONST_##subtype + +/* the C enumeration of a constant dict */ +#define TTYPE(subtype) PYALSASEQ_CONST_##subtype + +/* defines constant dict by type */ +#define TCONSTDICT(subtype) \ + static PyObject * TDICT(subtype); + +/* adds constant dict to module */ +#define TCONSTDICTADD(module, subtype, name) \ + _dictPYALSASEQ_CONST_##subtype = PyDict_New(); \ + if (TDICT(subtype) == NULL) { \ + return; \ + } \ + if (PyModule_AddObject(module, name, TDICT(subtype)) < 0) { \ + return; \ + } + +/* creates a typed constant and add it to the module and dictionary */ +#define TCONSTADD(module, subtype, name, value) { \ + PyObject *tmp = \ + Constant_create(name, value, TTYPE(subtype)); \ + if (tmp == NULL) { \ + return; \ + } \ + if (PyModule_AddObject(module, name, tmp) < 0) { \ + return; \ + } \ + PyDict_SetItem(TDICT(subtype), PyInt_FromLong(value), tmp); \ + } + +#define TCONSTRETURN(subtype, value) { \ + PyObject *key = PyInt_FromLong(value); \ + ConstantObject *constantObject = (ConstantObject *) \ + PyDict_GetItem(TDICT(subtype), key); \ + if (constantObject == NULL) { \ + return key; \ + } else { \ + Py_DECREF(key); \ + Py_INCREF(constantObject); \ + return (PyObject *)constantObject; \ + } \ + } + +#define TCONSTASSIGN(subtype, value, param) { \ + PyObject *key = PyInt_FromLong(value); \ + ConstantObject *constantObject = (ConstantObject *) \ + PyDict_GetItem(TDICT(subtype), key); \ + if (constantObject == NULL) { \ + param = key; \ + } else { \ + Py_DECREF(key); \ + Py_INCREF(constantObject); \ + param = (PyObject *)constantObject; \ + } \ + } + + +/* 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; \ + } + +/* sets the object into the dict */ +#define SETDICTOBJ(name, object) \ + PyDict_SetItemString(dict, name, object) + +/* sets a integer into the dict */ +#define SETDICTINT(name, value) \ + PyDict_SetItemString(dict, name, PyInt_FromLong(value)) + +/* sets note info dict (used by SeqEvent_get_data) */ +#define SETDICT_NOTE3 { \ + snd_seq_ev_note_t *data = &(event->data.note); \ + SETDICTINT("note.channel", data->channel); \ + SETDICTINT("note.note", data->note); \ + SETDICTINT("note.velocity", data->velocity); \ + } + +/* sets note info dict (used by SeqEvent_get_data) */ +#define SETDICT_NOTE5 { \ + snd_seq_ev_note_t *data = &(event->data.note); \ + SETDICTINT("note.channel", data->channel); \ + SETDICTINT("note.note", data->note); \ + SETDICTINT("note.velocity", data->velocity); \ + SETDICTINT("note.off_velocity", data->off_velocity); \ + SETDICTINT("note.duration", data->duration); \ + } + + + +/* sets control info dict (used by SeqEvent_get_data) */ +#define SETDICT_CTRL1 { \ + snd_seq_ev_ctrl_t *data = &(event->data.control); \ + SETDICTINT("control.value", data->value); \ + } + +/* sets control info dict (used by SeqEvent_get_data) */ +#define SETDICT_CTRL2 { \ + snd_seq_ev_ctrl_t *data = &(event->data.control); \ + SETDICTINT("control.channel", data->channel); \ + SETDICTINT("control.value", data->value); \ + } + +/* sets control info dict (used by SeqEvent_get_data) */ +#define SETDICT_CTRL3 { \ + snd_seq_ev_ctrl_t *data = &(event->data.control); \ + SETDICTINT("control.channel", data->channel); \ + SETDICTINT("control.param", data->param); \ + SETDICTINT("control.value", data->value); \ + } + +/* sets queue info dict (used by SeqEvent_get_data) */ +#define SETDICT_QUEU1 { \ + snd_seq_ev_queue_control_t *data = &(event->data.queue); \ + SETDICTINT("queue.queue", data->queue); \ + } + +/* sets addr info dict (used by SeqEvent_get_data) */ +#define SETDICT_ADDR1 { \ + snd_seq_addr_t *data = &(event->data.addr); \ + SETDICTINT("addr.client", data->client); \ + } + +/* sets addr info dict (used by SeqEvent_get_data) */ +#define SETDICT_ADDR2 { \ + snd_seq_addr_t *data = &(event->data.addr); \ + SETDICTINT("addr.client", data->client); \ + SETDICTINT("addr.port", data->port); \ + } + +/* sets connect info dict (used by SeqEvent_get_data) */ +#define SETDICT_CONN4 { \ + snd_seq_connect_t *data = &(event->data.connect); \ + SETDICTINT("connect.sender.client", data->sender.client); \ + SETDICTINT("connect.sender.port", data->sender.port); \ + SETDICTINT("connect.dest.client", data->dest.client); \ + SETDICTINT("connect.dest.port", data->dest.port); \ + } + +/* sets result info dict (used by SeqEvent_get_data) */ +#define SETDICT_RESU2 { \ + snd_seq_result_t *data = &(event->data.result); \ + SETDICTINT("result.event", data->event); \ + SETDICTINT("result.result", data->result); \ + } + +/* sets ext info dict (used by SeqEvent_get_data) */ +#define SETDICT_EXT { \ + snd_seq_ev_ext_t *data = &(event->data.ext); \ + PyObject *list = PyList_New(data->len); \ + int i = 0; \ + unsigned char *t = (unsigned char *) data->ptr; \ + for (i = 0; i < data->len; i++) { \ + PyList_Append(list, PyInt_FromLong(t[i])); \ + } \ + SETDICTOBJ("ext", list); \ + } + +/* gets integer from python param */ +#define GETDICTINT(name, param) { \ + PyObject *value = PyDict_GetItemString(dict, name); \ + if (value != NULL) { \ + if (!PyInt_Check(value)) { \ + PyErr_SetString(PyExc_TypeError, name " must be a integer"); \ + return NULL; \ + } \ + param = PyInt_AsLong(value); \ + } \ + } + +/* gets float from python param */ +#define GETDICTFLOAT(name, param1, param2) { \ + PyObject *value = PyDict_GetItemString(dict, name); \ + if (value != NULL) { \ + if (PyInt_Check(value)) { \ + param2 = 0; \ + param1 = PyInt_AsLong(value); \ + } else if (PyFloat_Check(value)) { \ + double d = PyFloat_AsDouble(value); \ + unsigned int i = d; \ + d -= i; \ + param2 = d * 1000000; \ + param1 = i; \ + } else { \ + PyErr_SetString(PyExc_TypeError, \ + name " must be a integer or float"); \ + return NULL; \ + } \ + } \ + } + +/* gets ext data from python list */ +#define GETDICTEXT(name) { \ + PyObject *list = PyDict_GetItemString(dict, name); \ + if (list != NULL) { \ + if (!PyList_Check(list)) { \ + PyErr_SetString(PyExc_TypeError, \ + name " must be a list of integers"); \ + return NULL; \ + } \ + FREECHECKED("buff", self->buff); \ + int len = PyList_Size(list); \ + self->event->data.ext.len = len; \ + if (len > 0) { \ + int i; \ + self->buff = malloc(len); \ + for (i = 0; i < len; i++) { \ + PyObject *item = PyList_GetItem(list, i); \ + if (!PyInt_Check(item)) { \ + PyErr_SetString(PyExc_TypeError, \ + name " must be a list of integers"); \ + return NULL; \ + } \ + self->buff[i] = PyInt_AsLong(item); \ + } \ + } \ + } \ + } + + + + + + +////////////////////////////////////////////////////////////////////////////// +// alsaseq.Constant implementation +////////////////////////////////////////////////////////////////////////////// + +/* alsaseq.Constant->type enumeration */ +enum { + PYALSASEQ_CONST_STREAMS, + PYALSASEQ_CONST_MODE, + PYALSASEQ_CONST_QUEUE, + PYALSASEQ_CONST_CLIENT_TYPE, + PYALSASEQ_CONST_PORT_CAP, + PYALSASEQ_CONST_PORT_TYPE, + PYALSASEQ_CONST_EVENT_TYPE, + PYALSASEQ_CONST_EVENT_TIMESTAMP, + PYALSASEQ_CONST_EVENT_TIMEMODE, + PYALSASEQ_CONST_ADDR_CLIENT, + PYALSASEQ_CONST_ADDR_PORT, +}; + +// constants dictionaries + +TCONSTDICT(STREAMS); +TCONSTDICT(MODE); +TCONSTDICT(QUEUE); +TCONSTDICT(CLIENT_TYPE); +TCONSTDICT(PORT_CAP); +TCONSTDICT(PORT_TYPE); +TCONSTDICT(EVENT_TYPE); +TCONSTDICT(EVENT_TIMESTAMP); +TCONSTDICT(EVENT_TIMEMODE); +TCONSTDICT(ADDR_CLIENT); +TCONSTDICT(ADDR_PORT); + + +/** alsaseq.Constant __doc__ */ +const char Constant__doc__ [] = + "Constant() -> Constant object\n" + "\n" + "Represents one of the many integer constants from the\n" + "libasound sequencer API.\n" + "\n" + "This class serves the following purposes:\n" + " a. wrap the SND_SEQ* constants to a python int;\n" + " b. provide a string representation of the constant;\n" + "\n" + "For a), this class is a subclass of the python int. Example:\n" + " >>> import alsaseq\n" + " >>> print alsaseq.SEQ_EVENT_NOTE\n" + " 5\n" + " >>> event = alsaseq.SeqEvent(alsaseq.SEQ_EVENT_NOTE)\n" + " >>> print event.queue\n" + " 253\n" + " >>> print event.dest\n" + " (0, 0)\n" + "\n" + "For b), you can get the name of the constant by calling the appropiate\n" + "method (__str__ or __repr__). Example:\n" + " >>> print str(event.queue), repr(event.queue)\n" + " SEQ_QUEUE_DIRECT SEQ_QUEUE_DIRECT(0xfd)\n" + " >>> print str(event.dest)\n" + " (SEQ_CLIENT_SYSTEM(0x0), SEQ_PORT_SYSTEM_TIMER(0x0))\n" + "\n" + "This class implements some of the bitwise operations from the\n" + "Python number protocol." + ; + +/** alsaseq.Constant object structure type */ +typedef struct { + PyObject_HEAD + ; + + /* value of constant */ + unsigned int 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("%s(0x%x)", + 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 ops supported) */ +NUMPROTOCOL2(Add, +) +NUMPROTOCOL2(Subtract, -) +NUMPROTOCOL2(Xor, ^) +NUMPROTOCOL2(Or, |) +NUMPROTOCOL2(And, &) +NUMPROTOCOL1(Invert, ~) + +/** alsaseq.Constant number protocol methods */ +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_as_number: &Constant_as_number, + tp_free: PyObject_Del, + tp_str: (reprfunc)Constant_str, + tp_repr: (reprfunc)Constant_repr +}; + + + + + +////////////////////////////////////////////////////////////////////////////// +// alsaseq.SequencerError implementation +////////////////////////////////////////////////////////////////////////////// + +/** alsaseq.SequencerError instance (initialized in initalsaseq) */ +static PyObject *SequencerError; + + + + + +////////////////////////////////////////////////////////////////////////////// +// alsaseq.SeqEvent implementation +////////////////////////////////////////////////////////////////////////////// + +/** alsaseq.SeqEvent __doc__ */ +const char SeqEvent__doc__ [] = + "SeqEvent(type[, timestamp[,timemode]]) -> SeqEvent object\n" + "\n" + "Creates an Alsa Sequencer Event object. The type must be one of the\n" + "alsaseq.SEQ_EVENT_* constants. The timestamp specifies if tick (midi)\n" + "or real time is used, must be alsaseq.SEQ_TIME_STAMP_TICK or\n" + "alsaseq.SEQ_TIME_STAMP_REAL. The timemode specifies if the time will\n" + "be absolute or relative, must be alsaseq.SEQ_TIME_MODE_ABS or\n" + "alsaseq.SEQ_TIME_MODE_REL.\n" + "\n" + "The timestamp and timemode defaults to SEQ_TIME_STAMP_TICK and\n" + "SEQ_TIME_MODE_ABS when they are not specified.\n" + "\n" + "SeqEvent objects are received or sent using a Sequencer object. The\n" + "data of the event can be set or retrieved using the set_data() or\n" + "get_data() methods; both use a dictionary. The rest of properties of\n" + "a SeqEvent can be accesed and changed using the SeqEvent attributes.\n" + "\n" + "The attributes and defaults values are:\n" + "type -- event type.\n" + "timestamp -- tick(midi) or real(nanoseconds). Default: SEQ_TIME_STAMP_TICK.\n" + "timemode -- relative or absolute. Default: SEQ_TIME_MODE_ABS.\n" + "queue -- queue id. Default: SEQ_QUEUE_DIRECT\n" + "time -- time of this event. Default: 0.\n" + "source -- source address tuple (client,port). Default: (0, 0).\n" + "dest -- dest address tuple (client, port). Default: (0, 0).\n" + "\n" + "There are dictionaries available as attributes, that contains\n" + "alsaseq.SEQ* constants that can be used for each attribute:\n" + "_dtype -- event type constants.\n" + "_dtimestamp -- event timestamp constants.\n" + "_dtimemode -- event timemode constants.\n" + "_dqueue -- queue id's.\n" + "_dclient -- client address (for source an dest).\n" + "_dport -- port address (for source an dest).\n" + ; + +/** alsaseq.SeqEvent object structure type */ +typedef struct { + PyObject_HEAD + ; + + /* alsa event */ + snd_seq_event_t *event; + + /* pointer copied/created from/for variable length events */ + unsigned char *buff; +} SeqEventObject; + +/** alsaseq.SeqEvent type (initialized later...) */ +static PyTypeObject SeqEventType; + +/** internal use: set type and update flags based on event type */ +static int +_SeqEvent_set_type(SeqEventObject *self, + long type) { + self->event->type = type; + + /* clean previous buff... */ + FREECHECKED("buff", self->buff); + memset(&(self->event->data), '\0', sizeof(self->event->data)); + + /* update flags */ + if (snd_seq_ev_is_variable_type(self->event)) { + snd_seq_ev_set_variable(self->event, 0, NULL); + } else if (snd_seq_ev_is_varusr_type(self->event)) { + snd_seq_ev_set_varusr(self->event, 0, NULL); + } else if (snd_seq_ev_is_fixed_type(self->event)) { + snd_seq_ev_set_fixed(self->event); + } else { + PyErr_SetString(PyExc_ValueError, + "Invalid value for type; " + "use one of alsaseq.SEQ_EVENT_* constants."); + return -1; + } + + return 0; +} + +/** internal use: set timestamp flag */ +static int +_SeqEvent_set_timestamp(SeqEventObject *self, + long timestamp) { + if (timestamp == SND_SEQ_TIME_STAMP_TICK) { + self->event->flags &= ~(SND_SEQ_TIME_STAMP_MASK); + self->event->flags |= SND_SEQ_TIME_STAMP_TICK; + } else if (timestamp == SND_SEQ_TIME_STAMP_REAL) { + self->event->flags &= ~(SND_SEQ_TIME_STAMP_MASK); + self->event->flags |= SND_SEQ_TIME_STAMP_REAL; + } else { + PyErr_SetString(PyExc_ValueError, + "Invalid value for timestamp; " + "use alsaseq.SEQ_TIME_STAMP_TICK or " + "alsaseq.SEQ_TIME_STAMP_REAL."); + return -1; + } + + return 0; +} + +/** internal use: set timemode flag */ +static int +_SeqEvent_set_timemode(SeqEventObject *self, + long timemode) { + if (timemode == SND_SEQ_TIME_MODE_ABS) { + self->event->flags &= ~(SND_SEQ_TIME_MODE_MASK); + self->event->flags |= SND_SEQ_TIME_MODE_ABS; + } else if (timemode == SND_SEQ_TIME_MODE_REL) { + self->event->flags &= ~(SND_SEQ_TIME_MODE_MASK); + self->event->flags |= SND_SEQ_TIME_MODE_REL; + } else { + PyErr_SetString(PyExc_ValueError, + "Invalid value for timemode; " + "use alsaseq.SEQ_TIME_MODE_ABS or " + "alsaseq.SEQ_TIME_MODE_REL."); + return -1; + } + + return 0; +} + + +/** alsaseq.SeqEvent tp_init */ +static int +SeqEvent_init(SeqEventObject *self, + PyObject *args, + PyObject *kwds) { + int type = 0; + int timestamp = SND_SEQ_TIME_STAMP_TICK; + int timemode = SND_SEQ_TIME_MODE_ABS; + char *kwlist [] = {"type", "timestamp", "timemode", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|ii", kwlist, &type, + ×tamp, &timemode)) { + return -1; + } + + if (_SeqEvent_set_type(self, type) !=0 ) { + return -1; + } + if (_SeqEvent_set_timestamp(self, timestamp) != 0) { + return -1; + } + if (_SeqEvent_set_timemode(self, timemode) != 0) { + return -1; + } + + snd_seq_ev_set_direct(self->event); + snd_seq_ev_set_subs(self->event); + + return 0; +} + + + +/** internal use: create a alsaseq.SeqEvent from a snd_seq_event_t structure */ +static PyObject * +SeqEvent_create(snd_seq_event_t *event) { + SeqEventObject *self = PyObject_New(SeqEventObject, &SeqEventType); + + if (self == NULL) { + return NULL; + } + + self->event = malloc(sizeof(snd_seq_event_t)); + if (self->event == NULL) { + PyObject_Del(self); + return PyErr_NoMemory(); + } + + memcpy(self->event, event, sizeof(snd_seq_event_t)); + if (snd_seq_ev_is_variable_type(self->event)) { + self->buff = malloc(self->event->data.ext.len); + memcpy(self->event->data.ext.ptr, self->buff, self->event->data.ext.len); + self->event->data.ext.ptr = self->buff; + } else { + self->buff = NULL; + } + + return (PyObject *) self; +} + +/** alsaseq.SeqEvent tp_new */ +static PyObject * +SeqEvent_new(PyTypeObject *type, + PyObject *args, + PyObject *kwds) { + SeqEventObject *self; + + self = (SeqEventObject *)type->tp_alloc(type, 0); + + self->event = malloc(sizeof(snd_seq_event_t)); + if (self->event == NULL) { + type->tp_free(self); + return PyErr_NoMemory(); + } + snd_seq_ev_clear(self->event); + self->buff = NULL; + + return (PyObject *) self; +} + +/** alsaseq.SeqEvent tp_dealloc */ +static void +SeqEvent_dealloc(SeqEventObject *self) { + FREECHECKED("event", self->event); + FREECHECKED("buff", self->buff); + + self->ob_type->tp_free(self); +} + +/** alsaseq.SeqEvent type attribute: __doc__ */ +const char SeqEvent_type__doc__ [] = + "type -> Constant object\n" + "\n" + "The type of this alsaseq.SeqEvent; Use one of the\n" + "alsaseq.SEQ_EVENT_* constants.\n\n" + "Note: changing a SeqEvent type will *ERASE* AND *CLEAN* its data!!" + ; + +/** alsaseq.SeqEvent type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_get_type(SeqEventObject *self) { + TCONSTRETURN(EVENT_TYPE, self->event->type); +} + +/** alsaseq.SeqEvent type attribute: tp_getset setter() */ +static int +SeqEvent_set_type(SeqEventObject *self, + PyObject *val) { + SETCHECKPYINT("type", val); + + return _SeqEvent_set_type(self, PyInt_AsLong(val)); +} + +/** alsaseq.SeqEvent tag attribute: __doc__ */ +const char SeqEvent_tag__doc__ [] = + "tag -> int\n" + "\n" + "The tag of this alsaseq.SeqEvent (range:0-255)." + ; + +/** alsaseq.SeqEvent tag attribute: tp_getset getter() */ +static PyObject * +SeqEvent_get_tag(SeqEventObject *self) { + return PyInt_FromLong(self->event->tag); +} + +/** alsaseq.SeqEvent tag attribute: tp_getset setter() */ +static int +SeqEvent_set_tag(SeqEventObject *self, + PyObject *val) { + long tag; + + SETCHECKPYINT("tag", val); + + tag = PyInt_AsLong(val); + if (tag < 0 || tag > 255) { + PyErr_Format(PyExc_ValueError, + "invalid value '%ld'; allowed range: 0 - 255", + tag); + return -1; + } + self->event->tag = tag; + return 0; +} + +/** alsaseq.seqEvent timestamp attribute: __doc__ */ +const char SeqEvent_timestamp__doc__ [] = + "timestamp -> Constant object\n" + "\n" + "The time stamp flag of this alsaseq.SeqEvent;\n" + "use alsaseq.SEQ_TIME_STAMP_TICK or alsaseq.SEQ_TIME_STAMP_REAL." + ; + +/** alsaseq.SeqEvent timestamp attribute: tp_getset getter() */ +static PyObject * +SeqEvent_get_timestamp(SeqEventObject *self) { + if (snd_seq_ev_is_tick(self->event)) { + TCONSTRETURN(EVENT_TIMESTAMP, SND_SEQ_TIME_STAMP_TICK); + } else if (snd_seq_ev_is_real(self->event)) { + TCONSTRETURN(EVENT_TIMESTAMP, SND_SEQ_TIME_STAMP_REAL); + } + + /* should never get here ... */ + return NULL; +} + +/** alsaseq.SeqEvent timestamp attribute: tp_getset setter() */ +static int +SeqEvent_set_timestamp(SeqEventObject *self, + PyObject *val) { + SETCHECKPYINT("timestamp", val); + + return _SeqEvent_set_timestamp(self, PyInt_AsLong(val)); +} + +/** alsaseq.SeqEvent timemode attribute: __doc__ */ +const char SeqEvent_timemode__doc__ [] = + "timemode -> Constant object\n" + "\n" + "The time mode flag of this alsaseq.SeqEvent;\n" + "use alsaseq.SEQ_TIME_MODE_ABS or alsaseq.SEQ_TIME_MODE_REL." + ; + +/** alsaseq.SeqEvent timemode attribute: tp_getset getter() */ +static PyObject * +SeqEvent_get_timemode(SeqEventObject *self) { + if (snd_seq_ev_is_abstime(self->event)) { + TCONSTRETURN(EVENT_TIMEMODE, SND_SEQ_TIME_MODE_ABS); + } else if (snd_seq_ev_is_reltime(self->event)) { + TCONSTRETURN(EVENT_TIMEMODE, SND_SEQ_TIME_MODE_REL); + } + + /* should never get here ... */ + return NULL; +} + +/** alsaseq.SeqEvent timemode attribute: tp_getset setter() */ +static int +SeqEvent_set_timemode(SeqEventObject *self, + PyObject *val) { + SETCHECKPYINT("timemode", val); + + return _SeqEvent_set_timemode(self, PyInt_AsLong(val)); +} + +/** alsaseq.SeqEvent queue attribute: __doc__ */ +const char SeqEvent_queue__doc__ [] = + "queue -> int\n" + "\n" + "The send queue id of this alsaseq.SeqEvent." + ; + +/** alsaseq.SeqEvent queue: tp_getset getter() */ +static PyObject * +SeqEvent_get_queue(SeqEventObject *self) { + TCONSTRETURN(QUEUE, self->event->queue); +} + +/** alsaseq.SeqEvent queue attribute: tp_getset setter() */ +static int +SeqEvent_set_queue(SeqEventObject *self, + PyObject *val) { + SETCHECKPYINT("queue", val); + + self->event->queue = PyInt_AsLong(val); + return 0; +} + +/** alsaseq.SeqEvent time attribute: __doc__ */ +const char SeqEvent_time__doc__ [] = + "time -> int or float\n" + "\n" + "The send time of this alsaseq.SeqEvent.\n" + "If the timestamp of the SeqEvent is SEQ_TIME_STAMP_TICK, an\n" + "integer value is used which represents the midi tick time; \n" + "if the timestamp is SEQ_TIME_STAMP_REAL, a float value is used\n" + "which represents seconds the same way Python time module.\n" + ; + +/** alsaseq.SeqEvent time attribute: tp_getset getter() */ +static PyObject * +SeqEvent_get_time(SeqEventObject *self) { + if (snd_seq_ev_is_real(self->event)) { + double time = self->event->time.time.tv_sec; + time += self->event->time.time.tv_nsec * 0.000001; + return PyFloat_FromDouble(time); + } else if (snd_seq_ev_is_tick(self->event)) { + long tick = self->event->time.tick; + return PyInt_FromLong(tick); + } + + /* should never get here... */ + return NULL; +} + +/** alsaseq.SeqEvent time attribute: tp_getset setter() */ +static int +SeqEvent_set_time(SeqEventObject *self, + PyObject *val) { + int is_int = PyInt_Check(val); + int is_float = PyFloat_Check(val); + + if (!(is_int || is_float)) { + PyErr_Format(PyExc_TypeError, + "integer or float expected"); + return -1; + } + + if (snd_seq_ev_is_real(self->event)) { + if (is_int) { + double time = PyInt_AsLong(val); + self->event->time.time.tv_sec = time; + self->event->time.time.tv_nsec = 0; + } else { + double time = PyFloat_AsDouble(val); + self->event->time.time.tv_sec = (unsigned int)time; + time -= self->event->time.time.tv_sec; + self->event->time.time.tv_nsec = time * 1000000; + } + } else if (snd_seq_ev_is_tick(self->event)) { + if (is_int) { + self->event->time.tick = PyInt_AsLong(val); + } else { + self->event->time.tick = PyFloat_AsDouble(val); + } + } else { + /* should never get here... */ + return -1; + } + return 0; +} + +/** alsaseq.SeqEvent source attribute: __doc __ */ +const char SeqEvent_source__doc__ [] = + "source -> tuple (client_id, port_id)\n" + "\n" + "Tuple representing the send source address of this alsaseq.SeqEvent.\n" + "The tuple is (client_id, port_id). If the client or port id are known,\n" + "the appropiate constant may be used, otherwise integers are expected." + ; + +/** alsaseq.SeqEvent source attribute: tp_getset getter() */ +static PyObject * +SeqEvent_get_source(SeqEventObject *self) { + int source_client = self->event->source.client; + int source_port = self->event->source.port; + PyObject *client, *port; + PyObject *tuple = PyTuple_New(2); + + TCONSTASSIGN(ADDR_CLIENT, source_client, client); + TCONSTASSIGN(ADDR_PORT, source_port, port); + + PyTuple_SetItem(tuple, 0, client); + PyTuple_SetItem(tuple, 1, port); + + return tuple; +} + +/** alsaseq.SeqEvent source attribute: tp_getset setter() */ +static int +SeqEvent_set_source(SeqEventObject *self, + PyObject *val) { + PyObject *client; + PyObject *port; + + if (!PyTuple_Check(val) || PyTuple_Size(val) != 2) { + PyErr_SetString(PyExc_TypeError, "expected tuple (client,port)"); + return -1; + } + + client = PyTuple_GetItem(val, 0); + port = PyTuple_GetItem(val, 1); + SETCHECKPYINT("source client", client); + SETCHECKPYINT("source port", port); + + self->event->source.client = PyInt_AsLong(client); + self->event->source.port = PyInt_AsLong(port); + + return 0; +} + +/** alsaseq.SeqEvent dest attribute: __doc __ */ +const char SeqEvent_dest__doc__ [] = + "dest -> tuple (client_id, port_id)\n" + "\n" + "Tuple representing the destination address of this alsaseq.SeqEvent.\n" + "The tuple is (client_id, port_id). If the client or port id are known,\n" + "the appropiate constant may be used, otherwise integers are expected." + ; + +/** alsaseq.SeqEvent dest attribute: tp_getset getter() */ +static PyObject * +SeqEvent_get_dest(SeqEventObject *self) { + int dest_client = self->event->dest.client; + int dest_port = self->event->dest.port; + PyObject *client, *port; + PyObject *tuple = PyTuple_New(2); + + TCONSTASSIGN(ADDR_CLIENT, dest_client, client); + TCONSTASSIGN(ADDR_PORT, dest_port, port); + + PyTuple_SetItem(tuple, 0, client); + PyTuple_SetItem(tuple, 1, port); + + return tuple; +} + +/** alsaseq.SeqEvent dest attribute: tp_getset setter() */ +static int +SeqEvent_set_dest(SeqEventObject *self, + PyObject *val) { + PyObject *client; + PyObject *port; + + if (!PyTuple_Check(val) || PyTuple_Size(val) != 2) { + PyErr_SetString(PyExc_TypeError, "expected tuple (client,port)"); + return -1; + } + + client = PyTuple_GetItem(val, 0); + port = PyTuple_GetItem(val, 1); + SETCHECKPYINT("dest client", client); + SETCHECKPYINT("dest port", port); + + self->event->dest.client = PyInt_AsLong(client); + self->event->dest.port = PyInt_AsLong(port); + + return 0; +} + +/** alsaseq.SeqEvent is_result_type attribute: __doc__ */ +const char SeqEvent_is_result_type__doc__ [] = + "is_result_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type result (True) or not\n" + "(False). Note: read-only attribute." + ; + + +/** alsaseq.SeqEvent is_result_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_result_type(SeqEventObject *self) { + if (snd_seq_ev_is_result_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_note_type attribute: __doc__ */ +const char SeqEvent_is_note_type__doc__ [] = + "is_note_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type note (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_note_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_note_type(SeqEventObject *self) { + if (snd_seq_ev_is_note_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_control_type attribute: __doc__ */ +const char SeqEvent_is_control_type__doc__ [] = + "is_control_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type control (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_note_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_control_type(SeqEventObject *self) { + if (snd_seq_ev_is_control_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_channel_type attribute: __doc__ */ +const char SeqEvent_is_channel_type__doc__ [] = + "is_channel_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type channel (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_channel_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_channel_type(SeqEventObject *self) { + if (snd_seq_ev_is_channel_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_queue_type attribute: __doc__ */ +const char SeqEvent_is_queue_type__doc__ [] = + "is_queue_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is a queue event (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_queue_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_queue_type(SeqEventObject *self) { + if (snd_seq_ev_is_queue_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_message_type attribute: __doc__ */ +const char SeqEvent_is_message_type__doc__ [] = + "is_message_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type message (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_message_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_message_type(SeqEventObject *self) { + if (snd_seq_ev_is_message_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_subscribe_type attribute: __doc__ */ +const char SeqEvent_is_subscribe_type__doc__ [] = + "is_subscribe_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type subscribe (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_subscribe_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_subscribe_type(SeqEventObject *self) { + if (snd_seq_ev_is_subscribe_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_sample_type attribute: __doc__ */ +const char SeqEvent_is_sample_type__doc__ [] = + "is_sample_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type sample (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_sample_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_sample_type(SeqEventObject *self) { + if (snd_seq_ev_is_sample_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_user_type attribute: __doc__ */ +const char SeqEvent_is_user_type__doc__ [] = + "is_user_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type user (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_user_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_user_type(SeqEventObject *self) { + if (snd_seq_ev_is_user_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_instr_type attribute: __doc__ */ +const char SeqEvent_is_instr_type__doc__ [] = + "is_instr_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type instr (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_instr_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_instr_type(SeqEventObject *self) { + if (snd_seq_ev_is_instr_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_fixed_type attribute: __doc__ */ +const char SeqEvent_is_fixed_type__doc__ [] = + "is_fixed_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type fixed (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_fixed_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_fixed_type(SeqEventObject *self) { + if (snd_seq_ev_is_fixed_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_variable_type attribute: __doc__ */ +const char SeqEvent_is_variable_type__doc__ [] = + "is_variable_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type variable (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_variable_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_variable_type(SeqEventObject *self) { + if (snd_seq_ev_is_variable_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_varusr_type attribute: __doc__ */ +const char SeqEvent_is_varusr_type__doc__ [] = + "is_message_type -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is of type varusr (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_varusr_type attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_varusr_type(SeqEventObject *self) { + if (snd_seq_ev_is_varusr_type(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_reserved attribute: __doc__ */ +const char SeqEvent_is_reserved__doc__ [] = + "is_reserved -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is reserved (True) or not (False).\n" + "Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_reserved attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_reserved(SeqEventObject *self) { + if (snd_seq_ev_is_reserved(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_prior attribute: __doc__ */ +const char SeqEvent_is_prior__doc__ [] = + "is_prior -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is prior (True) or not (False).\n" + "Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_prior attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_prior(SeqEventObject *self) { + if (snd_seq_ev_is_prior(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_fixed attribute: __doc__ */ +const char SeqEvent_is_fixed__doc__ [] = + "is_fixed -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is fixed (True) or not (False).\n" + "Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_fixed attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_fixed(SeqEventObject *self) { + if (snd_seq_ev_is_fixed(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_variable attribute: __doc__ */ +const char SeqEvent_is_variable__doc__ [] = + "is_variable -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is variable (True) or not (False).\n" + "Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_variable attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_variable(SeqEventObject *self) { + if (snd_seq_ev_is_variable(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_varusr attribute: __doc__ */ +const char SeqEvent_is_varusr__doc__ [] = + "is_varusr -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is varusr (True) or not (False).\n" + "Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_varusr attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_varusr(SeqEventObject *self) { + if (snd_seq_ev_is_varusr(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_tick attribute: __doc__ */ +const char SeqEvent_is_tick__doc__ [] = + "is_tick -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is tick timed (True) or not (False).\n" + "Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_tick attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_tick(SeqEventObject *self) { + if (snd_seq_ev_is_tick(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_real attribute: __doc__ */ +const char SeqEvent_is_real__doc__ [] = + "is_real -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is real timed (True) or not (False).\n" + "Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_real attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_real(SeqEventObject *self) { + if (snd_seq_ev_is_real(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_abstime attribute: __doc__ */ +const char SeqEvent_is_abstime__doc__ [] = + "is_abstime -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is abstime timed (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_abstime attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_abstime(SeqEventObject *self) { + if (snd_seq_ev_is_abstime(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_reltime attribute: __doc__ */ +const char SeqEvent_is_reltime__doc__ [] = + "is_reltime -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is a reltime timed (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_reltime attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_reltime(SeqEventObject *self) { + if (snd_seq_ev_is_reltime(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent is_direct attribute: __doc__ */ +const char SeqEvent_is_direct__doc__ [] = + "is_direct -> boolean\n" + "\n" + "Indicates if this alsaseq.SeqEvent is sent directly (True) or not\n" + "(False). Note: read-only attribute." + ; + +/** alsaseq.SeqEvent is_direct attribute: tp_getset getter() */ +static PyObject * +SeqEvent_is_direct(SeqEventObject *self) { + if (snd_seq_ev_is_direct(self->event)) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +/** alsaseq.SeqEvent _dtype attribute: __doc__ */ +const char SeqEvent__dtype__doc__ [] = + "_dtype -> dictionary\n" + "\n" + "Dictionary will available event type constants." + ; + +/** alsaseq.Seqevent _dtype attribute: tp_getset getter() */ +static PyObject * +SeqEvent__dtype(SeqEventObject *self) { + Py_INCREF(_dictPYALSASEQ_CONST_EVENT_TYPE); + return _dictPYALSASEQ_CONST_EVENT_TYPE; +} + +/** alsaseq.SeqEvent _dtimestamp attribute: __doc__ */ +const char SeqEvent__dtimestamp__doc__ [] = + "_dtimestamp -> dictionary\n" + "\n" + "Dictionary will available event timestamp constants." + ; + +/** alsaseq.Seqevent _dtimestamp attribute: tp_getset getter() */ +static PyObject * +SeqEvent__dtimestamp(SeqEventObject *self) { + Py_INCREF(_dictPYALSASEQ_CONST_EVENT_TIMESTAMP); + return _dictPYALSASEQ_CONST_EVENT_TIMESTAMP; +} + +/** alsaseq.SeqEvent _dtimemode attribute: __doc__ */ +const char SeqEvent__dtimemode__doc__ [] = + "_dtimemode -> dictionary\n" + "\n" + "Dictionary of available event timemode constants." + ; + +/** alsaseq.Seqevent _dtimemode attribute: tp_getset getter() */ +static PyObject * +SeqEvent__dtimemode(SeqEventObject *self) { + Py_INCREF(_dictPYALSASEQ_CONST_EVENT_TIMEMODE); + return _dictPYALSASEQ_CONST_EVENT_TIMEMODE; +} + +/** alsaseq.SeqEvent _dqueue attribute: __doc__ */ +const char SeqEvent__dqueue__doc__ [] = + "_dqueue -> dictionary\n" + "\n" + "Dictionary of known queue id constants." + ; + +/** alsaseq.Seqevent _dqueue attribute: tp_getset getter() */ +static PyObject * +SeqEvent__dqueue(SeqEventObject *self) { + Py_INCREF(_dictPYALSASEQ_CONST_QUEUE); + return _dictPYALSASEQ_CONST_QUEUE; +} + +/** alsaseq.SeqEvent _dclient attribute: __doc__ */ +const char SeqEvent__dclient__doc__ [] = + "_dclient -> dictionary\n" + "\n" + "Dictionary of known client addresses constants." + ; + +/** alsaseq.Seqevent _dclient attribute: tp_getset getter() */ +static PyObject * +SeqEvent__dclient(SeqEventObject *self) { + Py_INCREF(TDICT(ADDR_CLIENT)); + return TDICT(ADDR_CLIENT); +} + +/** alsaseq.SeqEvent _dport attribute: __doc__ */ +const char SeqEvent__dport__doc__ [] = + "_dport -> dictionary\n" + "\n" + "Dictionary of known port addresses constants." + ; + +/** alsaseq.Seqevent _dport attribute: tp_getset getter() */ +static PyObject * +SeqEvent__dport(SeqEventObject *self) { + Py_INCREF(TDICT(ADDR_PORT)); + return TDICT(ADDR_PORT); +} + + +/** alsaseq.SeqEvent tp_getset list */ +static PyGetSetDef SeqEvent_getset[] = { + {"type", + (getter) SeqEvent_get_type, + (setter) SeqEvent_set_type, + (char *) SeqEvent_type__doc__, + NULL}, + {"timestamp", + (getter) SeqEvent_get_timestamp, + (setter) SeqEvent_set_timestamp, + (char *) SeqEvent_timestamp__doc__, + NULL}, + {"timemode", + (getter) SeqEvent_get_timemode, + (setter) SeqEvent_set_timemode, + (char *) SeqEvent_timemode__doc__, + NULL}, + {"tag", + (getter) SeqEvent_get_tag, + (setter) SeqEvent_set_tag, + (char *) SeqEvent_tag__doc__, + NULL}, + {"queue", + (getter) SeqEvent_get_queue, + (setter) SeqEvent_set_queue, + (char *) SeqEvent_queue__doc__, + NULL}, + {"time", + (getter) SeqEvent_get_time, + (setter) SeqEvent_set_time, + (char *) SeqEvent_time__doc__, + NULL}, + {"source", + (getter) SeqEvent_get_source, + (setter) SeqEvent_set_source, + (char *) SeqEvent_source__doc__, + NULL}, + {"dest", + (getter) SeqEvent_get_dest, + (setter) SeqEvent_set_dest, + (char *) SeqEvent_dest__doc__, + NULL}, + {"is_result_type", + (getter) SeqEvent_is_result_type, + NULL, + (char *) SeqEvent_is_result_type__doc__, + NULL}, + {"is_note_type", + (getter) SeqEvent_is_note_type, + NULL, + (char *) SeqEvent_is_note_type__doc__, + NULL}, + {"is_control_type", + (getter) SeqEvent_is_control_type, + NULL, + (char *) SeqEvent_is_control_type__doc__, + NULL}, + {"is_channel_type", + (getter) SeqEvent_is_channel_type, + NULL, + (char *) SeqEvent_is_channel_type__doc__, + NULL}, + {"is_queue_type", + (getter) SeqEvent_is_queue_type, + NULL, + (char *) SeqEvent_is_queue_type__doc__, + NULL}, + {"is_message_type", + (getter) SeqEvent_is_message_type, + NULL, + (char *) SeqEvent_is_message_type__doc__, + NULL}, + {"is_subscribe_type", + (getter) SeqEvent_is_subscribe_type, + NULL, + (char *) SeqEvent_is_subscribe_type__doc__, + NULL}, + {"is_sample_type", + (getter) SeqEvent_is_sample_type, + NULL, + (char *) SeqEvent_is_sample_type__doc__, + NULL}, + {"is_user_type", + (getter) SeqEvent_is_user_type, + NULL, + (char *) SeqEvent_is_user_type__doc__, + NULL}, + {"is_instr_type", + (getter) SeqEvent_is_instr_type, + NULL, + (char *) SeqEvent_is_instr_type__doc__, + NULL}, + {"is_fixed_type", + (getter) SeqEvent_is_fixed_type, + NULL, + (char *) SeqEvent_is_fixed_type__doc__, + NULL}, + {"is_variable_type", + (getter) SeqEvent_is_variable_type, + NULL, + (char *) SeqEvent_is_variable_type__doc__, + NULL}, + {"is_varusr_type", + (getter) SeqEvent_is_varusr_type, + NULL, + (char *) SeqEvent_is_varusr_type__doc__, + NULL}, + {"is_reserved", + (getter) SeqEvent_is_reserved, + NULL, + (char *) SeqEvent_is_reserved__doc__, + NULL}, + {"is_prior", + (getter) SeqEvent_is_prior, + NULL, + (char *) SeqEvent_is_prior__doc__, + NULL}, + {"is_fixed", + (getter) SeqEvent_is_fixed, + NULL, + (char *) SeqEvent_is_fixed__doc__, + NULL}, + {"is_variable", + (getter) SeqEvent_is_variable, + NULL, + (char *) SeqEvent_is_variable__doc__, + NULL}, + {"is_varusr", + (getter) SeqEvent_is_varusr, + NULL, + (char *) SeqEvent_is_varusr__doc__, + NULL}, + {"is_tick", + (getter) SeqEvent_is_tick, + NULL, + (char *) SeqEvent_is_tick__doc__, + NULL}, + {"is_real", + (getter) SeqEvent_is_real, + NULL, + (char *) SeqEvent_is_real__doc__, + NULL}, + {"is_abstime", + (getter) SeqEvent_is_abstime, + NULL, + (char *) SeqEvent_is_abstime__doc__, + NULL}, + {"is_reltime", + (getter) SeqEvent_is_reltime, + NULL, + (char *) SeqEvent_is_reltime__doc__, + NULL}, + {"is_direct", + (getter) SeqEvent_is_direct, + NULL, + (char *) SeqEvent_is_direct__doc__, + NULL}, + {"_dtype", + (getter) SeqEvent__dtype, + NULL, + (char *) SeqEvent__dtype__doc__, + NULL}, + {"_dtimestamp", + (getter) SeqEvent__dtimestamp, + NULL, + (char *) SeqEvent__dtimestamp__doc__, + NULL}, + {"_dtimemode", + (getter) SeqEvent__dtimemode, + NULL, + (char *) SeqEvent__dtimemode__doc__, + NULL}, + {"_dqueue", + (getter) SeqEvent__dqueue, + NULL, + (char *) SeqEvent__dqueue__doc__, + NULL}, + {"_dclient", + (getter) SeqEvent__dclient, + NULL, + (char *) SeqEvent__dclient__doc__, + NULL}, + {"_dport", + (getter) SeqEvent__dport, + NULL, + (char *) SeqEvent__dport__doc__, + NULL}, + {NULL} +}; + +/** alsaseq.SeqEvent tp_repr */ +static PyObject * +SeqEvent_repr(SeqEventObject *self) { + PyObject *key = PyInt_FromLong(self->event->type); + ConstantObject *constObject = (ConstantObject *) + PyDict_GetItem(TDICT(EVENT_TYPE), key); + const char *typestr = "UNKNOWN"; + const char *timemode = ""; + unsigned int dtime = 0; + unsigned int ntime = 0; + Py_DECREF(key); + if (constObject != NULL) { + typestr = constObject->name; + } + + if (snd_seq_ev_is_real(self->event)) { + timemode = "real"; + dtime = self->event->time.time.tv_sec; + ntime += self->event->time.time.tv_nsec * 0.000001; + } else { + timemode = "tick"; + dtime = self->event->time.tick; + } + + return PyString_FromFormat("<alsaseq.SeqEvent type=%s(%d) flags=%d tag=%d " + "queue=%d time=%s(%u.%u) from=%d:%d to=%d:%d " + "at 0x%p>", + typestr, + self->event->type, self->event->flags, + self->event->tag, self->event->queue, + timemode, dtime, ntime, + (self->event->source).client, + (self->event->source).port, + (self->event->dest).client, + (self->event->dest).port, self); +} + +/** alsaseq.SeqEvent get_data() method: __doc__ */ +const char SeqEvent_get_data__doc__ [] = + "get_data() -> dict\n" + "\n" + "Returns a new dictionary with the data of this SeqEvent.\n" + "Changes to the returned dictionary will not change this SeqEvent data,\n" + "for changing an event data use the set_data() method.\n" + "\n" + "The dictionary items are key: value; where key is the name of the\n" + "data structure from alsa API and value is an integer or a list of them.\n" + "\n" + "The following name of structures are available:\n" + " 'note.channel' -> int\n" + " 'note.note' -> int\n" + " 'note.velocity' -> int\n" + " 'note.off_velocity' -> int\n" + " 'note.duration' -> int\n" + " 'control.channel' -> int\n" + " 'control.value' -> int\n" + " 'control.param' -> int\n" + " 'queue.queue' -> int\n" + " 'addr.client' -> int\n" + " 'addr.port' -> int\n" + " 'connect.sender.client' -> int\n" + " 'connect.sender.port' -> int\n" + " 'connect.dest.client' -> int\n" + " 'connect.dest.port' -> int\n" + " 'result.event' -> int\n" + " 'result.result' -> int\n" + " 'ext' -> list of int with sysex or variable data\n" + "\n" + "The exact items returned dependens on the event type of this SeqEvent.\n" + "For a control event, only 'control.*' may be returned; for a sysex \n" + "event, only 'ext' may be returned; for a note event, only 'note.*' may \n" + "be returned and so on." + ; + +/** alsaseq.SeqEvent get_data() method */ +static PyObject * +SeqEvent_get_data(SeqEventObject *self, + PyObject *args) { + PyObject *dict = PyDict_New(); + snd_seq_event_t *event = self->event; + + switch (event->type) { + case SND_SEQ_EVENT_SYSTEM: + case SND_SEQ_EVENT_RESULT: + SETDICT_RESU2; + break; + + case SND_SEQ_EVENT_NOTE: + SETDICT_NOTE5; + break; + + case SND_SEQ_EVENT_NOTEON: + case SND_SEQ_EVENT_NOTEOFF: + case SND_SEQ_EVENT_KEYPRESS: + SETDICT_NOTE3; + break; + + case SND_SEQ_EVENT_CONTROLLER: + SETDICT_CTRL3; + break; + + case SND_SEQ_EVENT_PGMCHANGE: + case SND_SEQ_EVENT_CHANPRESS: + case SND_SEQ_EVENT_PITCHBEND: + SETDICT_CTRL2; + break; + + case SND_SEQ_EVENT_CONTROL14: + case SND_SEQ_EVENT_NONREGPARAM: + case SND_SEQ_EVENT_REGPARAM: + SETDICT_CTRL3; + break; + + case SND_SEQ_EVENT_SONGPOS: + case SND_SEQ_EVENT_SONGSEL: + case SND_SEQ_EVENT_QFRAME: + case SND_SEQ_EVENT_TIMESIGN: + case SND_SEQ_EVENT_KEYSIGN: + SETDICT_CTRL1; + break; + + case SND_SEQ_EVENT_START: + case SND_SEQ_EVENT_CONTINUE: + case SND_SEQ_EVENT_STOP: + case SND_SEQ_EVENT_SETPOS_TICK: + case SND_SEQ_EVENT_TEMPO: + SETDICT_QUEU1; + break; + + case SND_SEQ_EVENT_CLOCK: + case SND_SEQ_EVENT_TICK: + break; + + case SND_SEQ_EVENT_QUEUE_SKEW: + SETDICT_QUEU1; + break; + + case SND_SEQ_EVENT_TUNE_REQUEST: + case SND_SEQ_EVENT_RESET: + case SND_SEQ_EVENT_SENSING: + break; + + case SND_SEQ_EVENT_CLIENT_START: + case SND_SEQ_EVENT_CLIENT_EXIT: + case SND_SEQ_EVENT_CLIENT_CHANGE: + SETDICT_ADDR1; + break; + + case SND_SEQ_EVENT_PORT_START: + case SND_SEQ_EVENT_PORT_EXIT: + case SND_SEQ_EVENT_PORT_CHANGE: + SETDICT_ADDR2; + break; + + case SND_SEQ_EVENT_PORT_SUBSCRIBED: + case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: + SETDICT_CONN4; + break; + + case SND_SEQ_EVENT_SYSEX: + SETDICT_EXT; + break; + } + + return dict; +} + +/** alsaseq.SeqEvent set_data() method: __doc__ */ +const char SeqEvent_set_data__doc__ [] = + "set_data(dict)" + "\n" + "Changes the data of this SeqEvent, updating internal data structure \n" + "from the given dictionary.\n" + "\n" + "The dictionary items should be the same as described in the get_data() \n" + "method and be the appropiate for this SeqEvent type.\n" + "\n" + "This method does not check if a given structure correspond to is valid \n" + "for this SeqEvent type; so setting the 'control.param' or the 'ext' \n" + "structures for a NOTE SeqEvent may change another data structure \n" + "or will simple have no effect once sent." + ; + +/** alsaseq.SeqEvent set_data() method */ +static PyObject * +SeqEvent_set_data(SeqEventObject *self, + PyObject *args) { + PyObject *dict = NULL; + snd_seq_event_t *event = self->event; + + if (!PyArg_ParseTuple(args, "O", + &dict)) { + return NULL; + } + + if (!PyDict_Check(dict)) { + PyErr_SetString(PyExc_TypeError, "must be a dictionary"); + return NULL; + } + + GETDICTINT("note.channel", event->data.note.channel); + GETDICTINT("note.note", event->data.note.note); + GETDICTINT("note.velocity", event->data.note.velocity); + GETDICTINT("note.off_velocity", event->data.note.off_velocity); + GETDICTINT("note.duration", event->data.note.duration); + + GETDICTINT("control.channel", event->data.control.channel); + GETDICTINT("control.param", event->data.control.param); + GETDICTINT("control.value", event->data.control.value); + + GETDICTEXT("ext"); + + GETDICTINT("queue.queue", event->data.queue.queue); + GETDICTINT("queue.param.value", event->data.queue.param.value); + + GETDICTINT("addr.client", event->data.addr.client); + GETDICTINT("addr.port", event->data.addr.port); + + GETDICTINT("connect.sender.client", event->data.connect.sender.client); + GETDICTINT("connect.sender.port", event->data.connect.sender.port); + GETDICTINT("connect.dest.client", event->data.connect.dest.client); + GETDICTINT("connect.dest.port", event->data.connect.dest.port); + + GETDICTINT("result.event", event->data.result.event); + GETDICTINT("result.result", event->data.result.result); + + Py_RETURN_NONE; +} + + +/** alsaseq.SeqEvent tp_methods */ +static PyMethodDef SeqEvent_methods[] = { + {"get_data", + (PyCFunction) SeqEvent_get_data, + METH_VARARGS, + SeqEvent_get_data__doc__}, + {"set_data", + (PyCFunction) SeqEvent_set_data, + METH_VARARGS, + SeqEvent_set_data__doc__}, + {NULL} +}; + +/** alsaseq.SeEvent type */ +static PyTypeObject SeqEventType = { + PyObject_HEAD_INIT(NULL) + tp_name: "alsaseq.SeqEvent", + tp_basicsize: sizeof(SeqEventObject), + tp_dealloc: (destructor) SeqEvent_dealloc, + tp_flags: Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + tp_doc: SeqEvent__doc__, + tp_init: (initproc)SeqEvent_init, + tp_new : SeqEvent_new, + tp_alloc: PyType_GenericAlloc, + tp_free: PyObject_Del, + tp_methods: SeqEvent_methods, + tp_getset: SeqEvent_getset, + tp_repr: (reprfunc)SeqEvent_repr, +}; + + + +////////////////////////////////////////////////////////////////////////////// +// alsaseq.Sequencer implementation +////////////////////////////////////////////////////////////////////////////// + +/** alsaseq.Sequencer __doc__ */ +const char Sequencer__doc__[] = + "Sequencer([name, clientname, streams, mode, maxreceiveevents]) " + "-> Sequencer object\n" + "\n" + "Creates and opens an ALSA sequencer. The features of the Sequencer are:\n" + "- send/receive events (as SeqEvent objects)\n" + "- create ports\n" + "- list all ALSA clients and their port connections\n" + "- connect/disconnect arbitrary ports\n" + "- create and control queues\n" + "- get info about a port or a client\n" + "\n" + "The name must correspond to the special ALSA name (for example: 'hw'); \n" + "if not specified, 'default' is used. The clientname is the name of this \n" + "client; if not specified, 'pyalsa-PID' is used.\n" + "\n" + "The streams specifies if you want to receive, send or both; use \n" + "SEQ_OPEN_INPUT, SEQ_OPEN_OUTPUT or SEQ_OPEN_DUPLEX. If not specified, \n" + "SEQ_OPEN_DUPLEX is used." + "\n" + "The mode specifies if this client should block or not, use SEQ_BLOCK or \n" + "SEQ_NONBLOCK. If not specified, SEQ_NONBLOCK is used." + "\n" + "The maxreceiveevents is a number that represents how many SeqEvent \n" + "objects are returned by the receive() method. If not specified, the \n" + "default is 4. \n" + "\n" + "There is no method for closing the sequencer, it will remain open as \n" + "long as the Sequencer object exists. For closing the sequencer you \n" + "must explicitly del() the returned object." + ; + +/** alsaseq.Sequencer object structure type */ +typedef struct { + PyObject_HEAD + + /* 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; + /* remaining bytes from input */ + int receive_bytes; +} 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 = NULL; + char tmpclientname[1024]; + + self->streams = SND_SEQ_OPEN_DUPLEX; + self->mode = SND_SEQ_NONBLOCK; + int maxreceiveevents = 4; + + char *kwlist[] = { "name", "clientname", "streams", "mode", + "maxreceiveevents", NULL }; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ssiii", kwlist, &name, + &clientname, &self->streams, + &self->mode, &maxreceiveevents)) { + return -1; + } + + if (clientname == NULL) { + tmpclientname[0] = 0; + sprintf(tmpclientname, "pyalsa-%d", getpid()); + clientname = tmpclientname; + } + + self->receive_fds = NULL; + self->receive_max = 0; + self->receive_bytes = 0; + self->receive_max_events = maxreceiveevents; + + + ret = snd_seq_open(&(self->handle), name, self->streams, + self->mode); + if (ret < 0) { + RAISESND(ret, "Failed to create sequencer"); + return -1; + } + ret = snd_seq_set_client_name(self->handle, clientname); + if (ret < 0) { + RAISESND(ret, "Failed to set client name"); + return -1; + } + + return 0; +} + +/** alsaseq.Sequencer: tp_dealloc */ +static void +Sequencer_dealloc(SequencerObject *self) { + 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__ */ +const char Sequencer_name__doc__ [] = + "name -> string\n" + "\n" + "The alsa device name of this alsaseq.Sequencer.\n" + "Note: read-only attribute." + ; + +/** alsaseq.Sequencer name attribute: tp_getset getter() */ +static PyObject * +Sequencer_get_name(SequencerObject *self) { + return PyString_FromString(snd_seq_name(self->handle)); +} + +/** alsaseq.Sequencer clientname attribute: __doc__ */ +const char Sequencer_clientname__doc__ [] = + "clientname -> string\n" + "\n" + "The client name of this alsaseq.Sequencer\n" + ; + +/** alsaseq.Sequencer clientname attribute: tp_getset getter() */ +static PyObject * +Sequencer_get_clientname(SequencerObject *self) { + snd_seq_client_info_t *cinfo; + + snd_seq_client_info_alloca(&cinfo); + snd_seq_get_client_info(self->handle, cinfo); + + return PyString_FromString(snd_seq_client_info_get_name(cinfo)); +} + +/** alsaseq.Sequencer clientname attribute: tp_getset setter() */ +static int +Sequencer_set_clientname(SequencerObject *self, + PyObject *val) { + char *buff; + + SETCHECKPYSTR("clientname", val); + + buff = PyString_AsString(val); + + snd_seq_set_client_name(self->handle, PyString_AsString(val)); + + return 0; +} + +/** alsaseq.Sequencer streams attribute: __doc__ */ +const char Sequencer_streams__doc__ [] = + "streams -> Constant object\n" + "\n" + "The streams of this alsaseq.Sequencer. Posible values:\n" + "alsaseq.SEQ_OPEN_OUTPUT, alsaseq.SEQ_OPEN_INPUT, \n" + "alsaseq.SEQ_OPEN_DUPLEX.\n" + "Note: read-only attribute." + ; + +/** alsaseq.Sequencer streams attribute: tp_getset getter() */ +static PyObject * +Sequencer_get_streams(SequencerObject *self) { + TCONSTRETURN(STREAMS, self->streams); +} + +/** alsaseq.Sequencer mode attribute: __doc__ */ +const char Sequencer_mode__doc__ [] = + "mode -> Constant object\n" + "\n" + "The blocking mode of this alsaseq.Sequencer. Use\n" + "alsaseq.SEQ_BLOCK, alsaseq.SEQ_NONBLOCK." + ; + +/** alsaseq.Sequencer mode attribute: tp_getset getter() */ +static PyObject *Sequencer_get_mode(SequencerObject *self) { + TCONSTRETURN(MODE, self->mode); +} + +/** alsaseq.Sequencer mode attribute: tp_getset setter() */ +static int +Sequencer_set_mode(SequencerObject *self, + PyObject *val) { + int ret, mode; + + SETCHECKPYINT("mode", val); + + 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 { + RAISESND(ret, "Failed to set mode"); + return -1; + } + return 0; +} + +/** alsaseq.Sequencer client_id attribute: __doc__ */ +const char Sequencer_client_id__doc__ [] = + "client_id -> int\n" + "\n" + "The client id of this alsaseq.Sequencer.\n" + "Note: read-only attribute." + ; + +/** alsaseq.Sequencer client_id attribute: tp_getset getter() */ +static PyObject * +Sequencer_get_client_id(SequencerObject *self) { + snd_seq_client_info_t *cinfo; + + snd_seq_client_info_alloca(&cinfo); + snd_seq_get_client_info(self->handle, cinfo); + return PyInt_FromLong(snd_seq_client_info_get_client(cinfo)); +} + +/** alsaseq.Sequencer: tp_getset list*/ +static PyGetSetDef Sequencer_getset[] = { + {"name", + (getter) Sequencer_get_name, + NULL, + (char *) Sequencer_name__doc__, + NULL}, + {"clientname", + (getter) Sequencer_get_clientname, + (setter) Sequencer_set_clientname, + (char *) Sequencer_clientname__doc__, + NULL}, + {"streams", + (getter) Sequencer_get_streams, + NULL, + (char *) Sequencer_streams__doc__, + NULL}, + {"mode", + (getter) Sequencer_get_mode, + (setter) Sequencer_set_mode, + (char *) Sequencer_mode__doc__, + NULL}, + {"client_id", + (getter) Sequencer_get_client_id, + NULL, + (char *) Sequencer_client_id__doc__, + NULL}, + {NULL} +}; + +/** alsaseq.Sequencer: tp_repr */ +static PyObject * +Sequencer_repr(SequencerObject *self) { + snd_seq_client_info_t *cinfo; + + snd_seq_client_info_alloca(&cinfo); + snd_seq_get_client_info(self->handle, cinfo); + + return PyString_FromFormat("<alsaseq.Sequencer name=%s client_id=%d " + "clientname=%s streams=%d mode=%d at 0x%p>", + snd_seq_name(self->handle), + snd_seq_client_info_get_client(cinfo), + snd_seq_client_info_get_name(cinfo), + self->streams, + self->mode, + self + ); +} + +/** alsaseq.Sequencer create_simple_port() method: __doc__ */ +const char Sequencer_create_simple_port__doc__ [] = + "create_simple_port(name, type, caps=0) -> int" + "\n" + "Creates a port for receiving or sending events.\n" + "\n" + "Parameters:\n" + " name -- name of the port\n" + " type -- type of port (use one of the alsaseq.SEQ_PORT_TYPE_*\n" + " constants)\n" + " caps -- capabilites of the port (use bitwise alsaseq.SEQ_PORT_CAP_*\n" + " constants). Default=0\n" + "Returns:\n" + " the port id.\n" + "Raises:\n" + " TypeError: if an invalid type was used in a parameter\n" + " ValueError: if an invalid value was used in a parameter\n" + " SequencerError: if ALSA can't create the port" + ; + + +/** alsaseq.Sequencer create_simple_port() method */ +static PyObject * +Sequencer_create_simple_port(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + char *name; + unsigned int type; + unsigned int caps=0; + char *kwlist[] = { "name", "type", "caps", NULL }; + int port; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "sI|I", kwlist, + &name, &type, &caps)) { + return NULL; + } + + port = snd_seq_create_simple_port(self->handle, name, caps, type); + if (port < 0) { + RAISESND(port, "Failed to create simple port"); + return NULL; + } + + return PyInt_FromLong(port); +} + +/** alsaseq.Sequencer connection_list() method: __doc__ */ +const char Sequencer_connection_list__doc__ [] = + "connection_list() -> list\n" + "\n" + "List all clients and their ports connections.\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 (client_id, port_id, info) tuples this\n" + " port is connected to (sends events);\n" + " info is the same of the get_connect_info() method.\n" + " write_conn -- a list of (client_id, port_id, info) tuples this\n" + " port is connected from (receives events);\n" + " info is the same of the get_connect_info() method.\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; + long tmplong; + 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); + + PyObject *dict = PyDict_New(); + + tmplong = snd_seq_query_subscribe_get_queue(query); + PyDict_SetItemString(dict, "queue", PyInt_FromLong(tmplong)); + + tmplong = snd_seq_query_subscribe_get_exclusive(query); + PyDict_SetItemString(dict, "exclusive", PyInt_FromLong(tmplong)); + + tmplong = snd_seq_query_subscribe_get_time_update(query); + PyDict_SetItemString(dict, "time_update", PyInt_FromLong(tmplong)); + + tmplong = snd_seq_query_subscribe_get_time_real(query); + PyDict_SetItemString(dict, "time_real", PyInt_FromLong(tmplong)); + + + PyObject *tuple = PyTuple_New(3); + PyTuple_SetItem(tuple, 0, PyInt_FromLong(addr->client)); + PyTuple_SetItem(tuple, 1, PyInt_FromLong(addr->port)); + PyTuple_SetItem(tuple, 2, dict); + + 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 connection_list() method */ +static PyObject * +Sequencer_connection_list(SequencerObject *self, + PyObject *args) { + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + PyObject *client, *port, *name; + 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); + + name = PyString_FromFormat("%s", snd_seq_client_info_get_name(cinfo)); + client = PyInt_FromLong(snd_seq_client_info_get_client(cinfo)); + PyTuple_SetItem(tuple, 0, name); + PyTuple_SetItem(tuple, 1, client); + + while (snd_seq_query_next_port(self->handle, pinfo) >= 0) { + /* create tuple for port info */ + PyObject *porttuple = PyTuple_New(3); + + name = PyString_FromFormat("%s", snd_seq_port_info_get_name(pinfo)); + port = PyInt_FromLong(snd_seq_port_info_get_port(pinfo)); + + PyTuple_SetItem(porttuple, 0, name); + PyTuple_SetItem(porttuple, 1, port); + + /* 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_client_info() method: __doc__ */ +const char Sequencer_get_client_info__doc__ [] = + "get_client_info(client_id = self.client_id) -> dictionary\n" + "\n" + "Retrieve info about an existing client.\n" + "\n" + "Parameters:\n" + " client_id -- the client id (defaults to: self.client_id)\n" + "Returns:\n" + " (dict) a dictionary with the following values:\n" + " id -- id of client.\n" + " type -- type of client (SEQ_USER_CLIENT or SEQ_KERNEL_CLIENT).\n" + " name -- name of client.\n" + " broadcast_filter -- broadcast filter flag of client as int.\n" + " error_bounce -- error bounce of client as int.\n" + " event_filter -- event filter of client as string.\n" + " num_ports -- number of opened ports of client.\n" + " event_lost -- number of lost events of client.\n" + "Raises:\n" + " SequencerError: ALSA error occurred." + ; + +/** alsaseq.Sequencer get_client_info() method */ +static PyObject * +Sequencer_get_client_info(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + snd_seq_client_info_t *cinfo; + int client_id = -1; + int ret; + PyObject *tmpobj; + long tmplong; + const char * tmpchar; + char *kwlist[] = { "client_id", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, + &client_id)) { + return NULL; + } + + snd_seq_client_info_alloca(&cinfo); + if (client_id == -1) { + ret = snd_seq_get_client_info(self->handle, cinfo); + if (ret < 0) { + RAISESND(ret, "Failed to retrieve client info for self.client_id"); + return NULL; + } + client_id = snd_seq_client_info_get_client(cinfo); + } else { + ret = snd_seq_get_any_client_info(self->handle, client_id, cinfo); + if (ret < 0) { + RAISESND(ret, "Failed to retrieve client info for '%d'", client_id); + return NULL; + } + } + + PyObject *dict = PyDict_New(); + if (dict == NULL) { + return NULL; + } + + TCONSTASSIGN(ADDR_CLIENT, client_id, tmpobj); + PyDict_SetItemString(dict, "id", tmpobj); + + tmplong = snd_seq_client_info_get_type(cinfo); + TCONSTASSIGN(CLIENT_TYPE, tmplong, tmpobj); + PyDict_SetItemString(dict, "type", tmpobj); + + tmpchar = snd_seq_client_info_get_name(cinfo); + tmpchar = (tmpchar == NULL ? "" : tmpchar); + PyDict_SetItemString(dict, "name", PyString_FromString(tmpchar)); + + tmplong = snd_seq_client_info_get_broadcast_filter(cinfo); + PyDict_SetItemString(dict, "broadcast_filter", PyInt_FromLong(tmplong)); + + tmplong = snd_seq_client_info_get_error_bounce(cinfo); + PyDict_SetItemString(dict, "error_bounce", PyInt_FromLong(tmplong)); + + tmpchar = (const char *)snd_seq_client_info_get_event_filter(cinfo); + tmpchar = (tmpchar == NULL ? "" : tmpchar); + PyDict_SetItemString(dict, "event_filter", PyString_FromString(tmpchar)); + + tmplong = snd_seq_client_info_get_num_ports(cinfo); + PyDict_SetItemString(dict, "num_ports", PyInt_FromLong(tmplong)); + + tmplong = snd_seq_client_info_get_event_lost(cinfo); + PyDict_SetItemString(dict, "event_lost", PyInt_FromLong(tmplong)); + + return dict; +} + +/** alsaseq.Sequencer get_port_info() method: __doc__ */ +const char Sequencer_get_port_info__doc__ [] = + "get_port_info(port_id, client_id = self.client_id) -> dictionary\n" + "\n" + "Retrieve info about an existing client's port.\n" + "\n" + "Parameters:\n" + " port_id -- the port id\n" + " client_id -- the client id (defaults to: self.client_id)\n" + "Returns:\n" + " (dict) a dictionary with the following values:\n" + " name -- the port name\n" + " type -- the port type bit flags\n" + " capability -- the port capability bit flags as integer\n" + "Raises:\n" + " SequencerError: ALSA error occurred." + ; + +/** alsaseq.Sequencer get_port_info() method */ +static PyObject * +Sequencer_get_port_info(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + snd_seq_port_info_t *pinfo; + snd_seq_client_info_t *cinfo; + int port_id; + int client_id; + const char *tmpchar; + long tmplong; + int ret; + char *kwlist[] = { "port_id", "client_id", NULL }; + + snd_seq_client_info_alloca(&cinfo); + ret = snd_seq_get_client_info(self->handle, cinfo); + if (ret < 0) { + RAISESND(ret, "Failed to determine self.client_id"); + return NULL; + } + client_id = snd_seq_client_info_get_client(cinfo); + + 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); + ret = snd_seq_get_any_port_info(self->handle, client_id, + port_id, pinfo); + if (ret < 0) { + RAISESND(ret, "Failed to get port info for %d:%d", client_id, port_id); + return NULL; + } + + tmpchar = snd_seq_port_info_get_name(pinfo); + tmpchar = (tmpchar == NULL ? "" : tmpchar); + PyDict_SetItemString(dict, "name", PyString_FromString(tmpchar)); + + tmplong = snd_seq_port_info_get_capability(pinfo); + PyDict_SetItemString(dict, "capability", PyInt_FromLong(tmplong)); + + tmplong = snd_seq_port_info_get_type(pinfo); + PyDict_SetItemString(dict, "type", PyInt_FromLong(tmplong)); + + return dict; +} + +/** alsaseq.Sequencer connect_ports() method: __doc__ */ +const char Sequencer_connect_ports__doc__ [] = + "connect_ports(srcaddr,dstaddr,queue,exclusive,time_update,time_real)\n" + "\n" + "Connect the ports specified by srcaddr and dstaddr.\n" + "\n" + "Parameters:\n" + " srcaddr -- (tuple) the (client_id, port_id) tuple for the source\n" + " port (the port sending events)\n" + " dstaddr -- (tuple) the (client_id, port_id) tuple for the destination\n" + " port (the port receiveng events)\n" + " queue -- the queue of the connection.\n" + " If not specified, defaults to 0.\n" + " exclusive -- 1 if the connection is exclusive, 0 otherwise.\n" + " If not specified, defaults to 0.\n" + " time_update -- 1 if the connection should update time, 0 otherwise.\n" + " If not specified, defaults to 0.\n" + " time_real -- 1 if the update time is in real, 0 if is in tick.\n" + " If not specified, defaults to 0.\n" + "Raises:\n" + " SequenceError: if ALSA can't connect the ports" + ; + +/** alsaseq.Sequencer connect_ports() method */ +static PyObject * +Sequencer_connect_ports(SequencerObject *self, + PyObject *args) { + snd_seq_addr_t sender, dest; + snd_seq_port_subscribe_t *sinfo; + int ret; + int queue = 0; + int exclusive = 0; + int time_update = 0; + int time_real = 0; + + if (!PyArg_ParseTuple(args, "(BB)(BB)|iiii", &(sender.client), + &(sender.port), &(dest.client), &(dest.port), + &queue, &exclusive, &time_update, &time_real)) { + return NULL; + } + + snd_seq_port_subscribe_alloca(&sinfo); + snd_seq_port_subscribe_set_sender(sinfo, &sender); + snd_seq_port_subscribe_set_dest(sinfo, &dest); + snd_seq_port_subscribe_set_queue(sinfo, queue); + snd_seq_port_subscribe_set_exclusive(sinfo, exclusive); + snd_seq_port_subscribe_set_time_update(sinfo, time_update); + snd_seq_port_subscribe_set_time_real(sinfo, time_real); + + ret = snd_seq_subscribe_port(self->handle, sinfo); + if (ret < 0) { + RAISESND(ret, "Failed to connect ports %d:%d -> %d:%d", + sender.client, sender.port, dest.client, dest.port); + return NULL; + } + + Py_RETURN_NONE; +} + +/** alsaseq.Sequencer disconnect_ports() method: __doc__ */ +const char Sequencer_disconnect_ports__doc__ [] = + "disconnect_ports(srcaddr, destaddr)\n" + "\n" + "Disconnect the ports specified by srcaddr and dstaddr.\n" + "\n" + "Parameters:\n" + " srcaddr -- (tuple) the client_id, port_id tuple for the source\n" + " port (the port sending events)\n" + " dstaddr -- (tuple) the client_id, port_id tuple for the destination\n" + " port (the port receiveng events)\n" + "Raises:\n" + " SequenceError: if ALSA can't disconnect the port" + ; + +/** alsaseq.Sequencer disconnect_ports() method */ +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) { + RAISESND(ret, "Failed to disconnect ports: %d:%d --> %d:%d", + sender.client, sender.port, dest.client, dest.port); + return NULL; + } + + Py_RETURN_NONE; +} + +/** alsaseq.Sequencer get_connect_info() method: __doc__ */ +const char Sequencer_get_connect_info__doc__ [] = + "get_connect_info(srcaddr, dstaddr) -> dictionary\n" + "\n" + "Retrieve the subscribe info of the specified connection.\n" + "\n" + "Parameters:\n" + " srcaddr -- (tuple) the client_id, port_id tuple for the source\n" + " port (the port sending events)\n" + " dstaddr -- (tuple) the client_id, port_id tuple for the destination\n" + " port (the port receiveng events)\n" + "Returns:\n" + " (dict) a dictionary with the following values:\n" + " queue -- \n" + " exclusive -- \n" + " time_update -- \n" + " time_real --- \n" + "Raises:\n" + " SequenceError: if ALSA can't retrieve the connection or the\n" + " connection doesn't exists." + ; + + +static PyObject* +Sequencer_get_connect_info(SequencerObject *self, + PyObject *args) { + snd_seq_addr_t sender, dest; + snd_seq_port_subscribe_t *sinfo; + int ret; + long tmplong; + + 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_get_port_subscription(self->handle, sinfo); + if (ret < 0) { + RAISESND(ret, "Failed to get port subscript: %d:%d --> %d:%d", + sender.client, sender.port, dest.client, dest.port); + return NULL; + } + + PyObject *dict = PyDict_New(); + + tmplong = snd_seq_port_subscribe_get_queue(sinfo); + PyDict_SetItemString(dict, "queue", PyInt_FromLong(tmplong)); + + tmplong = snd_seq_port_subscribe_get_exclusive(sinfo); + PyDict_SetItemString(dict, "exclusive", PyInt_FromLong(tmplong)); + + tmplong = snd_seq_port_subscribe_get_time_update(sinfo); + PyDict_SetItemString(dict, "time_update", PyInt_FromLong(tmplong)); + + tmplong = snd_seq_port_subscribe_get_time_real(sinfo); + PyDict_SetItemString(dict, "time_real", PyInt_FromLong(tmplong)); + + return dict; +} + +/** alsaseq.Sequencer receive_events() method: __doc__ */ +const char Sequencer_receive_events__doc__ [] = + "receive_events(timeout = 0, maxevents = self.receive_maxevents) -> list\n" + "\n" + "Receive events.\n" + "\n" + "Parameters:\n" + " timeout -- (int) time for wating for events in miliseconds\n" + " maxevents -- (int) max events to be returned\n" + "Returns:\n" + " (list) a list of alsaseq.SeqEvent objects\n" + "Raises:\n" + " TypeError: if an invalid type was used in a parameter\n" + " SequencerError: if ALSA error occurs." + ; + +/** alsaseq.Sequencer receive_events() method */ +static PyObject * +Sequencer_receive_events(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + int maxevents = self->receive_max_events; + snd_seq_event_t *event = NULL; + int timeout = 0; + int ret; + + char *kwlist[] = {"timeout", "maxevents", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|ii", kwlist, &timeout, + &maxevents)) { + return NULL; + } + + if (self->receive_fds == NULL) { + self->receive_max = snd_seq_poll_descriptors_count(self->handle, POLLOUT); + self->receive_fds = malloc(sizeof(struct pollfd) * self->receive_max); + } + + PyObject *list = PyList_New(0); + if (list == NULL) { + return NULL; + } + + + if (self->receive_bytes <= 0 && timeout != 0) { + snd_seq_poll_descriptors(self->handle, self->receive_fds, + self->receive_max, POLLIN); + ret = poll(self->receive_fds, self->receive_max, timeout); + if (ret == 0) { + return list; + } else if (ret < 0) { + RAISESTR("Failed to poll from receive: %s", strerror(-ret)); + return NULL; + } + } + + do { + ret = snd_seq_event_input(self->handle, &event); + self->receive_bytes = ret; + if (ret < 0) { + if (ret != -EAGAIN) { + } + break; + } + + + PyObject *SeqEventObject = SeqEvent_create(event); + if (SeqEventObject == NULL) { + RAISESTR("Error creating event!"); + return NULL; + } + + PyList_Append(list, SeqEventObject); + + maxevents --; + + } while (maxevents > 0 && ret > 0); + + return list; +} + +/** alsaseq.Sequencer output_event() method: __doc__ */ +const char Sequencer_output_event__doc__ [] = + "output_event(event)\n" + "\n" + "Put the the given event in the outputbuffer. This actually will enqueue\n" + "the event, to make sure it's sent, use the drain_output() method.\n" + "\n" + "Parameters:\n" + " event -- a SeqEvent object\n" + "Raises:\n" + " SequencerError: if ALSA can't send the event" + ; + +/** alsaseq.Sequencer output_event() method */ +static PyObject * +Sequencer_output_event(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + SeqEventObject *seqevent; + char *kwlist[] = {"event", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O", kwlist, + &seqevent)) { + return NULL; + } + + if (!PyObject_TypeCheck(seqevent, &SeqEventType)) { + PyErr_SetString(PyExc_TypeError, "alsaseq.SeqEvent expected"); + return NULL; + } + + snd_seq_event_output(self->handle, seqevent->event); + + Py_RETURN_NONE; +} + +/** alsaseq.Sequencer drain_output() method: __doc__ */ +const char Sequencer_drain_output__doc__ [] = + "drain_output()\n" + "\n" + "Drain the output, making sure all events int the buffer are sent.\n" + "The events sent may remain unprocessed, to make sure they are\n" + "processed, call the sync_output_queue() method.\n" + "\n" + "Raises:\n" + " SequencerError: if ALSA can't drain the output" + ; + +/** alsaseq.Sequencer drain_output() method */ +static PyObject *Sequencer_drain_output(SequencerObject *self, + PyObject *args) { + int ret; + + ret = snd_seq_drain_output(self->handle); + if (ret < 0) { + RAISESND(ret, "Failed to drain output"); + return NULL; + } + + Py_RETURN_NONE; +} + +/** alsaseq.Sequencer sync_output_queue() method: __doc__ */ +const char Sequencer_sync_output_queue__doc__ [] = + "sync_output_queue()\n" + "\n" + "Waits until all events of this clients are processed.\n" + "\n" + "Raises:\n" + " SequencerError: if an ALSA error occurs." + ; + +/** alsaseq.Sequencer sync_output_queue() method */ +static PyObject * +Sequencer_sync_output_queue(SequencerObject *self, + PyObject *args) { + int ret; + + ret = snd_seq_sync_output_queue(self->handle); + if (ret < 0) { + RAISESND(ret, "Failed to sync output queue"); + return NULL; + } + + Py_RETURN_NONE; +} + + +/** alsaseq.Sequencer parse_address() method: __doc__ */ +const char Sequencer_parse_address__doc__ [] = + "parse_address(string) -> tuple\n" + "\n" + "Parses the given string as an ALSA client:port. You can use client,\n" + "port id's or names.\n" + "\n" + "Parameters:\n" + " string -- the address in the format client:port.\n" + "Returns:\n" + " the tuple (client_id, port_id).\n" + "Raises:\n" + " SequencerError: if ALSA can't parse the given address" + ; + +/** alsaseq.Sequencer parse_address() method */ +static PyObject * +Sequencer_parse_address(SequencerObject *self, + PyObject *args) { + snd_seq_addr_t addr; + char *str = NULL; + int ret; + + if (!PyArg_ParseTuple(args, "s", &(str))) { + return NULL; + } + + ret = snd_seq_parse_address(self->handle, &addr, str); + if (ret < 0) { + RAISESND(ret, "Invalid client:port specification '%s'", str); + return NULL; + } + + PyObject *tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, PyInt_FromLong(addr.client)); + PyTuple_SetItem(tuple, 1, PyInt_FromLong(addr.port)); + + return tuple; +} + +/** alsaseq.Sequencer create_queue() method: __doc__ */ +const char Sequencer_create_queue__doc__ [] = + "create_queue(name=None)-> int\n" + "\n" + "Creates a queue with the optional given name.\n" + "\n" + "Parameters:\n" + " name -- the name of the queue.\n" + "Returns:\n" + " the queue id.\n" + "Raises:\n" + " SequencerError: if ALSA can't create the queue" + ; + +/** alsaseq.Sequencer create_queue() method */ +static PyObject * +Sequencer_create_queue(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + char *kwlist[] = {"name", NULL}; + char *queue_name = NULL; + int ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s", kwlist, + &queue_name)) { + return NULL; + } + + if (queue_name != NULL) { + ret = snd_seq_alloc_named_queue(self->handle, queue_name); + } else { + ret = snd_seq_alloc_queue(self->handle); + } + + if (ret < 0) { + RAISESND(ret, "Failed to create queue"); + return NULL; + } + + return PyInt_FromLong(ret); +} + +/** alsaseq.Sequencer delete_queue() method: __doc__ */ +const char Sequencer_delete_queue__doc__ [] = + "delete_queue(queue)\n" + "\n" + "Deletes (frees) a queue.\n" + "\n" + "Parameters:\n" + " queue -- the queue id.\n" + "Raises:\n" + " SequencerError: if ALSA can't delete the queue." + ; + +/** alsaseq.Sequencer delete_queue() method */ +static PyObject * +Sequencer_delete_queue(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + char *kwlist[] = {"queue", NULL}; + int queue_id; + int ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &queue_id)) { + return NULL; + } + + ret = snd_seq_free_queue(self->handle, queue_id); + if (ret < 0) { + RAISESND(ret, "Failed to create queue"); + return NULL; + } + + Py_RETURN_NONE; +} + + +/** alsaseq.Sequencer queue_tempo() method: __doc__ */ +const char Sequencer_queue_tempo__doc__ [] = + "queue_tempo(queue, tempo=None, ppq=None) -> tuple" + "\n" + "Query and changes the queue tempo. For querying (not changing) the queue\n" + "tempo, pass only the queue id.\n" + "\n" + "Parameters:\n" + " queue -- the queue id.\n" + " tempo -- the new queue tempo or None for keeping the current tempo.\n" + " ppq -- the new queue ppq or None for keeping the current tempo.\n" + "Returns:\n" + " a tuple (tempo, ppq) with the current or changed tempo.\n" + "Raises:\n" + " SequencerError: if ALSA can't change the queue tempo or an invalid\n" + " queue was specified." + ; + +/** alsaseq.Sequencer queue_tempo() method */ +static PyObject * +Sequencer_queue_tempo(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + char *kwlist[] = {"queue", "tempo", "ppq", NULL}; + int queueid, tempo=-1, ppq=-1; + int ret; + snd_seq_queue_tempo_t *queue_tempo; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|ii", kwlist, + &queueid, &tempo, &ppq)) { + return NULL; + } + + snd_seq_queue_tempo_alloca(&queue_tempo); + ret = snd_seq_get_queue_tempo(self->handle, queueid, queue_tempo); + if (ret < 0) { + RAISESND(ret, "Failed to retrieve current queue tempo"); + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i|ii", kwlist, + &queueid, &tempo, &ppq)) { + return NULL; + } + + if (tempo != -1) { + snd_seq_queue_tempo_set_tempo(queue_tempo, tempo); + } + if (ppq != -1) { + snd_seq_queue_tempo_set_ppq(queue_tempo, ppq); + } + + if (tempo != -1 && ppq != -1) { + ret = snd_seq_set_queue_tempo(self->handle, queueid, queue_tempo); + if (ret < 0) { + RAISESND(ret, "Failed to set queue tempo"); + return NULL; + } + } + + tempo = snd_seq_queue_tempo_get_tempo(queue_tempo); + ppq = snd_seq_queue_tempo_get_ppq(queue_tempo); + + PyObject *tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, PyInt_FromLong(tempo)); + PyTuple_SetItem(tuple, 1, PyInt_FromLong(ppq)); + + return tuple; +} + +/** alsaseq.Sequencer start_queue() method: __doc__ */ +const char Sequencer_start_queue__doc__ [] = + "start_queue(queue)\n" + "\n" + "Starts the specified queue.\n" + "\n" + "Parameters:\n" + " queue -- the queue id.\n" + "Raises:\n" + " SequencerError: if ALSA can't start the queue." + ; + +/** alsaseq.Sequencer start_queue() method */ +static PyObject * +Sequencer_start_queue(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + char *kwlist[] = {"queue", NULL}; + int queueid; + int ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &queueid)) { + return NULL; + } + + ret = snd_seq_start_queue(self->handle, queueid, NULL); + if (ret < 0) { + RAISESND(ret, "Failed to start queue"); + return NULL; + } + + Py_RETURN_NONE; +} + +/** alsaseq.Sequencer stop_queue() method: __doc__ */ +const char Sequencer_stop_queue__doc__ [] = + "stop_queue(queue)\n" + "\n" + "Stops the specified queue.\n" + "\n" + "Parameters:\n" + " queue -- the queue id.\n" + "Raises:\n" + " SequencerError: if ALSA can't stop the queue." + ; + +/** alsaseq.Sequencer stop_queue() method */ +static PyObject * +Sequencer_stop_queue(SequencerObject *self, + PyObject *args, + PyObject *kwds) { + char *kwlist[] = {"queue", NULL}; + int queueid; + int ret; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "i", kwlist, + &queueid)) { + return NULL; + } + + ret = snd_seq_stop_queue(self->handle, queueid, NULL); + if (ret < 0) { + RAISESND(ret, "Failed to stop queue"); + return NULL; + } + + Py_RETURN_NONE; +} + + + + +/** alsaseq.Sequencer tp_methods */ +static PyMethodDef Sequencer_methods[] = { + {"create_simple_port", + (PyCFunction) Sequencer_create_simple_port, + METH_VARARGS | METH_KEYWORDS, + Sequencer_create_simple_port__doc__ }, + {"connection_list", + (PyCFunction) Sequencer_connection_list, + METH_VARARGS, + Sequencer_connection_list__doc__ }, + {"get_client_info", + (PyCFunction) Sequencer_get_client_info, + METH_VARARGS | METH_KEYWORDS, + Sequencer_get_client_info__doc__ }, + {"get_port_info", + (PyCFunction) Sequencer_get_port_info, + METH_VARARGS | METH_KEYWORDS, + 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__ }, + {"get_connect_info", + (PyCFunction) Sequencer_get_connect_info, + METH_VARARGS, + Sequencer_get_connect_info__doc__}, + {"receive_events", + (PyCFunction) Sequencer_receive_events, + METH_VARARGS | METH_KEYWORDS, + Sequencer_receive_events__doc__}, + {"output_event", + (PyCFunction) Sequencer_output_event, + METH_VARARGS | METH_KEYWORDS, + Sequencer_output_event__doc__}, + {"drain_output", + (PyCFunction) Sequencer_drain_output, + METH_VARARGS, + Sequencer_drain_output__doc__}, + {"sync_output_queue", + (PyCFunction) Sequencer_sync_output_queue, + METH_VARARGS, + Sequencer_sync_output_queue__doc__}, + {"parse_address", + (PyCFunction) Sequencer_parse_address, + METH_VARARGS, + Sequencer_parse_address__doc__}, + {"create_queue", + (PyCFunction) Sequencer_create_queue, + METH_VARARGS | METH_KEYWORDS, + Sequencer_create_queue__doc__}, + {"delete_queue", + (PyCFunction) Sequencer_delete_queue, + METH_VARARGS | METH_KEYWORDS, + Sequencer_delete_queue__doc__}, + {"queue_tempo", + (PyCFunction) Sequencer_queue_tempo, + METH_VARARGS | METH_KEYWORDS, + Sequencer_queue_tempo__doc__}, + {"start_queue", + (PyCFunction) Sequencer_start_queue, + METH_VARARGS | METH_KEYWORDS, + Sequencer_start_queue__doc__}, + {"stop_queue", + (PyCFunction) Sequencer_stop_queue, + METH_VARARGS | METH_KEYWORDS, + Sequencer_stop_queue__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__ */ +const char alsaseq__doc__ [] = + "libasound alsaseq wrapper" + ; + +/** alsaseq module methods */ +static PyMethodDef alsaseq_methods[] = { + { NULL }, +}; + +PyMODINIT_FUNC +initalsaseq(void) { + PyObject *module; + + if (PyType_Ready(&ConstantType) < 0) { + return; + } + + if (PyType_Ready(&SeqEventType) < 0) { + return; + } + + if (PyType_Ready(&SequencerType) < 0) { + return; + } + + module = Py_InitModule3("alsaseq", alsaseq_methods, alsaseq__doc__); + + if (module == NULL) { + return; + } + + SequencerError = PyErr_NewException("alsaseq.SequencerError", NULL, NULL); + if (SequencerError == NULL) { + return; + } + + Py_INCREF(SequencerError); + PyModule_AddObject(module, "SequencerError", SequencerError); + + Py_INCREF(&SeqEventType); + PyModule_AddObject(module, "SeqEvent", (PyObject *) &SeqEventType); + + Py_INCREF(&SequencerType); + PyModule_AddObject(module, "Sequencer", (PyObject *) &SequencerType); + + Py_INCREF(&ConstantType); + PyModule_AddObject(module, "Constant", (PyObject *) &ConstantType); + + /* misc constants */ + PyModule_AddStringConstant(module, + "SEQ_LIB_VERSION_STR", + SND_LIB_VERSION_STR); + + /* add Constant dictionaries to module */ + TCONSTDICTADD(module, STREAMS, + "_dstreams"); + TCONSTDICTADD(module, MODE, + "_dmode"); + TCONSTDICTADD(module, QUEUE, + "_dqueue"); + TCONSTDICTADD(module, CLIENT_TYPE, + "_dclienttype"); + TCONSTDICTADD(module, PORT_CAP, + "_dportcap"); + TCONSTDICTADD(module, PORT_TYPE, + "_dporttype"); + TCONSTDICTADD(module, EVENT_TYPE, + "_deventtype"); + TCONSTDICTADD(module, EVENT_TIMESTAMP, + "_deventtimestamp"); + TCONSTDICTADD(module, EVENT_TIMEMODE, + "_deventtimemode"); + TCONSTDICTADD(module, ADDR_CLIENT, + "_dclient"); + TCONSTDICTADD(module, ADDR_PORT, + "_dport"); + + /* Sequencer streams */ + TCONSTADD(module, STREAMS, + "SEQ_OPEN_OUTPUT", + SND_SEQ_OPEN_OUTPUT); + TCONSTADD(module, STREAMS, + "SEQ_OPEN_INPUT", + SND_SEQ_OPEN_INPUT); + TCONSTADD(module, STREAMS, + "SEQ_OPEN_DUPLEX", + SND_SEQ_OPEN_DUPLEX); + + /* Sequencer blocking mode */ + TCONSTADD(module, MODE, + "SEQ_BLOCK", + 0); + TCONSTADD(module, MODE, + "SEQ_NONBLOCK", + SND_SEQ_NONBLOCK); + + /* Known queue id */ + TCONSTADD(module, QUEUE, + "SEQ_QUEUE_DIRECT", + SND_SEQ_QUEUE_DIRECT); + + /* client types */ + TCONSTADD(module, CLIENT_TYPE, + "SEQ_USER_CLIENT", + SND_SEQ_USER_CLIENT); + TCONSTADD(module, CLIENT_TYPE, + "SEQ_KERNEL_CLIENT", + SND_SEQ_KERNEL_CLIENT); + + /* Sequencer port cap */ + TCONSTADD(module, PORT_CAP, + "SEQ_PORT_CAP_NONE", + 0); + TCONSTADD(module, PORT_CAP, + "SEQ_PORT_CAP_WRITE", + SND_SEQ_PORT_CAP_WRITE); + TCONSTADD(module, PORT_CAP, + "SEQ_PORT_CAP_SYNC_WRITE", + SND_SEQ_PORT_CAP_SYNC_WRITE); + TCONSTADD(module, PORT_CAP, + "SEQ_PORT_CAP_SYNC_READ", + SND_SEQ_PORT_CAP_SYNC_READ); + TCONSTADD(module, PORT_CAP, + "SEQ_PORT_CAP_SUBS_WRITE", + SND_SEQ_PORT_CAP_SUBS_WRITE); + TCONSTADD(module, PORT_CAP, + "SEQ_PORT_CAP_SUBS_READ", + SND_SEQ_PORT_CAP_SUBS_READ); + TCONSTADD(module, PORT_CAP, + "SEQ_PORT_CAP_READ", + SND_SEQ_PORT_CAP_READ); + TCONSTADD(module, PORT_CAP, + "SEQ_PORT_CAP_NO_EXPORT", + SND_SEQ_PORT_CAP_NO_EXPORT); + TCONSTADD(module, PORT_CAP, + "SEQ_PORT_CAP_DUPLEX", + SND_SEQ_PORT_CAP_DUPLEX); + + /* Sequencer port type */ + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_SYNTHESIZER", + SND_SEQ_PORT_TYPE_SYNTHESIZER); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_SYNTH", + SND_SEQ_PORT_TYPE_SYNTH); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_SPECIFIC", + SND_SEQ_PORT_TYPE_SPECIFIC); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_SOFTWARE", + SND_SEQ_PORT_TYPE_SOFTWARE); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_SAMPLE", + SND_SEQ_PORT_TYPE_SAMPLE); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_PORT", + SND_SEQ_PORT_TYPE_PORT); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_MIDI_XG", + SND_SEQ_PORT_TYPE_MIDI_XG); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_MIDI_MT32", + SND_SEQ_PORT_TYPE_MIDI_MT32); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_MIDI_GS", + SND_SEQ_PORT_TYPE_MIDI_GS); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_MIDI_GM2", + SND_SEQ_PORT_TYPE_MIDI_GM2); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_MIDI_GM", + SND_SEQ_PORT_TYPE_MIDI_GM); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_MIDI_GENERIC", + SND_SEQ_PORT_TYPE_MIDI_GENERIC); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_HARDWARE", + SND_SEQ_PORT_TYPE_HARDWARE); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_DIRECT_SAMPLE", + SND_SEQ_PORT_TYPE_DIRECT_SAMPLE); + TCONSTADD(module, PORT_TYPE, + "SEQ_PORT_TYPE_APPLICATION", + SND_SEQ_PORT_TYPE_APPLICATION); + + /* SeqEvent event type */ + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SYSTEM", + SND_SEQ_EVENT_SYSTEM); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_RESULT", + SND_SEQ_EVENT_RESULT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_NOTE", + SND_SEQ_EVENT_NOTE); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_NOTEON", + SND_SEQ_EVENT_NOTEON); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_NOTEOFF", + SND_SEQ_EVENT_NOTEOFF); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_KEYPRESS", + SND_SEQ_EVENT_KEYPRESS); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_CONTROLLER", + SND_SEQ_EVENT_CONTROLLER); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_PGMCHANGE", + SND_SEQ_EVENT_PGMCHANGE); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_CHANPRESS", + SND_SEQ_EVENT_CHANPRESS); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_PITCHBEND", + SND_SEQ_EVENT_PITCHBEND); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_CONTROL14", + SND_SEQ_EVENT_CONTROL14); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_NONREGPARAM", + SND_SEQ_EVENT_NONREGPARAM); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_REGPARAM", + SND_SEQ_EVENT_REGPARAM); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SONGPOS", + SND_SEQ_EVENT_SONGPOS); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SONGSEL", + SND_SEQ_EVENT_SONGSEL); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_QFRAME", + SND_SEQ_EVENT_QFRAME); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_TIMESIGN", + SND_SEQ_EVENT_TIMESIGN); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_KEYSIGN", + SND_SEQ_EVENT_KEYSIGN); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_START", + SND_SEQ_EVENT_START); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_CONTINUE", + SND_SEQ_EVENT_CONTINUE); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_STOP", + SND_SEQ_EVENT_STOP); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SETPOS_TICK", + SND_SEQ_EVENT_SETPOS_TICK); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SETPOS_TIME", + SND_SEQ_EVENT_SETPOS_TIME); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_TEMPO", + SND_SEQ_EVENT_TEMPO); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_CLOCK", + SND_SEQ_EVENT_CLOCK); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_TICK", + SND_SEQ_EVENT_TICK); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_QUEUE_SKEW", + SND_SEQ_EVENT_QUEUE_SKEW); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SYNC_POS", + SND_SEQ_EVENT_SYNC_POS); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_TUNE_REQUEST", + SND_SEQ_EVENT_TUNE_REQUEST); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_RESET", + SND_SEQ_EVENT_RESET); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SENSING", + SND_SEQ_EVENT_SENSING); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_ECHO", + SND_SEQ_EVENT_ECHO); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_OSS", + SND_SEQ_EVENT_OSS); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_CLIENT_START", + SND_SEQ_EVENT_CLIENT_START); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_CLIENT_EXIT", + SND_SEQ_EVENT_CLIENT_EXIT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_CLIENT_CHANGE", + SND_SEQ_EVENT_CLIENT_CHANGE); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_PORT_START", + SND_SEQ_EVENT_PORT_START); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_PORT_EXIT", + SND_SEQ_EVENT_PORT_EXIT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_PORT_CHANGE", + SND_SEQ_EVENT_PORT_CHANGE); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_PORT_SUBSCRIBED", + SND_SEQ_EVENT_PORT_SUBSCRIBED); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_PORT_UNSUBSCRIBED", + SND_SEQ_EVENT_PORT_UNSUBSCRIBED); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SAMPLE", + SND_SEQ_EVENT_SAMPLE); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SAMPLE_CLUSTER", + SND_SEQ_EVENT_SAMPLE_CLUSTER); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SAMPLE_START", + SND_SEQ_EVENT_SAMPLE_START); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SAMPLE_STOP", + SND_SEQ_EVENT_SAMPLE_STOP); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SAMPLE_FREQ", + SND_SEQ_EVENT_SAMPLE_FREQ); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SAMPLE_VOLUME", + SND_SEQ_EVENT_SAMPLE_VOLUME); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SAMPLE_LOOP", + SND_SEQ_EVENT_SAMPLE_LOOP); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SAMPLE_POSITION", + SND_SEQ_EVENT_SAMPLE_POSITION); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SAMPLE_PRIVATE1", + SND_SEQ_EVENT_SAMPLE_PRIVATE1); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR0", + SND_SEQ_EVENT_USR0); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR1", + SND_SEQ_EVENT_USR1); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR2", + SND_SEQ_EVENT_USR2); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR3", + SND_SEQ_EVENT_USR3); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR4", + SND_SEQ_EVENT_USR4); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR5", + SND_SEQ_EVENT_USR5); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR6", + SND_SEQ_EVENT_USR6); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR7", + SND_SEQ_EVENT_USR7); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR8", + SND_SEQ_EVENT_USR8); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR9", + SND_SEQ_EVENT_USR9); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_BEGIN", + SND_SEQ_EVENT_INSTR_BEGIN); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_END", + SND_SEQ_EVENT_INSTR_END); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_INFO", + SND_SEQ_EVENT_INSTR_INFO); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_INFO_RESULT", + SND_SEQ_EVENT_INSTR_INFO_RESULT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_FINFO", + SND_SEQ_EVENT_INSTR_FINFO); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_FINFO_RESULT", + SND_SEQ_EVENT_INSTR_FINFO_RESULT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_RESET", + SND_SEQ_EVENT_INSTR_RESET); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_STATUS", + SND_SEQ_EVENT_INSTR_STATUS); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_STATUS_RESULT", + SND_SEQ_EVENT_INSTR_STATUS_RESULT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_PUT", + SND_SEQ_EVENT_INSTR_PUT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_GET", + SND_SEQ_EVENT_INSTR_GET); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_GET_RESULT", + SND_SEQ_EVENT_INSTR_GET_RESULT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_FREE", + SND_SEQ_EVENT_INSTR_FREE); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_LIST", + SND_SEQ_EVENT_INSTR_LIST); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_LIST_RESULT", + SND_SEQ_EVENT_INSTR_LIST_RESULT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_CLUSTER", + SND_SEQ_EVENT_INSTR_CLUSTER); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_CLUSTER_GET", + SND_SEQ_EVENT_INSTR_CLUSTER_GET); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_CLUSTER_RESULT", + SND_SEQ_EVENT_INSTR_CLUSTER_RESULT); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_INSTR_CHANGE", + SND_SEQ_EVENT_INSTR_CHANGE); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_SYSEX", + SND_SEQ_EVENT_SYSEX); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_BOUNCE", + SND_SEQ_EVENT_BOUNCE); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR_VAR0", + SND_SEQ_EVENT_USR_VAR0); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR_VAR1", + SND_SEQ_EVENT_USR_VAR1); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR_VAR2", + SND_SEQ_EVENT_USR_VAR2); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR_VAR3", + SND_SEQ_EVENT_USR_VAR3); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_USR_VAR4", + SND_SEQ_EVENT_USR_VAR4); + TCONSTADD(module, EVENT_TYPE, + "SEQ_EVENT_NONE", + SND_SEQ_EVENT_NONE); + + + /* SeqEvent event timestamp flags */ + TCONSTADD(module, EVENT_TIMESTAMP, + "SEQ_TIME_STAMP_TICK", + SND_SEQ_TIME_STAMP_TICK); + TCONSTADD(module, EVENT_TIMESTAMP, + "SEQ_TIME_STAMP_REAL", + SND_SEQ_TIME_STAMP_REAL); + + /* SeqEvent event timemode flags */ + TCONSTADD(module, EVENT_TIMEMODE, + "SEQ_TIME_MODE_ABS", + SND_SEQ_TIME_MODE_ABS); + TCONSTADD(module, EVENT_TIMEMODE, + "SEQ_TIME_MODE_REL", + SND_SEQ_TIME_MODE_REL); + + /* SeqEvent event addresses */ + TCONSTADD(module, ADDR_CLIENT, + "SEQ_CLIENT_SYSTEM", + SND_SEQ_CLIENT_SYSTEM); + TCONSTADD(module, ADDR_CLIENT, + "SEQ_ADDRESS_BROADCAST", + SND_SEQ_ADDRESS_BROADCAST); + TCONSTADD(module, ADDR_CLIENT, + "SEQ_ADDRESS_SUBSCRIBERS", + SND_SEQ_ADDRESS_SUBSCRIBERS); + TCONSTADD(module, ADDR_CLIENT, + "SEQ_ADDRESS_UNKNOWN", + SND_SEQ_ADDRESS_UNKNOWN); + TCONSTADD(module, ADDR_PORT, + "SEQ_PORT_SYSTEM_TIMER", + SND_SEQ_PORT_SYSTEM_TIMER); + TCONSTADD(module, ADDR_PORT, + "SEQ_PORT_SYSTEM_ANNOUNCE", + SND_SEQ_PORT_SYSTEM_ANNOUNCE); + TCONSTADD(module, ADDR_PORT, + "SEQ_ADDRESS_BROADCAST", + SND_SEQ_ADDRESS_BROADCAST); + TCONSTADD(module, ADDR_PORT, + "SEQ_ADDRESS_SUBSCRIBERS", + SND_SEQ_ADDRESS_SUBSCRIBERS); + TCONSTADD(module, ADDR_PORT, + "SEQ_ADDRESS_UNKNOWN", + SND_SEQ_ADDRESS_UNKNOWN); + +} diff -r 02b11d9f243d setup.py --- a/setup.py Tue Jan 22 11:56:20 2008 +0100 +++ b/setup.py Thu Jan 24 05:18:40 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 02b11d9f243d test/aconnect.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/aconnect.py Thu Jan 24 05:18:40 2008 -0300 @@ -0,0 +1,205 @@ +#! /usr/bin/python + +# aconnect.py -- python port of aconnect +# Copyright (C) 2008 Aldrin Martoq amartoq@dcc.uchile.cl +# +# Based on code from aconnect.c from ALSA project +# Copyright (C) 1999 Takashi Iwai +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 as +# published by the Free Software Foundation. +# +# 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +import sys +sys.path.insert(0, '../pyalsa') + +import getopt +import os +from alsaseq import * + +LIST_INPUT=1 +LIST_OUTPUT=2 + +def errormsg(msg, *args): + """ prints an error message to stderr """ + sys.stderr.write(msg % args) + sys.stderr.write('\n') + traceback.print_exc(file=sys.stderr) + +def fatal(msg, *args): + """ prints an error message to stderr, and dies """ + errormsg(msg, *args) + sys.exit(1) + +def init_seq(): + """ opens an alsa sequencer """ + try: + sequencer = Sequencer(name = 'default', + clientname = 'aconnect.py', + streams = SEQ_OPEN_DUPLEX, + mode = SEQ_BLOCK) + return sequencer + except SequencerError, e: + fatal("open sequencer: %e", e) + + +def usage(): + print \ + "aconnect - python ALSA sequencer connection manager\n" \ + "Copyright (C) 1999-2000 Takashi Iwai\n" \ + "Copyright (C) 2008 Aldrin Martoq amartoq@dcc.uchile.cl\n" \ + "Usage: \n" \ + " * Connection/disconnection between ports\n" \ + " %s [-options] sender receiver\n" \ + " sender, receiver = client:port pair\n" \ + " -d,--disconnect disconnect\n" \ + " -e,--exclusive exclusive connection\n" \ + " -r,--real # convert real-time-stamp on queue\n" \ + " -t,--tick # convert tick-time-stamp on queue\n" \ + " * List connected ports (no subscription action)\n" \ + " %s -i|-o [-options]\n" \ + " -i,--input list input (readable) ports\n" \ + " -o,--output list output (writable) ports\n" \ + " -l,--list list current connections of each port\n" \ + " * Remove all exported connections\n" \ + " -x,--removeall" \ + % (sys.argv[0], sys.argv[0]) + +def do_list_subs(conn, text): + l = [] + for c, p, i in conn: + s = "" + if i['exclusive']: + s+= "[ex]" + if i['time_update']: + if i['time_real']: + s += "[%s:%s]" % ("real", i['queue']) + else: + s += "[%s:%s]" % ("tick", i['queue']) + s = "%d:%d%s" % (c, p, s) + l.append(s) + if len(l): + print "\t%s: %s" % (text, ', '.join(l)) + + +def do_list_ports(sequencer, list_perm, list_subs): + connectionlist = sequencer.connection_list() + for connections in connectionlist: + clientname, clientid, connectedports = connections + clientinfo = sequencer.get_client_info(clientid) + type = clientinfo['type'] + count = [] + if type == SEQ_USER_CLIENT: + type = 'user' + else: + type = 'kernel' + for port in connectedports: + portname, portid, portconns = port + portinfo = sequencer.get_port_info(portid, clientid) + caps = portinfo['capability'] + if not (caps & SEQ_PORT_CAP_NO_EXPORT): + reads = SEQ_PORT_CAP_READ | SEQ_PORT_CAP_SUBS_READ + write = SEQ_PORT_CAP_WRITE | SEQ_PORT_CAP_SUBS_WRITE + if (list_perm & LIST_INPUT) and (caps & reads == reads): + count.append(port) + elif (list_perm & LIST_OUTPUT) and (caps & write == write): + count.append(port) + if len(count) > 0: + print "client %d: '%s' [type=%s]" % (clientid, clientname, type) + for port in count: + portname, portid, portconns = port + print " %3d '%-16s'" % (portid, portname) + if list_subs: + readconn, writeconn = portconns + do_list_subs(readconn, "Connecting To") + do_list_subs(writeconn, "Connected From") + +def do_unsubscribe(sequencer, args): + sender = sequencer.parse_address(args[0]) + dest = sequencer.parse_address(args[1]) + try: + sequencer.get_connect_info(sender, dest) + except SequencerError: + print 'No subscription is found' + return + try: + sequencer.disconnect_ports(sender, dest) + except SequencerError, e: + fatal("Failed to disconnect ports %s->%s - %s", sender, dest, e) + +def do_subscribe(sequencer, args, queue, exclusive, convert_time, convert_real): + sender = sequencer.parse_address(args[0]) + dest = sequencer.parse_address(args[1]) + try: + sequencer.get_connect_info(sender, dest) + print "Connection is already subscribed" + return + except SequencerError: + pass + sequencer.connect_ports(sender, dest, queue, exclusive, convert_time, convert_real) + +def main(): + sequencer = init_seq() + command = 'subscribe' + list_perm = 0 + list_subs = 0 + exclusive = 0 + convert_time = 0 + convert_real = 0 + queue = 0 + + try: + opts, args = getopt.getopt(sys.argv[1:], "dior:t:elx", ["disconnect", "input", "output", "real=", "tick=", "exclusive", "list", "removeall"]) + except getopt.GetoptError: + usage() + sys.exit(2) + for o, a in opts: + if o in ("-d", "--disconnect"): + command = 'unsubscribe' + elif o in ("-i", "--input"): + command = 'list' + list_perm |= LIST_INPUT + elif o in ("-o", "--output"): + command = 'list' + list_perm |= LIST_OUTPUT + elif o in ("-e", "--exclusive"): + exclusive = 1 + elif o in ("-r", "--real"): + queue = int(a) + convert_time = 1 + convert_real = 1 + elif o in ("-t", "--tick"): + queue = int(a) + convert_time = 1 + convert_real = 0 + elif o in ("-l", "--list"): + list_subs = 1 + elif o in ("-x", "--removeall"): + command = 'removeall' + + if command == 'list': + do_list_ports(sequencer, list_perm, list_subs) + return + + if len(args) != 2: + usage() + sys.exit(2) + + if command == 'unsubscribe': + do_unsubscribe(sequencer, args) + elif command == 'subscribe': + do_subscribe(sequencer, args, queue, exclusive, convert_time, convert_real) + + +if __name__ == '__main__': + main() diff -r 02b11d9f243d test/alsamemdebug.py --- a/test/alsamemdebug.py Tue Jan 22 11:56:20 2008 +0100 +++ b/test/alsamemdebug.py Thu Jan 24 05:18:40 2008 -0300 @@ -5,7 +5,8 @@ import gc import gc
def debuginit(): - gc.set_debug(gc.DEBUG_LEAK) + gc.set_debug(gc.DEBUG_LEAK | gc.DEBUG_STATS) + print "GC DEBUG: init."
def debug(what): from sys import getrefcount @@ -13,7 +14,9 @@ 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 'GD DEBUG LEAK:', o, hex(id(o)), type(o), getrefcount(o)-3, len(get_referrers(o))-2
def debugdone(): + gc.collect() + print "GC DEBUG LEAK done." None diff -r 02b11d9f243d test/aplaymidi.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/aplaymidi.py Thu Jan 24 05:18:40 2008 -0300 @@ -0,0 +1,501 @@ +#! /usr/bin/python + +# aplaymidi.py -- python port of aplaymidi +# Copyright (C) 2008 Aldrin Martoq amartoq@dcc.uchile.cl +# +# Based on code from aplaymidi.c from ALSA project +# Copyright (C) 2004-2006 Clemens Ladisch clemens@ladisch.de +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +import sys +sys.path.insert(0, '../pyalsa') + +import getopt +import os +import struct +import alsaseq +import traceback +import time +from alsaseq import * + +class SMFError(Exception): + """ Exception raised for errors while reading a SMF file """ + pass + + +def errormsg(msg, *args): + """ prints an error message to stderr """ + sys.stderr.write(msg % args) + sys.stderr.write('\n') + traceback.print_exc(file=sys.stderr) + + +def fatal(msg, *args): + """ prints an error message to stderr, and dies """ + errormsg(msg, *args) + sys.exit(1) + + +def init_seq(): + """ opens an alsa sequencer """ + try: + sequencer = Sequencer(name = 'default', + clientname = 'aplaymidi.py', + streams = SEQ_OPEN_DUPLEX, + mode = SEQ_BLOCK) + return sequencer + except SequencerError, e: + fatal("open sequencer: %e", e) + + +def parse_ports(sequencer, portspec): + """ parses one or more port addresses from the string, separated by ',' + example: 14:0,Timidity + """ + portlist = [] + if portspec == None: + return portlist + ports = portspec.split(',') + for port in ports: + try: + client, port = sequencer.parse_address(port) + portlist.append((client, port)) + except SequencerError, e: + fatal("Failed to parse port %s - %s", port, e) + return portlist + + +def create_source_port(sequencer): + try : + port = sequencer.create_simple_port(name = 'aplaymidi.py', + type = SEQ_PORT_TYPE_MIDI_GENERIC \ + | SEQ_PORT_TYPE_APPLICATION, + caps = SEQ_PORT_CAP_NONE) + return port + except SequencerError, e: + fatal("Failed to create port - %s", e) + + +def create_queue(sequencer): + try: + queue = sequencer.create_queue(name = 'aplaymidi') + return queue + except SequencerError, e: + fatal("Failed to create queue - %s", e) + + +def connect_ports(sequencer, port_id, ports): + client_id = sequencer.client_id + for client, port in ports: + sequencer.connect_ports((client_id, port_id), (client, port)) + + +def read_byte(file): + """ read a single byte + raises SMFError if end of file was detected + """ + s = file.read(1) + if s == '': + raise SMFError('End of file reached at position %d' % file.tell()) + return struct.unpack('B', s)[0] + + +def read_32_le(file): + """ reads a little-endian 32-bit integer """ + value = 0 + for p in (0, 8, 16, 24): + c = read_byte(file) + value |= c << p + return value + + +def read_id(file): + """ reads a 4-char identifier """ + return file.read(4) + + +def read_int(file, bytes): + """ reads a fixed-size big-endian number """ + value = 0 + while bytes > 0: + c = read_byte(file) + value = (value << 8) | c + bytes -= 1 + return value + + +def read_var(file): + """ reads a variable-length number """ + c = read_byte(file) + value = c & 0x7f + if c & 0x80: + c = read_byte(file) + value = (value << 7) | (c & 0x7f) + if c & 0x80: + c = read_byte(file) + value = (value << 7) | (c & 0x7f) + if c & 0x80: + c = read_byte(file) + value = (value << 7) | (c & 0x7f) + if c & 0x80: + raise SMFError('Invalid variable-length number at file position %d' % file.tell()) + return value + +def skip(file, bytes): + """ skip the specified number of bytes """ + file.read(bytes) + + +def read_track(file, chunk_len, ports, smtpe_timing): + """ reads one complete track from the file """ + + tick = 0 + last_cmd = 0 + port = 0 + typemap = {} + typemap[0x8] = SEQ_EVENT_NOTEOFF + typemap[0x9] = SEQ_EVENT_NOTEON + typemap[0xa] = SEQ_EVENT_KEYPRESS + typemap[0xb] = SEQ_EVENT_CONTROLLER + typemap[0xc] = SEQ_EVENT_PGMCHANGE + typemap[0xd] = SEQ_EVENT_CHANPRESS + typemap[0xe] = SEQ_EVENT_PITCHBEND + + eventlist = [] + + track_end = file.tell() + chunk_len + while file.tell() < track_end: + delta_ticks = read_var(file) + if delta_ticks < 0: + break + tick += delta_ticks + + c = read_byte(file) + + if c < 0: + break + + if c & 0x80: + cmd = c + if cmd < 0xf0: + last_cmd = cmd + else: + # running status + cmd = last_cmd + file.seek(file.tell() - 1) + if not cmd: + break + + + s = cmd >> 4 + if s >= 0x8 and s <= 0xa: + event = SeqEvent(type=typemap[s]) + event.dest = ports[port] + event.time = tick + event.set_data({'note.channel' : cmd & 0x0f, + 'note.note' : read_byte(file) & 0x7f, + 'note.velocity' : read_byte(file) & 0x7f + }) + eventlist.append(event) + elif s == 0xb or s == 0xe: + event = SeqEvent(type=typemap[s]) + event.dest = ports[port] + event.time = tick + event.set_data({'control.channel' : cmd & 0x0f, + 'control.param' : read_byte(file) & 0x7f, + 'control.value' : read_byte(file) & 0x7f + }) + eventlist.append(event) + elif s == 0xc or s == 0xd: + event = SeqEvent(type=typemap[s]) + event.dest = ports[port] + event.time = tick + event.set_data({'control.channel' : cmd & 0x0f, + 'control.value' : read_byte(file) & 0x7f + }) + eventlist.append(event) + elif s == 0xf: + if cmd == 0xf0 or cmd == 0xf7: + # sysex + l = read_var(file) + if l < 0: + break + if cmd == 0xf0: + l += 1 + event = SeqEvent(type=SEQ_EVENT_SYSEX) + event.dest = ports[port] + event.time = tick + sysexdata = [] + if cmd == 0xf0: + sysexdata.append(0xf0) + while len(sysexdata) < l: + sysexdata.append(read_byte(file)) + event.set_data({'ext' : sysexdata}) + eventlist.append(event) + elif cmd == 0xff: + c = read_byte(file) + l = read_var(file) + if l < 0: + break + if c == 0x21: + # port number + port = read_byte() % len(ports) + skip(file, l - 1) + elif c == 0x2f: + # end track + track_endtick = tick + skip(file, track_end - file.tell()) + return track_endtick, eventlist + elif c == 0x51: + # tempo + if l < 3: + break + if smtpe_timing: + skip(file, l) + else: + event = SeqEvent(type=SEQ_EVENT_TEMPO) + event.dest = (SEQ_CLIENT_SYSTEM, SEQ_PORT_SYSTEM_TIMER) + event.time = tick + tempo = read_byte(file) << 16 + tempo |= read_byte(file) << 8 + tempo |= read_byte(file) + event.set_data({'queue.param.value' : tempo}) + eventlist.append(event) + skip(file, l - 3) + else: + # ignore all other meta events + skip(file, l) + else: + break + else: + break + + raise SMFError('Invalid MIDI data at file position (%d)' % file.tell()) + +def read_smf(file, ports): + """ reads an entire MIDI file """ + + tempo = 0 + ppq = 0 + tracks = [] + + + header_len = read_int(file, 4) + if header_len < 6: + raise SMFError('Invalid header_len: %d' % header_len) + + type = read_int(file, 2) + if type != 0 and type != 1: + raise SMFError('Not supported type: %d' % type) + + num_tracks = read_int(file, 2) + if num_tracks < 1 or num_tracks > 1000: + raise SMFError('Invalid number of tracks: %d' % num_tracks) + + time_division = read_int(file, 2) + if time_division < 0: + raise SMFError('Invalid time division: %d' % time_division) + + smtpe_timing = time_division & 0x8000 + + if not smtpe_timing: + # time division is ticks per quarter + tempo = 500000 + ppq = time_division + else: + i = 0x80 - ((time_division >>8) & 0x7f) + time_division &= 0xff + if i == 24: + tempo = 500000 + ppq = 12 * time_division + elif i == 25: + tempo = 400000 + ppq = 10 * time_division + elif i == 29: + tempo = 100000000 + ppq = 2997 * time_division + elif i == 30: + tempo = 500000 + ppq = 15 * time_division + else: + raise SMFError('Invalid number of SMPTE frames per second (%d)' + % i) + + # read tracks + while num_tracks > 0: + # search for MTrk chunk + while True: + id = read_id(file) + chunk_len = read_int(file, 4) + if chunk_len < 0 or chunk_len >= 0x10000000: + raise SMFError('Invalid chunk length %d' % chunk_len) + if id == 'MTrk': + break + skip(file, chunk_len) + trackevents = read_track(file, chunk_len, ports, smtpe_timing) + tracks.append(trackevents) + num_tracks -= 1 + + return tempo, ppq, tracks + +def read_riff(file, ports): + """ reads an entire RIFF file """ + + raise NotImplementedError("Sorry, I couldn't find any RIFF midi file to implement this! -- Aldrin Martoq") + + +def play(sequencer, filename, end_delay, source_port, queue, ports): + file = sys.stdin + ok = None + if filename != '-': + try: + file = open(filename, 'rb') + except IOError, e: + fatal("Cannot open %s - %s", filename, e) + + id = read_id(file) + try: + if id == 'MThd': + ok = read_smf(file, ports) + elif id == 'RIFF': + ok = read_riff(file, ports) + else: + raise SMFError('unrecognized id') + except SMFError, e: + errormsg("%s is not a Standard MIDI File - %s", filename, e) + + # now play! + tempo, ppq, tracks = ok + sequencer.queue_tempo(queue, tempo, ppq) + sequencer.start_queue(queue) + + # add all events from tracks and sort them based on track, then on time + itrack = 0 + allevents = [] + for track in tracks: + endtick, eventlist = track + for event in eventlist: + d = {'itrack' : itrack, 'event' : event, 'time' : event.time} + allevents.append(d) + itrack += 1 + def compare(x, y): + ret = x['time'] - y['time'] + if ret == 0: + ret = x['itrack'] - y['itrack'] + return ret + allevents.sort(compare) + max_tick = allevents[-1]['time'] + + for d in allevents: + event = d['event'] + event.source = (0, 0) + event.queue = queue + if event.type == SEQ_EVENT_TEMPO: + event.set_data({'queue.queue' : queue}) + sequencer.output_event(event) + + # schedule queue stop at end of song + event = SeqEvent(SEQ_EVENT_STOP) + event.source = (0, 0) + event.queue = queue + event.time = max_tick + event.dest = (SEQ_CLIENT_SYSTEM, SEQ_PORT_SYSTEM_TIMER) + event.set_data({'queue.queue' : queue}) + sequencer.output_event(event) + + # make sure that the sequencer sees all our events + sequencer.drain_output() + sequencer.sync_output_queue() + + time.sleep(end_delay) + + if file != sys.stdin: + file.close() + + + +def list_ports(): + sequencer = init_seq() + print " Port Client name Port name"; + clientports = sequencer.connection_list() + for connections in clientports: + clientname, clientid, connectedports = connections + for port in connectedports: + portname, portid, connections = port + portinfo = sequencer.get_port_info(portid, clientid) + type = portinfo['type'] + caps = portinfo['capability'] + if type & SEQ_PORT_TYPE_MIDI_GENERIC and \ + caps & (SEQ_PORT_CAP_WRITE | SEQ_PORT_CAP_SUBS_WRITE): + print "%3d:%-3d %-32.32s %s" % (clientid, portid, clientname, portname) + +def usage(): + print "Usage: %s -p client:port[,...] [-d delay] midifile ...\n" \ + "-h, --help this help\n" \ + "-V, --version print current version\n" \ + "-l, --list list all possible output ports\n" \ + "-p, --port=client:port,... set port(s) to play to\n" \ + "-d, --delay=seconds delay after song ends\n" % (sys.argv[0]) + +def version(): + print "aplaymidi.py asoundlib version %s" % (SEQ_LIB_VERSION_STR) + + +def main(): + sequencer = init_seq() + ports = [] + end_delay = 2 + + try: + opts, args = getopt.getopt(sys.argv[1:], "hVlp:d:", ["help", "version", "list", "port=", "delay="]) + except getopt.GetoptError: + usage() + sys.exit(2) + for o, a in opts: + if o in ("-h", "--help"): + usage() + sys.exit(0) + elif o in ("-V", "--version"): + version() + sys.exit(0) + elif o in ("-l", "--list"): + list_ports() + sys.exit(0) + elif o in ("-p", "--port"): + ports = parse_ports(sequencer, a) + elif o in ("-d", "--delay"): + end_delay = int(a) + + if len(ports) < 1: + ports = parse_ports(sequencer, os.getenv('ALSA_OUTPUT_PORTS')) + if len(ports) < 1: + fatal("Please specify at least one port with --port.") + + if len(args) < 1: + fatal("Please specify a file to play.") + + source_port = create_source_port(sequencer) + queue = create_queue(sequencer) + connect_ports(sequencer, source_port, ports) + + for filename in args: + play(sequencer, filename, end_delay, source_port, queue, ports) + + +if __name__ == '__main__': + main() diff -r 02b11d9f243d test/aseqdump.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/aseqdump.py Thu Jan 24 05:18:40 2008 -0300 @@ -0,0 +1,193 @@ +#! /usr/bin/python + +# aseqdump.py -- python port of aplaymidi +# Copyright (C) 2008 Aldrin Martoq amartoq@dcc.uchile.cl +# +# Based on code from aseqdump.c, from ALSA project +# Copyright (C) 2005 Clemens Ladisch clemens@ladisch.de +# +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +import sys +sys.path.insert(0, '../pyalsa') + +import getopt +import os +import struct +import alsaseq +import traceback +import time +from alsaseq import * + +def errormsg(msg, *args): + """ prints an error message to stderr """ + sys.stderr.write(msg % args) + sys.stderr.write('\n') + traceback.print_exc(file=sys.stderr) + + +def fatal(msg, *args): + """ prints an error message to stderr, and dies """ + errormsg(msg, *args) + sys.exit(1) + + +def init_seq(): + """ opens an alsa sequencer """ + try: + sequencer = Sequencer(name = 'default', + clientname = 'aseqdump.py', + streams = SEQ_OPEN_DUPLEX, + mode = SEQ_BLOCK) + return sequencer + except SequencerError, e: + fatal("open sequencer: %e", e) + + +def parse_ports(sequencer, portspec): + """ parses one or more port addresses from the string, separated by ',' + example: 14:0,Timidity + """ + portlist = [] + if portspec == None: + return portlist + ports = portspec.split(',') + for port in ports: + try: + client, port = sequencer.parse_address(port) + portlist.append((client, port)) + except SequencerError, e: + fatal("Failed to parse port %s - %s", port, e) + return portlist + + +def create_source_port(sequencer): + try : + port = sequencer.create_simple_port(name = 'aseqdump.py', + type = SEQ_PORT_TYPE_MIDI_GENERIC + | SEQ_PORT_TYPE_APPLICATION, + caps = SEQ_PORT_CAP_WRITE + | SEQ_PORT_CAP_SUBS_WRITE) + return port + except SequencerError, e: + fatal("Failed to create port - %s", e) + + +def connect_ports(sequencer, port_id, ports): + client_id = sequencer.client_id + for client, port in ports: + sequencer.connect_ports((client, port),(client_id, port_id)) + +def dump_event(event): + print "%3d:%-3d" % ((event.source)[0], (event.source)[1]), + type = event.type + data = event.get_data() + if type == SEQ_EVENT_NOTEON: + print "Note on %2d %3d %3d" % \ + (data['note.channel'], data['note.note'], data['note.velocity']) + elif type == SEQ_EVENT_NOTEOFF: + print "Note off %2d %3d %3d" % \ + (data['note.channel'], data['note.note'], data['note.velocity']) + elif type == SEQ_EVENT_KEYPRESS: + print "Polyphonic aftertouch %2d %3d %3d" % \ + (data['note.channel'], data['note.note'], data['note.velocity']) + elif type == SEQ_EVENT_CONTROLLER: + print "Control change %2d %3d %3d" % \ + (data['control.channel'], data['control.param'], data['control.value']) + elif type == SEQ_EVENT_PGMCHANGE: + print "Program change %2d %3d" % \ + (data['control.channel'], data['control.value']) + else: + print "Event type %d" % type + +def list_ports(): + sequencer = init_seq() + print " Port Client name Port name"; + clientports = sequencer.connection_list() + for connections in clientports: + clientname, clientid, connectedports = connections + for port in connectedports: + portname, portid, connections = port + portinfo = sequencer.get_port_info(portid, clientid) + caps = portinfo['capability'] + if caps & (SEQ_PORT_CAP_READ | SEQ_PORT_CAP_SUBS_READ): + print "%3d:%-3d %-32.32s %s" % (clientid, portid, clientname, portname) + +def usage(): + print "Usage: %s [options]\n" \ + "\nAvailable options:\n" \ + "-h, --help this help\n" \ + "-V, --version show version\n" \ + "-l, --list list input ports\n" \ + "-p, --port=client:port,... source port(s)\n" \ + % (sys.argv[0]) + +def version(): + print "aseqdump.py asoundlib version %s" % (SEQ_LIB_VERSION_STR) + + +def main(): + sequencer = init_seq() + ports = [] + end_delay = 2 + + try: + opts, args = getopt.getopt(sys.argv[1:], "hVlp:", ["help", "version", "list", "port="]) + except getopt.GetoptError: + usage() + sys.exit(2) + for o, a in opts: + if o in ("-h", "--help"): + usage() + sys.exit(0) + elif o in ("-V", "--version"): + version() + sys.exit(0) + elif o in ("-l", "--list"): + list_ports() + sys.exit(0) + elif o in ("-p", "--port"): + ports = parse_ports(sequencer, a) + elif o in ("-d", "--delay"): + end_delay = int(a) + + source_port = create_source_port(sequencer) + connect_ports(sequencer, source_port, ports) + + sequencer.mode = SEQ_NONBLOCK + + if len(ports) > 0: + print "Waiting for data.", + else: + print "Waiting for data at port %d:0." % sequencer.client_id, + + print "Press Ctrl+C to end." + print "Source_ Event_________________ Ch _Data__" + + while True: + try: + eventlist = sequencer.receive_events(timeout=10069, maxevents = 1) + for event in eventlist: + dump_event(event) + except KeyboardInterrupt: + pass + break + + + +if __name__ == '__main__': + main() diff -r 02b11d9f243d test/seqtest1.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/seqtest1.py Thu Jan 24 05:18:40 2008 -0300 @@ -0,0 +1,189 @@ +#! /usr/bin/python +# Sample code for pyalsa Sequencer binding +# by Aldrin Martoq amartoq@dcc.uchile.cl +# version 2008012401 (UTC: 1201153962 secs since the epoch) +# +# This code is in the public domain, +# use it as base for creating your pyalsa +# sequencer 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 = "UNKNOWN(%d)" % (dict[key]) + else: + tmp = "N/A" + return tmp + + return "name=%s capability=%s type=%s" % (dl(dict, 'name'), da(dict, 'capability', alsaseq._dportcap), da(dict, 'type', alsaseq._dporttype)) + +def dump_list(connections, simple=True): + for clientports in connections: + clientname, clientid, portlist = clientports + print " client: %3d %s" % (clientid, clientname), + if not simple: + clientinfo = sequencer.get_client_info(clientid) + print "\t[%s]" % clientinfo + else: + print + for port in portlist: + portname, portid, connections = port + print " port: %3d:%-2d +-%s" % (clientid, portid, portname), + if not simple: + portinfo = sequencer.get_port_info(portid, clientid) + print "\t[%s]" % (dump_portinfo(portinfo)) + else: + print + readconn, writeconn = connections + for c,p,i in readconn: + if not simple: + print " connection to: %d:%d %s" % (c,p, i) + else: + print " connection to: %d:%d" % (c,p) + for c,p,i in writeconn: + if not simple: + print " connection to: %d:%d %s" % (c,p, i) + else: + print " connection to: %d:%d" % (c,p) + + +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, str(sequencer.streams)) +print " mode: %d (%s)" % (sequencer.mode, str(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, str(sequencer.mode)) +print + +print "03:Creating simple port ============================" +port_id = sequencer.create_simple_port("myport", alsaseq.SEQ_PORT_TYPE_APPLICATION,alsaseq.SEQ_PORT_CAP_WRITE | alsaseq.SEQ_PORT_CAP_SUBS_WRITE) +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:Retrieving clients and connections (as list) ====" +connections = sequencer.connection_list() +print " %s" % (connections) +print + +print "06:Retrieving clients and connections (detailed) ===" +connections = sequencer.connection_list() +dump_list(connections, False) +print + +print "07:Connecting 'arbitrary' ports... =================" +source = (alsaseq.SEQ_CLIENT_SYSTEM, alsaseq.SEQ_PORT_SYSTEM_ANNOUNCE) +dest = (sequencer.client_id, port_id) +print "%s ---> %s" % (str(source), str(dest)) +sequencer.connect_ports(source, dest) +print + +print "08:Retrieving clients and connections (simple) =====" +connections = sequencer.connection_list() +dump_list(connections) +print + +print "09:Disconnecting previous 'arbitrary' port =========" +print "%s -X-> %s" % (str(source), str(dest)) +sequencer.disconnect_ports(source, dest) +print + +print "10:Retrieving clients and connections (simple) =====" +connections = sequencer.connection_list() +dump_list(connections) +print + +print "11:Listing known streams constants =================" +print "%s" % alsaseq._dstreams.values() +print + +print "12:Listing known mode constants ====================" +print "%s" % alsaseq._dmode.values() +print + +print "13:Listing known queue constants ===================" +print "%s" % alsaseq._dqueue.values() +print + +print "14:Listing known client type constants =============" +print "%s" % alsaseq._dclienttype.values() +print + +print "15:Listing known port caps constants ===============" +print "%s" % alsaseq._dportcap.values() +print + +print "16:Listing known port types constants ==============" +print "%s" % alsaseq._dporttype.values() +print + +print "17:Listing known event type constants ==============" +print "%s" % alsaseq._deventtype.values() +print + +print "18:Listing known event timestamp constants =========" +print "%s" % alsaseq._deventtimestamp.values() +print + +print "19:Listing known event timemode constants ==========" +print "%s" % alsaseq._deventtimemode.values() +print + +print "20:Listing known client addresses constants ========" +print "%s" % alsaseq._dclient.values() +print + +print "21:Listing known port addresses constants ==========" +print "%s" % alsaseq._dport.values() +print + + +print "98:Removing sequencer ==============================" +debug([sequencer]) +del sequencer +print + +print "99:Listing sequencer (using new one) ===============" +dump_list(alsaseq.Sequencer().connection_list()) +print + +debugdone() +print "seqtest1.py done." diff -r 02b11d9f243d test/seqtest2.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/seqtest2.py Thu Jan 24 05:18:40 2008 -0300 @@ -0,0 +1,62 @@ +#! /usr/bin/python +# Sample code for pyalsa Sequencer binding +# by Aldrin Martoq amartoq@dcc.uchile.cl +# version 2008012401 (UTC: 1201153962 secs since the epoch) +# +# This code is in the public domain, +# use it as base for creating your pyalsa +# sequencer application. + +import sys +sys.path.insert(0, '../pyalsa') + +import alsaseq +import time +from alsamemdebug import debuginit, debug, debugdone + +debuginit() + +seq = alsaseq.Sequencer() + +def dump(event): + print "event: %s" % event + print " ", + for attr in alsaseq.SeqEvent.__dict__: + if attr.startswith('is_'): + t = event.__getattribute__(attr) + if t: + print "%s" % attr, + print + data = event.get_data() + print " data=%s" % data + +print "sequencer: %s" % seq + +port_id = seq.create_simple_port('hola', alsaseq.SEQ_PORT_TYPE_APPLICATION, + alsaseq.SEQ_PORT_CAP_SUBS_READ | alsaseq.SEQ_PORT_CAP_READ | alsaseq.SEQ_PORT_CAP_WRITE | alsaseq.SEQ_PORT_CAP_SUBS_WRITE + ) + +print "portid: %d" % port_id + +c=-2 +wait = 5000 + +while True: + if c == -1: + src = (alsaseq.SEQ_CLIENT_SYSTEM,alsaseq.SEQ_PORT_SYSTEM_ANNOUNCE) + dest = (seq.client_id, port_id) + print 'connecting %s -> %s' % (src, dest) + seq.connect_ports(src, dest) + if c == 5: + break + print 'waiting %s...' % wait + events = seq.receive_events(wait) + for event in events: + c = 0 + dump(event) + del event + c += 1 + +debug([seq]) + +debugdone() diff -r 02b11d9f243d test/seqtest3.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/seqtest3.py Thu Jan 24 05:18:40 2008 -0300 @@ -0,0 +1,71 @@ +#! /usr/bin/python +# Sample code for pyalsa Sequencer binding +# by Aldrin Martoq amartoq@dcc.uchile.cl +# version 2008012401 (UTC: 1201153962 secs since the epoch) +# +# This code is in the public domain, +# use it as base for creating your pyalsa +# sequencer application. + +import sys +sys.path.insert(0, '../pyalsa') + +from alsaseq import * +import time +from alsamemdebug import debuginit, debug, debugdone + +debuginit() + +def findmidiport(): + for connections in seq.connection_list(): + cname, cid, ports = connections + # skip midi through + if cname == 'Midi Through': + continue + for port in ports: + pname, pid, pconns = port + pinfo = seq.get_port_info(pid, cid) + type = pinfo['type'] + caps = pinfo['capability'] + if type & SEQ_PORT_TYPE_MIDI_GENERIC and caps & (SEQ_PORT_CAP_WRITE): + print "Using port: %s:%s" % (cname, pname) + return (cid, pid) + print "No midi port found -- install timidity or other software synth for testing!" + sys.exit(0) + +# create sequencer +seq = Sequencer() + +# find midi port +cid, pid = findmidiport() + +# create a queue +queue = seq.create_queue() +seq.start_queue(queue) +tempo, ppq = seq.queue_tempo(queue) +print "tempo: %d ppq: %d" % (tempo, ppq) + +# play notes: DO RE MI FA SOL LA +notes = [0x40, 0x42, 0x44, 0x45, 0x47, 0x49] +event = SeqEvent(SEQ_EVENT_NOTE) +for note in notes: + event.dest = (cid, pid) + event.queue = queue + event.time += ppq + event.set_data({'note.note' : note, 'note.velocity' : 64, 'note.duration' : ppq , 'note.off_velocity' : 64}) + print 'event: %s %s' % (event, event.get_data()) + seq.output_event(event) + seq.drain_output() + seq.sync_output_queue() + +# stop queue and delete queue +seq.stop_queue(queue) +seq.delete_queue(queue) + +debug([seq]) + +# close sequencer +del seq + + +debugdone()
On Thu, 24 Jan 2008, Aldrin Martoq wrote:
Hi hackers again,
I'm sending second PATCH for pyalsa sequencer binding. Major new features:
- documentation of all python API
- more info on subscription listing
- subscribe/unsubscribe
- events (SeqEvent class)
- send/receive/modify events
- creating and managing queues
I've already ported some existing applications to python versions:
- aconnect.py
- aplaymidi.py
- aseqdump.py
There are also 3 samples of code seqtest[1-3].py included in the patch.
It's been a long and tedious work (16 days); please tell me anyhow to improve and get this done and finished. Thanks again!!!
Thank you for your work. This patch looks promising. I will review the patch today.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
On Thu, 24 Jan 2008, Aldrin Martoq wrote:
Hi hackers again,
I'm sending second PATCH for pyalsa sequencer binding. Major new features:
- documentation of all python API
- more info on subscription listing
- subscribe/unsubscribe
- events (SeqEvent class)
- send/receive/modify events
- creating and managing queues
I've already ported some existing applications to python versions:
- aconnect.py
- aplaymidi.py
- aseqdump.py
There are also 3 samples of code seqtest[1-3].py included in the patch.
Thanks again. I applied your patch to HG, so it will be in 1.0.16rc2:
http://hg.alsa-project.org/alsa-python/rev/111a3ae58cce
Please, sync with this tree. I removed some compiler warnings.
Jaroslav
----- Jaroslav Kysela perex@perex.cz Linux Kernel Sound Maintainer ALSA Project, Red Hat, Inc.
participants (2)
-
Aldrin Martoq
-
Jaroslav Kysela