Got seek_ppqn/seek_samples the other way around :-)
[calfbox.git] / scripting.c
blob7b1144fe957a7d6cfe3e3b48a2cae956fe641838
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.document = cbox_document_new();
322 app.rt = cbox_rt_new(app.document);
323 app.engine = cbox_engine_new(app.document, app.rt);
324 app.rt->engine = app.engine;
325 cbox_config_init(config_file);
326 cbox_wavebank_init();
327 engine_initialised = 1;
329 Py_INCREF(Py_None);
330 return Py_None;
333 static PyObject *cbox_python_shutdown_engine(PyObject *self, PyObject *args)
335 if (!PyArg_ParseTuple(args, ":shutdown_engine"))
336 return NULL;
337 if (!engine_initialised)
338 return PyErr_Format(PyExc_Exception, "Engine not initialised");
340 CBOX_DELETE(app.engine);
341 CBOX_DELETE(app.rt);
342 cbox_document_destroy(app.document);
343 cbox_wavebank_close();
344 cbox_config_close();
345 cbox_dom_close();
346 engine_initialised = FALSE;
348 Py_INCREF(Py_None);
349 return Py_None;
352 static PyObject *cbox_python_start_audio(PyObject *self, PyObject *args)
354 PyObject *callback = NULL;
355 if (!PyArg_ParseTuple(args, "|O:start_audio", &callback))
356 return NULL;
357 if (!engine_initialised)
358 return PyErr_Format(PyExc_Exception, "Engine not initialised");
359 if (audio_running)
360 return PyErr_Format(PyExc_Exception, "Audio already started");
362 struct cbox_open_params params;
363 GError *error = NULL;
365 struct cbox_command_target target;
366 if (callback && callback != Py_None)
367 cbox_command_target_init(&target, bridge_to_python_callback, callback);
369 if (!cbox_io_init(&app.io, &params, (callback && callback != Py_None) ? &target : NULL, &error))
370 return PyErr_Format(PyExc_IOError, "Cannot initialise sound I/O: %s", (error && error->message) ? error->message : "Unknown error");
372 cbox_rt_set_io(app.rt, &app.io);
373 cbox_scene_new(app.document, app.engine);
374 cbox_rt_start(app.rt, (callback && callback != Py_None) ? &target : NULL);
375 audio_running = TRUE;
377 Py_INCREF(Py_None);
378 return Py_None;
381 static PyObject *cbox_python_start_noaudio(PyObject *self, PyObject *args)
383 PyObject *callback = NULL;
384 int sample_rate = 0;
385 if (!PyArg_ParseTuple(args, "i|O:start_noaudio", &sample_rate, &callback))
386 return NULL;
387 if (!engine_initialised)
388 return PyErr_Format(PyExc_Exception, "Engine not initialised");
389 if (audio_running)
390 return PyErr_Format(PyExc_Exception, "Audio already started");
392 struct cbox_command_target target;
393 if (callback && callback != Py_None)
394 cbox_command_target_init(&target, bridge_to_python_callback, callback);
396 cbox_rt_set_offline(app.rt, sample_rate, 1024);
397 cbox_scene_new(app.document, app.engine);
398 cbox_rt_start(app.rt, (callback && callback != Py_None) ? &target : NULL);
399 audio_running = TRUE;
401 Py_INCREF(Py_None);
402 return Py_None;
405 static PyObject *cbox_python_stop_audio(PyObject *self, PyObject *args)
407 if (!PyArg_ParseTuple(args, ":stop_audio"))
408 return NULL;
409 if (!engine_initialised)
410 return PyErr_Format(PyExc_Exception, "Engine not initialised");
411 if (!audio_running)
412 return PyErr_Format(PyExc_Exception, "Audio not running");
414 while(app.engine->scene_count > 0)
415 CBOX_DELETE(app.engine->scenes[0]);
416 cbox_rt_stop(app.rt);
417 cbox_io_close(&app.io);
418 audio_running = FALSE;
419 Py_INCREF(Py_None);
420 return Py_None;
423 #endif
425 static PyMethodDef CboxMethods[] = {
426 {"do_cmd", cbox_python_do_cmd, METH_VARARGS, "Execute a CalfBox command using a global path."},
427 #if CALFBOX_AS_MODULE
428 {"init_engine", cbox_python_init_engine, METH_VARARGS, "Initialise the CalfBox engine using optional config file."},
429 {"shutdown_engine", cbox_python_shutdown_engine, METH_VARARGS, "Shutdown the CalfBox engine."},
430 {"start_audio", cbox_python_start_audio, METH_VARARGS, "Start real-time audio processing using I/O settings from the current config."},
431 {"start_noaudio", cbox_python_start_noaudio, METH_VARARGS, "Start dummy audio processing using sample rate specified as argument."},
432 {"stop_audio", cbox_python_stop_audio, METH_VARARGS, "Stop real-time audio processing."},
433 #endif
434 {NULL, NULL, 0, NULL}
437 static PyModuleDef CboxModule = {
438 PyModuleDef_HEAD_INIT, "_cbox", NULL, -1, CboxMethods,
439 NULL, NULL, NULL, NULL
442 #if CALFBOX_AS_MODULE
444 PyMODINIT_FUNC
445 PyInit__cbox(void)
447 PyObject *m = PyModule_Create(&CboxModule);
448 if (!m)
449 return NULL;
450 Py_INCREF(&CboxCallbackType);
451 if (PyType_Ready(&CboxCallbackType) < 0)
452 return NULL;
453 PyModule_AddObject(m, "Callback", (PyObject *)&CboxCallbackType);
455 return m;
458 #else
460 PyObject*
461 PyInit_cbox(void)
463 PyObject *m = PyModule_Create(&CboxModule);
464 PyModule_AddObject(m, "Callback", (PyObject *)&CboxCallbackType);
465 return m;
468 void cbox_script_run(const char *name)
470 FILE *fp = fopen(name, "rb");
471 if (!fp)
473 g_warning("Cannot open script file '%s': %s", name, strerror(errno));
474 return;
476 PyImport_AppendInittab("_cbox", &PyInit_cbox);
477 Py_Initialize();
478 if (PyType_Ready(&CboxCallbackType) < 0)
480 g_warning("Cannot install the C callback type");
481 return;
483 Py_INCREF(&CboxCallbackType);
484 engine_initialised = TRUE;
486 if (PyRun_SimpleFile(fp, name) == 1)
488 GError *error = NULL;
489 set_error_from_python(&error);
490 cbox_print_error(error);
492 Py_Finalize();
495 #endif
496 #endif