Permalink
Fetching contributors…
Cannot retrieve contributors at this time
639 lines (556 sloc) 13.7 KB
/*
* Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com)
*
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.
*
* See https://github.com/libpd/libpd/wiki for documentation
*
*/
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#ifndef LIBPD_NO_NUMERIC
# include <locale.h>
#endif
#include "z_libpd.h"
#include "x_libpdreceive.h"
#include "z_hooks.h"
#include "s_stuff.h"
#include "m_imp.h"
#include "g_all_guis.h"
#if PD_MINOR_VERSION < 46
# define HAVE_SCHED_TICK_ARG
#endif
#ifdef HAVE_SCHED_TICK_ARG
# define SCHED_TICK(x) sched_tick(x)
#else
# define SCHED_TICK(x) sched_tick()
#endif
// forward declares
void pd_init(void);
int sys_startgui(const char *libdir);
void sys_stopgui(void);
int sys_pollgui(void);
// (optional) built in pd externals setup functions
#ifdef LIBPD_EXTRA
void bob_tilde_setup();
void bonk_tilde_setup();
void choice_setup();
void fiddle_tilde_setup();
void loop_tilde_setup();
void lrshift_tilde_setup();
void pique_setup();
void sigmund_tilde_setup();
void stdout_setup();
#endif
static PERTHREAD t_atom *argv = NULL;
static PERTHREAD t_atom *curr = NULL;
static PERTHREAD int argm = 0;
static PERTHREAD int argc = 0;
static void *get_object(const char *s) {
t_pd *x = gensym(s)->s_thing;
return x;
}
// this is called instead of sys_main() to start things
int libpd_init(void) {
static int initialized = 0;
if (initialized) return -1; // only allow init once (for now)
initialized = 1;
signal(SIGFPE, SIG_IGN);
libpd_start_message(32); // allocate array for message assembly
// are all these settings necessary?
sys_externalschedlib = 0;
sys_printtostderr = 0;
sys_usestdpath = 0; // don't use pd_extrapath, only sys_searchpath
sys_debuglevel = 0;
sys_noloadbang = 0;
sys_hipriority = 0;
sys_nmidiin = 0;
sys_nmidiout = 0;
#ifdef HAVE_SCHED_TICK_ARG
sys_time = 0;
#endif
pd_init();
STUFF->st_soundin = NULL;
STUFF->st_soundout = NULL;
STUFF->st_schedblocksize = DEFDACBLKSIZE;
sys_init_fdpoll();
libpdreceive_setup();
sys_set_audio_api(API_DUMMY);
STUFF->st_searchpath = NULL;
sys_libdir = gensym("");
#ifdef LIBPD_EXTRA
bob_tilde_setup();
bonk_tilde_setup();
choice_setup();
fiddle_tilde_setup();
loop_tilde_setup();
lrshift_tilde_setup();
pique_setup();
sigmund_tilde_setup();
stdout_setup();
#endif
#ifndef LIBPD_NO_NUMERIC
setlocale(LC_NUMERIC, "C");
#endif
return 0;
}
void libpd_clear_search_path(void) {
sys_lock();
namelist_free(STUFF->st_searchpath);
STUFF->st_searchpath = NULL;
sys_unlock();
}
void libpd_add_to_search_path(const char *s) {
sys_lock();
STUFF->st_searchpath = namelist_append(STUFF->st_searchpath, s, 0);
sys_unlock();
}
void *libpd_openfile(const char *basename, const char *dirname) {
void * retval;
sys_lock();
pd_globallock();
retval = (void *)glob_evalfile(NULL, gensym(basename), gensym(dirname));
pd_globalunlock();
sys_unlock();
return retval;
}
void libpd_closefile(void *x) {
sys_lock();
pd_free((t_pd *)x);
sys_unlock();
}
int libpd_getdollarzero(void *x) {
sys_lock();
pd_pushsym((t_pd *)x);
int dzero = canvas_getdollarzero();
pd_popsym((t_pd *)x);
sys_unlock();
return dzero;
}
int libpd_init_audio(int inChans, int outChans, int sampleRate) {
int indev[MAXAUDIOINDEV], inch[MAXAUDIOINDEV],
outdev[MAXAUDIOOUTDEV], outch[MAXAUDIOOUTDEV];
indev[0] = outdev[0] = DEFAULTAUDIODEV;
inch[0] = inChans;
outch[0] = outChans;
sys_lock();
sys_set_audio_settings(1, indev, 1, inch,
1, outdev, 1, outch, sampleRate, -1, 1, DEFDACBLKSIZE);
sched_set_using_audio(SCHED_AUDIO_CALLBACK);
sys_reopen_audio();
sys_unlock();
return 0;
}
int libpd_process_raw(const float *inBuffer, float *outBuffer) {
size_t n_in = STUFF->st_inchannels * DEFDACBLKSIZE;
size_t n_out = STUFF->st_outchannels * DEFDACBLKSIZE;
t_sample *p;
size_t i;
sys_lock();
sys_microsleep(0);
for (p = STUFF->st_soundin, i = 0; i < n_in; i++) {
*p++ = *inBuffer++;
}
memset(STUFF->st_soundout, 0, n_out * sizeof(t_sample));
SCHED_TICK(pd_this->pd_systime + STUFF->st_time_per_dsp_tick);
for (p = STUFF->st_soundout, i = 0; i < n_out; i++) {
*outBuffer++ = *p++;
}
sys_unlock();
return 0;
}
static const t_sample sample_to_short = SHRT_MAX,
short_to_sample = 1.0 / (t_sample) SHRT_MAX;
#define PROCESS(_x, _y) \
int i, j, k; \
t_sample *p0, *p1; \
sys_lock(); \
sys_microsleep(0); \
for (i = 0; i < ticks; i++) { \
for (j = 0, p0 = STUFF->st_soundin; j < DEFDACBLKSIZE; j++, p0++) { \
for (k = 0, p1 = p0; k < STUFF->st_inchannels; k++, p1 += DEFDACBLKSIZE) \
{ \
*p1 = *inBuffer++ _x; \
} \
} \
memset(STUFF->st_soundout, 0, \
STUFF->st_outchannels*DEFDACBLKSIZE*sizeof(t_sample)); \
SCHED_TICK(pd_this->pd_systime + STUFF->st_time_per_dsp_tick); \
for (j = 0, p0 = STUFF->st_soundout; j < DEFDACBLKSIZE; j++, p0++) { \
for (k = 0, p1 = p0; k < STUFF->st_outchannels; k++, p1 += DEFDACBLKSIZE) \
{ \
*outBuffer++ = *p1 _y; \
} \
} \
} \
sys_unlock(); \
return 0;
int libpd_process_short(const int ticks, const short *inBuffer, short *outBuffer) {
PROCESS(* short_to_sample, * sample_to_short)
}
int libpd_process_float(const int ticks, const float *inBuffer, float *outBuffer) {
PROCESS(,)
}
int libpd_process_double(const int ticks, const double *inBuffer, double *outBuffer) {
PROCESS(,)
}
#define GETARRAY \
t_garray *garray = (t_garray *) pd_findbyclass(gensym(name), garray_class); \
if (!garray) {sys_unlock(); return -1;} \
int libpd_arraysize(const char *name) {
int retval;
sys_lock();
GETARRAY
retval = garray_npoints(garray);
sys_unlock();
return retval;
}
#define MEMCPY(_x, _y) \
GETARRAY \
if (n < 0 || offset < 0 || offset + n > garray_npoints(garray)) return -2; \
t_word *vec = ((t_word *) garray_vec(garray)) + offset; \
int i; \
for (i = 0; i < n; i++) _x = _y;
int libpd_read_array(float *dest, const char *name, int offset, int n) {
sys_lock();
MEMCPY(*dest++, (vec++)->w_float)
sys_unlock();
return 0;
}
int libpd_write_array(const char *name, int offset, const float *src, int n) {
sys_lock();
MEMCPY((vec++)->w_float, *src++)
sys_unlock();
return 0;
}
void libpd_set_float(t_atom *v, float x) {
SETFLOAT(v, x);
}
void libpd_set_symbol(t_atom *v, const char *sym) {
SETSYMBOL(v, gensym(sym));
}
int libpd_list(const char *recv, int n, t_atom *v) {
t_pd *dest;
sys_lock();
dest = get_object(recv);
if (dest == NULL)
{
sys_unlock();
return -1;
}
pd_list(dest, &s_list, n, v);
sys_unlock();
return 0;
}
int libpd_message(const char *recv, const char *msg, int n, t_atom *v) {
t_pd *dest;
sys_lock();
dest = get_object(recv);
if (dest == NULL)
{
sys_unlock();
return -1;
}
pd_typedmess(dest, gensym(msg), n, v);
sys_unlock();
return 0;
}
int libpd_start_message(int max_length) {
if (max_length > argm) {
t_atom *v = realloc(argv, max_length * sizeof(t_atom));
if (v) {
argv = v;
argm = max_length;
} else {
return -1;
}
}
argc = 0;
curr = argv;
return 0;
}
#define ADD_ARG(f) f(curr, x); curr++; argc++;
void libpd_add_float(float x) {
ADD_ARG(SETFLOAT);
}
void libpd_add_symbol(const char *s) {
t_symbol *x;
sys_lock();
x = gensym(s);
sys_unlock();
ADD_ARG(SETSYMBOL);
}
int libpd_finish_list(const char *recv) {
return libpd_list(recv, argc, argv);
}
int libpd_finish_message(const char *recv, const char *msg) {
return libpd_message(recv, msg, argc, argv);
}
void *libpd_bind(const char *sym) {
t_symbol *x;
sys_lock();
x = gensym(sym);
sys_unlock();
return libpdreceive_new(x);
}
void libpd_unbind(void *p) {
sys_lock();
pd_free((t_pd *)p);
sys_unlock();
}
int libpd_is_float(t_atom *a) {
return (a)->a_type == A_FLOAT;
}
int libpd_is_symbol(t_atom *a) {
return (a)->a_type == A_SYMBOL;
}
float libpd_get_float(t_atom *a) {
return (a)->a_w.w_float;
}
char *libpd_get_symbol(t_atom *a) {
return (a)->a_w.w_symbol->s_name;
}
t_atom *libpd_next_atom(t_atom *a) {
return a + 1;
}
void libpd_set_printhook(const t_libpd_printhook hook) {
sys_printhook = (t_printhook) hook;
}
void libpd_set_banghook(const t_libpd_banghook hook) {
libpd_banghook = hook;
}
void libpd_set_floathook(const t_libpd_floathook hook) {
libpd_floathook = hook;
}
void libpd_set_symbolhook(const t_libpd_symbolhook hook) {
libpd_symbolhook = hook;
}
void libpd_set_listhook(const t_libpd_listhook hook) {
libpd_listhook = hook;
}
void libpd_set_messagehook(const t_libpd_messagehook hook) {
libpd_messagehook = hook;
}
int libpd_symbol(const char *recv, const char *sym) {
void *obj;
sys_lock();
obj = get_object(recv);
if (obj == NULL)
{
sys_unlock();
return -1;
}
pd_symbol(obj, gensym(sym));
sys_unlock();
return 0;
}
int libpd_float(const char *recv, float x) {
void *obj;
sys_lock();
obj = get_object(recv);
if (obj == NULL)
{
sys_unlock();
return -1;
}
pd_float(obj, x);
sys_unlock();
return 0;
}
int libpd_bang(const char *recv) {
void *obj;
sys_lock();
obj = get_object(recv);
if (obj == NULL)
{
sys_unlock();
return -1;
}
pd_bang(obj);
sys_unlock();
return 0;
}
int libpd_blocksize(void) {
return DEFDACBLKSIZE;
}
int libpd_exists(const char *sym) {
int retval;
sys_lock();
retval = (get_object(sym) != NULL);
sys_unlock();
return retval;
}
#define CHECK_CHANNEL if (channel < 0) return -1;
#define CHECK_PORT if (port < 0 || port > 0x0fff) return -1;
#define CHECK_RANGE_7BIT(v) if (v < 0 || v > 0x7f) return -1;
#define CHECK_RANGE_8BIT(v) if (v < 0 || v > 0xff) return -1;
#define PORT (channel >> 4)
#define CHANNEL (channel & 0x0f)
int libpd_noteon(int channel, int pitch, int velocity) {
CHECK_CHANNEL
CHECK_RANGE_7BIT(pitch)
CHECK_RANGE_7BIT(velocity)
sys_lock();
inmidi_noteon(PORT, CHANNEL, pitch, velocity);
sys_unlock();
return 0;
}
int libpd_controlchange(int channel, int controller, int value) {
CHECK_CHANNEL
CHECK_RANGE_7BIT(controller)
CHECK_RANGE_7BIT(value)
sys_lock();
inmidi_controlchange(PORT, CHANNEL, controller, value);
sys_unlock();
return 0;
}
int libpd_programchange(int channel, int value) {
CHECK_CHANNEL
CHECK_RANGE_7BIT(value)
sys_lock();
inmidi_programchange(PORT, CHANNEL, value);
sys_unlock();
return 0;
}
int libpd_pitchbend(int channel, int value) {
CHECK_CHANNEL
if (value < -8192 || value > 8191) return -1;
sys_lock();
inmidi_pitchbend(PORT, CHANNEL, value + 8192);
sys_unlock();
// Note: For consistency with Pd, we center the output of [pitchin]
// at 8192.
return 0;
}
int libpd_aftertouch(int channel, int value) {
CHECK_CHANNEL
CHECK_RANGE_7BIT(value)
sys_lock();
inmidi_aftertouch(PORT, CHANNEL, value);
sys_unlock();
return 0;
}
int libpd_polyaftertouch(int channel, int pitch, int value) {
CHECK_CHANNEL
CHECK_RANGE_7BIT(pitch)
CHECK_RANGE_7BIT(value)
sys_lock();
inmidi_polyaftertouch(PORT, CHANNEL, pitch, value);
sys_unlock();
return 0;
}
int libpd_midibyte(int port, int byte) {
CHECK_PORT
CHECK_RANGE_8BIT(byte)
sys_lock();
inmidi_byte(port, byte);
sys_unlock();
return 0;
}
int libpd_sysex(int port, int byte) {
CHECK_PORT
CHECK_RANGE_8BIT(byte)
sys_lock();
inmidi_sysex(port, byte);
sys_unlock();
return 0;
}
int libpd_sysrealtime(int port, int byte) {
CHECK_PORT
CHECK_RANGE_8BIT(byte)
sys_lock();
inmidi_realtimein(port, byte);
sys_unlock();
return 0;
}
void libpd_set_noteonhook(const t_libpd_noteonhook hook) {
libpd_noteonhook = hook;
}
void libpd_set_controlchangehook(const t_libpd_controlchangehook hook) {
libpd_controlchangehook = hook;
}
void libpd_set_programchangehook(const t_libpd_programchangehook hook) {
libpd_programchangehook = hook;
}
void libpd_set_pitchbendhook(const t_libpd_pitchbendhook hook) {
libpd_pitchbendhook = hook;
}
void libpd_set_aftertouchhook(const t_libpd_aftertouchhook hook) {
libpd_aftertouchhook = hook;
}
void libpd_set_polyaftertouchhook(const t_libpd_polyaftertouchhook hook) {
libpd_polyaftertouchhook = hook;
}
void libpd_set_midibytehook(const t_libpd_midibytehook hook) {
libpd_midibytehook = hook;
}
int libpd_start_gui(char *path) {
int retval;
sys_lock();
retval = sys_startgui(path);
sys_unlock();
return retval;
}
void libpd_stop_gui(void) {
sys_lock();
sys_stopgui();
sys_unlock();
}
void libpd_poll_gui(void) {
sys_lock();
sys_pollgui();
sys_unlock();
}
t_pdinstance *libpd_new_instance(void) {
#ifdef PDINSTANCE
return pdinstance_new();
#else
return 0;
#endif
}
void libpd_set_instance(t_pdinstance *x) {
#ifdef PDINSTANCE
pd_setinstance(x);
#endif
}
void libpd_free_instance(t_pdinstance *x) {
#ifdef PDINSTANCE
pdinstance_free(x);
#endif
}
t_pdinstance *libpd_this_instance(void) {
return pd_this;
}
t_pdinstance *libpd_get_instance(int index) {
#ifdef PDINSTANCE
if(index < 0 || index >= pd_ninstances) {return 0;}
return pd_instances[index];
#else
return pd_this;
#endif
}
int libpd_num_instances(void) {
#ifdef PDINSTANCE
return pd_ninstances;
#else
return 1;
#endif
}
void libpd_set_verbose(int verbose) {
if (verbose < 0) verbose = 0;
sys_verbose = verbose;
}
int libpd_get_verbose(void) {
return sys_verbose;
}
// dummy routines needed because we don't use s_file.c
void glob_loadpreferences(t_pd *dummy, t_symbol *s) {}
void glob_savepreferences(t_pd *dummy, t_symbol *s) {}
void glob_forgetpreferences(t_pd *dummy) {}
void sys_loadpreferences(const char *filename, int startingup) {}
int sys_oktoloadfiles(int done) {return 1;}
void sys_savepreferences(const char *filename) {} // used in s_path.c