/* * Basic Python bindings for libpd * * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com) * Updated 2013,2020 Dan Wilcox (danomatika@gmail.com) * * For information on usage and redistribution, and for a DISCLAIMER OF * ALL WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ %module pylibpd /* initializing pd */ // libpd_init() in %init void libpd_clear_search_path(void); void libpd_add_to_search_path(const char *path); /* opening patches */ %rename(__libpd_openfile) libpd_openfile; %rename(__libpd_closefile) libpd_closefile; %rename(__libpd_getdollarzero) libpd_getdollarzero; void *libpd_openfile(const char *name, const char *dir); void libpd_closefile(void *p); int libpd_getdollarzero(void *p); /* audio processing */ int libpd_blocksize(void); int libpd_init_audio(int inChannels, int outChannels, int sampleRate); #define TYPEMAPS(t) \ %typemap(in) t *inBuffer { \ Py_ssize_t dummy; \ if (PyObject_AsReadBuffer($input, (const void **)&$1, &dummy)) return NULL; \ } \ %typemap(in) t *outBuffer { \ Py_ssize_t dummy; \ if (PyObject_AsWriteBuffer($input, (void **)&$1, &dummy)) return NULL; \ } TYPEMAPS(float) TYPEMAPS(short) TYPEMAPS(double) int libpd_process_float(const int ticks, const float *inBuffer, float *outBuffer); int libpd_process_short(const int ticks, const short *inBuffer, short *outBuffer); int libpd_process_double(const int ticks, const double *inBuffer, double *outBuffer); int libpd_process_raw(const float *inBuffer, float *outBuffer); int libpd_process_raw_short(const short *inBuffer, short *outBuffer); int libpd_process_raw_double(const double *inBuffer, double *outBuffer); /* array access */ int libpd_arraysize(const char *name); int libpd_resize_array(const char *name, long size); int libpd_read_array(float *outBuffer, const char *name, int offset, int n); int libpd_write_array(const char *name, int offset, const float *inBuffer, int n); /* sending messages to pd */ int libpd_bang(const char *recv); int libpd_float(const char *recv, float x); int libpd_symbol(const char *recv, const char *symbol); %rename(__libpd_start_message) libpd_start_message; %rename(__libpd_add_float) libpd_add_float; %rename(__libpd_add_symbol) libpd_add_symbol; %rename(__libpd_finish_list) libpd_finish_list; %rename(__libpd_finish_message) libpd_finish_message; int libpd_start_message(int maxlen); void libpd_add_float(float x); void libpd_add_symbol(const char *symbol); int libpd_finish_list(const char *recv); int libpd_finish_message(const char *recv, const char *msg); /* sending compound messages: atom array */ // probably need to be wrapped manually due to t_atom //void libpd_set_float(t_atom *a, float x); //int libpd_list(const char *recv, int argc, t_atom *argv); //int libpd_message(const char *recv, const char *msg, int argc, t_atom *argv); /* receiving messages from pd */ %rename(__libpd_bind) libpd_bind; %rename(__libpd_unbind) libpd_unbind; void *libpd_bind(const char *recv); void libpd_unbind(void *p); int libpd_exists(const char *recv); #define SET_CALLBACK(s) \ int libpd_set_##s##_callback(PyObject *callback); SET_CALLBACK(print) SET_CALLBACK(bang) SET_CALLBACK(float) SET_CALLBACK(symbol) SET_CALLBACK(list) SET_CALLBACK(message) // probably need to be wrapped manually due to t_atom //int libpd_is_float(t_atom *a); //int libpd_is_symbol(t_atom *a); //float libpd_get_float(t_atom *a); //const char *libpd_get_symbol(t_atom *a); //t_atom *libpd_next_atom(t_atom *a); /* sending MIDI messages to pd */ int libpd_noteon(int channel, int pitch, int velocity); int libpd_controlchange(int channel, int controller, int value); int libpd_programchange(int channel, int value); int libpd_pitchbend(int channel, int value); int libpd_aftertouch(int channel, int value); int libpd_polyaftertouch(int channel, int pitch, int value); int libpd_midibyte(int port, int byte); int libpd_sysex(int port, int byte); int libpd_sysrealtime(int port, int byte); /* receiving MIDI messages from pd */ SET_CALLBACK(noteon) SET_CALLBACK(controlchange) SET_CALLBACK(programchange) SET_CALLBACK(pitchbend) SET_CALLBACK(aftertouch) SET_CALLBACK(polyaftertouch) SET_CALLBACK(midibyte) /* GUI */ int libpd_start_gui(char *path); void libpd_stop_gui(void); int libpd_poll_gui(void); /* multiple instances */ // probably need to be handled manually due to t_pdinstance? //t_pdinstance *libpd_new_instance(void); //void libpd_set_instance(t_pdinstance *p); //void libpd_free_instance(t_pdinstance *p); //t_pdinstance *libpd_this_instance(void); //t_pdinstance *libpd_main_instance(void); //int libpd_num_instances(void); //void libpd_set_instancedata(void *data); //void* libpd_get_instancedata(void); /* log level */ void libpd_set_verbose(int verbose); int libpd_get_verbose(void); /* manual bindings */ %pythoncode %{ import array def __process_args(args): if __libpd_start_message(len(args)): return -2 for arg in args: if isinstance(arg, str): __libpd_add_symbol(arg) else: if isinstance(arg, int) or isinstance(arg, float): __libpd_add_float(arg) else: return -1 return 0 def libpd_list(recv, *args): return __process_args(args) or __libpd_finish_list(recv) def libpd_message(recv, symbol, *args): return __process_args(args) or __libpd_finish_message(recv, symbol) __libpd_patches = {} def libpd_open_patch(patchannel, dir = '.'): ptr = __libpd_openfile(patchannel, dir) if not ptr: raise IOError("unable to open patch: %s/%s" % (dir, patch)) dz = __libpd_getdollarzero(ptr) __libpd_patches[dz] = ptr return dz def libpd_close_patch(dz): __libpd_closefile(__libpd_patches[dz]) del __libpd_patches[dz] __libpd_subscriptions = {} def libpd_subscribe(recv): if recv not in __libpd_subscriptions: __libpd_subscriptions[recv] = __libpd_bind(recv) def libpd_unsubscribe(recv): __libpd_unbind(__libpd_subscriptions[recv]) del __libpd_subscriptions[recv] def libpd_compute_audio(flag): libpd_message('pd', 'dsp', flag) def libpd_release(): for p in __libpd_patches.values(): __libpd_closefile(p) __libpd_patches.clear() for p in __libpd_subscriptions.values(): __libpd_unbind(p) __libpd_subscriptions.clear() class PdManager: def __init__(self, inChannels, outChannels, sampleRate, ticks): self.__ticks = ticks self.__outbuf = array.array('b', '\x00\x00'.encode() * outChannels * libpd_blocksize()) libpd_compute_audio(1) libpd_init_audio(inChannels, outChannels, sampleRate) def process(self, inBuffer): libpd_process_short(self.__ticks, inBuffer, self.__outbuf) return self.__outbuf %} %{ #include "z_libpd.h" #include "util/z_print_util.h" static PyObject *convertArgs(const char *recv, const char *symbol, int n, t_atom *args) { int i = (symbol) ? 2 : 1; n += i; PyObject *result = PyTuple_New(n); PyTuple_SetItem(result, 0, PyString_FromString(recv)); if (symbol) { PyTuple_SetItem(result, 1, PyString_FromString(symbol)); } int j; for (j = 0; i < n; i++, j++) { t_atom *a = &args[j]; PyObject *x = NULL; if (libpd_is_float(a)) { x = PyFloat_FromDouble(libpd_get_float(a)); } else if (libpd_is_symbol(a)) { x = PyString_FromString(libpd_get_symbol(a)); } PyTuple_SetItem(result, i, x); } return result; } #define MAKE_CALLBACK(s, args1, cmd, args2) \ static PyObject *s##_callback = NULL; \ static int libpd_set_##s##_callback(PyObject *callback) { \ Py_XDECREF(s##_callback); \ if (PyCallable_Check(callback)) { \ s##_callback = callback; \ Py_INCREF(s##_callback); \ return 0; \ } else { \ s##_callback = NULL; \ return -1; \ } \ } \ static void pylibpd_##s args1 { \ if (s##_callback) { \ PyObject *pyargs = cmd args2; \ PyObject *result = PyObject_CallObject(s##_callback, pyargs); \ Py_XDECREF(result); \ Py_DECREF(pyargs); \ } \ } MAKE_CALLBACK(print, (const char *s), Py_BuildValue, ("(s)", s)) MAKE_CALLBACK(bang, (const char *recv), Py_BuildValue, ("(s)", recv)) MAKE_CALLBACK(float, (const char *recv, float x), Py_BuildValue, ("(sf)", recv, x)) MAKE_CALLBACK(symbol, (const char *recv, const char *symbol), Py_BuildValue, ("(ss)", recv, symbol)) MAKE_CALLBACK(list, (const char *recv, int n, t_atom *pd_args), convertArgs, (recv, NULL, n, pd_args)) MAKE_CALLBACK(message, (const char *recv, const char *symbol, int n, t_atom *pd_args), convertArgs, (recv, symbol, n, pd_args)) MAKE_CALLBACK(noteon, (int channel, int pitch, int velocity), Py_BuildValue, ("(iii)", channel, pitch, velocity)) MAKE_CALLBACK(controlchange, (int channel, int controller, int velocity), Py_BuildValue, ("(iii)", channel, controller, velocity)) MAKE_CALLBACK(programchange, (int channel, int value), Py_BuildValue, ("(ii)", channel, value)) MAKE_CALLBACK(pitchbend, (int channel, int value), Py_BuildValue, ("(ii)", channel, value)) MAKE_CALLBACK(aftertouch, (int channel, int velocity), Py_BuildValue, ("(ii)", channel, velocity)) MAKE_CALLBACK(polyaftertouch, (int channel, int pitch, int velocity), Py_BuildValue, ("(iii)", channel, pitch, velocity)) MAKE_CALLBACK(midibyte, (int port, int byte), Py_BuildValue, ("(ii)", port, byte)) %} %init %{ #define ASSIGN_CALLBACK(s) libpd_set_##s##hook(pylibpd_##s); ASSIGN_CALLBACK(print) ASSIGN_CALLBACK(bang) ASSIGN_CALLBACK(float) ASSIGN_CALLBACK(symbol) ASSIGN_CALLBACK(list) ASSIGN_CALLBACK(message) ASSIGN_CALLBACK(noteon) ASSIGN_CALLBACK(controlchange) ASSIGN_CALLBACK(programchange) ASSIGN_CALLBACK(pitchbend) ASSIGN_CALLBACK(aftertouch) ASSIGN_CALLBACK(polyaftertouch) ASSIGN_CALLBACK(midibyte) libpd_init(); %}