/* Pd architecture file, written by Albert Graef <Dr.Graef@t-online.de>. This was derived from minimal.cpp included in the Faust distribution. Please note that this is to be compiled as a shared library, which is then loaded dynamically by Pd as an external. */ #include <stdlib.h> #include <string.h> #include <math.h> #include <string> using namespace std; struct Meta { void declare (const char* key, const char* value) {} }; //------------------------------------------------------------------- // Generic min and max using c++ inline //------------------------------------------------------------------- inline int max (unsigned int a, unsigned int b) { return (a>b) ? a : b; } inline int max (int a, int b) { return (a>b) ? a : b; } inline long max (long a, long b) { return (a>b) ? a : b; } inline long max (int a, long b) { return (a>b) ? a : b; } inline long max (long a, int b) { return (a>b) ? a : b; } inline float max (float a, float b) { return (a>b) ? a : b; } inline float max (int a, float b) { return (a>b) ? a : b; } inline float max (float a, int b) { return (a>b) ? a : b; } inline float max (long a, float b) { return (a>b) ? a : b; } inline float max (float a, long b) { return (a>b) ? a : b; } inline double max (double a, double b) { return (a>b) ? a : b; } inline double max (int a, double b) { return (a>b) ? a : b; } inline double max (double a, int b) { return (a>b) ? a : b; } inline double max (long a, double b) { return (a>b) ? a : b; } inline double max (double a, long b) { return (a>b) ? a : b; } inline double max (float a, double b) { return (a>b) ? a : b; } inline double max (double a, float b) { return (a>b) ? a : b; } inline int min (int a, int b) { return (a<b) ? a : b; } inline long min (long a, long b) { return (a<b) ? a : b; } inline long min (int a, long b) { return (a<b) ? a : b; } inline long min (long a, int b) { return (a<b) ? a : b; } inline float min (float a, float b) { return (a<b) ? a : b; } inline float min (int a, float b) { return (a<b) ? a : b; } inline float min (float a, int b) { return (a<b) ? a : b; } inline float min (long a, float b) { return (a<b) ? a : b; } inline float min (float a, long b) { return (a<b) ? a : b; } inline double min (double a, double b) { return (a<b) ? a : b; } inline double min (int a, double b) { return (a<b) ? a : b; } inline double min (double a, int b) { return (a<b) ? a : b; } inline double min (long a, double b) { return (a<b) ? a : b; } inline double min (double a, long b) { return (a<b) ? a : b; } inline double min (float a, double b) { return (a<b) ? a : b; } inline double min (double a, float b) { return (a<b) ? a : b; } // abs is now predefined //template<typename T> T abs (T a) { return (a<T(0)) ? -a : a; } inline int lsr (int x, int n) { return int(((unsigned int)x) >> n); } /****************************************************************************** ******************************************************************************* VECTOR INTRINSICS ******************************************************************************* *******************************************************************************/ //inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((unsigned)(calloc((nmemb*size)+15,sizeof(char)))+15 & 0xfffffff0); } inline void *aligned_calloc(size_t nmemb, size_t size) { return (void*)((size_t)(calloc((nmemb*size)+15,sizeof(char)))+15 & ~15); } <<includeIntrinsic>> /****************************************************************************** ******************************************************************************* ABSTRACT USER INTERFACE ******************************************************************************* *******************************************************************************/ class UI { bool fStopped; public: UI() : fStopped(false) {} virtual ~UI() {} virtual void addButton(const char* label, float* zone) = 0; virtual void addToggleButton(const char* label, float* zone) = 0; virtual void addCheckButton(const char* label, float* zone) = 0; virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step) = 0; virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step) = 0; virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step) = 0; virtual void addNumDisplay(const char* label, float* zone, int precision) = 0; virtual void addTextDisplay(const char* label, float* zone, const char* names[], float min, float max) = 0; virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max) = 0; virtual void addVerticalBargraph(const char* label, float* zone, float min, float max) = 0; virtual void openFrameBox(const char* label) = 0; virtual void openTabBox(const char* label) = 0; virtual void openHorizontalBox(const char* label) = 0; virtual void openVerticalBox(const char* label) = 0; virtual void closeBox() = 0; virtual void run() = 0; void stop() { fStopped = true; } bool stopped() { return fStopped; } virtual void declare(float* zone, const char* key, const char* value) {} }; /*************************************************************************** Pd UI interface ***************************************************************************/ enum ui_elem_type_t { UI_BUTTON, UI_TOGGLE_BUTTON, UI_CHECK_BUTTON, UI_V_SLIDER, UI_H_SLIDER, UI_NUM_ENTRY, UI_V_BARGRAPH, UI_H_BARGRAPH, UI_END_GROUP, UI_V_GROUP, UI_H_GROUP, UI_T_GROUP }; struct ui_elem_t { ui_elem_type_t type; char *label; float *zone; float init, min, max, step; }; class PdUI : public UI { public: int nelems; ui_elem_t *elems; PdUI(); PdUI(const char *s); virtual ~PdUI(); protected: string path; void add_elem(ui_elem_type_t type, const char *label = NULL); void add_elem(ui_elem_type_t type, const char *label, float *zone); void add_elem(ui_elem_type_t type, const char *label, float *zone, float init, float min, float max, float step); void add_elem(ui_elem_type_t type, const char *label, float *zone, float min, float max); public: virtual void addButton(const char* label, float* zone); virtual void addToggleButton(const char* label, float* zone); virtual void addCheckButton(const char* label, float* zone); virtual void addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step); virtual void addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step); virtual void addNumEntry(const char* label, float* zone, float init, float min, float max, float step); virtual void addNumDisplay(const char* label, float* zone, int precision); virtual void addTextDisplay(const char* label, float* zone, const char* names[], float min, float max); virtual void addHorizontalBargraph(const char* label, float* zone, float min, float max); virtual void addVerticalBargraph(const char* label, float* zone, float min, float max); virtual void openFrameBox(const char* label); virtual void openTabBox(const char* label); virtual void openHorizontalBox(const char* label); virtual void openVerticalBox(const char* label); virtual void closeBox(); virtual void run(); }; static string mangle(const char *s) { const char *s0 = s; string t = ""; if (!s) return t; while (*s) if (isalnum(*s)) t += *(s++); else { const char *s1 = s; while (*s && !isalnum(*s)) ++s; if (s1 != s0 && *s) t += "-"; } return t; } static string normpath(string path) { path = string("/")+path; int pos = path.find("//"); while (pos >= 0) { path.erase(pos, 1); pos = path.find("//"); } return path; } static string pathcat(string path, string label) { if (path.empty()) return normpath(label); else if (label.empty()) return normpath(path); else return normpath(path+"/"+label); } PdUI::PdUI() { nelems = 0; elems = NULL; path = ""; } PdUI::PdUI(const char *s) { nelems = 0; elems = NULL; path = s?s:""; } PdUI::~PdUI() { if (elems) { for (int i = 0; i < nelems; i++) if (elems[i].label) free(elems[i].label); free(elems); } } inline void PdUI::add_elem(ui_elem_type_t type, const char *label) { ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t)); if (elems1) elems = elems1; else return; string s = pathcat(path, mangle(label)); elems[nelems].type = type; elems[nelems].label = strdup(s.c_str()); elems[nelems].zone = NULL; elems[nelems].init = 0.0; elems[nelems].min = 0.0; elems[nelems].max = 0.0; elems[nelems].step = 0.0; nelems++; } inline void PdUI::add_elem(ui_elem_type_t type, const char *label, float *zone) { ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t)); if (elems1) elems = elems1; else return; string s = pathcat(path, mangle(label)); elems[nelems].type = type; elems[nelems].label = strdup(s.c_str()); elems[nelems].zone = zone; elems[nelems].init = 0.0; elems[nelems].min = 0.0; elems[nelems].max = 1.0; elems[nelems].step = 1.0; nelems++; } inline void PdUI::add_elem(ui_elem_type_t type, const char *label, float *zone, float init, float min, float max, float step) { ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t)); if (elems1) elems = elems1; else return; string s = pathcat(path, mangle(label)); elems[nelems].type = type; elems[nelems].label = strdup(s.c_str()); elems[nelems].zone = zone; elems[nelems].init = init; elems[nelems].min = min; elems[nelems].max = max; elems[nelems].step = step; nelems++; } inline void PdUI::add_elem(ui_elem_type_t type, const char *label, float *zone, float min, float max) { ui_elem_t *elems1 = (ui_elem_t*)realloc(elems, (nelems+1)*sizeof(ui_elem_t)); if (elems1) elems = elems1; else return; string s = pathcat(path, mangle(label)); elems[nelems].type = type; elems[nelems].label = strdup(s.c_str()); elems[nelems].zone = zone; elems[nelems].init = 0.0; elems[nelems].min = min; elems[nelems].max = max; elems[nelems].step = 0.0; nelems++; } void PdUI::addButton(const char* label, float* zone) { add_elem(UI_BUTTON, label, zone); } void PdUI::addToggleButton(const char* label, float* zone) { add_elem(UI_TOGGLE_BUTTON, label, zone); } void PdUI::addCheckButton(const char* label, float* zone) { add_elem(UI_CHECK_BUTTON, label, zone); } void PdUI::addVerticalSlider(const char* label, float* zone, float init, float min, float max, float step) { add_elem(UI_V_SLIDER, label, zone, init, min, max, step); } void PdUI::addHorizontalSlider(const char* label, float* zone, float init, float min, float max, float step) { add_elem(UI_H_SLIDER, label, zone, init, min, max, step); } void PdUI::addNumEntry(const char* label, float* zone, float init, float min, float max, float step) { add_elem(UI_NUM_ENTRY, label, zone, init, min, max, step); } // FIXME: addNumDisplay and addTextDisplay not implemented in Faust yet? void PdUI::addNumDisplay(const char* label, float* zone, int precision) {} void PdUI::addTextDisplay(const char* label, float* zone, const char* names[], float min, float max) {} void PdUI::addHorizontalBargraph(const char* label, float* zone, float min, float max) { add_elem(UI_H_BARGRAPH, label, zone, min, max); } void PdUI::addVerticalBargraph(const char* label, float* zone, float min, float max) { add_elem(UI_V_BARGRAPH, label, zone, min, max); } void PdUI::openFrameBox(const char* label) { if (!path.empty()) path += "/"; path += mangle(label); } void PdUI::openTabBox(const char* label) { if (!path.empty()) path += "/"; path += mangle(label); } void PdUI::openHorizontalBox(const char* label) { if (!path.empty()) path += "/"; path += mangle(label); } void PdUI::openVerticalBox(const char* label) { if (!path.empty()) path += "/"; path += mangle(label); } void PdUI::closeBox() { int pos = path.rfind("/"); if (pos < 0) pos = 0; path.erase(pos); } void PdUI::run() {} /****************************************************************************** ******************************************************************************* FAUST DSP ******************************************************************************* *******************************************************************************/ //---------------------------------------------------------------- // abstract definition of a signal processor //---------------------------------------------------------------- class dsp { protected: int fSamplingFreq; public: dsp() {} virtual ~dsp() {} virtual int getNumInputs() = 0; virtual int getNumOutputs() = 0; virtual void buildUserInterface(UI* interface) = 0; virtual void init(int samplingRate) = 0; virtual void compute(int len, float** inputs, float** outputs) = 0; }; //---------------------------------------------------------------------------- // FAUST generated signal processor //---------------------------------------------------------------------------- <<includeclass>> #include <stdio.h> #include <string.h> #include "m_pd.h" #define faust_setup(name) xfaust_setup(name) #define xfaust_setup(name) name ## _tilde_setup(void) #define sym(name) xsym(name) #define xsym(name) #name // time for "active" toggle xfades in secs #define XFADE_TIME 0.1f static t_class *faust_class; struct t_faust { t_object x_obj; mydsp *dsp; PdUI *ui; string *label; int active, xfade, n_xfade, rate, n_in, n_out; t_sample **inputs, **outputs, **buf; t_outlet *out; t_sample f; }; static t_symbol *s_button, *s_checkbox, *s_vslider, *s_hslider, *s_nentry, *s_vbargraph, *s_hbargraph; static inline void zero_samples(int k, int n, t_sample **out) { for (int i = 0; i < k; i++) #ifdef __STDC_IEC_559__ /* IEC 559 a.k.a. IEEE 754 floats can be initialized faster like this */ memset(out[i], 0, n*sizeof(t_sample)); #else for (int j = 0; j < n; j++) out[i][j] = 0.0f; #endif } static inline void copy_samples(int k, int n, t_sample **out, t_sample **in) { for (int i = 0; i < k; i++) memcpy(out[i], in[i], n*sizeof(t_sample)); } static t_int *faust_perform(t_int *w) { t_faust *x = (t_faust *)(w[1]); int n = (int)(w[2]); if (!x->dsp || !x->buf) return (w+3); if (x->xfade > 0) { float d = 1.0f/x->n_xfade, f = (x->xfade--)*d; d = d/n; x->dsp->compute(n, x->inputs, x->buf); if (x->active) if (x->n_in == x->n_out) /* xfade inputs -> buf */ for (int j = 0; j < n; j++, f -= d) for (int i = 0; i < x->n_out; i++) x->outputs[i][j] = f*x->inputs[i][j]+(1.0f-f)*x->buf[i][j]; else /* xfade 0 -> buf */ for (int j = 0; j < n; j++, f -= d) for (int i = 0; i < x->n_out; i++) x->outputs[i][j] = (1.0f-f)*x->buf[i][j]; else if (x->n_in == x->n_out) /* xfade buf -> inputs */ for (int j = 0; j < n; j++, f -= d) for (int i = 0; i < x->n_out; i++) x->outputs[i][j] = f*x->buf[i][j]+(1.0f-f)*x->inputs[i][j]; else /* xfade buf -> 0 */ for (int j = 0; j < n; j++, f -= d) for (int i = 0; i < x->n_out; i++) x->outputs[i][j] = f*x->buf[i][j]; } else if (x->active) { x->dsp->compute(n, x->inputs, x->buf); copy_samples(x->n_out, n, x->outputs, x->buf); } else if (x->n_in == x->n_out) { copy_samples(x->n_out, n, x->buf, x->inputs); copy_samples(x->n_out, n, x->outputs, x->buf); } else zero_samples(x->n_out, n, x->outputs); return (w+3); } static void faust_dsp(t_faust *x, t_signal **sp) { int n = sp[0]->s_n, sr = (int)sp[0]->s_sr; if (x->rate <= 0) { /* default sample rate is whatever Pd tells us */ PdUI *ui = x->ui; float *z = NULL; if (ui->nelems > 0 && (z = (float*)malloc(ui->nelems*sizeof(float)))) { /* save the current control values */ for (int i = 0; i < ui->nelems; i++) if (ui->elems[i].zone) z[i] = *ui->elems[i].zone; } /* set the proper sample rate; this requires reinitializing the dsp */ x->rate = sr; x->dsp->init(sr); if (z) { /* restore previous control values */ for (int i = 0; i < ui->nelems; i++) if (ui->elems[i].zone) *ui->elems[i].zone = z[i]; free(z); } } if (n > 0) x->n_xfade = (int)(x->rate*XFADE_TIME/n); dsp_add(faust_perform, 2, x, n); for (int i = 0; i < x->n_in; i++) x->inputs[i] = sp[i+1]->s_vec; for (int i = 0; i < x->n_out; i++) x->outputs[i] = sp[x->n_in+i+1]->s_vec; if (x->buf != NULL) for (int i = 0; i < x->n_out; i++) { x->buf[i] = (t_sample*)malloc(n*sizeof(t_sample)); if (x->buf[i] == NULL) { for (int j = 0; j < i; j++) free(x->buf[j]); free(x->buf); x->buf = NULL; break; } } } static int pathcmp(const char *s, const char *t) { int n = strlen(s), m = strlen(t); if (n == 0 || m == 0) return 0; else if (t[0] == '/') return strcmp(s, t); else if (n <= m || s[n-m-1] != '/') return strcmp(s+1, t); else return strcmp(s+n-m, t); } static void faust_any(t_faust *x, t_symbol *s, int argc, t_atom *argv) { if (!x->dsp) return; PdUI *ui = x->ui; if (s == &s_bang) { for (int i = 0; i < ui->nelems; i++) if (ui->elems[i].label && ui->elems[i].zone) { t_atom args[6]; t_symbol *_s; switch (ui->elems[i].type) { case UI_BUTTON: _s = s_button; break; case UI_TOGGLE_BUTTON: case UI_CHECK_BUTTON: _s = s_checkbox; break; case UI_V_SLIDER: _s = s_vslider; break; case UI_H_SLIDER: _s = s_hslider; break; case UI_NUM_ENTRY: _s = s_nentry; break; case UI_V_BARGRAPH: _s = s_vbargraph; break; case UI_H_BARGRAPH: _s = s_hbargraph; break; default: continue; } SETSYMBOL(&args[0], gensym(ui->elems[i].label)); SETFLOAT(&args[1], *ui->elems[i].zone); SETFLOAT(&args[2], ui->elems[i].init); SETFLOAT(&args[3], ui->elems[i].min); SETFLOAT(&args[4], ui->elems[i].max); SETFLOAT(&args[5], ui->elems[i].step); outlet_anything(x->out, _s, 6, args); } } else { const char *label = s->s_name; int count = 0; for (int i = 0; i < ui->nelems; i++) if (ui->elems[i].label && pathcmp(ui->elems[i].label, label) == 0) { if (argc == 0) { if (ui->elems[i].zone) { t_atom arg; SETFLOAT(&arg, *ui->elems[i].zone); outlet_anything(x->out, gensym(ui->elems[i].label), 1, &arg); } ++count; } else if (argc == 1 && (argv[0].a_type == A_FLOAT || argv[0].a_type == A_DEFFLOAT) && ui->elems[i].zone) { float f = atom_getfloat(argv); *ui->elems[i].zone = f; ++count; } else pd_error(x, "[faust] %s: bad control argument: %s", x->label->c_str(), label); } if (count == 0 && strcmp(label, "active") == 0) if (argc == 0) { t_atom arg; SETFLOAT(&arg, (float)x->active); outlet_anything(x->out, gensym((char*)"active"), 1, &arg); } else if (argc == 1 && (argv[0].a_type == A_FLOAT || argv[0].a_type == A_DEFFLOAT)) { float f = atom_getfloat(argv); x->active = (int)f; x->xfade = x->n_xfade; } } } static void faust_free(t_faust *x) { if (x->label) delete x->label; if (x->dsp) delete x->dsp; if (x->ui) delete x->ui; if (x->inputs) free(x->inputs); if (x->outputs) free(x->outputs); if (x->buf) { for (int i = 0; i < x->n_out; i++) if (x->buf[i]) free(x->buf[i]); free(x->buf); } } static void *faust_new(t_symbol *s, int argc, t_atom *argv) { t_faust *x = (t_faust*)pd_new(faust_class); int sr = -1; t_symbol *id = NULL; x->active = 1; for (int i = 0; i < argc; i++) if (argv[i].a_type == A_FLOAT || argv[i].a_type == A_DEFFLOAT) sr = (int)argv[i].a_w.w_float; else if (argv[i].a_type == A_SYMBOL || argv[i].a_type == A_DEFSYMBOL) id = argv[i].a_w.w_symbol; x->rate = sr; if (sr <= 0) sr = 44100; x->xfade = 0; x->n_xfade = (int)(sr*XFADE_TIME/64); x->inputs = x->outputs = x->buf = NULL; x->label = new string(sym(mydsp) "~"); x->dsp = new mydsp(); x->ui = new PdUI(id?id->s_name:NULL); if (!x->dsp || !x->ui || !x->label) goto error; if (id) { *x->label += " "; *x->label += id->s_name; } x->n_in = x->dsp->getNumInputs(); x->n_out = x->dsp->getNumOutputs(); if (x->n_in > 0) x->inputs = (t_sample**)malloc(x->n_in*sizeof(t_sample*)); if (x->n_out > 0) { x->outputs = (t_sample**)malloc(x->n_out*sizeof(t_sample*)); x->buf = (t_sample**)malloc(x->n_out*sizeof(t_sample*)); } if (x->n_in > 0 && x->inputs == NULL || x->n_out > 0 && (x->outputs == NULL || x->buf == NULL)) goto error; for (int i = 0; i < x->n_out; i++) x->buf[i] = NULL; x->dsp->init(sr); x->dsp->buildUserInterface(x->ui); for (int i = 0; i < x->n_in; i++) inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); x->out = outlet_new(&x->x_obj, 0); for (int i = 0; i < x->n_out; i++) outlet_new(&x->x_obj, &s_signal); return (void *)x; error: faust_free(x); x->dsp = NULL; x->ui = NULL; x->inputs = x->outputs = x->buf = NULL; return (void *)x; } extern "C" void faust_setup(mydsp) { t_symbol *s = gensym(sym(mydsp) "~"); faust_class = class_new(s, (t_newmethod)faust_new, (t_method)faust_free, sizeof(t_faust), CLASS_DEFAULT, A_GIMME, A_NULL); class_addmethod(faust_class, (t_method)faust_dsp, gensym((char*)"dsp"), A_NULL); class_addanything(faust_class, faust_any); class_addmethod(faust_class, nullfn, &s_signal, A_NULL); s_button = gensym((char*)"button"); s_checkbox = gensym((char*)"checkbox"); s_vslider = gensym((char*)"vslider"); s_hslider = gensym((char*)"hslider"); s_nentry = gensym((char*)"nentry"); s_vbargraph = gensym((char*)"vbargraph"); s_hbargraph = gensym((char*)"hbargrap"); /* give some indication that we're loaded and ready to go */ mydsp dsp = mydsp(); post("[faust] %s: %d inputs, %d outputs", sym(mydsp) "~", dsp.getNumInputs(), dsp.getNumOutputs()); }