How To Write Pure Data Externals
How To Write Pure Data Externals
write an External
for puredata
johannes m zmölnig
5 a signal-external: pan 18
5.1 variables of a signalclass . . . . . . . . . . . . . . . . . . . . . 18
5.2 signal-classes . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.3 construction of signal-inlets and -outlets . . . . . . . . . . . . 19
5.4 DSP-methods . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.5 perform-routine . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.6 the code: pan . . . . . . . . . . . . . . . . . . . . . . . . . . 21
A pd's message-system 23
A.1 atoms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
A.2 selectors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
B pd-types 24
1
C important functions in m_pd.h 25
C.1 functions: atoms . . . . . . . . . . . . . . . . . . . . . . . . . 25
C.1.1 SETFLOAT . . . . . . . . . . . . . . . . . . . . . . . . 25
C.1.2 SETSYMBOL . . . . . . . . . . . . . . . . . . . . . . . 25
C.1.3 SETPOINTER . . . . . . . . . . . . . . . . . . . . . . 25
C.1.4 atom_getoat . . . . . . . . . . . . . . . . . . . . . . . 25
C.1.5 atom_getoatarg . . . . . . . . . . . . . . . . . . . . . 25
C.1.6 atom_getint . . . . . . . . . . . . . . . . . . . . . . . . 25
C.1.7 atom_getsymbol . . . . . . . . . . . . . . . . . . . . . 26
C.1.8 atom_gensym . . . . . . . . . . . . . . . . . . . . . . . 26
C.1.9 atom_string . . . . . . . . . . . . . . . . . . . . . . . . 26
C.1.10 gensym . . . . . . . . . . . . . . . . . . . . . . . . . . 26
C.2 functions: classes . . . . . . . . . . . . . . . . . . . . . . . . . 26
C.2.1 class_new . . . . . . . . . . . . . . . . . . . . . . . . . 26
C.2.2 class_addmethod . . . . . . . . . . . . . . . . . . . . . 27
C.2.3 class_addbang . . . . . . . . . . . . . . . . . . . . . . 27
C.2.4 class_addoat . . . . . . . . . . . . . . . . . . . . . . . 28
C.2.5 class_addsymbol . . . . . . . . . . . . . . . . . . . . . 28
C.2.6 class_addpointer . . . . . . . . . . . . . . . . . . . . . 28
C.2.7 class_addlist . . . . . . . . . . . . . . . . . . . . . . . 28
C.2.8 class_addanything . . . . . . . . . . . . . . . . . . . . 29
C.2.9 class_addcreator . . . . . . . . . . . . . . . . . . . . . 29
C.2.10 class_sethelpsymbol . . . . . . . . . . . . . . . . . . . 29
C.2.11 pd_new . . . . . . . . . . . . . . . . . . . . . . . . . . 29
C.3 functions: inlets and outlets . . . . . . . . . . . . . . . . . . . 30
C.3.1 inlet_new . . . . . . . . . . . . . . . . . . . . . . . . . 30
C.3.2 oatinlet_new . . . . . . . . . . . . . . . . . . . . . . . 30
C.3.3 symbolinlet_new . . . . . . . . . . . . . . . . . . . . . 31
C.3.4 pointerinlet_new . . . . . . . . . . . . . . . . . . . . . 31
C.3.5 outlet_new . . . . . . . . . . . . . . . . . . . . . . . . 31
C.3.6 outlet_bang . . . . . . . . . . . . . . . . . . . . . . . . 32
C.3.7 outlet_oat . . . . . . . . . . . . . . . . . . . . . . . . 32
C.3.8 outlet_symbol . . . . . . . . . . . . . . . . . . . . . . 32
C.3.9 outlet_pointer . . . . . . . . . . . . . . . . . . . . . . 32
C.3.10 outlet_list . . . . . . . . . . . . . . . . . . . . . . . . . 32
C.3.11 outlet_anything . . . . . . . . . . . . . . . . . . . . . . 32
C.4 functions: DSP . . . . . . . . . . . . . . . . . . . . . . . . . . 33
C.4.1 CLASS_MAINSIGNALIN . . . . . . . . . . . . . . . . 34
C.4.2 dsp_add . . . . . . . . . . . . . . . . . . . . . . . . . . 34
C.4.3 sys_getsr . . . . . . . . . . . . . . . . . . . . . . . . . 34
C.5 functions: memory . . . . . . . . . . . . . . . . . . . . . . . . 34
2
C.5.1 getbytes . . . . . . . . . . . . . . . . . . . . . . . . . . 34
C.5.2 copybytes . . . . . . . . . . . . . . . . . . . . . . . . . 34
C.5.3 freebytes . . . . . . . . . . . . . . . . . . . . . . . . . . 35
C.6 functions: output . . . . . . . . . . . . . . . . . . . . . . . . . 35
C.6.1 post . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
C.6.2 error . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3
1 denitions and prerequisites
4
Unlike externals, libraries can be imported by pd with special operations.
After a library has been imported, all included externals have been loaded
into memory and are available as objects.
pd supports to modes to import libraries:
#include "m_pd.h"
1 If a class my_lib is already existent, an object my_lib will be instantiated and the
procedure is done. Thus, no library has been loaded. Therefore no library that is named
like an already used class-name like, say, abs, can be loaded.
2 or another system-dependent lename-extensions (s.a.)
5
2.2 a class and its dataspace
First a new class has to be prepared and the dataspace for this class has to
be dened.
2.3 methodspace
6
2.4 generation of a new class
void helloworld_setup(void)
{
helloworld_class = class_new(gensym("helloworld"),
(t_newmethod)helloworld_new,
0, sizeof(t_helloworld),
CLASS_DEFAULT, 0);
class_addbang(helloworld_class, helloworld_bang);
}
class_new The function class_new creates a new class and returns a poin-
ter to this prototype.
The rst argument is the symbolic name of the class.
Das erste Argument ist der symbolische Name der Klasse.
The next two arguments dene the constructor and dstructor of the class.
Whenever a classobject is created in a pd-patch, the class-constructor
(t_newmethod)helloworld_new instantiates the object and initializes the
dataspace.
Whenever an object is destroyed (either by closing the containing patch
or by deleting the object from the patch) the destructor frees the dynami-
cally reserved memory. The allocated memory for the static dataspace is
automatically reserved and freed.
Therefore we do not have to provide a destructor in this example, the
argument is set to 0.
To enable pd to reserve and free enough memory for the static dataspace,
the size of the datastructure has to be passed as the fourth argument.
The fth argument has inuence on the graphical representaion of the
classobjects. The default-value is CLASS_DEFAULT or simply 0.
The remaining arguments dene the arguments of an object and its type.
Up to six numeric and symbolic object-arguments can be dened via
A_DEFFLOAT and A_DEFSYMBOL. If more arguments are to be passed to the
7
object or if the order of atomtypes should by more exible, A_GIMME can be
used for passing an arbitrary list of atoms.
The list of object-arguments is terminated by 0. In this example we
have no object-arguments at all for the class.
void *helloworld_new(void)
{
t_helloworld *x = (t_helloworld *)pd_new(helloworld_class);
8
2.6 the code: helloworld
#include "m_pd.h"
void *helloworld_new(void)
{
t_helloworld *x = (t_helloworld *)pd_new(helloworld_class);
void helloworld_setup(void) {
helloworld_class = class_new(gensym("helloworld"),
(t_newmethod)helloworld_new,
0, sizeof(t_helloworld),
CLASS_DEFAULT, 0);
class_addbang(helloworld_class, helloworld_bang);
}
9
3.1 object-variables
3.2 object-arguments
It is quite usefull for a counter, if a initial value can be dened by the user.
Therefore this initial value should be passed to the object at creation-time.
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addbang(counter_class, counter_bang);
}
3.3 constructor
The constructor has some new tasks. On the one hand, a variable value has
to be initialized, on the other hand, an outlet for the object has to be created.
void *counter_new(t_floatarg f)
{
t_counter *x = (t_counter *)pd_new(counter_class);
x->i_count=f;
outlet_new(&x->x_obj, &s_float);
10
return (void *)x;
}
The constructor-method has one argument of type t_floatarg as decla-
red in the setup-routine by class_new. This argument is used to initialize
the counter.
A new outlet is created with the function outlet_new. The rst argument
is a pointer to the interna of the object the new outlet is created for.
The second argument is a symbolic description of the outlet-type. Since
out counter should output numeric values it is of type oat.
outlet_new returns a pointer to the new outlet and saves this very pointer
in the t_object-variable x_obj.ob_outlet. If only one outlet is used, the
pointer need not additionally be stored in the dataspace. If more than one
outlets are used, the pointers have to be stored in the dataspace, because the
t_object-variable can only hold one outletpointer.
When triggered, the countervalue should be sent to the outlet and afterwards
be incremented by 1.
void counter_bang(t_counter *x)
{
t_float f=x->i_count;
x->i_count++;
outlet_float(x->x_obj.ob_outlet, f);
}
The function outlet_float sends a oating-point-value (second argu-
ment) to the outlet that is specied by the rst argument.
We rst store the counter in a oatingpoint-buer. Afterwards the coun-
ter is incremented and not before that the buervariable is sent to the outlet.
What appears to be unnecessary on the rst glance, makes sense after
further inspection: The buervariable has been realized as t_float, since
outlet_float expects a oatingpoint-value and a typecast is inevitable.
If the countervalue was sent to the outlet before being incremented, this
could result in an unwanted (though welldened) behaviour: If the counter-
outlet directly triggered its own inlet, the counter-method would be called
although the countervalue was not yet incremented. Normally this is not
what we want.
The same (correct) result could of course be obtained with a single line,
but this would obscure the reentrant-problem.
11
3.5 the code: counter
#include "m_pd.h"
void *counter_new(t_floatarg f)
{
t_counter *x = (t_counter *)pd_new(counter_class);
x->i_count=f;
outlet_new(&x->x_obj, &s_float);
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addbang(counter_class, counter_bang);
}
12
4 a complex external: counter
The simple counter of the previous chapter can easily be extended to more
complexity. It might be quite usefull to be able to reset the counter to an
initial value, to set upper and lower boudaries and to control the step-width.
Each overrun should send a bang-Message to a second outlet and reset the
counter to the initial value.
The dataspace has been extended to hold variables for stepwidth and
upper and lower boundaries. Furthermore pointers for two outlets have been
added.
The new classobjects should have methods for dierent messages, like set
and reset. Therefore the methodspace has to be extended too.
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_GIMME, 0);
class_addmethod(counter_class,
(t_method)counter_reset,
gensym("reset"), 0);
13
class_addmethod adds a method for an arbitrary selector to an class.
The rst argument is the class the method (second argument) will be
added to.
The third argument is the symbolic selector that should be associated
with the method.
The remaining 0-terminated arguments describe the list of atoms that
follows the selector.
class_addmethod(counter_class,
(t_method)counter_set, gensym("set"),
A_DEFFLOAT, 0);
class_addmethod(counter_class,
(t_method)counter_bound, gensym("bound"),
A_DEFFLOAT, A_DEFFLOAT, 0);
class_sethelpsymbol(counter_class, gensym("help-counter"));
When creating the object, several arguments should be passed by the user.
x->step=1;
14
switch(argc){
default:
case 3:
x->step=atom_getfloat(argv+2);
case 2:
f2=atom_getfloat(argv+1);
case 1:
f1=atom_getfloat(argv);
break;
case 0:
}
if (argc<2)f2=f1;
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
x->i_count=x->i_down;
If three arguments are passed, these should be the lower boundary, the
upper boundary and the step width.
If only two arguments are passed, the step-width defaults to 1. If only
one argument is passed, this should be the initial value of the counter with
step-width of 1.
inlet_new(&x->x_obj, &x->x_obj.ob_pd,
gensym("list"), gensym("bound"));
15
• It is not possible to add methods for more than one selector to a right
inlet. Particularly it is not possible to add a universal method for
arbitrary selectors to a right inlet.
floatinlet_new(&x->x_obj, &x->step);
The method for the bang-message has to fullll the more complex tasks.
16
}
}
outlet_float(x->f_out, f);
}
17
t_float f=x->i_count;
t_int step = x->step;
x->i_count+=step;
if (x->i_down-x->i_up) {
if ((step>0) && (x->i_count > x->i_up)) {
x->i_count = x->i_down;
outlet_bang(x->b_out);
} else if (x->i_count < x->i_down) {
x->i_count = x->i_up;
outlet_bang(x->b_out);
}
}
outlet_float(x->f_out, f);
}
x->step=1;
switch(argc){
default:
18
case 3:
x->step=atom_getfloat(argv+2);
case 2:
f2=atom_getfloat(argv+1);
case 1:
f1=atom_getfloat(argv);
break;
case 0:
}
if (argc<2)f2=f1;
x->i_down = (f1<f2)?f1:f2;
x->i_up = (f1>f2)?f1:f2;
x->i_count=x->i_down;
inlet_new(&x->x_obj, &x->x_obj.ob_pd,
gensym("list"), gensym("bound"));
floatinlet_new(&x->x_obj, &x->step);
void counter_setup(void) {
counter_class = class_new(gensym("counter"),
(t_newmethod)counter_new,
0, sizeof(t_counter),
CLASS_DEFAULT,
A_GIMME, 0);
19
A_DEFFLOAT, A_DEFFLOAT, 0);
class_sethelpsymbol(counter_class, gensym("help-counter"));
}
5 a signal-external: pan
Signalclasses are normal pd-classes, that oer additional methods for signals.
All methods and concepts that can be realized with normal objectclasses
can therefore be realized with signalclasses too.
Per agreement, the symbolic names of signalclasses end with a tilde .
The class pan shall demonstrate, how signalclasses are written.
A signal on the left inlet is mixed with a signal on the second inlet. Der
mixing-factor between 0 and 1 is dened via a t_float-message on a third
inlet.
t_sample f_pan;
t_float f;
} t_pan_tilde;
5.2 signal-classes
void pan_tilde_setup(void) {
pan_tilde_class = class_new(gensym("pan~"),
(t_newmethod)pan_tilde_new,
0, sizeof(t_pan_tilde),
CLASS_DEFAULT,
20
A_DEFFLOAT, 0);
class_addmethod(pan_tilde_class,
(t_method)pan_tilde_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(pan_tilde_class, t_pan_tilde, f);
}
A method for signal-processing has to be provided by each signalclass.
Whenever pd's audioengine is started, a message with the selector dsp
is sent to each object. Each class that has a method for the dsp-message is
recognized as signalclass.
Signalclasses that want to provide signal-inlets have to declare this via the
CLASS_MAINSIGNALIN-macro. This enables signals at the rst (default) inlet.
If more than one signal-inlet is needed, they have to be created explicitly in
the constructor-method.
Inlets that are declared as signal-inlets cannot provide methods for t_float-
messages any longer.
The rst argument of the macro is a pointer to the signalclass. The second
argument is the type of the class's dataspace.
The last argument is a dummy-variable out of the dataspace that is
needed to replace non-existing signal at the signal-inlet(s) with t_float-
messages.
void *pan_tilde_new(t_floatarg f)
{
t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class);
x->f_pan = f;
outlet_new(&x->x_obj, &s_signal);
21
Signal-outlets are also created like normal (message-)outlets, by setting
the outlet-selector to signal.
5.4 DSP-methods
5.5 perform-routine
22
return argument equals the argument of the perform-routine plus the number
of pointervariables (as declared as the second argument of dsp_add) plus one.
return (w+6);
}
23
t_sample *in1 = (t_sample *)(w[2]);
t_sample *in2 = (t_sample *)(w[3]);
t_sample *out = (t_sample *)(w[4]);
int n = (int)(w[5]);
t_sample f_pan = (x->f_pan<0)?0.0:(x->f_pan>1)?1.0:x->f_pan;
return (w+6);
}
void *pan_tilde_new(t_floatarg f)
{
t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class);
x->f_pan = f;
void pan_tilde_setup(void) {
pan_tilde_class = class_new(gensym("pan~"),
(t_newmethod)pan_tilde_new,
0, sizeof(t_pan_tilde),
CLASS_DEFAULT,
A_DEFFLOAT, 0);
class_addmethod(pan_tilde_class,
(t_method)pan_tilde_dsp, gensym("dsp"), 0);
CLASS_MAINSIGNALIN(pan_tilde_class, t_pan_tilde, f);
}
24
A pd's message-system
A.1 atoms
• A_POINTER: a pointer
A.2 selectors
The selector is a symbol that denes the type of a message. There are ve
predened selectors:
25
Since the symbols for these selectors are used quite often, their address
in the lookup-table can be queried directly, without having to use gensym:
selector lookup-routine lookup-address
bang gensym("bang") &s_bang
float gensym("float") &s_float
symbol gensym("symbol") &s_symbol
pointer gensym("pointer") &s_pointer
list gensym("list") &s_list
(signal) gensym("signal") &s_symbol
Other selectors can be used as well. The receiving class has to provide
a method for a specique selector or for anything, which is any arbitrary
selector.
Messages that have no explicit selector and start with a numerical value,
are recognized automatically either as oat-message (only one atom) or as
list-message (several atoms).
For example, messages 12.429 and float 12.429 are identical. Li-
kewise, the messages list 1 for you is identical to 1 for you.
B pd-types
26
C important functions in m_pd.h
C.1.1 SETFLOAT
SETFLOAT(atom, f)
This macro sets the type of atom to A_FLOAT and stores the numerical value
f in this atom.
C.1.2 SETSYMBOL
SETSYMBOL(atom, s)
This macro sets the type of atom to A_SYMBOL and stores the symbolic pointer
s in this atom.
C.1.3 SETPOINTER
SETPOINTER(atom, pt)
This macro sets the type of atom to A_POINTER and stores the pointer pt in
this atom.
C.1.4 atom_getoat
C.1.5 atom_getoatarg
C.1.6 atom_getint
27
C.1.7 atom_getsymbol
C.1.8 atom_gensym
C.1.9 atom_string
Converts an atom a into a C-string buf. The memory to this char-Buer has
to be reserved manually and its length has to be declared in bufsize.
C.1.10 gensym
Checks, whether the C-string *s has already been inserted into the symbol-
table. If no entry exists, it is created. A pointer to the symbol is returned.
C.2.1 class_new
Generates a class with the symbolic name name. newmethod is the constructor
that creates an instance of the class and returns a pointer to this instance.
If memory is reserved dynamically, this memory has to be freed by the
destructor-method freemethod (without any return argument), when the
object is destroyed.
28
size is the static size of the class-dataspace, that is returned by sizeof(t_mydata).
flags dene the presentation of the graphical object. A (more or less
arbitrary) combination of following objects is possible:
ag description
CLASS_DEFAULT a normal object with one inlet
CLASS_PD object (without graphical presentation)
CLASS_GOBJ pure graphical object (like arrays, graphs,...)
CLASS_PATCHABLE a normal object (with one inlet)
CLASS_NOINLET the default inlet is suppressed
Flags the description of which is printed in italic are of small importance
for writing externals.
The remaining arguments arg1,... dene the types of object-arguments
passed at the creation of a class-object. A maximum of six typechecked argu-
ments can be passed to an object. The list of argument-types are terminated
by 0.
Possible types of arguments are:
A_DEFFLOAT a numerical value
A_DEFSYMBOL a symbolical value
A_GIMME a list of atoms of arbitrary length and types
If more than six arguments are to be passed, A_GIMME has to be used and
a manual type-check has to be made.
C.2.2 class_addmethod
C.2.3 class_addbang
29
Adds a method fn for bang-messages to the class c.
The argument of the bang-method is a pointer to the class-dataspace:
void my_bang_method(t_mydata *x);
C.2.4 class_addoat
C.2.5 class_addsymbol
C.2.6 class_addpointer
C.2.7 class_addlist
30
C.2.8 class_addanything
C.2.9 class_addcreator
C.2.10 class_sethelpsymbol
C.2.11 pd_new
Generates a new instance of the class cls and returns a pointer to this
instance.
31
C.3 functions: inlets and outlets
All routines for inlets and outlets need a reference to the object-interna of the
class-instance. When instantiating a new object, the necessary dataspace-
variable of the t_object-type is initialized. This variable has to be passed
as the owner-object to the various inlet- and outlet-routines.
C.3.1 inlet_new
• It is not possible to add methods for more than one selector to a right
inlet. Particularly it is not possible to add a universal method for
arbitrary selectors to a right inlet.
C.3.2 oatinlet_new
Generates a new passive inlet for the object that is pointed at by owner.
This inlet enables numerical values to be written directly into the memory
fp, without calling a dedicated method.
32
C.3.3 symbolinlet_new
Generates a new passive inlet for the object that is pointed at by owner.
This inlet enables symbolic values to be written directly into the memory
*sp, without calling a dedicated method.
C.3.4 pointerinlet_new
Generates a new passive inlet for the object that is pointed at by owner.
This inlet enables pointer to be written directly into the memory gp, without
calling a dedicated method.
C.3.5 outlet_new
Generates a new outlet for the object that is pointed at by owner. The
Symbol s indicates the type of the outlet.
symbol symbol-addresse outlet-type
bang &s_bang message (bang)
oat &s_float message (oat)
symbol &s_symbol message (symbol)
pointer &s_gpointer message (pointer)
list &s_list message (list)
0 message
signal &s_signal signal
There are no real dierences between outlets of the various message-types.
At any rate, it makes code more easily readable, if the use of outlet is shown
at creation-time. For outlets for any messages a null-pointer is used. Signal-
outlet must be declared with &s_signal.
Variables if the type t_object provide pointer to one outlet. Whene-
ver a new outlet is generated, its address is stored in the objectvariable
(*owner).ob_outlet.
If more than one message-outlet is needed, the outlet-pointers that are
returned by outlet_new have to be stored manually in the dataspace to
address the given outlets.
33
C.3.6 outlet_bang
C.3.7 outlet_oat
C.3.8 outlet_symbol
C.3.9 outlet_pointer
C.3.10 outlet_list
C.3.11 outlet_anything
34
C.4 functions: DSP
DSP-method
perform-routine
35
C.4.1 CLASS_MAINSIGNALIN
The macro CLASS_MAINSIGNALIN declares, that the class will use signal-
inlets.
The rst macro-argument is a pointer to the signal-class. The second ar-
gument is the type of the class-dataspace. The third argument is a (dummy-
)oatingpoint-variable of the dataspace, that is needed to automatically con-
vert oat-messages into signals if no signal is present at the signal-inlet.
No oat-methods can be used for signal-inlets, that are created this way.
C.4.2 dsp_add
C.4.3 sys_getsr
float sys_getsr(void);
C.5.1 getbytes
C.5.2 copybytes
Copies nbytes bytes from *src into a newly allocated memory. The address
of this memory is returned.
36
C.5.3 freebytes
C.6.1 post
C.6.2 error
37