/* * Copyright (c) 2010 Peter Brinkmann (peter.brinkmann@gmail.com) * Copyright (c) 2012-2021 libpd team * * 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 #include #include #include #include #ifndef LIBPD_NO_NUMERIC # include #endif #include "z_libpd.h" #include "x_libpdreceive.h" #include "z_hooks.h" #include "m_imp.h" #include "g_all_guis.h" #include "libpd_rs_bundled/header/libpd_rs_bundled.h" // pd_init() doesn't call socket_init() which is needed on windows for // libpd_start_gui() to work #if (defined(_WIN32) || defined(_WIN64)) && PD_MINOR_VERSION > 50 # include "s_net.h" # define SOCKET_INIT socket_init(); #else # define SOCKET_INIT #endif #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); void sys_setchsr(int chin, int chout, int sr); int sys_startgui(const char *libdir); void sys_stopgui(void); int sys_pollgui(void); // (optional) setup functions for built-in "extra" externals #ifdef LIBPD_EXTRA void bob_tilde_setup(void); void bonk_tilde_setup(void); void choice_setup(void); void fiddle_tilde_setup(void); void loop_tilde_setup(void); void lrshift_tilde_setup(void); void pd_tilde_setup(void); void pique_setup(void); void sigmund_tilde_setup(void); void stdout_setup(void); #endif static PERTHREAD t_atom *s_argv = NULL; static PERTHREAD t_atom *s_curr = NULL; static PERTHREAD int s_argm = 0; static PERTHREAD int s_argc = 0; static void *get_object(const char *s) { t_pd *x = gensym(s)->s_thing; return x; } // note: could we use pd_this instead? static int s_initialized = 0; // this is called instead of sys_main() to start things int libpd_init(void) { if (s_initialized) return -1; // only allow init once (for now) s_initialized = 1; #ifndef __EMSCRIPTEN__ signal(SIGFPE, SIG_IGN); #endif libpd_start_message(32); // allocate array for message assembly 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; STUFF->st_impdata = &libpd_mainimp; sys_init_fdpoll(); libpdreceive_setup(); STUFF->st_searchpath = NULL; sys_libdir = gensym(""); SOCKET_INIT post("pd %d.%d.%d%s", PD_MAJOR_VERSION, PD_MINOR_VERSION, PD_BUGFIX_VERSION, PD_TEST_VERSION); #ifdef LIBPD_EXTRA bob_tilde_setup(); bonk_tilde_setup(); choice_setup(); fiddle_tilde_setup(); loop_tilde_setup(); lrshift_tilde_setup(); pd_tilde_setup(); pique_setup(); sigmund_tilde_setup(); stdout_setup(); #endif #ifdef LIBPD_RS_EXTRA libpd_rs_bundled_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 *path) { sys_lock(); STUFF->st_searchpath = namelist_append(STUFF->st_searchpath, path, 0); sys_unlock(); } void *libpd_openfile(const char *name, const char *dir) { void *retval; sys_lock(); pd_globallock(); retval = (void *)glob_evalfile(NULL, gensym(name), gensym(dir)); pd_globalunlock(); sys_unlock(); return retval; } void libpd_closefile(void *p) { sys_lock(); pd_free((t_pd *)p); sys_unlock(); } int libpd_getdollarzero(void *p) { sys_lock(); pd_pushsym((t_pd *)p); int dzero = canvas_getdollarzero(); pd_popsym((t_pd *)p); sys_unlock(); return dzero; } int libpd_blocksize(void) { return DEFDACBLKSIZE; } int libpd_init_audio(int inChannels, int outChannels, int sampleRate) { sys_lock(); sched_set_using_audio(SCHED_AUDIO_CALLBACK); sys_setchsr(inChannels, outChannels, sampleRate); 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_pollgui(); \ 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 PROCESS_RAW(_x, _y) \ 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_pollgui(); \ for (p = STUFF->st_soundin, i = 0; i < n_in; i++) { \ *p++ = *inBuffer++ _x; \ } \ 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++ _y; \ } \ sys_unlock(); \ return 0; int libpd_process_raw(const float *inBuffer, float *outBuffer) { PROCESS_RAW(,) } int libpd_process_raw_short(const short *inBuffer, short *outBuffer) { PROCESS_RAW(* short_to_sample, * sample_to_short) } int libpd_process_raw_double(const double *inBuffer, double *outBuffer) { PROCESS_RAW(,) } #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; } int libpd_resize_array(const char *name, long size) { sys_lock(); GETARRAY garray_resize_long(garray, size); sys_unlock(); return 0; } #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; } int libpd_read_array_double(double *dest, const char *name, int offset, int n) { sys_lock(); MEMCPY(*dest++, (vec++)->w_float) sys_unlock(); return 0; } int libpd_write_array_double(const char *name, int offset, const double *src, int n) { sys_lock(); MEMCPY((vec++)->w_float, *src++) 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; } static int libpd_dofloat(const char *recv, t_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_float(const char *recv, float x) { return libpd_dofloat(recv, x); } int libpd_double(const char *recv, double x) { return libpd_dofloat(recv, x); } int libpd_symbol(const char *recv, const char *symbol) { void *obj; sys_lock(); obj = get_object(recv); if (obj == NULL) { sys_unlock(); return -1; } pd_symbol(obj, gensym(symbol)); sys_unlock(); return 0; } int libpd_start_message(int maxlen) { if (maxlen > s_argm) { t_atom *v = realloc(s_argv, maxlen * sizeof(t_atom)); if (v) { s_argv = v; s_argm = maxlen; } else { return -1; } } s_argc = 0; s_curr = s_argv; return 0; } #define ADD_ARG(f) f(s_curr, x); s_curr++; s_argc++; void libpd_add_float(float x) { ADD_ARG(SETFLOAT); } void libpd_add_double(double x) { ADD_ARG(SETFLOAT); } void libpd_add_symbol(const char *symbol) { t_symbol *x; sys_lock(); x = gensym(symbol); sys_unlock(); ADD_ARG(SETSYMBOL); } int libpd_finish_list(const char *recv) { return libpd_list(recv, s_argc, s_argv); } int libpd_finish_message(const char *recv, const char *msg) { return libpd_message(recv, msg, s_argc, s_argv); } void libpd_set_float(t_atom *a, float x) { SETFLOAT(a, x); } void libpd_set_double(t_atom *v, double x) { SETFLOAT(v, x); } void libpd_set_symbol(t_atom *a, const char *symbol) { SETSYMBOL(a, gensym(symbol)); } int libpd_list(const char *recv, int argc, t_atom *argv) { t_pd *obj; sys_lock(); obj = get_object(recv); if (obj == NULL) { sys_unlock(); return -1; } pd_list(obj, &s_list, argc, argv); sys_unlock(); return 0; } int libpd_message(const char *recv, const char *msg, int argc, t_atom *argv) { t_pd *obj; sys_lock(); obj = get_object(recv); if (obj == NULL) { sys_unlock(); return -1; } pd_typedmess(obj, gensym(msg), argc, argv); sys_unlock(); return 0; } void *libpd_bind(const char *recv) { t_symbol *x; sys_lock(); x = gensym(recv); sys_unlock(); return libpdreceive_new(x); } void libpd_unbind(void *p) { sys_lock(); pd_free((t_pd *)p); sys_unlock(); } int libpd_exists(const char *recv) { int retval; sys_lock(); retval = (get_object(recv) != NULL); sys_unlock(); return retval; } // when setting hooks, use mainimp if pd is not yet inited #define IMP (s_initialized ? LIBPDSTUFF : &libpd_mainimp) void libpd_set_printhook(const t_libpd_printhook hook) { if (!s_initialized) // set default hook sys_printhook = (t_printhook)hook; else // set instance hook STUFF->st_printhook = (t_printhook)hook; } void libpd_set_banghook(const t_libpd_banghook hook) { IMP->i_hooks.h_banghook = hook; } void libpd_set_floathook(const t_libpd_floathook hook) { IMP->i_hooks.h_floathook = hook; IMP->i_hooks.h_doublehook = NULL; } void libpd_set_doublehook(const t_libpd_doublehook hook) { IMP->i_hooks.h_floathook = NULL; IMP->i_hooks.h_doublehook = hook; } void libpd_set_symbolhook(const t_libpd_symbolhook hook) { IMP->i_hooks.h_symbolhook = hook; } void libpd_set_listhook(const t_libpd_listhook hook) { IMP->i_hooks.h_listhook = hook; } void libpd_set_messagehook(const t_libpd_messagehook hook) { IMP->i_hooks.h_messagehook = hook; } 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; } double libpd_get_double(t_atom *a) { return (a)->a_w.w_float; } const 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; } #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; } // note: for consistency with Pd, we center the output of [pitchin] at 8192 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(); 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) { IMP->i_hooks.h_noteonhook = hook; } void libpd_set_controlchangehook(const t_libpd_controlchangehook hook) { IMP->i_hooks.h_controlchangehook = hook; } void libpd_set_programchangehook(const t_libpd_programchangehook hook) { IMP->i_hooks.h_programchangehook = hook; } void libpd_set_pitchbendhook(const t_libpd_pitchbendhook hook) { IMP->i_hooks.h_pitchbendhook = hook; } void libpd_set_aftertouchhook(const t_libpd_aftertouchhook hook) { IMP->i_hooks.h_aftertouchhook = hook; } void libpd_set_polyaftertouchhook(const t_libpd_polyaftertouchhook hook) { IMP->i_hooks.h_polyaftertouchhook = hook; } void libpd_set_midibytehook(const t_libpd_midibytehook hook) { IMP->i_hooks.h_midibytehook = hook; } int libpd_start_gui(const 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(); } int libpd_poll_gui(void) { int retval; sys_lock(); retval = sys_pollgui(); sys_unlock(); return (retval); } t_pdinstance *libpd_new_instance(void) { #ifdef PDINSTANCE t_pdinstance *pd = pdinstance_new(); pd->pd_stuff->st_impdata = libpdimp_new(); return pd; #else return NULL; #endif } void libpd_set_instance(t_pdinstance *pd) { #ifdef PDINSTANCE pd_setinstance(pd); #endif } void libpd_free_instance(t_pdinstance *pd) { #ifdef PDINSTANCE if (pd == &pd_maininstance) return; libpdimp_free(pd->pd_stuff->st_impdata); pdinstance_free(pd); #endif } t_pdinstance *libpd_this_instance(void) { return pd_this; } t_pdinstance *libpd_main_instance(void) { return &pd_maininstance; } int libpd_num_instances(void) { #ifdef PDINSTANCE return pd_ninstances; #else return 1; #endif } void libpd_set_instancedata(void *data, t_libpd_freehook freehook) { LIBPDSTUFF->i_data = data; LIBPDSTUFF->i_data_freehook = freehook; } void* libpd_get_instancedata() { return LIBPDSTUFF->i_data; } 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