Merge pull request #1 from atsampson/master
[calfbox.git] / scripting.c
blob68589d743df5376343753d6f7f0edae499b7c3e2
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 "tarfile.h"
308 #include "wavebank.h"
309 #include "scene.h"
311 static gboolean audio_running = FALSE;
313 static PyObject *cbox_python_init_engine(PyObject *self, PyObject *args)
315 const char *config_file = NULL;
316 if (!PyArg_ParseTuple(args, "|z:init_engine", &config_file))
317 return NULL;
318 if (engine_initialised)
319 return PyErr_Format(PyExc_Exception, "Engine already initialised");
321 cbox_dom_init();
322 app.tarpool = cbox_tarpool_new();
323 app.document = cbox_document_new();
324 app.rt = cbox_rt_new(app.document);
325 app.engine = cbox_engine_new(app.document, app.rt);
326 app.rt->engine = app.engine;
327 cbox_config_init(config_file);
328 cbox_wavebank_init();
329 engine_initialised = 1;
331 Py_INCREF(Py_None);
332 return Py_None;
335 static PyObject *cbox_python_shutdown_engine(PyObject *self, PyObject *args)
337 if (!PyArg_ParseTuple(args, ":shutdown_engine"))
338 return NULL;
339 if (!engine_initialised)
340 return PyErr_Format(PyExc_Exception, "Engine not initialised");
342 CBOX_DELETE(app.engine);
343 CBOX_DELETE(app.rt);
344 cbox_tarpool_destroy(app.tarpool);
345 cbox_document_destroy(app.document);
346 cbox_wavebank_close();
347 cbox_config_close();
348 cbox_dom_close();
349 engine_initialised = FALSE;
351 Py_INCREF(Py_None);
352 return Py_None;
355 static PyObject *cbox_python_start_audio(PyObject *self, PyObject *args)
357 PyObject *callback = NULL;
358 if (!PyArg_ParseTuple(args, "|O:start_audio", &callback))
359 return NULL;
360 if (!engine_initialised)
361 return PyErr_Format(PyExc_Exception, "Engine not initialised");
362 if (audio_running)
363 return PyErr_Format(PyExc_Exception, "Audio already started");
365 struct cbox_open_params params;
366 GError *error = NULL;
368 struct cbox_command_target target;
369 if (callback && callback != Py_None)
370 cbox_command_target_init(&target, bridge_to_python_callback, callback);
372 if (!cbox_io_init(&app.io, &params, (callback && callback != Py_None) ? &target : NULL, &error))
373 return PyErr_Format(PyExc_IOError, "Cannot initialise sound I/O: %s", (error && error->message) ? error->message : "Unknown error");
375 const char *effect_preset_name = cbox_config_get_string("master", "effect");
377 cbox_rt_set_io(app.rt, &app.io);
378 cbox_scene_new(app.document, app.engine);
379 cbox_rt_start(app.rt, (callback && callback != Py_None) ? &target : NULL);
380 if (effect_preset_name && *effect_preset_name)
382 app.engine->effect = cbox_module_new_from_fx_preset(effect_preset_name, app.document, app.rt, app.engine, &error);
383 if (!app.engine->effect)
384 return PyErr_Format(PyExc_IOError, "Cannot load master effect preset %s: %s", effect_preset_name, (error && error->message) ? error->message : "Unknown error");
386 audio_running = TRUE;
388 Py_INCREF(Py_None);
389 return Py_None;
392 static PyObject *cbox_python_start_noaudio(PyObject *self, PyObject *args)
394 PyObject *callback = NULL;
395 int sample_rate = 0;
396 if (!PyArg_ParseTuple(args, "i|O:start_noaudio", &sample_rate, &callback))
397 return NULL;
398 if (!engine_initialised)
399 return PyErr_Format(PyExc_Exception, "Engine not initialised");
400 if (audio_running)
401 return PyErr_Format(PyExc_Exception, "Audio already started");
403 struct cbox_command_target target;
404 if (callback && callback != Py_None)
405 cbox_command_target_init(&target, bridge_to_python_callback, callback);
407 cbox_rt_set_offline(app.rt, sample_rate, 1024);
408 cbox_scene_new(app.document, app.engine);
409 cbox_rt_start(app.rt, (callback && callback != Py_None) ? &target : NULL);
410 audio_running = TRUE;
412 Py_INCREF(Py_None);
413 return Py_None;
416 static PyObject *cbox_python_stop_audio(PyObject *self, PyObject *args)
418 if (!PyArg_ParseTuple(args, ":stop_audio"))
419 return NULL;
420 if (!engine_initialised)
421 return PyErr_Format(PyExc_Exception, "Engine not initialised");
422 if (!audio_running)
423 return PyErr_Format(PyExc_Exception, "Audio not running");
425 while(app.engine->scene_count > 0)
426 CBOX_DELETE(app.engine->scenes[0]);
427 cbox_rt_stop(app.rt);
428 cbox_io_close(&app.io);
429 audio_running = FALSE;
430 Py_INCREF(Py_None);
431 return Py_None;
434 #endif
436 static PyMethodDef CboxMethods[] = {
437 {"do_cmd", cbox_python_do_cmd, METH_VARARGS, "Execute a CalfBox command using a global path."},
438 #if CALFBOX_AS_MODULE
439 {"init_engine", cbox_python_init_engine, METH_VARARGS, "Initialise the CalfBox engine using optional config file."},
440 {"shutdown_engine", cbox_python_shutdown_engine, METH_VARARGS, "Shutdown the CalfBox engine."},
441 {"start_audio", cbox_python_start_audio, METH_VARARGS, "Start real-time audio processing using I/O settings from the current config."},
442 {"start_noaudio", cbox_python_start_noaudio, METH_VARARGS, "Start dummy audio processing using sample rate specified as argument."},
443 {"stop_audio", cbox_python_stop_audio, METH_VARARGS, "Stop real-time audio processing."},
444 #endif
445 {NULL, NULL, 0, NULL}
448 static PyModuleDef CboxModule = {
449 PyModuleDef_HEAD_INIT, "_cbox", NULL, -1, CboxMethods,
450 NULL, NULL, NULL, NULL
453 #if CALFBOX_AS_MODULE
455 PyMODINIT_FUNC
456 PyInit__cbox(void)
458 PyObject *m = PyModule_Create(&CboxModule);
459 if (!m)
460 return NULL;
461 Py_INCREF(&CboxCallbackType);
462 if (PyType_Ready(&CboxCallbackType) < 0)
463 return NULL;
464 PyModule_AddObject(m, "Callback", (PyObject *)&CboxCallbackType);
466 return m;
469 #else
471 PyObject*
472 PyInit_cbox(void)
474 PyObject *m = PyModule_Create(&CboxModule);
475 PyModule_AddObject(m, "Callback", (PyObject *)&CboxCallbackType);
476 return m;
479 void cbox_script_run(const char *name)
481 FILE *fp = fopen(name, "rb");
482 if (!fp)
484 g_warning("Cannot open script file '%s': %s", name, strerror(errno));
485 return;
487 PyImport_AppendInittab("_cbox", &PyInit_cbox);
488 Py_Initialize();
489 if (PyType_Ready(&CboxCallbackType) < 0)
491 g_warning("Cannot install the C callback type");
492 return;
494 Py_INCREF(&CboxCallbackType);
495 engine_initialised = TRUE;
497 if (PyRun_SimpleFile(fp, name) == 1)
499 GError *error = NULL;
500 set_error_from_python(&error);
501 cbox_print_error(error);
503 Py_Finalize();
506 #endif
507 #endif