Merge branch 'master' of https://github.com/calf-studio-gear/calf
[calf.git] / bigbull / calfpytools.cpp
blob06a7629779ac13bb4f40714e2b01e49813056b3b
1 #include <Python.h>
2 #include "ttl.h"
3 #include "ttldata.h"
4 #include <list>
5 #include <map>
6 #include <iostream>
7 #include <fstream>
8 #include <jack/jack.h>
10 //////////////////////////////////////////////////// PyJackClient
12 struct PyJackPort;
14 typedef std::map<jack_port_t *, PyJackPort *> PortHandleMap;
16 struct AsyncOperationInfo
18 enum OpCode { INVALID_OP, PORT_REGISTERED, PORT_UNREGISTERED, PORTS_CONNECTED, PORTS_DISCONNECTED };
20 OpCode opcode;
21 jack_port_id_t port, port2;
23 AsyncOperationInfo() { opcode = INVALID_OP; }
24 AsyncOperationInfo(OpCode _opcode, jack_port_id_t _port, jack_port_id_t _port2 = 0)
25 : opcode(_opcode), port(_port), port2(_port2) {}
26 inline int argc() {
27 if (opcode == PORT_REGISTERED || opcode == PORT_UNREGISTERED) return 1;
28 if (opcode == PORTS_CONNECTED || opcode == PORTS_DISCONNECTED) return 2;
29 return 0;
33 typedef std::list<AsyncOperationInfo> PortOperationList;
35 struct PyJackClient
37 PyObject_HEAD
38 jack_client_t *client;
39 PortHandleMap *port_handle_map;
40 pthread_mutex_t port_op_mutex;
41 PortOperationList *port_op_queue;
44 static PyTypeObject jackclient_type = {
45 PyObject_HEAD_INIT(NULL)
46 0, /*ob_size*/
47 "calfpytools.JackClient", /*tp_name*/
48 sizeof(PyJackClient), /*tp_basicsize*/
51 struct PyJackPort
53 PyObject_HEAD
54 PyJackClient *client;
55 jack_port_t *port;
58 static PyTypeObject jackport_type = {
59 PyObject_HEAD_INIT(NULL)
60 0, /*ob_size*/
61 "calfpytools.JackPort", /*tp_name*/
62 sizeof(PyJackPort), /*tp_basicsize*/
65 static void register_cb(jack_port_id_t port, int registered, void *arg)
67 PyJackClient *self = (PyJackClient *)arg;
68 pthread_mutex_lock(&self->port_op_mutex);
69 self->port_op_queue->push_back(AsyncOperationInfo(registered ? AsyncOperationInfo::PORT_REGISTERED : AsyncOperationInfo::PORT_UNREGISTERED, port));
70 pthread_mutex_unlock(&self->port_op_mutex);
73 static void connect_cb(jack_port_id_t port1, jack_port_id_t port2, int connected, void *arg)
75 PyJackClient *self = (PyJackClient *)arg;
76 pthread_mutex_lock(&self->port_op_mutex);
77 self->port_op_queue->push_back(AsyncOperationInfo(connected ? AsyncOperationInfo::PORTS_CONNECTED : AsyncOperationInfo::PORTS_DISCONNECTED, port1, port2));
78 pthread_mutex_unlock(&self->port_op_mutex);
81 static PyObject *jackclient_open(PyJackClient *self, PyObject *args)
83 const char *name;
84 int options = 0;
85 jack_status_t status = (jack_status_t)0;
87 if (!PyArg_ParseTuple(args, "s|i:open", &name, &options))
88 return NULL;
90 self->client = jack_client_open(name, (jack_options_t)options, &status);
91 self->port_handle_map = new PortHandleMap;
92 pthread_mutexattr_t attr;
93 pthread_mutexattr_init(&attr);
94 pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
95 pthread_mutex_init(&self->port_op_mutex, &attr);
96 self->port_op_queue = new PortOperationList;
98 jack_set_port_registration_callback(self->client, register_cb, self);
99 jack_set_port_connect_callback(self->client, connect_cb, self);
100 jack_activate(self->client);
102 return Py_BuildValue("i", status);
105 static int jackclient_dealloc(PyJackPort *self)
107 if (self->client)
109 PyObject_CallMethod((PyObject *)self, strdup("close"), NULL);
110 assert(!self->client);
113 return 0;
116 #define CHECK_CLIENT if (!self->client) { PyErr_SetString(PyExc_ValueError, "Client not opened"); return NULL; }
119 static PyObject *jackclient_get_name(PyJackClient *self, PyObject *args)
121 if (!PyArg_ParseTuple(args, ":get_name"))
122 return NULL;
124 CHECK_CLIENT
126 return Py_BuildValue("s", jack_get_client_name(self->client));
129 static PyObject *create_jack_port(PyJackClient *client, jack_port_t *port)
131 PortHandleMap::iterator it = client->port_handle_map->find(port);
132 if (it != client->port_handle_map->end())
134 Py_INCREF(it->second);
135 return Py_BuildValue("O", it->second);
137 if (port)
139 PyObject *cobj = PyCObject_FromVoidPtr(port, NULL);
140 PyObject *args = Py_BuildValue("OO", client, cobj);
141 PyObject *newobj = _PyObject_New(&jackport_type);
142 jackport_type.tp_init(newobj, args, NULL);
143 (*client->port_handle_map)[port] = (PyJackPort *)newobj;
144 Py_DECREF(args);
145 return newobj;
147 Py_INCREF(Py_None);
148 return Py_None;
151 static PyObject *jackclient_register_port(PyJackClient *self, PyObject *args)
153 const char *name, *type = JACK_DEFAULT_AUDIO_TYPE;
154 unsigned long flags = 0, buffer_size = 0;
155 if (!PyArg_ParseTuple(args, "s|sii:register_port", &name, &type, &flags, &buffer_size))
156 return NULL;
158 CHECK_CLIENT
160 jack_port_t *port = jack_port_register(self->client, name, type, flags, buffer_size);
161 return create_jack_port(self, port);
164 static PyObject *jackclient_get_port(PyJackClient *self, PyObject *args)
166 const char *name;
167 if (!PyArg_ParseTuple(args, "s:get_port", &name))
168 return NULL;
170 CHECK_CLIENT
172 jack_port_t *port = jack_port_by_name(self->client, name);
173 return create_jack_port(self, port);
176 static PyObject *jackclient_get_cobj(PyJackClient *self, PyObject *args)
178 if (!PyArg_ParseTuple(args, ":get_cobj"))
179 return NULL;
181 CHECK_CLIENT
183 return PyCObject_FromVoidPtr((void *)self->client, NULL);
186 static PyObject *jackclient_get_ports(PyJackClient *self, PyObject *args)
188 const char *name = NULL, *type = NULL;
189 unsigned long flags = 0;
190 if (!PyArg_ParseTuple(args, "|ssi:get_ports", &name, &type, &flags))
191 return NULL;
193 CHECK_CLIENT
195 const char **p = jack_get_ports(self->client, name, type, flags);
196 PyObject *res = PyList_New(0);
197 if (!p)
198 return res;
200 for (const char **q = p; *q; q++)
202 PyList_Append(res, PyString_FromString(*q));
203 // free(q);
205 free(p);
207 return res;
210 static PyObject *jackclient_connect(PyJackClient *self, PyObject *args)
212 char *from_port = NULL, *to_port = NULL;
213 if (!PyArg_ParseTuple(args, "ss:connect", &from_port, &to_port))
214 return NULL;
216 CHECK_CLIENT
218 int result = jack_connect(self->client, from_port, to_port);
220 switch(result)
222 case 0:
223 Py_INCREF(Py_True);
224 return Py_True;
225 case EEXIST:
226 Py_INCREF(Py_False);
227 return Py_False;
228 default:
229 PyErr_SetString(PyExc_RuntimeError, "Connection error");
230 return NULL;
234 static PyObject *create_jack_port_by_id(PyJackClient *self, jack_port_id_t id)
236 return create_jack_port(self, jack_port_by_id(self->client, id));
239 static PyObject *jackclient_get_message(PyJackClient *self, PyObject *args)
241 if (!PyArg_ParseTuple(args, ":get_message"))
242 return NULL;
244 CHECK_CLIENT
246 PyObject *obj = NULL;
247 AsyncOperationInfo info;
248 pthread_mutex_lock(&self->port_op_mutex);
249 if (self->port_op_queue->empty())
251 obj = Py_None;
252 Py_INCREF(Py_None);
254 else
256 info = self->port_op_queue->front();
257 self->port_op_queue->pop_front();
259 pthread_mutex_unlock(&self->port_op_mutex);
260 if (!obj)
262 if (info.argc() == 1)
263 obj = Py_BuildValue("iO", info.opcode, create_jack_port_by_id(self, info.port));
264 else
265 if (info.argc() == 2)
266 obj = Py_BuildValue("iOO", info.opcode, create_jack_port_by_id(self, info.port), create_jack_port_by_id(self, info.port2));
268 return obj;
271 static PyObject *jackclient_disconnect(PyJackClient *self, PyObject *args)
273 char *from_port = NULL, *to_port = NULL;
274 if (!PyArg_ParseTuple(args, "ss:disconnect", &from_port, &to_port))
275 return NULL;
277 CHECK_CLIENT
279 int result = jack_disconnect(self->client, from_port, to_port);
281 switch(result)
283 case 0:
284 Py_INCREF(Py_None);
285 return Py_None;
286 default:
287 PyErr_SetString(PyExc_RuntimeError, "Disconnection error");
288 return NULL;
292 static PyObject *jackclient_close(PyJackClient *self, PyObject *args)
294 if (!PyArg_ParseTuple(args, ":close"))
295 return NULL;
297 CHECK_CLIENT
299 jack_deactivate(self->client);
300 jack_client_close(self->client);
301 self->client = NULL;
302 delete self->port_handle_map;
303 delete self->port_op_queue;
304 pthread_mutex_destroy(&self->port_op_mutex);
306 Py_INCREF(Py_None);
307 return Py_None;
310 static PyMethodDef jackclient_methods[] = {
311 {"open", (PyCFunction)jackclient_open, METH_VARARGS, "Open a client"},
312 {"close", (PyCFunction)jackclient_close, METH_VARARGS, "Close a client"},
313 {"get_name", (PyCFunction)jackclient_get_name, METH_VARARGS, "Retrieve client name"},
314 {"get_port", (PyCFunction)jackclient_get_port, METH_VARARGS, "Create port object from name of existing JACK port"},
315 {"get_ports", (PyCFunction)jackclient_get_ports, METH_VARARGS, "Get a list of port names based on specified name, type and flag filters"},
316 {"register_port", (PyCFunction)jackclient_register_port, METH_VARARGS, "Register a new port and return an object that represents it"},
317 {"get_cobj", (PyCFunction)jackclient_get_cobj, METH_VARARGS, "Retrieve jack_client_t pointer for the client as CObject"},
318 {"get_message", (PyCFunction)jackclient_get_message, METH_VARARGS, "Retrieve next port registration/connection message from the message queue"},
319 {"connect", (PyCFunction)jackclient_connect, METH_VARARGS, "Connect two ports with given names"},
320 {"disconnect", (PyCFunction)jackclient_disconnect, METH_VARARGS, "Disconnect two ports with given names"},
321 {NULL, NULL, 0, NULL}
324 //////////////////////////////////////////////////// PyJackPort
326 static int jackport_init(PyJackPort *self, PyObject *args, PyObject *kwds)
328 PyJackClient *client = NULL;
329 PyObject *cobj = NULL;
331 if (!PyArg_ParseTuple(args, "O!O:__init__", &jackclient_type, &client, &cobj))
332 return 0;
333 if (!PyCObject_Check(cobj))
335 PyErr_SetString(PyExc_TypeError, "Port constructor cannot be called explicitly");
336 return 0;
338 Py_INCREF(client);
340 self->client = client;
341 self->port = (jack_port_t *)PyCObject_AsVoidPtr(cobj);
343 return 0;
346 static int jackport_dealloc(PyJackPort *self)
348 // if not unregistered, decref (unregister decrefs automatically)
349 if (self->client) {
350 self->client->port_handle_map->erase(self->port);
351 Py_DECREF(self->client);
354 return 0;
357 #define CHECK_PORT_CLIENT if (!self->client || !self->client->client) { PyErr_SetString(PyExc_ValueError, "Client not opened"); return NULL; }
358 #define CHECK_PORT if (!self->port) { PyErr_SetString(PyExc_ValueError, "The port is not valid"); return NULL; }
360 static PyObject *jackport_strrepr(PyJackPort *self, int quotes)
362 CHECK_PORT_CLIENT
363 CHECK_PORT
365 int flags = jack_port_flags(self->port);
366 int flags_io = flags & (JackPortIsInput | JackPortIsOutput);
367 return PyString_FromFormat("<calfpytools.JackPort, name=%s%s%s, flags=%s%s%s%s>",
368 quotes ? "\"" : "",
369 jack_port_name(self->port),
370 quotes ? "\"" : "",
371 (flags_io == JackPortIsInput) ? "in" : (flags_io == JackPortIsOutput ? "out" : "?"),
372 flags & JackPortIsPhysical ? "|physical" : "",
373 flags & JackPortCanMonitor ? "|can_monitor" : "",
374 flags & JackPortIsTerminal ? "|terminal" : "");
377 static PyObject *jackport_str(PyJackPort *self)
379 return jackport_strrepr(self, 0);
382 static PyObject *jackport_repr(PyJackPort *self)
384 return jackport_strrepr(self, 1);
387 static PyObject *jackport_get_full_name(PyJackPort *self, PyObject *args)
389 if (!PyArg_ParseTuple(args, ":get_full_name"))
390 return NULL;
392 CHECK_PORT_CLIENT
393 CHECK_PORT
395 return Py_BuildValue("s", jack_port_name(self->port));
398 static PyObject *jackport_get_flags(PyJackPort *self, PyObject *args)
400 if (!PyArg_ParseTuple(args, ":get_flags"))
401 return NULL;
403 CHECK_PORT_CLIENT
404 CHECK_PORT
406 return PyInt_FromLong(jack_port_flags(self->port));
409 static PyObject *jackport_is_valid(PyJackPort *self, PyObject *args)
411 if (!PyArg_ParseTuple(args, ":is_valid"))
412 return NULL;
414 return PyBool_FromLong(self->client && self->client->client && self->port);
417 static PyObject *jackport_is_mine(PyJackPort *self, PyObject *args)
419 if (!PyArg_ParseTuple(args, ":is_mine"))
420 return NULL;
422 CHECK_PORT_CLIENT
423 CHECK_PORT
425 return PyBool_FromLong(self->client && self->client->client && self->port && jack_port_is_mine(self->client->client, self->port));
428 static PyObject *jackport_get_name(PyJackPort *self, PyObject *args)
430 if (!PyArg_ParseTuple(args, ":get_name"))
431 return NULL;
433 CHECK_PORT_CLIENT
434 CHECK_PORT
436 return Py_BuildValue("s", jack_port_short_name(self->port));
439 static PyObject *jackport_get_type(PyJackPort *self, PyObject *args)
441 if (!PyArg_ParseTuple(args, ":get_type"))
442 return NULL;
444 CHECK_PORT_CLIENT
445 CHECK_PORT
447 return Py_BuildValue("s", jack_port_type(self->port));
450 static PyObject *jackport_get_cobj(PyJackPort *self, PyObject *args)
452 if (!PyArg_ParseTuple(args, ":get_cobj"))
453 return NULL;
455 CHECK_PORT_CLIENT
456 CHECK_PORT
458 return PyCObject_FromVoidPtr((void *)self->port, NULL);
461 static PyObject *jackport_get_aliases(PyJackPort *self, PyObject *args)
463 if (!PyArg_ParseTuple(args, ":get_aliases"))
464 return NULL;
466 CHECK_PORT_CLIENT
467 CHECK_PORT
469 char buf1[256], buf2[256];
470 char *const aliases[2] = { buf1, buf2 };
471 int count = jack_port_get_aliases(self->port, aliases);
473 PyObject *alist = PyList_New(0);
474 for (int i = 0; i < count; i++)
475 PyList_Append(alist, PyString_FromString(aliases[i]));
476 return alist;
479 static PyObject *jackport_get_connections(PyJackPort *self, PyObject *args)
481 if (!PyArg_ParseTuple(args, ":get_aliases"))
482 return NULL;
484 CHECK_PORT_CLIENT
485 CHECK_PORT
487 const char **conns = jack_port_get_all_connections(self->client->client, self->port);
489 PyObject *res = PyList_New(0);
490 if (conns)
492 for (const char **p = conns; *p; p++)
493 PyList_Append(res, PyString_FromString(*p));
496 return res;
499 static PyObject *jackport_set_name(PyJackPort *self, PyObject *args)
501 const char *name;
502 if (!PyArg_ParseTuple(args, "s:set_name", &name))
503 return NULL;
505 CHECK_PORT
507 jack_port_set_name(self->port, name);
509 return Py_BuildValue("s", jack_port_short_name(self->port));
512 static PyObject *jackport_unregister(PyJackPort *self, PyObject *args)
514 if (!PyArg_ParseTuple(args, ":unregister"))
515 return NULL;
517 CHECK_PORT
519 PyJackClient *client = self->client;
521 client->port_handle_map->erase(self->port);
522 jack_port_unregister(self->client->client, self->port);
523 self->port = NULL;
524 self->client = NULL;
526 Py_DECREF(client);
527 client = NULL;
528 Py_INCREF(Py_None);
529 return Py_None;
532 static PyMethodDef jackport_methods[] = {
533 {"unregister", (PyCFunction)jackport_unregister, METH_VARARGS, "Unregister a port"},
534 {"is_valid", (PyCFunction)jackport_is_valid, METH_VARARGS, "Checks if the port object is valid (registered)"},
535 {"is_mine", (PyCFunction)jackport_is_mine, METH_VARARGS, "Checks if the port object is valid (registered)"},
536 {"get_full_name", (PyCFunction)jackport_get_full_name, METH_VARARGS, "Retrieve full port name (including client name)"},
537 {"get_name", (PyCFunction)jackport_get_name, METH_VARARGS, "Retrieve short port name (without client name)"},
538 {"get_type", (PyCFunction)jackport_get_type, METH_VARARGS, "Retrieve port type name"},
539 {"get_flags", (PyCFunction)jackport_get_flags, METH_VARARGS, "Retrieve port flags (defined in module, ie. calfpytools.JackPortIsInput)"},
540 {"set_name", (PyCFunction)jackport_set_name, METH_VARARGS, "Set short port name"},
541 {"get_aliases", (PyCFunction)jackport_get_aliases, METH_VARARGS, "Retrieve a list of port aliases"},
542 {"get_connections", (PyCFunction)jackport_get_connections, METH_VARARGS, "Retrieve a list of ports the port is connected to"},
543 {"get_cobj", (PyCFunction)jackport_get_cobj, METH_VARARGS, "Retrieve jack_port_t pointer for the port"},
544 {NULL, NULL, 0, NULL}
548 //////////////////////////////////////////////////// calfpytools
550 static PyObject *calfpytools_scan_ttl_file(PyObject *self, PyObject *args)
552 char *ttl_name = NULL;
553 if (!PyArg_ParseTuple(args, "s:scan_ttl_file", &ttl_name))
554 return NULL;
556 std::ifstream istr(ttl_name, std::ifstream::in);
557 TTLLexer lexer(&istr);
558 lexer.yylex();
559 return lexer.grab();
562 static PyObject *calfpytools_scan_ttl_string(PyObject *self, PyObject *args)
564 char *data = NULL;
565 if (!PyArg_ParseTuple(args, "s:scan_ttl_string", &data))
566 return NULL;
568 std::string data_str = data;
569 std::stringstream str(data_str);
570 TTLLexer lexer(&str);
571 lexer.yylex();
572 return lexer.grab();
575 static PyMethodDef module_methods[] = {
576 {"scan_ttl_file", calfpytools_scan_ttl_file, METH_VARARGS, "Scan a TTL file, return a list of token tuples"},
577 {"scan_ttl_string", calfpytools_scan_ttl_string, METH_VARARGS, "Scan a TTL string, return a list of token tuples"},
578 {NULL, NULL, 0, NULL}
581 PyMODINIT_FUNC initcalfpytools()
583 jackclient_type.tp_new = PyType_GenericNew;
584 jackclient_type.tp_flags = Py_TPFLAGS_DEFAULT;
585 jackclient_type.tp_doc = "JACK client object";
586 jackclient_type.tp_methods = jackclient_methods;
587 jackclient_type.tp_dealloc = (destructor)jackclient_dealloc;
588 if (PyType_Ready(&jackclient_type) < 0)
589 return;
591 jackport_type.tp_new = PyType_GenericNew;
592 jackport_type.tp_flags = Py_TPFLAGS_DEFAULT;
593 jackport_type.tp_doc = "JACK port object (created by client)";
594 jackport_type.tp_methods = jackport_methods;
595 jackport_type.tp_init = (initproc)jackport_init;
596 jackport_type.tp_dealloc = (destructor)jackport_dealloc;
597 jackport_type.tp_str = (reprfunc)jackport_str;
598 jackport_type.tp_repr = (reprfunc)jackport_repr;
599 if (PyType_Ready(&jackport_type) < 0)
600 return;
602 PyObject *mod = Py_InitModule3("calfpytools", module_methods, "Python utilities for Calf");
603 Py_INCREF(&jackclient_type);
604 Py_INCREF(&jackport_type);
605 PyModule_AddObject(mod, "JackClient", (PyObject *)&jackclient_type);
606 PyModule_AddObject(mod, "JackPort", (PyObject *)&jackport_type);
608 PyModule_AddObject(mod, "JackPortIsInput", PyInt_FromLong(JackPortIsInput));
609 PyModule_AddObject(mod, "JackPortIsOutput", PyInt_FromLong(JackPortIsOutput));
610 PyModule_AddObject(mod, "JackPortIsPhysical", PyInt_FromLong(JackPortIsPhysical));
611 PyModule_AddObject(mod, "JackPortCanMonitor", PyInt_FromLong(JackPortCanMonitor));
612 PyModule_AddObject(mod, "JackPortIsTerminal", PyInt_FromLong(JackPortIsTerminal));
613 PyModule_AddObject(mod, "JACK_DEFAULT_AUDIO_TYPE", PyString_FromString(JACK_DEFAULT_AUDIO_TYPE));
614 PyModule_AddObject(mod, "JACK_DEFAULT_MIDI_TYPE", PyString_FromString(JACK_DEFAULT_MIDI_TYPE));