libpd-sys 0.3.4

Rust bindings for libpd
/*
 * 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();
%}