diff --git a/CMakeLists.txt b/CMakeLists.txt index d3954343..6805f67d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ option_ex (SFIZZ_LV2_UI "Enable LV2 plug-in user interface" ON) option_ex (SFIZZ_VST "Enable VST plug-in build" ON) option_ex (SFIZZ_AU "Enable AU plug-in build" APPLE) option_ex (SFIZZ_VST2 "Enable VST2 plug-in build (unsupported)" OFF) +option_ex (SFIZZ_PUREDATA "Enable Puredata plug-in build" OFF) option_ex (SFIZZ_BENCHMARKS "Enable benchmarks build" OFF) option_ex (SFIZZ_TESTS "Enable tests build" OFF) option_ex (SFIZZ_DEMOS "Enable feature demos build" OFF) diff --git a/cmake/PuredataConfig.cmake b/cmake/PuredataConfig.cmake new file mode 100644 index 00000000..eb794d1d --- /dev/null +++ b/cmake/PuredataConfig.cmake @@ -0,0 +1,16 @@ +find_path(PUREDATA_INCLUDE_DIR "m_pd.h" PATH_SUFFIXES "pd") + +if(PUREDATA_INCLUDE_DIR) + message(STATUS "Puredata headers: ${PUREDATA_INCLUDE_DIR}") +else() + message(STATUS "Puredata headers not found, using our own") + set(PUREDATA_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/plugins/puredata/external/pd/include") +endif() + +if(WIN32) + set(PUREDATA_SUFFIX ".dll") +elseif(APPLE) + set(PUREDATA_SUFFIX ".pd_darwin") +else() + set(PUREDATA_SUFFIX ".pd_linux") +endif() diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 3201b27d..77f13374 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -58,3 +58,7 @@ endif() if(SFIZZ_VST OR SFIZZ_AU OR SFIZZ_VST2) add_subdirectory(vst) endif() + +if(SFIZZ_PUREDATA) + add_subdirectory(puredata) +endif() diff --git a/plugins/puredata/CMakeLists.txt b/plugins/puredata/CMakeLists.txt new file mode 100644 index 00000000..e6495246 --- /dev/null +++ b/plugins/puredata/CMakeLists.txt @@ -0,0 +1,38 @@ +# Puredata plugin specific settings +include(PuredataConfig) + +set(PUREDATA_BINARY_DIR "${PROJECT_BINARY_DIR}/pd/sfizz") + +set(PUREDATA_RESOURCES + "sfizz~-help.pd" + "example.sfz") + +function(copy_puredata_resources TARGET SOURCE_DIR DESTINATION_DIR) + set(_deps) + foreach(res ${PUREDATA_RESOURCES}) + get_filename_component(_dir "${res}" DIRECTORY) + file(MAKE_DIRECTORY "${DESTINATION_DIR}/${_dir}") + add_custom_command( + OUTPUT "${DESTINATION_DIR}/${res}" + COMMAND "${CMAKE_COMMAND}" "-E" "copy" + "${SOURCE_DIR}/${res}" "${DESTINATION_DIR}/${res}" + DEPENDS "${SOURCE_DIR}/${res}") + list(APPEND _deps "${DESTINATION_DIR}/${res}") + endforeach() + add_custom_target("${TARGET}_puredata_resources" DEPENDS ${_deps}) + add_dependencies("${TARGET}" "${TARGET}_puredata_resources") +endfunction() + +add_library(sfizz_puredata MODULE sfizz_puredata.c) +target_include_directories(sfizz_puredata PRIVATE "${PUREDATA_INCLUDE_DIR}") +target_link_libraries(sfizz_puredata PRIVATE sfizz::sfizz) + +set_target_properties(sfizz_puredata PROPERTIES + PREFIX "" + SUFFIX "${PUREDATA_SUFFIX}" + OUTPUT_NAME "sfizz" + LIBRARY_OUTPUT_DIRECTORY "${PUREDATA_BINARY_DIR}/$<0:>") + +copy_puredata_resources(sfizz_puredata + "${CMAKE_CURRENT_SOURCE_DIR}" + "${PUREDATA_BINARY_DIR}") diff --git a/plugins/puredata/example.sfz b/plugins/puredata/example.sfz new file mode 100644 index 00000000..1ab72699 --- /dev/null +++ b/plugins/puredata/example.sfz @@ -0,0 +1,22 @@ + +ampeg_attack=0.25 +ampeg_decay=5 +ampeg_sustain=50 +ampeg_release=2 +fil_type=lpf_4p +resonance=3 +cutoff=2000 +oscillator_phase=-1 +cutoff_oncc300=3600 +cutoff_curvecc300=1 + + +sample=*saw +oscillator_multi=3 +oscillator_detune=10 +lfo1_freq=5 +lfo1_pitch=30 + + +sample=*triangle +transpose=-12 diff --git a/plugins/puredata/external/pd/LICENSE.txt b/plugins/puredata/external/pd/LICENSE.txt new file mode 100644 index 00000000..a56a51eb --- /dev/null +++ b/plugins/puredata/external/pd/LICENSE.txt @@ -0,0 +1,30 @@ +This software is copyrighted by Miller Puckette and others. The following +terms (the "Standard Improved BSD License") apply to all files associated with +the software unless explicitly disclaimed in individual files: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. +3. The name of the author may not be used to endorse or promote + products derived from this software without specific prior + written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR +BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/puredata/external/pd/include/m_pd.h b/plugins/puredata/external/pd/include/m_pd.h new file mode 100644 index 00000000..1bcbfff4 --- /dev/null +++ b/plugins/puredata/external/pd/include/m_pd.h @@ -0,0 +1,911 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#ifndef __m_pd_h_ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +extern "C" { +#endif + +#define PD_MAJOR_VERSION 0 +#define PD_MINOR_VERSION 51 +#define PD_BUGFIX_VERSION 4 +#define PD_TEST_VERSION "" +extern int pd_compatibilitylevel; /* e.g., 43 for pd 0.43 compatibility */ + +/* old name for "MSW" flag -- we have to take it for the sake of many old +"nmakefiles" for externs, which will define NT and not MSW */ +#if defined(NT) && !defined(MSW) +#define MSW +#endif + +/* These pragmas are only used for MSVC, not MinGW or Cygwin */ +#ifdef _MSC_VER +/* #pragma warning( disable : 4091 ) */ +#pragma warning( disable : 4305 ) /* uncast const double to float */ +#pragma warning( disable : 4244 ) /* uncast float/int conversion etc. */ +#pragma warning( disable : 4101 ) /* unused automatic variables */ +#endif /* _MSC_VER */ + + /* the external storage class is "extern" in UNIX; in MSW it's ugly. */ +#ifdef _WIN32 +#ifdef PD_INTERNAL +#define EXTERN __declspec(dllexport) extern +#else +#define EXTERN __declspec(dllimport) extern +#endif /* PD_INTERNAL */ +#else +#define EXTERN extern +#endif /* _WIN32 */ + + /* On most c compilers, you can just say "struct foo;" to declare a + structure whose elements are defined elsewhere. On MSVC, when compiling + C (but not C++) code, you have to say "extern struct foo;". So we make + a stupid macro: */ +#if defined(_MSC_VER) && !defined(_LANGUAGE_C_PLUS_PLUS) \ + && !defined(__cplusplus) +#define EXTERN_STRUCT extern struct +#else +#define EXTERN_STRUCT struct +#endif + +/* Define some attributes, specific to the compiler */ +#if defined(__GNUC__) +#define ATTRIBUTE_FORMAT_PRINTF(a, b) __attribute__ ((format (printf, a, b))) +#else +#define ATTRIBUTE_FORMAT_PRINTF(a, b) +#endif + +#if !defined(_SIZE_T) && !defined(_SIZE_T_) +#include /* just for size_t -- how lame! */ +#endif + +/* Microsoft Visual Studio is not C99, but since VS2015 has included most C99 headers: + https://docs.microsoft.com/en-us/previous-versions/hh409293(v=vs.140)#c-runtime-library + These definitions recreate stdint.h types, but only in pre-2015 Visual Studio: */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef signed __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#else +# include +#endif + +/* for FILE, needed by sys_fopen() and sys_fclose() only */ +#include + +#define MAXPDSTRING 1000 /* use this for anything you want */ +#define MAXPDARG 5 /* max number of args we can typecheck today */ + +/* signed and unsigned integer types the size of a pointer: */ +#if !defined(PD_LONGINTTYPE) +#if defined(_WIN32) && defined(_WIN64) +#define PD_LONGINTTYPE long long +#else +#define PD_LONGINTTYPE long +#endif +#endif + +#if !defined(PD_FLOATSIZE) + /* normally, our floats (t_float, t_sample,...) are 32bit */ +# define PD_FLOATSIZE 32 +#endif + +#if PD_FLOATSIZE == 32 +# define PD_FLOATTYPE float +/* an unsigned int of the same size as FLOATTYPE: */ +# define PD_FLOATUINTTYPE uint32_t + +#elif PD_FLOATSIZE == 64 +# define PD_FLOATTYPE double +# define PD_FLOATUINTTYPE uint64_t +#else +# error invalid FLOATSIZE: must be 32 or 64 +#endif + +typedef PD_LONGINTTYPE t_int; /* pointer-size integer */ +typedef PD_FLOATTYPE t_float; /* a float type at most the same size */ +typedef PD_FLOATTYPE t_floatarg; /* float type for function calls */ + +typedef struct _symbol +{ + const char *s_name; + struct _class **s_thing; + struct _symbol *s_next; +} t_symbol; + +EXTERN_STRUCT _array; +#define t_array struct _array /* g_canvas.h */ + +/* pointers to glist and array elements go through a "stub" which sticks +around after the glist or array is freed. The stub itself is deleted when +both the glist/array is gone and the refcount is zero, ensuring that no +gpointers are pointing here. */ + +#define GP_NONE 0 /* the stub points nowhere (has been cut off) */ +#define GP_GLIST 1 /* the stub points to a glist element */ +#define GP_ARRAY 2 /* ... or array */ + +typedef struct _gstub +{ + union + { + struct _glist *gs_glist; /* glist we're in */ + struct _array *gs_array; /* array we're in */ + } gs_un; + int gs_which; /* GP_GLIST/GP_ARRAY */ + int gs_refcount; /* number of gpointers pointing here */ +} t_gstub; + +typedef struct _gpointer /* pointer to a gobj in a glist */ +{ + union + { + struct _scalar *gp_scalar; /* scalar we're in (if glist) */ + union word *gp_w; /* raw data (if array) */ + } gp_un; + int gp_valid; /* number which must match gpointee */ + t_gstub *gp_stub; /* stub which points to glist/array */ +} t_gpointer; + +typedef union word +{ + t_float w_float; + t_symbol *w_symbol; + t_gpointer *w_gpointer; + t_array *w_array; + struct _binbuf *w_binbuf; + int w_index; +} t_word; + +typedef enum +{ + A_NULL, + A_FLOAT, + A_SYMBOL, + A_POINTER, + A_SEMI, + A_COMMA, + A_DEFFLOAT, + A_DEFSYM, + A_DOLLAR, + A_DOLLSYM, + A_GIMME, + A_CANT +} t_atomtype; + +#define A_DEFSYMBOL A_DEFSYM /* better name for this */ + +typedef struct _atom +{ + t_atomtype a_type; + union word a_w; +} t_atom; + +EXTERN_STRUCT _class; +#define t_class struct _class + +EXTERN_STRUCT _outlet; +#define t_outlet struct _outlet + +EXTERN_STRUCT _inlet; +#define t_inlet struct _inlet + +EXTERN_STRUCT _binbuf; +#define t_binbuf struct _binbuf + +EXTERN_STRUCT _clock; +#define t_clock struct _clock + +EXTERN_STRUCT _outconnect; +#define t_outconnect struct _outconnect + +EXTERN_STRUCT _glist; +#define t_glist struct _glist +#define t_canvas struct _glist /* LATER lose this */ + +EXTERN_STRUCT _template; + +typedef t_class *t_pd; /* pure datum: nothing but a class pointer */ + +typedef struct _gobj /* a graphical object */ +{ + t_pd g_pd; /* pure datum header (class) */ + struct _gobj *g_next; /* next in list */ +} t_gobj; + +typedef struct _scalar /* a graphical object holding data */ +{ + t_gobj sc_gobj; /* header for graphical object */ + t_symbol *sc_template; /* template name (LATER replace with pointer) */ + t_word sc_vec[1]; /* indeterminate-length array of words */ +} t_scalar; + +typedef struct _text /* patchable object - graphical, with text */ +{ + t_gobj te_g; /* header for graphical object */ + t_binbuf *te_binbuf; /* holder for the text */ + t_outlet *te_outlet; /* linked list of outlets */ + t_inlet *te_inlet; /* linked list of inlets */ + short te_xpix; /* x&y location (within the toplevel) */ + short te_ypix; + short te_width; /* requested width in chars, 0 if auto */ + unsigned int te_type:2; /* from defs below */ +} t_text; + +#define T_TEXT 0 /* just a textual comment */ +#define T_OBJECT 1 /* a MAX style patchable object */ +#define T_MESSAGE 2 /* a MAX type message */ +#define T_ATOM 3 /* a cell to display a number or symbol */ + +#define te_pd te_g.g_pd + + /* t_object is synonym for t_text (LATER unify them) */ + +typedef struct _text t_object; + +#define ob_outlet te_outlet +#define ob_inlet te_inlet +#define ob_binbuf te_binbuf +#define ob_pd te_g.g_pd +#define ob_g te_g + +typedef void (*t_method)(void); +typedef void *(*t_newmethod)(void); + +/* in ARM 64 a varargs prototype generates a different function call sequence +from a fixed one, so in that special case we make a more restrictive +definition for t_gotfn. This will break some code in the "chaos" package +in Pd extended. (that code will run incorrectly anyhow so why not catch it +at compile time anyhow.) */ +#if defined(__APPLE__) && defined(__aarch64__) +typedef void (*t_gotfn)(void *x); +#else +typedef void (*t_gotfn)(void *x, ...); +#endif + +/* ---------------- pre-defined objects and symbols --------------*/ +EXTERN t_pd pd_objectmaker; /* factory for creating "object" boxes */ +EXTERN t_pd pd_canvasmaker; /* factory for creating canvases */ + +/* --------- prototypes from the central message system ----------- */ +EXTERN void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void pd_forwardmess(t_pd *x, int argc, t_atom *argv); +EXTERN t_symbol *gensym(const char *s); +EXTERN t_gotfn getfn(const t_pd *x, t_symbol *s); +EXTERN t_gotfn zgetfn(const t_pd *x, t_symbol *s); +EXTERN void nullfn(void); +EXTERN void pd_vmess(t_pd *x, t_symbol *s, const char *fmt, ...); + +/* the following macros are for sending non-type-checkable messages, i.e., +using function lookup but circumventing type checking on arguments. Only +use for internal messaging protected by A_CANT so that the message can't +be generated at patch level. */ +#define mess0(x, s) ((*getfn((x), (s)))((x))) +typedef void (*t_gotfn1)(void *x, void *arg1); +#define mess1(x, s, a) ((*(t_gotfn1)getfn((x), (s)))((x), (a))) +typedef void (*t_gotfn2)(void *x, void *arg1, void *arg2); +#define mess2(x, s, a,b) ((*(t_gotfn2)getfn((x), (s)))((x), (a),(b))) +typedef void (*t_gotfn3)(void *x, void *arg1, void *arg2, void *arg3); +#define mess3(x, s, a,b,c) ((*(t_gotfn3)getfn((x), (s)))((x), (a),(b),(c))) +typedef void (*t_gotfn4)(void *x, + void *arg1, void *arg2, void *arg3, void *arg4); +#define mess4(x, s, a,b,c,d) \ + ((*(t_gotfn4)getfn((x), (s)))((x), (a),(b),(c),(d))) +typedef void (*t_gotfn5)(void *x, + void *arg1, void *arg2, void *arg3, void *arg4, void *arg5); +#define mess5(x, s, a,b,c,d,e) \ + ((*(t_gotfn5)getfn((x), (s)))((x), (a),(b),(c),(d),(e))) + +EXTERN void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_pd *pd_newest(void); + +/* --------------- memory management -------------------- */ +EXTERN void *getbytes(size_t nbytes); +EXTERN void *getzbytes(size_t nbytes); +EXTERN void *copybytes(const void *src, size_t nbytes); +EXTERN void freebytes(void *x, size_t nbytes); +EXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize); + +/* -------------------- atoms ----------------------------- */ + +#define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_index = 0) +#define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_index = 0) +#define SETPOINTER(atom, gp) ((atom)->a_type = A_POINTER, \ + (atom)->a_w.w_gpointer = (gp)) +#define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f)) +#define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \ + (atom)->a_w.w_symbol = (s)) +#define SETDOLLAR(atom, n) ((atom)->a_type = A_DOLLAR, \ + (atom)->a_w.w_index = (n)) +#define SETDOLLSYM(atom, s) ((atom)->a_type = A_DOLLSYM, \ + (atom)->a_w.w_symbol= (s)) + +EXTERN t_float atom_getfloat(const t_atom *a); +EXTERN t_int atom_getint(const t_atom *a); +EXTERN t_symbol *atom_getsymbol(const t_atom *a); +EXTERN t_symbol *atom_gensym(const t_atom *a); +EXTERN t_float atom_getfloatarg(int which, int argc, const t_atom *argv); +EXTERN t_int atom_getintarg(int which, int argc, const t_atom *argv); +EXTERN t_symbol *atom_getsymbolarg(int which, int argc, const t_atom *argv); + +EXTERN void atom_string(const t_atom *a, char *buf, unsigned int bufsize); + +/* ------------------ binbufs --------------- */ + +EXTERN t_binbuf *binbuf_new(void); +EXTERN void binbuf_free(t_binbuf *x); +EXTERN t_binbuf *binbuf_duplicate(const t_binbuf *y); + +EXTERN void binbuf_text(t_binbuf *x, const char *text, size_t size); +EXTERN void binbuf_gettext(const t_binbuf *x, char **bufp, int *lengthp); +EXTERN void binbuf_clear(t_binbuf *x); +EXTERN void binbuf_add(t_binbuf *x, int argc, const t_atom *argv); +EXTERN void binbuf_addv(t_binbuf *x, const char *fmt, ...); +EXTERN void binbuf_addbinbuf(t_binbuf *x, const t_binbuf *y); +EXTERN void binbuf_addsemi(t_binbuf *x); +EXTERN void binbuf_restore(t_binbuf *x, int argc, const t_atom *argv); +EXTERN void binbuf_print(const t_binbuf *x); +EXTERN int binbuf_getnatom(const t_binbuf *x); +EXTERN t_atom *binbuf_getvec(const t_binbuf *x); +EXTERN int binbuf_resize(t_binbuf *x, int newsize); +EXTERN void binbuf_eval(const t_binbuf *x, t_pd *target, int argc, const t_atom *argv); +EXTERN int binbuf_read(t_binbuf *b, const char *filename, const char *dirname, + int crflag); +EXTERN int binbuf_read_via_canvas(t_binbuf *b, const char *filename, const t_canvas *canvas, + int crflag); +EXTERN int binbuf_read_via_path(t_binbuf *b, const char *filename, const char *dirname, + int crflag); +EXTERN int binbuf_write(const t_binbuf *x, const char *filename, const char *dir, + int crflag); +EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir); +EXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, const t_atom *av, + int tonew); + +/* ------------------ clocks --------------- */ + +EXTERN t_clock *clock_new(void *owner, t_method fn); +EXTERN void clock_set(t_clock *x, double systime); +EXTERN void clock_delay(t_clock *x, double delaytime); +EXTERN void clock_unset(t_clock *x); +EXTERN void clock_setunit(t_clock *x, double timeunit, int sampflag); +EXTERN double clock_getlogicaltime(void); +EXTERN double clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */ +EXTERN double clock_gettimesince(double prevsystime); +EXTERN double clock_gettimesincewithunits(double prevsystime, + double units, int sampflag); +EXTERN double clock_getsystimeafter(double delaytime); +EXTERN void clock_free(t_clock *x); + +/* ----------------- pure data ---------------- */ +EXTERN t_pd *pd_new(t_class *cls); +EXTERN void pd_free(t_pd *x); +EXTERN void pd_bind(t_pd *x, t_symbol *s); +EXTERN void pd_unbind(t_pd *x, t_symbol *s); +EXTERN t_pd *pd_findbyclass(t_symbol *s, const t_class *c); +EXTERN void pd_pushsym(t_pd *x); +EXTERN void pd_popsym(t_pd *x); +EXTERN void pd_bang(t_pd *x); +EXTERN void pd_pointer(t_pd *x, t_gpointer *gp); +EXTERN void pd_float(t_pd *x, t_float f); +EXTERN void pd_symbol(t_pd *x, t_symbol *s); +EXTERN void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv); +#define pd_class(x) (*(x)) + +/* ----------------- pointers ---------------- */ +EXTERN void gpointer_init(t_gpointer *gp); +EXTERN void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto); +EXTERN void gpointer_unset(t_gpointer *gp); +EXTERN int gpointer_check(const t_gpointer *gp, int headok); + +/* ----------------- patchable "objects" -------------- */ +EXTERN t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, + t_symbol *s2); +EXTERN t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp); +EXTERN t_inlet *floatinlet_new(t_object *owner, t_float *fp); +EXTERN t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp); +EXTERN t_inlet *signalinlet_new(t_object *owner, t_float f); +EXTERN void inlet_free(t_inlet *x); + +EXTERN t_outlet *outlet_new(t_object *owner, t_symbol *s); +EXTERN void outlet_bang(t_outlet *x); +EXTERN void outlet_pointer(t_outlet *x, t_gpointer *gp); +EXTERN void outlet_float(t_outlet *x, t_float f); +EXTERN void outlet_symbol(t_outlet *x, t_symbol *s); +EXTERN void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_symbol *outlet_getsymbol(t_outlet *x); +EXTERN void outlet_free(t_outlet *x); +EXTERN t_object *pd_checkobject(t_pd *x); + + +/* -------------------- canvases -------------- */ + +EXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir); + +EXTERN void canvas_setargs(int argc, const t_atom *argv); +EXTERN void canvas_getargs(int *argcp, t_atom **argvp); +EXTERN t_symbol *canvas_getcurrentdir(void); +EXTERN t_glist *canvas_getcurrent(void); +EXTERN void canvas_makefilename(const t_glist *c, const char *file, + char *result, int resultsize); +EXTERN t_symbol *canvas_getdir(const t_glist *x); +EXTERN char sys_font[]; /* default typeface set in s_main.c */ +EXTERN char sys_fontweight[]; /* default font weight set in s_main.c */ +EXTERN int sys_hostfontsize(int fontsize, int zoom); +EXTERN int sys_zoomfontwidth(int fontsize, int zoom, int worstcase); +EXTERN int sys_zoomfontheight(int fontsize, int zoom, int worstcase); +EXTERN int sys_fontwidth(int fontsize); +EXTERN int sys_fontheight(int fontsize); +EXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b); +EXTERN int canvas_open(const t_canvas *x, const char *name, const char *ext, + char *dirresult, char **nameresult, unsigned int size, int bin); + +/* ---------------- widget behaviors ---------------------- */ + +EXTERN_STRUCT _widgetbehavior; +#define t_widgetbehavior struct _widgetbehavior + +EXTERN_STRUCT _parentwidgetbehavior; +#define t_parentwidgetbehavior struct _parentwidgetbehavior +EXTERN const t_parentwidgetbehavior *pd_getparentwidget(t_pd *x); + +/* -------------------- classes -------------- */ + +#define CLASS_DEFAULT 0 /* flags for new classes below */ +#define CLASS_PD 1 +#define CLASS_GOBJ 2 +#define CLASS_PATCHABLE 3 +#define CLASS_NOINLET 8 + +#define CLASS_TYPEMASK 3 + +EXTERN t_class *class_new(t_symbol *name, t_newmethod newmethod, + t_method freemethod, size_t size, int flags, t_atomtype arg1, ...); + +EXTERN t_class *class_new64(t_symbol *name, t_newmethod newmethod, + t_method freemethod, size_t size, int flags, t_atomtype arg1, ...); + +EXTERN void class_free(t_class *c); + +#ifdef PDINSTANCE +EXTERN t_class *class_getfirst(void); +#endif + +EXTERN void class_addcreator(t_newmethod newmethod, t_symbol *s, + t_atomtype type1, ...); +EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel, + t_atomtype arg1, ...); +EXTERN void class_addbang(t_class *c, t_method fn); +EXTERN void class_addpointer(t_class *c, t_method fn); +EXTERN void class_doaddfloat(t_class *c, t_method fn); +EXTERN void class_addsymbol(t_class *c, t_method fn); +EXTERN void class_addlist(t_class *c, t_method fn); +EXTERN void class_addanything(t_class *c, t_method fn); +EXTERN void class_sethelpsymbol(t_class *c, t_symbol *s); +EXTERN void class_setwidget(t_class *c, const t_widgetbehavior *w); +EXTERN void class_setparentwidget(t_class *c, const t_parentwidgetbehavior *w); +EXTERN const char *class_getname(const t_class *c); +EXTERN const char *class_gethelpname(const t_class *c); +EXTERN const char *class_gethelpdir(const t_class *c); +EXTERN void class_setdrawcommand(t_class *c); +EXTERN int class_isdrawcommand(const t_class *c); +EXTERN void class_domainsignalin(t_class *c, int onset); +EXTERN void class_set_extern_dir(t_symbol *s); +#define CLASS_MAINSIGNALIN(c, type, field) \ + class_domainsignalin(c, (char *)(&((type *)0)->field) - (char *)0) + + /* prototype for functions to save Pd's to a binbuf */ +typedef void (*t_savefn)(t_gobj *x, t_binbuf *b); +EXTERN void class_setsavefn(t_class *c, t_savefn f); +EXTERN t_savefn class_getsavefn(const t_class *c); +EXTERN void obj_saveformat(const t_object *x, t_binbuf *bb); /* add format to bb */ + + /* prototype for functions to open properties dialogs */ +typedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist); +EXTERN void class_setpropertiesfn(t_class *c, t_propertiesfn f); +EXTERN t_propertiesfn class_getpropertiesfn(const t_class *c); + +typedef void (*t_classfreefn)(t_class *); +EXTERN void class_setfreefn(t_class *c, t_classfreefn fn); + +#ifndef PD_CLASS_DEF +#define class_addbang(x, y) class_addbang((x), (t_method)(y)) +#define class_addpointer(x, y) class_addpointer((x), (t_method)(y)) +#define class_addfloat(x, y) class_doaddfloat((x), (t_method)(y)) +#define class_addsymbol(x, y) class_addsymbol((x), (t_method)(y)) +#define class_addlist(x, y) class_addlist((x), (t_method)(y)) +#define class_addanything(x, y) class_addanything((x), (t_method)(y)) +#endif + +#if PD_FLOATSIZE == 64 +# define class_new class_new64 +#endif + +/* ------------ printing --------------------------------- */ +EXTERN void post(const char *fmt, ...); +EXTERN void startpost(const char *fmt, ...); +EXTERN void poststring(const char *s); +EXTERN void postfloat(t_floatarg f); +EXTERN void postatom(int argc, const t_atom *argv); +EXTERN void endpost(void); +EXTERN void error(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); +EXTERN void verbose(int level, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3); +EXTERN void bug(const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(1, 2); +EXTERN void pd_error(const void *object, const char *fmt, ...) ATTRIBUTE_FORMAT_PRINTF(2, 3); +EXTERN void logpost(const void *object, const int level, const char *fmt, ...) + ATTRIBUTE_FORMAT_PRINTF(3, 4); + +/* ------------ system interface routines ------------------- */ +EXTERN int sys_isabsolutepath(const char *dir); +EXTERN void sys_bashfilename(const char *from, char *to); +EXTERN void sys_unbashfilename(const char *from, char *to); +EXTERN int open_via_path(const char *dir, const char *name, const char *ext, + char *dirresult, char **nameresult, unsigned int size, int bin); +EXTERN int sched_geteventno(void); +EXTERN double sys_getrealtime(void); +EXTERN int (*sys_idlehook)(void); /* hook to add idle time computation */ + +/* Win32's open()/fopen() do not handle UTF-8 filenames so we need + * these internal versions that handle UTF-8 filenames the same across + * all platforms. They are recommended for use in external + * objectclasses as well so they work with Unicode filenames on Windows */ +EXTERN int sys_open(const char *path, int oflag, ...); +EXTERN int sys_close(int fd); +EXTERN FILE *sys_fopen(const char *filename, const char *mode); +EXTERN int sys_fclose(FILE *stream); + +/* ------------ threading ------------------- */ +EXTERN void sys_lock(void); +EXTERN void sys_unlock(void); +EXTERN int sys_trylock(void); + + +/* --------------- signals ----------------------------------- */ + +typedef PD_FLOATTYPE t_sample; +typedef union _sampleint_union { + t_sample f; + PD_FLOATUINTTYPE i; +} t_sampleint_union; +#define MAXLOGSIG 32 +#define MAXSIGSIZE (1 << MAXLOGSIG) + +typedef struct _signal +{ + int s_n; /* number of points in the array */ + t_sample *s_vec; /* the array */ + t_float s_sr; /* sample rate */ + int s_refcount; /* number of times used */ + int s_isborrowed; /* whether we're going to borrow our array */ + struct _signal *s_borrowedfrom; /* signal to borrow it from */ + struct _signal *s_nextfree; /* next in freelist */ + struct _signal *s_nextused; /* next in used list */ + int s_vecsize; /* allocated size of array in points */ +} t_signal; + +typedef t_int *(*t_perfroutine)(t_int *args); + +EXTERN t_int *plus_perform(t_int *args); +EXTERN t_int *zero_perform(t_int *args); +EXTERN t_int *copy_perform(t_int *args); + +EXTERN void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n); +EXTERN void dsp_add_copy(t_sample *in, t_sample *out, int n); +EXTERN void dsp_add_scalarcopy(t_float *in, t_sample *out, int n); +EXTERN void dsp_add_zero(t_sample *out, int n); + +EXTERN int sys_getblksize(void); +EXTERN t_float sys_getsr(void); +EXTERN int sys_get_inchannels(void); +EXTERN int sys_get_outchannels(void); + +EXTERN void dsp_add(t_perfroutine f, int n, ...); +EXTERN void dsp_addv(t_perfroutine f, int n, t_int *vec); +EXTERN void pd_fft(t_float *buf, int npoints, int inverse); +EXTERN int ilog2(int n); + +EXTERN void mayer_fht(t_sample *fz, int n); +EXTERN void mayer_fft(int n, t_sample *real, t_sample *imag); +EXTERN void mayer_ifft(int n, t_sample *real, t_sample *imag); +EXTERN void mayer_realfft(int n, t_sample *real); +EXTERN void mayer_realifft(int n, t_sample *real); + +EXTERN float *cos_table; +#define LOGCOSTABSIZE 9 +#define COSTABSIZE (1<> 1) & 0x20000000)); +} + +#elif PD_FLOATSIZE == 64 + +typedef union +{ + t_float f; + unsigned int ui[2]; +}t_bigorsmall64; + +static inline int PD_BADFLOAT(t_float f) /* malformed double */ +{ + t_bigorsmall64 pun; + pun.f = f; + pun.ui[1] &= 0x7ff00000; + return((pun.ui[1] == 0) | (pun.ui[1] == 0x7ff00000)); +} + +static inline int PD_BIGORSMALL(t_float f) /* exponent outside (-512,512) */ +{ + t_bigorsmall64 pun; + pun.f = f; + return((pun.ui[1] & 0x20000000) == ((pun.ui[1] >> 1) & 0x20000000)); +} + +#endif /* PD_FLOATSIZE */ +#else /* not INTEL or ARM */ +#define PD_BADFLOAT(f) 0 +#define PD_BIGORSMALL(f) 0 +#endif + +#else /* _MSC_VER */ +#if PD_FLOATSIZE == 32 +#define PD_BADFLOAT(f) ((((*(unsigned int*)&(f))&0x7f800000)==0) || \ + (((*(unsigned int*)&(f))&0x7f800000)==0x7f800000)) +/* more stringent test: anything not between 1e-19 and 1e19 in absolute val */ +#define PD_BIGORSMALL(f) ((((*(unsigned int*)&(f))&0x60000000)==0) || \ + (((*(unsigned int*)&(f))&0x60000000)==0x60000000)) +#else /* 64 bits... don't know what to do here */ +#define PD_BADFLOAT(f) (!(((f) >= 0) || ((f) <= 0))) +#define PD_BIGORSMALL(f) ((f) > 1e150 || (f) < -1e150 \ + || (f) > -1e-150 && (f) < 1e-150 ) +#endif +#endif /* _MSC_VER */ + /* get version number at run time */ +EXTERN void sys_getversion(int *major, int *minor, int *bugfix); + +EXTERN_STRUCT _instancemidi; +#define t_instancemidi struct _instancemidi + +EXTERN_STRUCT _instanceinter; +#define t_instanceinter struct _instanceinter + +EXTERN_STRUCT _instancecanvas; +#define t_instancecanvas struct _instancecanvas + +EXTERN_STRUCT _instanceugen; +#define t_instanceugen struct _instanceugen + +EXTERN_STRUCT _instancestuff; +#define t_instancestuff struct _instancestuff + +#ifndef PDTHREADS +#define PDTHREADS 1 +#endif + +struct _pdinstance +{ + double pd_systime; /* global time in Pd ticks */ + t_clock *pd_clock_setlist; /* list of set clocks */ + t_canvas *pd_canvaslist; /* list of all root canvases */ + struct _template *pd_templatelist; /* list of all templates */ + int pd_instanceno; /* ordinal number of this instance */ + t_symbol **pd_symhash; /* symbol table hash table */ + t_instancemidi *pd_midi; /* private stuff for x_midi.c */ + t_instanceinter *pd_inter; /* private stuff for s_inter.c */ + t_instanceugen *pd_ugen; /* private stuff for d_ugen.c */ + t_instancecanvas *pd_gui; /* semi-private stuff in g_canvas.h */ + t_instancestuff *pd_stuff; /* semi-private stuff in s_stuff.h */ + t_pd *pd_newest; /* most recently created object */ +#ifdef PDINSTANCE + t_symbol pd_s_pointer; + t_symbol pd_s_float; + t_symbol pd_s_symbol; + t_symbol pd_s_bang; + t_symbol pd_s_list; + t_symbol pd_s_anything; + t_symbol pd_s_signal; + t_symbol pd_s__N; + t_symbol pd_s__X; + t_symbol pd_s_x; + t_symbol pd_s_y; + t_symbol pd_s_; +#endif +#if PDTHREADS + int pd_islocked; +#endif +}; +#define t_pdinstance struct _pdinstance +EXTERN t_pdinstance pd_maininstance; + +/* m_pd.c */ +#ifdef PDINSTANCE +EXTERN t_pdinstance *pdinstance_new(void); +EXTERN void pd_setinstance(t_pdinstance *x); +EXTERN void pdinstance_free(t_pdinstance *x); +#endif /* PDINSTANCE */ + +#if defined(PDTHREADS) && defined(PDINSTANCE) +#ifdef _MSC_VER +#define PERTHREAD __declspec(thread) +#else +#define PERTHREAD __thread +#endif /* _MSC_VER */ +#else +#define PERTHREAD +#endif + +#ifdef PDINSTANCE +extern PERTHREAD t_pdinstance *pd_this; +EXTERN t_pdinstance **pd_instances; +EXTERN int pd_ninstances; +#else +#define pd_this (&pd_maininstance) +#endif /* PDINSTANCE */ + +#ifdef PDINSTANCE +#define s_pointer (pd_this->pd_s_pointer) +#define s_float (pd_this->pd_s_float) +#define s_symbol (pd_this->pd_s_symbol) +#define s_bang (pd_this->pd_s_bang) +#define s_list (pd_this->pd_s_list) +#define s_anything (pd_this->pd_s_anything) +#define s_signal (pd_this->pd_s_signal) +#define s__N (pd_this->pd_s__N) +#define s__X (pd_this->pd_s__X) +#define s_x (pd_this->pd_s_x) +#define s_y (pd_this->pd_s_y) +#define s_ (pd_this->pd_s_) +#else +EXTERN t_symbol s_pointer, s_float, s_symbol, s_bang, s_list, s_anything, + s_signal, s__N, s__X, s_x, s_y, s_; +#endif + +EXTERN t_canvas *pd_getcanvaslist(void); +EXTERN int pd_getdspstate(void); + +/* x_text.c */ +EXTERN t_binbuf *text_getbufbyname(t_symbol *s); /* get binbuf from text obj */ +EXTERN void text_notifybyname(t_symbol *s); /* notify it was modified */ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +} +#endif + +#define __m_pd_h_ +#endif /* __m_pd_h_ */ diff --git a/plugins/puredata/sfizz_puredata.c b/plugins/puredata/sfizz_puredata.c new file mode 100644 index 00000000..89422f7a --- /dev/null +++ b/plugins/puredata/sfizz_puredata.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: BSD-2-Clause + +// This code is part of the sfizz library and is licensed under a BSD 2-clause +// license. You should have receive a LICENSE.md file along with the code. +// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz + +#include +#include +#include +#include +#include +#include + +static t_class* cls_sfizz_tilde; + +typedef struct _sfizz_tilde { + t_object obj; + t_outlet* outputs[2]; + sfizz_synth_t* synth; + int midi[3]; + int midinum; + t_symbol* dir; + char* filepath; +} t_sfizz_tilde; + +static void sfizz_tilde_set_file(t_sfizz_tilde* self, const char* file) +{ + const char* dir = self->dir->s_name; + char* filepath; + if (file[0] != '\0') { + filepath = malloc(strlen(dir) + 1 + strlen(file) + 1); + sprintf(filepath, "%s/%s", dir, file); + } + else { + filepath = malloc(1); + *filepath = '\0'; + } + free(self->filepath); + self->filepath = filepath; +} + +static bool sfizz_tilde_do_load(t_sfizz_tilde* self) +{ + bool loaded; + if (self->filepath[0] != '\0') + loaded = sfizz_load_file(self->synth, self->filepath); + else + loaded = sfizz_load_string(self->synth, "default.sfz", "sample=*sine"); + return loaded; +} + +static void* sfizz_tilde_new(t_symbol* sym, int argc, t_atom argv[]) +{ + (void)sym; + t_sfizz_tilde* self = (t_sfizz_tilde*)pd_new(cls_sfizz_tilde); + + const char* file; + if (argc == 0) + file = ""; + else if (argc == 1 && argv[0].a_type == A_SYMBOL) + file = argv[0].a_w.w_symbol->s_name; + else { + pd_free((t_pd*)self); + return NULL; + } + + self->dir = canvas_getcurrentdir(); + + self->outputs[0] = outlet_new(&self->obj, &s_signal); + self->outputs[1] = outlet_new(&self->obj, &s_signal); + + sfizz_synth_t* synth = sfizz_create_synth(); + self->synth = synth; + + sfizz_set_sample_rate(synth, sys_getsr()); + sfizz_set_samples_per_block(synth, sys_getblksize()); + + sfizz_tilde_set_file(self, file); + if (!sfizz_tilde_do_load(self)) { + pd_free((t_pd*)self); + return NULL; + } + + return self; +} + +static void sfizz_tilde_free(t_sfizz_tilde* self) +{ + if (self->filepath) + free(self->filepath); + if (self->synth) + sfizz_free(self->synth); + if (self->outputs[0]) + outlet_free(self->outputs[0]); + if (self->outputs[1]) + outlet_free(self->outputs[1]); +} + +static t_int* sfizz_tilde_perform(t_int* w) +{ + t_sfizz_tilde* self; + t_sample* outputs[2]; + t_int nframes; + + w++; + self = (t_sfizz_tilde*)*w++; + outputs[0] = (t_sample*)*w++; + outputs[1] = (t_sample*)*w++; + nframes = (t_int)*w++; + + sfizz_render_block(self->synth, outputs, 2, nframes); + + return w; +} + +static void sfizz_tilde_dsp(t_sfizz_tilde* self, t_signal** sp) +{ + dsp_add( + &sfizz_tilde_perform, 4, (t_int)self, + (t_int)sp[0]->s_vec, (t_int)sp[1]->s_vec, (t_int)sp[0]->s_n); +} + +static void sfizz_tilde_midiin(t_sfizz_tilde* self, t_float f) +{ + int byte = (int)f; + bool isstatus = (byte & 0x80) != 0; + + int* midi = self->midi; + int midinum = self->midinum; + + // + if (isstatus) { + midi[0] = byte; + midinum = 1; + } + else if (midinum != -1 && midinum < 3) + midi[midinum++] = byte; + else + midinum = -1; + + // + switch (midinum) { + case 2: + switch (midi[0] & 0xf0) { + case 0xd0: // channel aftertouch + sfizz_send_channel_aftertouch(self->synth, 0, midi[1]); + break; + } + break; + case 3: + switch (midi[0] & 0xf0) { + case 0x90: // note on + if (midi[2] == 0) + goto noteoff; + sfizz_send_note_on(self->synth, 0, midi[1], midi[2]); + break; + case 0x80: // note off + noteoff: + sfizz_send_note_off(self->synth, 0, midi[1], midi[2]); + break; + case 0xb0: // controller + sfizz_send_cc(self->synth, 0, midi[1], midi[2]); + break; + case 0xa0: // key aftertouch + sfizz_send_poly_aftertouch(self->synth, 0, midi[1], midi[2]); + break; + case 0xe0: // pitch bend + sfizz_send_pitch_wheel(self->synth, 0, (midi[1] + (midi[2] << 7)) - 8192); + break; + + } + break; + } + + self->midinum = midinum; +} + +static void sfizz_tilde_load(t_sfizz_tilde* self, t_symbol* sym) +{ + sfizz_tilde_set_file(self, sym->s_name); + sfizz_tilde_do_load(self); +} + +static void sfizz_tilde_reload(t_sfizz_tilde* self, t_float value) +{ + (void)value; + sfizz_tilde_do_load(self); +} + +static void sfizz_tilde_hdcc(t_sfizz_tilde* self, t_float cc, t_float value) +{ + sfizz_automate_hdcc(self->synth, 0, (int)cc, value); +} + +static void sfizz_tilde_voices(t_sfizz_tilde* self, t_float value) +{ + int numvoices = (int)value; + if (numvoices < 1) + numvoices = 1; + + sfizz_set_num_voices(self->synth, numvoices); +} + +#if defined(_WIN32) +__declspec(dllexport) +#else +__attribute__((visibility("default"))) +#endif +void sfizz_setup() +{ + post("sfizz external for Puredata"); + + cls_sfizz_tilde = class_new( + gensym("sfizz~"), + (t_newmethod)&sfizz_tilde_new, + (t_method)&sfizz_tilde_free, + sizeof(t_sfizz_tilde), + CLASS_DEFAULT, A_GIMME, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_dsp, gensym("dsp"), A_CANT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_midiin, &s_float, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_load, gensym("load"), A_DEFSYM, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_reload, gensym("reload"), A_DEFFLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_hdcc, gensym("hdcc"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod( + cls_sfizz_tilde, (t_method)&sfizz_tilde_voices, gensym("voices"), A_FLOAT, A_NULL); +} diff --git a/plugins/puredata/sfizz~-help.pd b/plugins/puredata/sfizz~-help.pd new file mode 100644 index 00000000..96600cba --- /dev/null +++ b/plugins/puredata/sfizz~-help.pd @@ -0,0 +1,46 @@ +#N canvas 932 395 659 405 12; +#X declare -lib sfizz; +#X obj 4 3 cnv 15 650 80 empty empty sfizz 20 30 0 40 -261682 -66577 +0; +#X text 45 54 A synthesizer for instruments in SFZ format; +#X obj 241 165 r sfizz-ctl; +#X text 33 167 create a SFZ instrument; +#X obj 29 324 midiin; +#X obj 29 349 s sfizz-ctl; +#X text 26 301 connect MIDI-in; +#X obj 162 223 dac~; +#X msg 180 346 voices \$1; +#X obj 180 371 s sfizz-ctl; +#X text 178 302 modify the polyphony; +#X obj 183 326 hsl 128 15 1 256 0 1 empty empty empty -2 -8 0 10 -262144 +-1 -1 3138 1; +#X floatatom 273 346 5 0 0 0 - - -; +#X obj 378 372 s sfizz-ctl; +#X obj 381 327 hsl 128 15 0 1 0 1 empty empty empty -2 -8 0 10 -262144 +-1 -1 6350 1; +#X floatatom 471 347 5 0 0 0 - - -; +#X text 377 303 modulate a parameter; +#X msg 378 347 hdcc 300 \$1; +#X obj 27 115 declare -lib sfizz; +#X obj 163 192 sfizz~ example.sfz; +#X text 18 93 load the sfizz library; +#X msg 221 115 \; pd dsp 1; +#X text 218 94 click to turn on DSP; +#X text 153 247 output stereo audio; +#X msg 366 223 reload; +#X obj 366 248 s sfizz-ctl; +#X obj 466 248 s sfizz-ctl; +#X msg 466 223 load example.sfz; +#X text 364 200 reload the instrument or load another; +#X connect 2 0 19 0; +#X connect 4 0 5 0; +#X connect 8 0 9 0; +#X connect 11 0 8 0; +#X connect 11 0 12 0; +#X connect 14 0 17 0; +#X connect 14 0 15 0; +#X connect 17 0 13 0; +#X connect 19 0 7 0; +#X connect 19 1 7 1; +#X connect 24 0 25 0; +#X connect 27 0 26 0;