Add an example demonstrating use of sequencer API.
[calfbox.git] / scripting.c
blob895bc5dcdd66a168fa67feda28b84e62676dba7d
1 /*
2 Calf Box, an open source musical instrument.
3 Copyright (C) 2010-2011 Krzysztof Foltman
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "config.h"
21 #if USE_PYTHON
23 #include "app.h"
24 #include "blob.h"
25 #include "errors.h"
26 #include "scripting.h"
27 #include <assert.h>
28 #include <glib.h>
29 #include <Python.h>
31 struct PyCboxCallback
33 PyObject_HEAD
34 struct cbox_command_target *target;
37 static PyObject *
38 PyCboxCallback_New(PyTypeObject *type, PyObject *args, PyObject *kwds)
40 struct PyCboxCallback *self;
42 self = (struct PyCboxCallback *)type->tp_alloc(type, 0);
43 if (self != NULL) {
44 self->target = NULL;
47 return (PyObject *)self;
50 static int
51 PyCboxCallback_Init(struct PyCboxCallback *self, PyObject *args, PyObject *kwds)
53 PyObject *cobj = NULL;
54 if (!PyArg_ParseTuple(args, "O!:init", &PyCapsule_Type, &cobj))
55 return -1;
57 self->target = PyCapsule_GetPointer(cobj, NULL);
60 static PyObject *cbox_python_do_cmd_on(struct cbox_command_target *ct, PyObject *self, PyObject *args);
62 static PyObject *
63 PyCboxCallback_Call(PyObject *_self, PyObject *args, PyObject *kwds)
65 struct PyCboxCallback *self = (struct PyCboxCallback *)_self;
67 return cbox_python_do_cmd_on(self->target, _self, args);
70 PyTypeObject CboxCallbackType = {
71 PyObject_HEAD_INIT(NULL)
72 .tp_name = "_cbox.Callback",
73 .tp_basicsize = sizeof(struct PyCboxCallback),
74 .tp_flags = Py_TPFLAGS_DEFAULT,
75 .tp_doc = "Callback for feedback channel to Cbox C code",
76 .tp_init = (initproc)PyCboxCallback_Init,
77 .tp_new = PyCboxCallback_New,
78 .tp_call = PyCboxCallback_Call
81 static gboolean set_error_from_python(GError **error)
83 PyObject *ptype = NULL, *pvalue = NULL, *ptraceback = NULL;
84 PyErr_Fetch(&ptype, &pvalue, &ptraceback);
85 PyObject *ptypestr = PyObject_Str(ptype);
86 PyObject *pvaluestr = PyObject_Str(pvalue);
87 PyObject *ptypestr_unicode = PyUnicode_AsUTF8String(ptypestr);
88 PyObject *pvaluestr_unicode = PyUnicode_AsUTF8String(pvaluestr);
89 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s: %s", PyBytes_AsString(ptypestr_unicode), PyBytes_AsString(pvaluestr_unicode));
90 Py_DECREF(pvaluestr_unicode);
91 Py_DECREF(ptypestr_unicode);
92 //g_error("%s:%s", PyString_AsString(ptypestr), PyString_AsString(pvaluestr));
93 Py_DECREF(ptypestr);
94 Py_DECREF(pvaluestr);
95 Py_DECREF(ptype);
96 Py_XDECREF(pvalue);
97 Py_XDECREF(ptraceback);
98 return FALSE;
101 static gboolean bridge_to_python_callback(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
103 PyObject *callback = ct->user_data;
105 int argc = strlen(cmd->arg_types);
106 PyObject *arg_values = PyList_New(argc);
107 for (int i = 0; i < argc; i++)
109 if (cmd->arg_types[i] == 's')
111 PyList_SetItem(arg_values, i, PyUnicode_FromString(cmd->arg_values[i]));
113 else
114 if (cmd->arg_types[i] == 'o')
116 struct cbox_objhdr *oh = cmd->arg_values[i];
117 char buf[40];
118 uuid_unparse(oh->instance_uuid.uuid, buf);
119 PyList_SetItem(arg_values, i, PyUnicode_FromString(buf));
121 else
122 if (cmd->arg_types[i] == 'i')
124 PyList_SetItem(arg_values, i, PyLong_FromLong(*(int *)cmd->arg_values[i]));
126 else
127 if (cmd->arg_types[i] == 'f')
129 PyList_SetItem(arg_values, i, PyFloat_FromDouble(*(double *)cmd->arg_values[i]));
131 else
132 if (cmd->arg_types[i] == 'b')
134 struct cbox_blob *blob = cmd->arg_values[i];
135 PyList_SetItem(arg_values, i, PyByteArray_FromStringAndSize(blob->data, blob->size));
137 else
139 PyList_SetItem(arg_values, i, Py_None);
140 Py_INCREF(Py_None);
143 struct PyCboxCallback *fbcb = NULL;
145 PyObject *args = PyTuple_New(3);
146 PyTuple_SetItem(args, 0, PyUnicode_FromString(cmd->command));
147 PyObject *pyfb = NULL;
148 if (fb)
150 struct PyCboxCallback *fbcb = PyObject_New(struct PyCboxCallback, &CboxCallbackType);
151 fbcb->target = fb;
152 pyfb = (PyObject *)fbcb;
154 else
156 pyfb = Py_None;
157 Py_INCREF(Py_None);
159 PyTuple_SetItem(args, 1, pyfb);
160 PyTuple_SetItem(args, 2, arg_values);
162 PyObject *result = PyObject_Call(callback, args, NULL);
163 Py_DECREF(args);
165 if (fbcb)
166 fbcb->target = NULL;
168 if (result)
170 Py_DECREF(result);
171 return TRUE;
174 return set_error_from_python(error);
177 static PyObject *cbox_python_do_cmd_on(struct cbox_command_target *ct, PyObject *self, PyObject *args)
179 const char *command = NULL;
180 PyObject *callback = NULL;
181 PyObject *list = NULL;
182 if (!PyArg_ParseTuple(args, "sOO!:do_cmd", &command, &callback, &PyList_Type, &list))
183 return NULL;
185 int len = PyList_Size(list);
186 void *extra = malloc(len * sizeof(double));
187 struct cbox_osc_command cmd;
188 GError *error = NULL;
189 char *arg_types = malloc(len + 1);
190 void **arg_values = malloc(2 * len * sizeof(void *));
191 void **arg_extra = &arg_values[len];
192 cmd.command = command;
193 cmd.arg_types = arg_types;
194 cmd.arg_values = arg_values;
195 double *arg_space = extra;
196 gboolean free_blobs = FALSE;
197 for (int i = 0; i < len; i++)
199 cmd.arg_values[i] = &arg_space[i];
200 PyObject *value = PyList_GetItem(list, i);
202 if (PyLong_Check(value))
204 arg_types[i] = 'i';
205 *(int *)arg_values[i] = PyLong_AsLong(value);
207 else
208 if (PyFloat_Check(value))
210 arg_types[i] = 'f';
211 *(double *)arg_values[i] = PyFloat_AsDouble(value);
213 else
214 if (PyUnicode_Check(value))
216 PyObject *utf8str = PyUnicode_AsUTF8String(value);
217 arg_types[i] = 's';
218 arg_extra[i] = utf8str;
219 arg_values[i] = PyBytes_AsString(utf8str);
221 else
222 if (PyByteArray_Check(value))
224 const void *buf = PyByteArray_AsString(value);
225 ssize_t len = PyByteArray_Size(value);
227 if (buf)
229 // note: this is not really acquired, the blob is freed using free and not cbox_blob_destroy
230 struct cbox_blob *blob = cbox_blob_new_acquire_data((void *)buf, len);
231 arg_types[i] = 'b';
232 arg_values[i] = blob;
233 free_blobs = TRUE;
235 else
236 arg_types[i] = 'N';
238 else
240 PyObject *ob_type = (PyObject *)value->ob_type;
241 PyObject *typename_unicode = PyObject_Str(ob_type);
242 PyObject *typename_bytes = PyUnicode_AsUTF8String(typename_unicode);
243 g_error("Cannot serialize Python type '%s'", PyBytes_AsString(typename_bytes));
244 Py_DECREF(typename_bytes);
245 Py_DECREF(typename_unicode);
247 assert(0);
250 arg_types[len] = '\0';
252 struct cbox_command_target target;
253 cbox_command_target_init(&target, bridge_to_python_callback, callback);
255 // cbox_osc_command_dump(&cmd);
256 Py_INCREF(callback);
257 gboolean result = ct->process_cmd(ct, callback != Py_None ? &target : NULL, &cmd, &error);
258 Py_DECREF(callback);
260 if (free_blobs)
262 for (int i = 0; i < len; i++)
264 if (arg_types[i] == 'b')
265 free(arg_values[i]);
266 if (arg_types[i] == 's')
267 Py_DECREF((PyObject *)arg_extra[i]);
270 free(arg_space);
271 free(arg_values);
272 free(arg_types);
274 if (!result)
275 return PyErr_Format(PyExc_Exception, "%s", error ? error->message : "Unknown error");
277 Py_RETURN_NONE;
280 static PyObject *cbox_python_do_cmd(PyObject *self, PyObject *args)
282 return cbox_python_do_cmd_on(&app.cmd_target, self, args);
285 static PyMethodDef CboxMethods[] = {
286 {"do_cmd", cbox_python_do_cmd, METH_VARARGS, "Execute a CalfBox command using a global path."},
287 {NULL, NULL, 0, NULL}
290 static PyModuleDef CboxModule = {
291 PyModuleDef_HEAD_INIT, "_cbox", NULL, -1, CboxMethods,
292 NULL, NULL, NULL, NULL
295 static PyObject*
296 PyInit_cbox(void)
298 PyObject *m = PyModule_Create(&CboxModule);
299 PyModule_AddObject(m, "Callback", (PyObject *)&CboxCallbackType);
300 return m;
303 void cbox_script_run(const char *name)
305 FILE *fp = fopen(name, "rb");
306 if (!fp)
308 g_warning("Cannot open script file '%s': %s", name, strerror(errno));
309 return;
311 PyImport_AppendInittab("_cbox", &PyInit_cbox);
312 Py_Initialize();
313 if (PyType_Ready(&CboxCallbackType) < 0)
315 g_warning("Cannot install the C callback type");
316 return;
318 Py_INCREF(&CboxCallbackType);
320 if (PyRun_SimpleFile(fp, name) == 1)
322 GError *error = NULL;
323 set_error_from_python(&error);
324 cbox_print_error(error);
326 Py_Finalize();
329 #endif