Slightly hacky and not fully complete implementation of tar-based sound banks.
[calfbox.git] / scripting.c
blobfa46222d31373aa7488902cc10d9136e779c2974
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 "engine.h"
26 #include "errors.h"
27 #include "scripting.h"
28 #include <assert.h>
29 #include <glib.h>
31 // This is a workaround for what I consider a defect in pyconfig.h
32 #undef _XOPEN_SOURCE
33 #undef _POSIX_C_SOURCE
35 #include <Python.h>
37 static gboolean engine_initialised = FALSE;
39 struct PyCboxCallback
41 PyObject_HEAD
42 struct cbox_command_target *target;
45 static PyObject *
46 PyCboxCallback_New(PyTypeObject *type, PyObject *args, PyObject *kwds)
48 struct PyCboxCallback *self;
50 self = (struct PyCboxCallback *)type->tp_alloc(type, 0);
51 if (self != NULL) {
52 self->target = NULL;
55 return (PyObject *)self;
58 static int
59 PyCboxCallback_Init(struct PyCboxCallback *self, PyObject *args, PyObject *kwds)
61 PyObject *cobj = NULL;
62 if (!PyArg_ParseTuple(args, "O!:init", &PyCapsule_Type, &cobj))
63 return -1;
65 self->target = PyCapsule_GetPointer(cobj, NULL);
66 return 0;
69 static PyObject *cbox_python_do_cmd_on(struct cbox_command_target *ct, PyObject *self, PyObject *args);
71 static PyObject *
72 PyCboxCallback_Call(PyObject *_self, PyObject *args, PyObject *kwds)
74 struct PyCboxCallback *self = (struct PyCboxCallback *)_self;
76 return cbox_python_do_cmd_on(self->target, _self, args);
79 PyTypeObject CboxCallbackType = {
80 PyVarObject_HEAD_INIT(NULL, 0)
81 .tp_name = "_cbox.Callback",
82 .tp_basicsize = sizeof(struct PyCboxCallback),
83 .tp_flags = Py_TPFLAGS_DEFAULT,
84 .tp_doc = "Callback for feedback channel to Cbox C code",
85 .tp_init = (initproc)PyCboxCallback_Init,
86 .tp_new = PyCboxCallback_New,
87 .tp_call = PyCboxCallback_Call
90 static gboolean set_error_from_python(GError **error)
92 PyObject *ptype = NULL, *pvalue = NULL, *ptraceback = NULL;
93 PyErr_Fetch(&ptype, &pvalue, &ptraceback);
94 PyObject *ptypestr = PyObject_Str(ptype);
95 PyObject *pvaluestr = PyObject_Str(pvalue);
96 PyObject *ptypestr_unicode = PyUnicode_AsUTF8String(ptypestr);
97 PyObject *pvaluestr_unicode = PyUnicode_AsUTF8String(pvaluestr);
98 g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s: %s", PyBytes_AsString(ptypestr_unicode), PyBytes_AsString(pvaluestr_unicode));
99 Py_DECREF(pvaluestr_unicode);
100 Py_DECREF(ptypestr_unicode);
101 //g_error("%s:%s", PyString_AsString(ptypestr), PyString_AsString(pvaluestr));
102 Py_DECREF(ptypestr);
103 Py_DECREF(pvaluestr);
104 Py_DECREF(ptype);
105 Py_XDECREF(pvalue);
106 Py_XDECREF(ptraceback);
107 return FALSE;
110 static gboolean bridge_to_python_callback(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
112 PyObject *callback = ct->user_data;
114 int argc = strlen(cmd->arg_types);
115 PyObject *arg_values = PyList_New(argc);
116 for (int i = 0; i < argc; i++)
118 if (cmd->arg_types[i] == 's')
120 PyList_SetItem(arg_values, i, PyUnicode_FromString(cmd->arg_values[i]));
122 else
123 if (cmd->arg_types[i] == 'o')
125 struct cbox_objhdr *oh = cmd->arg_values[i];
126 char buf[40];
127 cbox_uuid_tostring(&oh->instance_uuid, buf);
128 PyList_SetItem(arg_values, i, PyUnicode_FromString(buf));
130 else
131 if (cmd->arg_types[i] == 'u')
133 struct cbox_uuid *uuid = cmd->arg_values[i];
134 char buf[40];
135 cbox_uuid_tostring(uuid, buf);
136 PyList_SetItem(arg_values, i, PyUnicode_FromString(buf));
138 else
139 if (cmd->arg_types[i] == 'i')
141 PyList_SetItem(arg_values, i, PyLong_FromLong(*(int *)cmd->arg_values[i]));
143 else
144 if (cmd->arg_types[i] == 'f')
146 PyList_SetItem(arg_values, i, PyFloat_FromDouble(*(double *)cmd->arg_values[i]));
148 else
149 if (cmd->arg_types[i] == 'b')
151 struct cbox_blob *blob = cmd->arg_values[i];
152 PyList_SetItem(arg_values, i, PyByteArray_FromStringAndSize(blob->data, blob->size));
154 else
156 PyList_SetItem(arg_values, i, Py_None);
157 Py_INCREF(Py_None);
160 struct PyCboxCallback *fbcb = NULL;
162 PyObject *args = PyTuple_New(3);
163 PyTuple_SetItem(args, 0, PyUnicode_FromString(cmd->command));
164 PyObject *pyfb = NULL;
165 if (fb)
167 struct PyCboxCallback *fbcb = PyObject_New(struct PyCboxCallback, &CboxCallbackType);
168 fbcb->target = fb;
169 pyfb = (PyObject *)fbcb;
171 else
173 pyfb = Py_None;
174 Py_INCREF(Py_None);
176 PyTuple_SetItem(args, 1, pyfb);
177 PyTuple_SetItem(args, 2, arg_values);
179 PyObject *result = PyObject_Call(callback, args, NULL);
180 Py_DECREF(args);
182 if (fbcb)
183 fbcb->target = NULL;
185 if (result)
187 Py_DECREF(result);
188 return TRUE;
191 return set_error_from_python(error);
194 static PyObject *cbox_python_do_cmd_on(struct cbox_command_target *ct, PyObject *self, PyObject *args)
196 const char *command = NULL;
197 PyObject *callback = NULL;
198 PyObject *list = NULL;
199 if (!PyArg_ParseTuple(args, "sOO!:do_cmd", &command, &callback, &PyList_Type, &list))
200 return NULL;
202 int len = PyList_Size(list);
203 void *extra = malloc(len * sizeof(double));
204 struct cbox_osc_command cmd;
205 GError *error = NULL;
206 char *arg_types = malloc(len + 1);
207 void **arg_values = malloc(2 * len * sizeof(void *));
208 void **arg_extra = &arg_values[len];
209 cmd.command = command;
210 cmd.arg_types = arg_types;
211 cmd.arg_values = arg_values;
212 double *arg_space = extra;
213 gboolean free_blobs = FALSE;
214 for (int i = 0; i < len; i++)
216 cmd.arg_values[i] = &arg_space[i];
217 PyObject *value = PyList_GetItem(list, i);
219 if (PyLong_Check(value))
221 arg_types[i] = 'i';
222 *(int *)arg_values[i] = PyLong_AsLong(value);
224 else
225 if (PyFloat_Check(value))
227 arg_types[i] = 'f';
228 *(double *)arg_values[i] = PyFloat_AsDouble(value);
230 else
231 if (PyUnicode_Check(value))
233 PyObject *utf8str = PyUnicode_AsUTF8String(value);
234 arg_types[i] = 's';
235 arg_extra[i] = utf8str;
236 arg_values[i] = PyBytes_AsString(utf8str);
238 else
239 if (PyByteArray_Check(value))
241 const void *buf = PyByteArray_AsString(value);
242 ssize_t len = PyByteArray_Size(value);
244 if (buf)
246 // note: this is not really acquired, the blob is freed using free and not cbox_blob_destroy
247 struct cbox_blob *blob = cbox_blob_new_acquire_data((void *)buf, len);
248 arg_types[i] = 'b';
249 arg_values[i] = blob;
250 free_blobs = TRUE;
252 else
253 arg_types[i] = 'N';
255 else
257 PyObject *ob_type = (PyObject *)value->ob_type;
258 PyObject *typename_unicode = PyObject_Str(ob_type);
259 PyObject *typename_bytes = PyUnicode_AsUTF8String(typename_unicode);
260 PyObject *exc = PyErr_Format(PyExc_ValueError, "Cannot decode Python type '%s' to execute '%s'", PyBytes_AsString(typename_bytes), command);
261 Py_DECREF(typename_bytes);
262 Py_DECREF(typename_unicode);
264 return exc;
267 arg_types[len] = '\0';
269 struct cbox_command_target target;
270 cbox_command_target_init(&target, bridge_to_python_callback, callback);
272 // cbox_osc_command_dump(&cmd);
273 Py_INCREF(callback);
274 gboolean result = ct->process_cmd(ct, callback != Py_None ? &target : NULL, &cmd, &error);
275 Py_DECREF(callback);
277 if (free_blobs)
279 for (int i = 0; i < len; i++)
281 if (arg_types[i] == 'b')
282 free(arg_values[i]);
283 if (arg_types[i] == 's')
284 Py_DECREF((PyObject *)arg_extra[i]);
287 free(arg_space);
288 free(arg_values);
289 free(arg_types);
291 if (!result)
292 return PyErr_Format(PyExc_Exception, "%s", error ? error->message : "Unknown error");
294 Py_RETURN_NONE;
297 static PyObject *cbox_python_do_cmd(PyObject *self, PyObject *args)
299 if (!engine_initialised)
300 return PyErr_Format(PyExc_Exception, "Engine not initialised");
301 return cbox_python_do_cmd_on(&app.cmd_target, self, args);
304 #if CALFBOX_AS_MODULE
306 #include "config-api.h"
307 #include "wavebank.h"
308 #include "scene.h"
310 static gboolean audio_running = FALSE;
312 static PyObject *cbox_python_init_engine(PyObject *self, PyObject *args)
314 const char *config_file = NULL;
315 if (!PyArg_ParseTuple(args, "|z:init_engine", &config_file))
316 return NULL;
317 if (engine_initialised)
318 return PyErr_Format(PyExc_Exception, "Engine already initialised");
320 cbox_dom_init();
321 app.tarpool = cbox_tarpool_new();
322 app.document = cbox_document_new();
323 app.rt = cbox_rt_new(app.document);
324 app.engine = cbox_engine_new(app.document, app.rt);
325 app.rt->engine = app.engine;
326 cbox_config_init(config_file);
327 cbox_wavebank_init();
328 engine_initialised = 1;
330 Py_INCREF(Py_None);
331 return Py_None;
334 static PyObject *cbox_python_shutdown_engine(PyObject *self, PyObject *args)
336 if (!PyArg_ParseTuple(args, ":shutdown_engine"))
337 return NULL;
338 if (!engine_initialised)
339 return PyErr_Format(PyExc_Exception, "Engine not initialised");
341 CBOX_DELETE(app.engine);
342 CBOX_DELETE(app.rt);
343 cbox_document_destroy(app.document);
344 cbox_wavebank_close();
345 cbox_config_close();
346 cbox_dom_close();
347 engine_initialised = FALSE;
349 Py_INCREF(Py_None);
350 return Py_None;
353 static PyObject *cbox_python_start_audio(PyObject *self, PyObject *args)
355 PyObject *callback = NULL;
356 if (!PyArg_ParseTuple(args, "|O:start_audio", &callback))
357 return NULL;
358 if (!engine_initialised)
359 return PyErr_Format(PyExc_Exception, "Engine not initialised");
360 if (audio_running)
361 return PyErr_Format(PyExc_Exception, "Audio already started");
363 struct cbox_open_params params;
364 GError *error = NULL;
366 struct cbox_command_target target;
367 if (callback && callback != Py_None)
368 cbox_command_target_init(&target, bridge_to_python_callback, callback);
370 if (!cbox_io_init(&app.io, &params, (callback && callback != Py_None) ? &target : NULL, &error))
371 return PyErr_Format(PyExc_IOError, "Cannot initialise sound I/O: %s", (error && error->message) ? error->message : "Unknown error");
373 cbox_rt_set_io(app.rt, &app.io);
374 cbox_scene_new(app.document, app.engine);
375 cbox_rt_start(app.rt, (callback && callback != Py_None) ? &target : NULL);
376 audio_running = TRUE;
378 Py_INCREF(Py_None);
379 return Py_None;
382 static PyObject *cbox_python_start_noaudio(PyObject *self, PyObject *args)
384 PyObject *callback = NULL;
385 int sample_rate = 0;
386 if (!PyArg_ParseTuple(args, "i|O:start_noaudio", &sample_rate, &callback))
387 return NULL;
388 if (!engine_initialised)
389 return PyErr_Format(PyExc_Exception, "Engine not initialised");
390 if (audio_running)
391 return PyErr_Format(PyExc_Exception, "Audio already started");
393 struct cbox_command_target target;
394 if (callback && callback != Py_None)
395 cbox_command_target_init(&target, bridge_to_python_callback, callback);
397 cbox_rt_set_offline(app.rt, sample_rate, 1024);
398 cbox_scene_new(app.document, app.engine);
399 cbox_rt_start(app.rt, (callback && callback != Py_None) ? &target : NULL);
400 audio_running = TRUE;
402 Py_INCREF(Py_None);
403 return Py_None;
406 static PyObject *cbox_python_stop_audio(PyObject *self, PyObject *args)
408 if (!PyArg_ParseTuple(args, ":stop_audio"))
409 return NULL;
410 if (!engine_initialised)
411 return PyErr_Format(PyExc_Exception, "Engine not initialised");
412 if (!audio_running)
413 return PyErr_Format(PyExc_Exception, "Audio not running");
415 while(app.engine->scene_count > 0)
416 CBOX_DELETE(app.engine->scenes[0]);
417 cbox_rt_stop(app.rt);
418 cbox_io_close(&app.io);
419 audio_running = FALSE;
420 Py_INCREF(Py_None);
421 return Py_None;
424 #endif
426 static PyMethodDef CboxMethods[] = {
427 {"do_cmd", cbox_python_do_cmd, METH_VARARGS, "Execute a CalfBox command using a global path."},
428 #if CALFBOX_AS_MODULE
429 {"init_engine", cbox_python_init_engine, METH_VARARGS, "Initialise the CalfBox engine using optional config file."},
430 {"shutdown_engine", cbox_python_shutdown_engine, METH_VARARGS, "Shutdown the CalfBox engine."},
431 {"start_audio", cbox_python_start_audio, METH_VARARGS, "Start real-time audio processing using I/O settings from the current config."},
432 {"start_noaudio", cbox_python_start_noaudio, METH_VARARGS, "Start dummy audio processing using sample rate specified as argument."},
433 {"stop_audio", cbox_python_stop_audio, METH_VARARGS, "Stop real-time audio processing."},
434 #endif
435 {NULL, NULL, 0, NULL}
438 static PyModuleDef CboxModule = {
439 PyModuleDef_HEAD_INIT, "_cbox", NULL, -1, CboxMethods,
440 NULL, NULL, NULL, NULL
443 #if CALFBOX_AS_MODULE
445 PyMODINIT_FUNC
446 PyInit__cbox(void)
448 PyObject *m = PyModule_Create(&CboxModule);
449 if (!m)
450 return NULL;
451 Py_INCREF(&CboxCallbackType);
452 if (PyType_Ready(&CboxCallbackType) < 0)
453 return NULL;
454 PyModule_AddObject(m, "Callback", (PyObject *)&CboxCallbackType);
456 return m;
459 #else
461 PyObject*
462 PyInit_cbox(void)
464 PyObject *m = PyModule_Create(&CboxModule);
465 PyModule_AddObject(m, "Callback", (PyObject *)&CboxCallbackType);
466 return m;
469 void cbox_script_run(const char *name)
471 FILE *fp = fopen(name, "rb");
472 if (!fp)
474 g_warning("Cannot open script file '%s': %s", name, strerror(errno));
475 return;
477 PyImport_AppendInittab("_cbox", &PyInit_cbox);
478 Py_Initialize();
479 if (PyType_Ready(&CboxCallbackType) < 0)
481 g_warning("Cannot install the C callback type");
482 return;
484 Py_INCREF(&CboxCallbackType);
485 engine_initialised = TRUE;
487 if (PyRun_SimpleFile(fp, name) == 1)
489 GError *error = NULL;
490 set_error_from_python(&error);
491 cbox_print_error(error);
493 Py_Finalize();
496 #endif
497 #endif