From ab047cfb36d6dc94db2eaf669da566029ce049b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?IOhannes=20m=20zm=C3=B6lnig?= Date: Tue, 25 Mar 2014 11:42:00 +0000 Subject: renamed HOWTO to 'pd-externals-HOWTO' no longer language-specific names... svn path=/trunk/; revision=17291 --- .../externals-howto/HOWTO-externals-en.tex | 1827 -------------------- doc/tutorials/externals-howto/Makefile | 4 +- .../externals-howto/pd-externals-HOWTO.tex | 1827 ++++++++++++++++++++ 3 files changed, 1829 insertions(+), 1829 deletions(-) delete mode 100644 doc/tutorials/externals-howto/HOWTO-externals-en.tex create mode 100644 doc/tutorials/externals-howto/pd-externals-HOWTO.tex diff --git a/doc/tutorials/externals-howto/HOWTO-externals-en.tex b/doc/tutorials/externals-howto/HOWTO-externals-en.tex deleted file mode 100644 index ab5f3688..00000000 --- a/doc/tutorials/externals-howto/HOWTO-externals-en.tex +++ /dev/null @@ -1,1827 +0,0 @@ -% format latexg -*- latex -*- - -\documentclass[12pt, a4paper,english,titlepage]{article} - -%% HOWTO write an external for Pd -%% Copyright (c) 2001-2014 by IOhannes m zmölnig -%% -%% Permission is granted to copy, distribute and/or modify this document -%% under the terms of the GNU Free Documentation License, Version 1.2 -%% or any later version published by the Free Software Foundation; -%% with no Invariant Sections, no Front-Cover Texts, and no Back-Cover -%% Texts. A copy of the license is included in the LICENSE.txt file. - -\usepackage[utf8]{inputenc} - -\usepackage[T1]{fontenc} -\usepackage[english]{babel} - -% add hypertext support (fine for latex2html) -\usepackage{html} -\usepackage{hyperref} - -% add landscape support (for rotating text through 90deg) -\usepackage{lscape} - -\makeatletter -\@ifpackageloaded{tex4ht} - {\let\iftexforht\@firstoftwo} - {\let\iftexforht\@secondoftwo} -\makeatother - -\iftexforht{\newcommand\pdtilde{\textasciitilde}}{\newcommand\pdtilde{$\sim$}} - -\title{ -HOWTO \\ -write an External \\ -for {\em Pure Data} -} - -\author{ -IOhannes m zmölnig \\ -\\ -\href{http://iem.at/}{institute of electronic music and acoustics} -} - -%\date{} - -\begin{document} -\maketitle - -\begin{abstract} -Pd is a graphical real-time computer-music system that follows the tradition of -IRCAMs {\em ISPW-max}. - -Although plenty of functions are built into Pd, -it is sometimes a pain or simply impossible to create a patch with a certain -functionality out of the given primitives and combinations of these. - -Therefore, Pd can be extended with self made primitives (``objects'') -that are written in complex programming-languages, like {\tt C/C++}. - -This document aims to explain how to write such primitives in {\tt C}, -the popular language that was used to realize Pd. - -\iftexforht{You can download this HOWTO as \href{pd-externals-HOWTO.pdf}{pdf (English)}.}{} - -\end{abstract} - - -\vfill -\newpage - -\tableofcontents - -\vfill -\newpage - -\section{definitions and prerequisites} -Pd refers to the graphical real-time computer-music environment {\em Pure Data} -by Miller~S.~Puckette. - -To fully understand this document, it is necessary to -be acquainted with Pd and to -have a general understanding of programming techniques especially in {\tt C}. - -To write externals yourself, a {\tt C}-compiler that supports the -{\tt ANSI-C}-Standard, like the {\em Gnu C-compiler} (gcc) on linux-systems or -{\em Visual-C++} on windos-plattforms, will be necessary. - -\subsection{classes, instances, objects} -Pd is written in the programming-language {\tt C}. -Due to its graphical nature, Pd is a {\em object-oriented} system. -Unfortunately {\tt C} does not support very well the use of classes. -Thus the resulting source-code is not as elegant as {\tt C++}-code would be, for instance. - -In this document, the expression {\em class} refers to the realisation of a concept -combining data and manipulators on this data. - -Concrete {\em instances of a class} are called {\em objects}. - -\subsection{internals, externals und libraries} - -To avoid confusion of ideas, the expressions {\em internal}, {\em external} and -{\em library} should be explained here. - -\paragraph{Internal} -An {\em internal} is a class that is built into Pd. -Plenty of primitives, such as ``+'', ``pack'' or ``sig\pdtilde'' are {\em internals}. - -\paragraph{External} -An {\em external} is a class that is not built into Pd but is loaded at runtime. -Once loaded into Pd's memory, {\em externals} cannot be distinguished from -{\em internals} any more. - -\paragraph{Library} -A {\em library} is a collection of {\em externals} that are compiled into a -single binary-file. - -{\em Library}-files have to follow a system dependent naming convention: - -\begin{tabular}{c||c|c|c} -library & linux&irix&Win32 \\ -\hline -{\tt my\_lib}&{\tt my\_lib.pd\_linux}&{\tt my\_lib.pd\_irix}& -{\tt my\_lib.dll}\\ -\end{tabular} - -The simplest form of a {\em library} includes exactly one {\em external} -bearing the same name as the {\em library}. - -Unlike {\em externals}, {\em libraries} can be imported by Pd with special operations. -After a {\em library} has been imported, -all included {\em externals} have been loaded into memory and are available as objects. - -Pd supports two modes to import {\em libraries}: - -\begin{itemize} -\item via the command line-option ``{\tt -lib my\_lib}'' -\item by creating an object ``{\tt my\_lib}'' -\end{itemize} - -The first method loads a {\em library} when Pd is started. -This method is preferably used for {\em libraries} that contain several {\em externals}. - -The other method should be used for {\em libraries} that contain exactly -one {\em external} bearing the same name. -Pd checks first, whether a class named ``my\_lib'' is already loaded. -If this is not the case\footnote{ -If a class ``my\_lib'' is already existent, an object ``my\_lib'' will be instantiated -and the procedure is done. -Thus, no {\em library} has been loaded. -Therefore no {\em library} that is named like an already used class-name like, say, ``abs'', -can be loaded.}, all paths are searched for a file called -``{\tt my\_lib.pd\_linux}''\footnote{or another system-dependent filename-extensions (s.a.)}. -If such file is found, all included {\em externals} are loaded into memory by calling a -routine \verb+my_lib_setup()+. -After loading, a class ``my\_lib'' is (again) looked for as a (newly loaded) {\em external}. -If so, an instance of this class is created, else the instantiation fails and an error is -printed. -Anyhow, all {\em external}-classes declared in the {\em library} are loaded by now. - - -\section{my first external: {\tt helloworld}} -Usually the first attempt learning a programming-language is a ``hello world''-application. - -In our case, an object class should be created, that prints the line ``hello world!!'' to -the standard error every time it is triggered with a ``bang''-message. - - - -\subsection{the interface to Pd} -To write a Pd-external a well-defined interface is needed. -This is provided in the header-file ``m\_pd.h''. - -\begin{verbatim} -#include "m_pd.h" -\end{verbatim} - -\subsection{a class and its data space} -First a new class has to be prepared and the data space for this class has to be defined. - -\begin{verbatim} -static t_class *helloworld_class; - -typedef struct _helloworld { - t_object x_obj; -} t_helloworld; -\end{verbatim} - -\verb+hello_worldclass+ is going to be a pointer to the new class. - -The structure \verb+t_helloworld+ (of the type \verb+_helloworld+) is -the data space of the class. - -An absolutely necessary element of the data space is a variable of the type -\verb+t_object+, which is used to store internal object-properties like -the graphical presentation of the object or data about inlets and outlets. - -\verb+t_object+ has to be the first entry in the structure ! - -Because a simple ``hello world''-application needs no variables, -the structure is empty apart from the \verb+t_object+. - - -\subsection{method space} -Apart from the data space, a class needs a set of manipulators (methods) to -manipulate the data with. - -If a message is sent to an instance of our class, a method is called. -These methods are the interfaces to the message system of Pd. -On principal they have no return argument and are therefore of the -type \verb+void+. - -\begin{verbatim} -void helloworld_bang(t_helloworld *x) -{ - post("Hello world !!"); -} -\end{verbatim} - - -This method has an argument of the type \verb+t_helloworld+, -which would enable us to manipulate the data space. - -Since we only want to output ``Hello world!'' -(and, by the way, our data space is quite sparse), -we renounce a manipulation. - -The command \verb+post(char *c,...)+ sends a string to the standard error. -A carriage return is added automatically. -Apart from this, the \verb+post+-command works like the {\tt C}-command \verb+printf()+. - -\subsection{generation of a new class} -To generate a new class, information of the data space and the method space of this class, -have to be passed to Pd when a library is loaded. - -On loading a new library ``my\_lib'', -Pd tries to call a function ``my\_lib\_setup()''. -This function (or functions called by it) -declares the new classes and their properties. -It is only called once, when the library is loaded. -If the function-call fails (e.g., because no function of the specified name is present) -no external of the library will be loaded. - -\begin{verbatim} -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); -} -\end{verbatim} - -\paragraph{class\_new} - -The function \verb+class_new+ creates a new class and returns a pointer to this prototype. - -The first argument is the symbolic name of the class. - -The next two arguments define the constructor and destructor of the class. - -Whenever a class object is created in a Pd-patch, -the class-constructor \verb+(t_newmethod)helloworld_new+ instantiates the object -and initialises the data space. - -Whenever an object is destroyed -(either by closing the containing patch or by deleting the object from the patch) -the destructor frees the dynamically reserved memory. -The allocated memory for the static data space 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 data space, -the size of the data structure has to be passed as the fourth argument. - -The fifth argument has influence on the graphical representation of the class objects. -The default-value is \verb+CLASS_DEFAULT+ or simply ``0''. - -The remaining arguments define the arguments of an object and its type. - -Up to six numeric and symbolic object-arguments can be defined via -\verb+A_DEFFLOAT+ and \verb+A_DEFSYMBOL+. -If more arguments are to be passed to the object -or if the order of atom types should by more flexible, -\verb+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. - -\paragraph{class\_addbang} -We still have to add a method space to the class. - -\verb+class_addbang+ adds a method for a ``bang''-message to the class that is -defined in the first argument. -The added method is defined in the second argument. - - -\subsection{constructor: instantiation of an object} -Each time, an object is created in a Pd-patch, the -constructor that is defined with the \verb+class_new+-command, -generates a new instance of the class. - -The constructor has to be of type \verb+void *+. - -\begin{verbatim} -void *helloworld_new(void) -{ - t_helloworld *x = (t_helloworld *)pd_new(helloworld_class); - - return (void *)x; -} -\end{verbatim} - - -The arguments of the constructor-method depend on the object-arguments -defined with \verb+class_new+. - -\begin{tabular}{l|l} -\verb+class_new+-argument&constructor-argument\\ -\hline -\verb+A_DEFFLOAT+&\verb+t_floatarg f+ \\ -\verb+A_DEFSYMBOL+&\verb+t_symbol *s+ \\ -\verb+A_GIMME+&\verb+t_symbol *s, int argc, t_atom *argv+ -\end{tabular} - -Because there are no object-arguments for our ``hello world''-class, -the constructor has anon too. - -The function \verb+pd_new+ reserves memory for the data space, -initialises the variables that are internal to the object and -returns a pointer to the data space. - -The type-cast to the data space is necessary. - -Normally, the constructor would initialise the object-variables. -However, since we have none, this is not necessary. - - -The constructor has to return a pointer to the instantiated data space. - -\subsection{the code: \tt helloworld} - -\begin{verbatim} -#include "m_pd.h" - -static t_class *helloworld_class; - -typedef struct _helloworld { - t_object x_obj; -} t_helloworld; - -void helloworld_bang(t_helloworld *x) -{ - post("Hello world !!"); -} - -void *helloworld_new(void) -{ - t_helloworld *x = (t_helloworld *)pd_new(helloworld_class); - - return (void *)x; -} - -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); -} -\end{verbatim} - - -\section{a simple external: {\tt counter}} - -Now we want to realize a simple counter as an external. -A ``bang''-trigger outputs the counter-value on the outlet and -afterwards increases the counter-value by 1. - -This class is similar to the previous one, -but the data space is extended by a variable ``counter'' and the -result is written as a message to an outlet instead of -a string to the standard error. - -\subsection{object-variables} -Of course, a counter needs a state-variable to store the actual counter-value. - -State-variables that belong to class instances belong to the data space. - -\begin{verbatim} -typedef struct _counter { - t_object x_obj; - t_int i_count; -} t_counter; -\end{verbatim} - -The integer variable \verb+i_count+ stores the counter-value. - -\subsection{object-arguments} -It is quite useful for a counter, if a initial value can be defined by the user. -Therefore this initial value should be passed to the object at creation-time. - -\begin{verbatim} -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); -} -\end{verbatim} - -So we have an additional argument in the function \verb+class_new+: -\verb+A_DEFFLOAT+ tells Pd, that the object needs one argument of the -type \verb+t_floatarg+. -If no argument is passed, this will default to ``0''. - -\subsection{constructor} -The constructor has some new tasks. -On the one hand, a variable value has to be initialised, -on the other hand, an outlet for the object has to be created. - -\begin{verbatim} -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); - - return (void *)x; -} -\end{verbatim} - -The constructor-method has one argument of type \verb+t_floatarg+ as declared -in the setup-routine by \verb+class_new+. -This argument is used to initialise the counter. - -A new outlet is created with the function \verb+outlet_new+. -The first 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 ``float''. - -\verb+outlet_new+ returns a pointer to the new outlet and saves this very pointer -in the \verb+t_object+-variable \verb+x_obj.ob_outlet+. -If only one outlet is used, the pointer need not additionally be stored in the data space. -If more than one outlets are used, the pointers have to be stored in the data space, -because the \verb+t_object+-variable can only hold one outlet pointer. - -\subsection{the counter method} -When triggered, the counter value should be sent to the outlet -and afterwards be incremented by 1. - -\begin{verbatim} -void counter_bang(t_counter *x) -{ - t_float f=x->i_count; - x->i_count++; - outlet_float(x->x_obj.ob_outlet, f); -} -\end{verbatim} - -The function \verb+outlet_float+ sends a floating-point-value (second argument) to the outlet -that is specified by the first argument. - -We first store the counter in a floating point-buffer. -Afterwards the counter is incremented and not before that the buffer variable is sent -to the outlet. - -What appears to be unnecessary on the first glance, makes sense after further -inspection: -The buffer variable has been realized as \verb+t_float+, -since \verb+outlet_float+ expects a floating point-value and a typecast is -inevitable. - -If the counter value was sent to the outlet before being incremented, -this could result in an unwanted (though well defined) behaviour: -If the counter-outlet directly triggered its own inlet, -the counter-method would be called although the counter value 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 {\em reentrant}-problem. - -\subsection{the code: \tt counter} - -\begin{verbatim} -#include "m_pd.h" - -static t_class *counter_class; - -typedef struct _counter { - t_object x_obj; - t_int i_count; -} t_counter; - -void counter_bang(t_counter *x) -{ - t_float f=x->i_count; - x->i_count++; - outlet_float(x->x_obj.ob_outlet, f); -} - -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); - - return (void *)x; -} - -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); -} -\end{verbatim} - - -\section{a complex external: \tt counter} - -The simple counter of the previous chapter can easily be extended to more complexity. -It might be quite useful to be able to reset the counter to an initial value, -to set upper and lower boundaries 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. - -\subsection{extended data space} - -\begin{verbatim} -typedef struct _counter { - t_object x_obj; - t_int i_count; - t_float step; - t_int i_down, i_up; - t_outlet *f_out, *b_out; -} t_counter; -\end{verbatim} - -The data space has been extended to hold variables for step width and -upper and lower boundaries. -Furthermore pointers for two outlets have been added. - -\subsection{extension of the class} -The new class objects should have methods for different messages, -like ``set'' and ``reset''. -Therefore the method space has to be extended too. - -\begin{verbatim} - counter_class = class_new(gensym("counter"), - (t_newmethod)counter_new, - 0, sizeof(t_counter), - CLASS_DEFAULT, - A_GIMME, 0); -\end{verbatim} - -The class generator \verb+class_new+ has been extended by the argument \verb+A_GIMME+. -This enables a dynamic number of arguments to be passed at the instantiation of the object. - -\begin{verbatim} - class_addmethod(counter_class, - (t_method)counter_reset, - gensym("reset"), 0); -\end{verbatim} - -\verb+class_addmethod+ adds a method for an arbitrary selector to an class. - -The first 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. - -\begin{verbatim} - 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); -\end{verbatim} - -A method for ``set'' followed by a numerical value is added, -as well as a method for the selector ``bound'' followed by two numerical values. - -\begin{verbatim} - class_sethelpsymbol(counter_class, gensym("help-counter")); -\end{verbatim} - -If a Pd-object is right-clicked, a help-patch describing the object-class can be opened. -By default, this patch is located in the directory ``{\em doc/5.reference/}'' and -is named like the symbolic class name. - -An alternative help-patch can be defined with the -\verb+class_sethelpsymbol+-command. - -\subsection{construction of in- and outlets} - -When creating the object, several arguments should be passed by the user. - -\begin{verbatim} -void *counter_new(t_symbol *s, int argc, t_atom *argv) -\end{verbatim} -Because of the declaration of arguments in the \verb+class_new+-function -with \verb+A_GIMME+, -the constructor has following arguments: - -\begin{tabular}{c|l} -\verb+t_symbol *s+ & the symbolic name,\\ -& that was used for object creation \\ -\verb+int argc+ & the number of arguments passed to the object\\ -\verb+t_atom *argv+ & a pointer to a list of {\tt argc} atoms -\end{tabular} - -\begin{verbatim} - t_float f1=0, f2=0; - - x->step=1; - 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: - break; - } - if (argc<2)f2=f1; - x->i_down = (f1i_up = (f1>f2)?f1:f2; - - x->i_count=x->i_down; -\end{verbatim} - -If three arguments are passed, these should be the {\em lower boundary}, -the {\em upper boundary} and the {\em step width}. - -If only two arguments are passed, the step-width defaults to ``1''. -If only one argument is passed, this should be the {\em initial value} of the counter with -step-width of ``1''. - -\begin{verbatim} - inlet_new(&x->x_obj, &x->x_obj.ob_pd, - gensym("list"), gensym("bound")); -\end{verbatim} - -The function \verb+inlet_new+ creates a new ``active'' inlet. -``Active'' means, that a class-method is called each time -a message is sent to an ``active'' inlet. - -Due to the software-architecture, the first inlet is always ``active''. - -The first two arguments of the \verb+inlet_new+-function are -pointers to the interna of the object and to the graphical presentation of the object. - -The symbolic selector that is specified by the third argument is to be -substituted by another symbolic selector (fourth argument) for this inlet. - -Because of this substitution of selectors, -a message on a certain right inlet can be treated as a message with -a certain selector on the leftmost inlet. - -This means: -\begin{itemize} -\item The substituting selector has to be declared by \verb+class_addmethod+ -in the setup-routine. -\item It is possible to simulate a certain right inlet, by sending a message with -this inlet's selector to the leftmost inlet. -\item 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. -\end{itemize} - -\begin{verbatim} - floatinlet_new(&x->x_obj, &x->step); -\end{verbatim} -\verb+floatinlet_new+ generates a new ``passive'' inlet for numerical values. -``Passive'' inlets allow parts of the data space-memory to be written directly -from outside. -Therefore it is not possible to check for illegal inputs. - -The first argument is a pointer to the internal infrastructure of the object. -The second argument is the address in the data space-memory, -where other objects can write too. - -``Passive'' inlets can be created for pointers, symbolic or -numerical (floating point\footnote{ -That's why the {\tt step}-width of the class\/data space is realized as {\tt t\_float}.}) -values. - - -\begin{verbatim} - x->f_out = outlet_new(&x->x_obj, &s_float); - x->b_out = outlet_new(&x->x_obj, &s_bang); -\end{verbatim} - -The pointers returned by \verb+outlet_new+ have to be saved in the class\/data space -to be used later by the outlet-routines. - -The order of the generation of inlets and outlets is important, -since it corresponds to the order of inlets and outlets in the -graphical representation of the object. - -\subsection{extended method space} - -The method for the ``bang''-message has to full fill the more complex tasks. - -\begin{verbatim} -void counter_bang(t_counter *x) -{ - 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); -} -\end{verbatim} - -Each outlet is identified by the \verb+outlet_...+-functions via the -pointer to this outlets. - -The remaining methods still have to be implemented: - -\begin{verbatim} -void counter_reset(t_counter *x) -{ - x->i_count = x->i_down; -} - -void counter_set(t_counter *x, t_floatarg f) -{ - x->i_count = f; -} - -void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2) -{ - x->i_down = (f1i_up = (f1>f2)?f1:f2; -} -\end{verbatim} - -\subsection{the code: \tt counter} - -\begin{verbatim} -#include "m_pd.h" - -static t_class *counter_class; - -typedef struct _counter { - t_object x_obj; - t_int i_count; - t_float step; - t_int i_down, i_up; - t_outlet *f_out, *b_out; -} t_counter; - -void counter_bang(t_counter *x) -{ - 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); -} - -void counter_reset(t_counter *x) -{ - x->i_count = x->i_down; -} - -void counter_set(t_counter *x, t_floatarg f) -{ - x->i_count = f; -} - -void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2) -{ - x->i_down = (f1i_up = (f1>f2)?f1:f2; -} - -void *counter_new(t_symbol *s, int argc, t_atom *argv) -{ - t_counter *x = (t_counter *)pd_new(counter_class); - t_float f1=0, f2=0; - - x->step=1; - 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: - break; - } - if (argc<2)f2=f1; - - x->i_down = (f1i_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); - - x->f_out = outlet_new(&x->x_obj, &s_float); - x->b_out = outlet_new(&x->x_obj, &s_bang); - - return (void *)x; -} - -void counter_setup(void) { - counter_class = class_new(gensym("counter"), - (t_newmethod)counter_new, - 0, sizeof(t_counter), - CLASS_DEFAULT, - A_GIMME, 0); - - class_addbang (counter_class, counter_bang); - class_addmethod(counter_class, - (t_method)counter_reset, gensym("reset"), 0); - 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")); -} -\end{verbatim} - - -\section{a signal-external: {\tt pan\pdtilde}} -Signal classes are normal Pd-classes, that offer additional methods for signals. - - -All methods and concepts that can be realized with normal object classes can -therefore be realized with signal classes too. - -Per agreement, the symbolic names of signal classes end with a tilde \pdtilde. - -The class ``pan\pdtilde'' shall demonstrate, how signal classes are written. - -A signal on the left inlet is mixed with a signal on the second inlet. -The mixing-factor between 0 and 1 is defined via a \verb+t_float+-message -on a third inlet. - -\subsection{variables of a signal class} -Since a signal-class is only an extended normal class, -there are no principal differences between the data spaces. - -\begin{verbatim} -typedef struct _pan_tilde { - t_object x_obj; - - t_sample f_pan; - t_float f; - - t_inlet *x_in2; - t_inlet *x_in3; - - t_outlet*x_out; - -} t_pan_tilde; -\end{verbatim} - -Only one variable \verb+f_pan+ for the {\em mixing-factor} of the panning-function is needed. - -The other variable \verb+f+ is needed whenever a signal-inlet is needed too. -If no signal but only a float-message is present at a signal-inlet, this -variable is used to automatically convert the float to signal. - -Finally, we have the members \verb+x_in2+, \verb+x_in3+ and \verb+x_out+, -which are needed to store handles to the various extra inlets (resp. outlets) of the object. - -\subsection{signal-classes} - -\begin{verbatim} -void pan_tilde_setup(void) { - pan_tilde_class = class_new(gensym("pan~"), - (t_newmethod)pan_tilde_new, - (t_method)pan_tilde_free, - 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); -} -\end{verbatim} - -Something has changed with the \verb+class_new+ function: -the third argument specifies a ``free-method'' (aka {\em destructor}), which is called whenever an instance of the object -is to be deleted (just like the ``new-method'' is called whenever an instance is to be created). -In the prior examples this was set to \verb+0+ (meaning: we don't care), -but in this example we have to clean up some ressources when we don't need them any more. - -More interestingly, a method for signal-processing has to be provided by each signal class. - -Whenever Pd's audio engine is started, a message with the selector ``dsp'' -is sent to each object. -Each class that has a method for the ``dsp''-message is recognised as signal class. - -Signal classes that want to provide signal-inlets have to -declare this via the \verb+CLASS_MAINSIGNALIN+-macro. -This enables signals at the first (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 \verb+t_float+-messages any longer. - -The first argument of the macro is a pointer to the signal class. -The second argument is the type of the class's data space. - -The last argument is a dummy-variable out of the data space that is needed -to replace non-existing signal at the signal-inlet(s) with \verb+t_float+-messages. - -\subsection{construction of signal-inlets and -outlets} - -\begin{verbatim} -void *pan_tilde_new(t_floatarg f) -{ - t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class); - - x->f_pan = f; - - x->x_in2 = inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); - x->x_in3 = floatinlet_new (&x->x_obj, &x->f_pan); - - x->x_out = outlet_new(&x->x_obj, &s_signal); - - return (void *)x; -} -\end{verbatim} - -Additional signal-inlets are added like other inlets with the routine \verb+inlet_new+. -The last two arguments are references to the symbolic selector ``signal'' -in the lookup-table. - -Signal-outlets are also created like normal (message-)outlets, -by setting the outlet-selector to ``signal''. - -The newly created inlets/outlets are ``user-allocated'' data. -Pd will keep track of all the ressources it automatically creates (like the default inlet), -and will eventually free these ressources once they are no longer needed. -However, if we request an ``extra'' ressource (like the additional inlets/outlets in this example; -or - more commonly - memory that is allocated via \verb+malloc+ or similar), -we have to make sure ourselves, that these ressources are freed when no longer needed. -If we fail to do so, we will invariably create a dreaded {\em memory leak}. - -Therefore, we store the ``handles'' to the newly created inlets/outlets as returned by the \verb+..._new+ routines -for later use. - - -\subsection{DSP-methods} -Whenever Pd's audio engine is turned on, -all signal-objects declare their perform-routines that are to be added to the DSP-tree. - -The ``dsp''-method has two arguments, the pointer to the class-data space, and -a pointer to an array of signals. - -The signals are arranged in the array in such way, -that they are ordered in a clockwise way in the graphical representation of the -object.\footnote{ -If both left and right in- and out-signals exist, this means: -First is the leftmost in-signal followed by the right in-signals; -after the right out-signals, finally there comes the leftmost out-signal.} - -\begin{verbatim} -void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp) -{ - dsp_add(pan_tilde_perform, 5, x, - sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); -} -\end{verbatim} - -\verb+dsp_add+ adds a {\em perform}-routine (as declared in the first argument) -to the DSP-tree. - -The second argument is the number of the following pointers to diverse variables. -Which pointers to which variables are passed is not limited. - -Here, sp[0] is the first in-signal, sp[1] represents the second in-signal and -sp[3] points to the out-signal. - -The structure \verb+t_signal+ contains a pointer to the -its signal-vector \verb+().s_vec+ (an array of samples of type \verb+t_sample+), -and the length of this signal-vector \verb+().s_n+. - -Since all signal vectors of a patch (not including it's sub-patches) are of the same length, -it is sufficient to get the length of one of these vectors. - -\subsection{perform-routine} -The perform-routine is the DSP-heart of each signal class. - -A pointer to an integer-array is passed to it. -This array contains the pointers, that were passed via \verb+dsp_add+, -which must be casted back to their real type. - -The perform-routine has to return a pointer to integer, -that points to the address behind the stored pointers of the routine. -This means, that the return argument equals the -argument of the perform-routine plus -the number of pointer variables (as declared as the second argument -of \verb+dsp_add+) plus one. - -\begin{verbatim} -t_int *pan_tilde_perform(t_int *w) -{ - t_pan_tilde *x = (t_pan_tilde *)(w[1]); - 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; - - while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan; - - return (w+6); -} -\end{verbatim} - -Each sample of the signal vectors is read and manipulated in the \verb+while+-loop. - - -Optimisation of the DSP-tree tries to avoid unnecessary copy-operations. -Therefore it is possible, that in- and out-signal are located -at the same address in the memory. -In this case, the programmer has to be careful not to write into the out-signal -before having read the in-signal to avoid overwriting data that is not yet saved. - -\subsection{destructor} - -\begin{verbatim} -void pan_tilde_free(t_pan_tilde *x) -{ - inlet_free(x->x_in2); - inlet_free(x->x_in3); - outlet_free(x->x_out); -} -\end{verbatim} - -The object has some dynamically allocated ressources, -namely the additional inlets and outlets we created in the constructor. - -Since Pd doesn't track dynamically allocated ressources for us, -we have to free them manually in the ``free-method'' (aka: destructor). -We do so by calling \verb+inlet_free+ (resp. \verb+outlet_free+) on the handles to -our additional iolets. - -Note that we do not need to free the default first outlet. -As it is created automatically by Pd, it is also freed automatically. - -\subsection{the code: \tt pan\pdtilde} - -\begin{verbatim} -#include "m_pd.h" - -static t_class *pan_tilde_class; - -typedef struct _pan_tilde { - t_object x_obj; - t_sample f_pan; - t_sample f; - - t_inlet *x_in2; - t_inlet *x_in3; - t_outlet*x_out; -} t_pan_tilde; - -t_int *pan_tilde_perform(t_int *w) -{ - t_pan_tilde *x = (t_pan_tilde *)(w[1]); - 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; - - while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan; - - return (w+6); -} - -void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp) -{ - dsp_add(pan_tilde_perform, 5, x, - sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); -} - -void pan_tilde_free(t_pan_tilde *x) -{ - inlet_free(x->x_in2); - inlet_free(x->x_in3); - outlet_free(x->x_out); -} - -void *pan_tilde_new(t_floatarg f) -{ - t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class); - - x->f_pan = f; - - x->x_in2=inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); - x->x_in3=floatinlet_new (&x->x_obj, &x->f_pan); - x->x_out=outlet_new(&x->x_obj, &s_signal); - - return (void *)x; -} - -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); -} -\end{verbatim} - -\vfill -\newpage -\begin{appendix} - -\section{Pd's message-system} -Non-audio-data are distributed via a message-system. -Each message consists of a ``selector'' and a list of atoms. - -\subsection{atoms} - -There are three kinds of atoms: -\begin{itemize} -\item {\em A\_FLOAT}: a numerical value (floating point) -\item {\em A\_SYMBOL}: a symbolic value (string) -\item {\em A\_POINTER}: a pointer -\end{itemize} - -Numerical values are always floating point-values (\verb+t_float+), -even if they could be displayed as integer values. - -Each symbol is stored in a lookup-table for reasons of performance. -The command \verb+gensym+ looks up a string in the lookup-table and -returns the address of the symbol. -If the string is not yet to be found in the table, -a new symbol is added. - -Atoms of type {\em A\_POINTER} are not very important (for simple externals). - -The type of an atom \verb+a+ is stored in the structure-element \verb+a.a_type+. - -\subsection{selectors} -The selector is a symbol that defines the type of a message. -There are five predefined selectors: -\begin{itemize} -\item ``{\tt bang}'' labels a trigger event. -A ``bang''-message consists only of the selector and contains no lists of atoms. -\item ``{\tt float}'' labels a numerical value. -The list of a ``float''-Message contains one single atom of type {\em A\_FLOAT} -\item ``{\tt symbol}'' labels a symbolic value. -The list of a ``symbol''-Message contains one single atom of type {\em A\_SYMBOL} -\item ``{\tt pointer}'' labels a pointer value. -The list of a ``pointer''-Message contains one single atom of type {\em A\_POINTER} -\item ``{\tt list}'' labels a list of one or more atoms of arbitrary type. -\end{itemize} - -Since the symbols for these selectors are used quite often, -their address in the lookup-table can be queried directly, -without having to use \verb+gensym+: - -\begin{tabular}{l||l|l} -selector&lookup-routine&lookup-address\\ -\hline -\tt bang &\verb+gensym("bang")+ & \verb+&s_bang+ \\ -\tt float &\verb+gensym("float")+ & \verb+&s_float+ \\ -\tt symbol &\verb+gensym("symbol")+ & \verb+&s_symbol+ \\ -\tt pointer &\verb+gensym("pointer")+ & \verb+&s_pointer+ \\ -\tt list &\verb+gensym("list")+ & \verb+&s_list+ \\ ---- (signal) &\verb+gensym("signal")+&\verb+&s_symbol+ -\end{tabular} - -Other selectors can be used as well. -The receiving class has to provide a method for a specifique selector -or for ``anything'', which is any arbitrary selector. - -Messages that have no explicit selector and start with a numerical value, -are recognised automatically either as ``float''-message (only one atom) or as -``list''-message (several atoms). - -For example, messages ``\verb+12.429+'' and ``\verb+float 12.429+'' are identical. -Likewise, the messages ``\verb+list 1 for you+'' is identical to ``\verb+1 for you+''. - -\section{Pd-types} -Since Pd is used on several platforms, -many ordinary types of variables, like \verb|int|, are re-defined. -To write portable code, it is reasonable to use types provided by Pd. - -Apart from this there are many predefined types, -that should make the life of the programmer simpler. - -Generally, Pd-types start with \verb|t_|. - -\begin{tabular}{c|l} -Pd-type & description \\ -\hline\hline -\verb+t_atom+& atom \\ -\verb+t_float+ & floating point value\\ -\verb+t_symbol+ & symbol \\ -\verb+t_gpointer+ & pointer (to graphical objects) \\ -\hline -\verb+t_int+ & integer value \\ -\verb+t_signal+ & structure of a signal \\ -\verb+t_sample+ & audio signal-value (floating point)\\ -\verb+t_outlet+ & outlet of an object \\ -\verb+t_inlet+ & inlet of an object \\ -\verb+t_object+ & object-interna \\ -\hline -\verb+t_class+ & a Pd-class \\ -\verb+t_method+ & class-method \\ -\verb+t_newmethod+ & pointer to a constructor (new-routine) \\ -\end{tabular} - - -\section{important functions in ``m\_pd.h''} - -\subsection{functions: atoms} - -\subsubsection{SETFLOAT} -\begin{verbatim} -SETFLOAT(atom, f) -\end{verbatim} -This macro sets the type of \verb+atom+ to \verb+A_FLOAT+ -and stores the numerical value \verb+f+ in this atom. - -\subsubsection{SETSYMBOL} -\begin{verbatim} -SETSYMBOL(atom, s) -\end{verbatim} -This macro sets the type of \verb+atom+ to \verb+A_SYMBOL+ -and stores the symbolic pointer \verb+s+ in this atom. - -\subsubsection{SETPOINTER} -\begin{verbatim} -SETPOINTER(atom, pt) -\end{verbatim} -This macro sets the type of \verb+atom+ to \verb+A_POINTER+ -and stores the pointer \verb+pt+ in this atom. - -\subsubsection{atom\_getfloat} -\begin{verbatim} -t_float atom_getfloat(t_atom *a); -\end{verbatim} -If the type of the atom \verb+a+ is \verb+A_FLOAT+, -the numerical value of this atom else ``0.0'' is returned. - -\subsubsection{atom\_getfloatarg} -\begin{verbatim} -t_float atom_getfloatarg(int which, int argc, t_atom *argv) -\end{verbatim} -If the type of the atom -- that is found at in the atom-list -\verb+argv+ with the length \verb+argc+ at the place \verb+which+ -- -is \verb+A_FLOAT+, the numerical value of this atom else ``0.0'' is returned. - -\subsubsection{atom\_getint} -\begin{verbatim} -t_int atom_getint(t_atom *a); -\end{verbatim} -If the type of the atom \verb+a+ is \verb+A_FLOAT+, -its numerical value is returned as integer else ``0'' is returned. - -\subsubsection{atom\_getsymbol} -\begin{verbatim} -t_symbol atom_getsymbol(t_atom *a); -\end{verbatim} -If the type of the atom \verb+a+ is \verb+A_SYMBOL+, -a pointer to this symbol is returned, else a null-pointer ``0'' is returned. - -\subsubsection{atom\_gensym} -\begin{verbatim} -t_symbol *atom_gensym(t_atom *a); -\end{verbatim} -If the type of the atom \verb+a+ is \verb+A_SYMBOL+, -a pointer to this symbol is returned. - -Atoms of a different type, are ``reasonably'' converted into a string. -This string is -- on demand -- inserted into the symbol-table. -A pointer to this symbol is returned. - -\subsubsection{atom\_string} -\begin{verbatim} -void atom_string(t_atom *a, char *buf, unsigned int bufsize); -\end{verbatim} -Converts an atom \verb+a+ into a {\tt C}-string \verb+buf+. -The memory to this char-Buffer has to be reserved manually and -its length has to be declared in \verb+bufsize+. - -\subsubsection{gensym} -\begin{verbatim} -t_symbol *gensym(char *s); -\end{verbatim} -Checks, whether the C-string \verb+*s+ has already been inserted into the symbol-table. -If no entry exists, it is created. -A pointer to the symbol is returned. - -\subsection{functions: classes} -\subsubsection{class\_new} -\begin{verbatim} -t_class *class_new(t_symbol *name, - t_newmethod newmethod, t_method freemethod, - size_t size, int flags, - t_atomtype arg1, ...); -\end{verbatim} -Generates a class with the symbolic name \verb+name+. -\verb+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 \verb+freemethod+ (without any return argument), -when the object is destroyed. - -\verb+size+ is the static size of the class-data space, -that is returned by \verb+sizeof(t_mydata)+. - -\verb+flags+ define the presentation of the graphical object. -A (more or less arbitrary) combination of following objects is possible: - -\begin{tabular}{l|l} -flag&description\\ -\hline -\verb+CLASS_DEFAULT+ & a normal object with one inlet \\ -\verb+CLASS_PD+ & \em object (without graphical presentation) \\ -\verb+CLASS_GOBJ+ & \em pure graphical object (like arrays, graphs,...)\\ -\verb+CLASS_PATCHABLE+ & \em a normal object (with one inlet) \\ -\verb+CLASS_NOINLET+ & the default inlet is suppressed \\ -\end{tabular} - -Flags the description of which is printed in {\em italic} -are of small importance for writing externals. - -The remaining arguments \verb+arg1,...+ define the -types of object-arguments passed at the creation of a class-object. -A maximum of six type checked arguments can be passed to an object. -The list of argument-types are terminated by ``0''. - -Possible types of arguments are: - -\begin{tabular}{l|l} -\verb+A_DEFFLOAT+ & a numerical value \\ -\verb+A_DEFSYMBOL+ & a symbolical value \\ -\verb+A_GIMME+ & a list of atoms of arbitrary length and types \\ -\end{tabular} - -If more than six arguments are to be passed, -\verb+A_GIMME+ has to be used and a manual type-check has to be made. - -\subsubsection{class\_addmethod} -\begin{verbatim} -void class_addmethod(t_class *c, t_method fn, t_symbol *sel, - t_atomtype arg1, ...); -\end{verbatim} -Adds a method \verb+fn+ for a selector \verb+sel+ to a class \verb+c+. - -The remaining arguments \verb+arg1,...+ define the -types of the list of atoms that follow the selector. -A maximum of six type-checked arguments can be passed. -If more than six arguments are to be passed, -\verb+A_GIMME+ has to be used and a manual type-check has to be made. - -The list of arguments is terminated by ``0''. - - -Possible types of arguments are: - -\begin{tabular}{l|l} -\verb+A_DEFFLOAT+ & a numerical value \\ -\verb+A_DEFSYMBOL+ & a symbolical value \\ -\verb+A_POINTER+ & a pointer \\ -\verb+A_GIMME+ & a list of atoms of arbitrary length and types \\ -\end{tabular} - -\subsubsection{class\_addbang} -\begin{verbatim} -void class_addbang(t_class *c, t_method fn); -\end{verbatim} -Adds a method \verb+fn+ for ``bang''-messages to the class \verb+c+. - -The argument of the ``bang''-method is a pointer to the class-data space: - -\verb+void my_bang_method(t_mydata *x);+ - -\subsubsection{class\_addfloat} -\begin{verbatim} -void class_addfloat(t_class *c, t_method fn); -\end{verbatim} -Adds a method \verb+fn+ for ``float''-messages to the class \verb+c+. - -The arguments of the ``float''-method is a pointer to the class-data space and -a floating point-argument: - -\verb+void my_float_method(t_mydata *x, t_floatarg f);+ - -\subsubsection{class\_addsymbol} -\begin{verbatim} -void class_addsymbol(t_class *c, t_method fn); -\end{verbatim} -Adds a method \verb+fn+ for ``symbol''-messages to the class \verb+c+. - -The arguments of the ``symbol''-method is a pointer to the class-data space and -a pointer to the passed symbol: - -\verb+void my_symbol_method(t_mydata *x, t_symbol *s);+ - -\subsubsection{class\_addpointer} -\begin{verbatim} -void class_addpointer(t_class *c, t_method fn); -\end{verbatim} -Adds a method \verb+fn+ for ``pointer''-messages to the class \verb+c+. - -The arguments of the ``pointer''-method is a pointer to the class-data space and -a pointer to a pointer: - -\verb+void my_pointer_method(t_mydata *x, t_gpointer *pt);+ - -\subsubsection{class\_addlist} -\begin{verbatim} -void class_addlist(t_class *c, t_method fn); -\end{verbatim} -Adds a method \verb+fn+ for ``list''-messages to the class \verb+c+. - -The arguments of the ``list''-method are -- apart from a pointer to the class-data space -- -a pointer to the selector-symbol (always \verb+&s_list+), -the number of atoms and a pointer to the list of atoms: - -\verb+void my_list_method(t_mydata *x,+ - -\verb+ t_symbol *s, int argc, t_atom *argv);+ - -\subsubsection{class\_addanything} -\begin{verbatim} -void class_addanything(t_class *c, t_method fn); -\end{verbatim} -Adds a method \verb+fn+ for an arbitrary message to the class \verb+c+. - -The arguments of the anything-method are -- apart from a pointer to the class-data space -- -a pointer to the selector-symbol, -the number of atoms and a pointer to the list of atoms: - -\verb+void my_any_method(t_mydata *x,+ - -\verb+ t_symbol *s, int argc, t_atom *argv);+ - -\subsubsection{class\_addcreator} -\begin{verbatim} - void class_addcreator(t_newmethod newmethod, t_symbol *s, - t_atomtype type1, ...); -\end{verbatim} -Adds a creator-symbol \verb+s+, alternative to the symbolic class name, -to the constructor \verb+newmethod+. -Thus, objects can be created either by their ``real'' class name or -an alias-name (p.e. an abbreviation, like the internal ``float'' resp. ``f''). - -The ``0''-terminated list of types corresponds to that of \verb+class_new+. - -\subsubsection{class\_sethelpsymbol} -\begin{verbatim} -void class_sethelpsymbol(t_class *c, t_symbol *s); -\end{verbatim} - -If a Pd-object is right-clicked, a help-patch for the corresponding object class -can be opened. -By default this is a patch with the symbolic class name in the -directory ``{\em doc/5.reference/}''. - -The name of the help-patch for the class that is pointed to by \verb+c+ -is changed to the symbol \verb+s+. - -Therefore, several similar classes can share a single help-patch. - -Path-information is relative to the default help path {\em doc/5.reference/}. - -\subsubsection{pd\_new} -\begin{verbatim} -t_pd *pd_new(t_class *cls); -\end{verbatim} -Generates a new instance of the class \verb+cls+ and -returns a pointer to this instance. - -\subsection{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 data space-variable of the \verb+t_object+-type is initialised. -This variable has to be passed as the \verb+owner+-object to the -various inlet- and outlet-routines. - -\subsubsection{inlet\_new} -\begin{verbatim} -t_inlet *inlet_new(t_object *owner, t_pd *dest, - t_symbol *s1, t_symbol *s2); -\end{verbatim} -Generates an additional ``active'' inlet for the object -that is pointed at by \verb+owner+. -Generally, \verb+dest+ points at ``\verb+owner.ob_pd+''. - -The selector \verb+s1+ at the new inlet is substituted by the selector \verb+s2+. - -If a message with selector \verb+s1+ appears at the new inlet, -the class-method for the selector \verb+s2+ is called. - -This means -\begin{itemize} -\item The substituting selector has to be declared by \verb+class_addmethod+ -in the setup-routine. -\item It is possible to simulate a certain right inlet, by sending a message with -this inlet's selector to the leftmost inlet. - -Using an empty symbol (\verb+gensym("")+) as selector -makes it impossible to address a right inlet via the leftmost one. - -\item 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. -\end{itemize} - -\subsubsection{floatinlet\_new} -\begin{verbatim} -t_inlet *floatinlet_new(t_object *owner, t_float *fp); -\end{verbatim} -Generates a new ``passive'' inlet for the object that is pointed at by \verb+owner+. -This inlet enables numerical values to be written directly into the -memory \verb+fp+, without calling a dedicated method. - -\subsubsection{symbolinlet\_new} -\begin{verbatim} -t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp); -\end{verbatim} -Generates a new ``passive'' inlet for the object that is pointed at by \verb+owner+. -This inlet enables symbolic values to be written directly into the -memory \verb+*sp+, without calling a dedicated method. - - -\subsubsection{pointerinlet\_new} -\begin{verbatim} -t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp); -\end{verbatim} -Generates a new ``passive'' inlet for the object that is pointed at by \verb+owner+. -This inlet enables pointer to be written directly into the -memory \verb+gp+, without calling a dedicated method. - -\subsubsection{outlet\_new} -\begin{verbatim} -t_outlet *outlet_new(t_object *owner, t_symbol *s); -\end{verbatim} -Generates a new outlet for the object that is pointed at by \verb+owner+. -The Symbol \verb+s+ indicates the type of the outlet. - -\begin{tabular}{c|l||l} -symbol & symbol-address & outlet-type \\ -\hline\hline -``bang'' & \verb+&s_bang+ & message (bang)\\ -``float'' & \verb+&s_float+ & message (float)\\ -``symbol'' & \verb+&s_symbol+ & message (symbol) \\ -``pointer'' & \verb+&s_gpointer+ & message (pointer)\\ -``list'' & \verb+&s_list+ & message (list) \\ ---- & 0 & message \\ -\hline -``signal'' & \verb+&s_signal+ & signal \\ -\end{tabular} - -There are no real differences 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 \verb+&s_signal+. - -Variables if the type \verb+t_object+ provide pointer to one outlet. -Whenever a new outlet is generated, its address is stored in the -object variable \verb+(*owner).ob_outlet+. - -If more than one message-outlet is needed, -the outlet-pointers that are returned by \verb+outlet_new+ -have to be stored manually in the data space -to address the given outlets. - -\subsubsection{outlet\_bang} -\begin{verbatim} -void outlet_bang(t_outlet *x); -\end{verbatim} -Outputs a ``bang''-message at the outlet specified by \verb+x+. - -\subsubsection{outlet\_float} -\begin{verbatim} -void outlet_float(t_outlet *x, t_float f); -\end{verbatim} -Outputs a ``float''-message with the numeric value \verb+f+ -at the outlet specified by \verb+x+. - -\subsubsection{outlet\_symbol} -\begin{verbatim} -void outlet_symbol(t_outlet *x, t_symbol *s); -\end{verbatim} -Outputs a ``symbol''-message with the symbolic value \verb+s+ -at the outlet specified by \verb+x+. - -\subsubsection{outlet\_pointer} -\begin{verbatim} -void outlet_pointer(t_outlet *x, t_gpointer *gp); -\end{verbatim} -Outputs a ``pointer''-message with the pointer \verb+gp+ -at the outlet specified by \verb+x+. - -\subsubsection{outlet\_list} -\begin{verbatim} -void outlet_list(t_outlet *x, - t_symbol *s, int argc, t_atom *argv); -\end{verbatim} -Outputs a ``list''-message at the outlet specified by \verb+x+. -The list contains \verb+argc+ atoms. -\verb+argv+ points to the first element of the atom-list. - -Independent of the symbol \verb+s+, the selector ``list'' will precede -the list. - -To make the code more readable, -\verb+s+ should point to the symbol list -(either via \verb+gensym("list")+ or via \verb+&s_list+) - -\subsubsection{outlet\_anything} -\begin{verbatim} -void outlet_anything(t_outlet *x, - t_symbol *s, int argc, t_atom *argv); -\end{verbatim} -Outputs a message at the outlet specified by \verb+x+. - -The message-selector is specified with \verb+s+. -It is followed by \verb+argc+ atoms. -\verb+argv+ points to the first element of the atom-list. - -\subsection{functions: DSP} -If a class should provide methods for digital signal-processing, -a method for the selector ``dsp'' (followed by no atoms) -has to be added to this class - -Whenever Pd's audio engine is started, -all objects that provide a ``dsp''-method are identified as instances of signal classes. - -\paragraph{DSP-method} - -\begin{verbatim} -void my_dsp_method(t_mydata *x, t_signal **sp) -\end{verbatim} - -In the ``dsp''-method a class method for signal-processing -is added to the DSP-tree by the function \verb+dsp_add+. - -Apart from the data space \verb+x+ of the object, -an array of signals is passed. -The signals in the array are arranged in such a way, -that they can be read in the graphical representation of the object -clockwisely. - -In case there are both two in- and out-signals, this means: - -\begin{tabular}{c|r} -pointer & to signal \\ -\hline\hline -sp[0] & left in-signal \\ -sp[1] & right in-signal \\ -sp[2] & right out-signal \\ -sp[3] & left out-signal \\ -\end{tabular} - -The signal structure contains apart from other things: - -\begin{tabular}{c|l} -structure-element & description \\ -\hline -\verb+s_n+ & length of the signal vector \\ -\verb+s_vec+ & pointer to the signal vector \\ -\end{tabular} - -The signal vector is an array of samples of type \verb+t_sample+. - -\paragraph{perform-routine} -\begin{verbatim} -t_int *my_perform_routine(t_int *w) -\end{verbatim} - - -A pointer \verb+w+ to an array (of integer) is passed to -the perform-routine that is inserted into the DSP-tree by \verb+class_add+. - -In this array the pointers that are passed via \verb+dsp_add+ are stored. -These pointers have to be casted back to their original type. - -The first pointer is stored at \verb+w[1]+ !!! - -The perform-routine has to return a pointer to integer, -that points directly behind the memory, where the object's pointers are stored. -This means, that the return-argument equals the routine's argument \verb+w+ -plus the number of used pointers -(as defined in the second argument of \verb+dsp_add+) plus one. - -\subsubsection{CLASS\_MAINSIGNALIN} -\begin{verbatim} -CLASS_MAINSIGNALIN(, , ); -\end{verbatim} -The macro \verb+CLASS_MAINSIGNALIN+ declares, -that the class will use signal-inlets. - -The first macro-argument is a pointer to the signal-class. -The second argument is the type of the class-data space. -The third argument is a (dummy-)floating point-variable of the data space, -that is needed to automatically convert ``float''-messages into signals -if no signal is present at the signal-inlet. - -No ``float''-methods can be used for signal-inlets, that are created this way. - -\subsubsection{dsp\_add} -\begin{verbatim} -void dsp_add(t_perfroutine f, int n, ...); -\end{verbatim} -Adds the perform-routine \verb+f+ to the DSP-tree. -The perform-routine is called at each DSP-cycle. - -The second argument\verb+n+ defines the number of following pointer-arguments - -Which pointers to which data are passes is not limited. -Generally, pointers to the data space of the object and to the -signal-vectors are reasonable. -The length of the signal-vectors should also be passed to manipulate signals effectively. - -\subsubsection{sys\_getsr} -\begin{verbatim} -float sys_getsr(void); -\end{verbatim} -Returns the sampler ate of the system. - -\subsection{functions: memory} -\subsubsection{getbytes} -\begin{verbatim} -void *getbytes(size_t nbytes); -\end{verbatim} -Reserves \verb+nbytes+ bytes and returns a pointer to the allocated memory. - -\subsubsection{copybytes} -\begin{verbatim} -void *copybytes(void *src, size_t nbytes); -\end{verbatim} -Copies \verb+nbytes+ bytes from \verb+*src+ into a newly allocated memory. -The address of this memory is returned. - -\subsubsection{freebytes} -\begin{verbatim} -void freebytes(void *x, size_t nbytes); -\end{verbatim} -Frees \verb+nbytes+ bytes at address \verb+*x+. - -\subsection{functions: output} -\subsubsection{post} -\begin{verbatim} -void post(char *fmt, ...); -\end{verbatim} - -Writes a {\tt C}-string to the standard error (shell). - -\subsubsection{error} -\begin{verbatim} -void error(char *fmt, ...); -\end{verbatim} - -Writes a {\tt C}-string as an error-message to the standard error (shell). - -The object that has output the error-message is marked and -can be identified via the Pd-menu {\em Find->Find last error}. - -\end{appendix} - -\end{document} - diff --git a/doc/tutorials/externals-howto/Makefile b/doc/tutorials/externals-howto/Makefile index e1ce8d3a..8d42f47f 100644 --- a/doc/tutorials/externals-howto/Makefile +++ b/doc/tutorials/externals-howto/Makefile @@ -1,4 +1,4 @@ -HOWTO=HOWTO-externals-en +HOWTO=pd-externals-HOWTO HOWTO_EXAMPLES=example1 example2 example3 example4 @@ -27,7 +27,7 @@ pdf: $(HOWTO).pdf html: $(HOWTO).tex $(HOWTO).pdf mkdir -p $(HTMLDIR) $(HTLATEX) $< "$(HTLATEX_OPTIONS2)" "$(HTLATEX_OPTIONS3)" "-d$(HTMLDIR)/" - cp $(HOWTO).pdf "$(HTMLDIR)/pd-externals-HOWTO.pdf" + cp $(HOWTO).pdf "$(HTMLDIR)/" clean: -rm -f *.aux *.log *.toc *.out *.dvi diff --git a/doc/tutorials/externals-howto/pd-externals-HOWTO.tex b/doc/tutorials/externals-howto/pd-externals-HOWTO.tex new file mode 100644 index 00000000..ab5f3688 --- /dev/null +++ b/doc/tutorials/externals-howto/pd-externals-HOWTO.tex @@ -0,0 +1,1827 @@ +% format latexg -*- latex -*- + +\documentclass[12pt, a4paper,english,titlepage]{article} + +%% HOWTO write an external for Pd +%% Copyright (c) 2001-2014 by IOhannes m zmölnig +%% +%% Permission is granted to copy, distribute and/or modify this document +%% under the terms of the GNU Free Documentation License, Version 1.2 +%% or any later version published by the Free Software Foundation; +%% with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +%% Texts. A copy of the license is included in the LICENSE.txt file. + +\usepackage[utf8]{inputenc} + +\usepackage[T1]{fontenc} +\usepackage[english]{babel} + +% add hypertext support (fine for latex2html) +\usepackage{html} +\usepackage{hyperref} + +% add landscape support (for rotating text through 90deg) +\usepackage{lscape} + +\makeatletter +\@ifpackageloaded{tex4ht} + {\let\iftexforht\@firstoftwo} + {\let\iftexforht\@secondoftwo} +\makeatother + +\iftexforht{\newcommand\pdtilde{\textasciitilde}}{\newcommand\pdtilde{$\sim$}} + +\title{ +HOWTO \\ +write an External \\ +for {\em Pure Data} +} + +\author{ +IOhannes m zmölnig \\ +\\ +\href{http://iem.at/}{institute of electronic music and acoustics} +} + +%\date{} + +\begin{document} +\maketitle + +\begin{abstract} +Pd is a graphical real-time computer-music system that follows the tradition of +IRCAMs {\em ISPW-max}. + +Although plenty of functions are built into Pd, +it is sometimes a pain or simply impossible to create a patch with a certain +functionality out of the given primitives and combinations of these. + +Therefore, Pd can be extended with self made primitives (``objects'') +that are written in complex programming-languages, like {\tt C/C++}. + +This document aims to explain how to write such primitives in {\tt C}, +the popular language that was used to realize Pd. + +\iftexforht{You can download this HOWTO as \href{pd-externals-HOWTO.pdf}{pdf (English)}.}{} + +\end{abstract} + + +\vfill +\newpage + +\tableofcontents + +\vfill +\newpage + +\section{definitions and prerequisites} +Pd refers to the graphical real-time computer-music environment {\em Pure Data} +by Miller~S.~Puckette. + +To fully understand this document, it is necessary to +be acquainted with Pd and to +have a general understanding of programming techniques especially in {\tt C}. + +To write externals yourself, a {\tt C}-compiler that supports the +{\tt ANSI-C}-Standard, like the {\em Gnu C-compiler} (gcc) on linux-systems or +{\em Visual-C++} on windos-plattforms, will be necessary. + +\subsection{classes, instances, objects} +Pd is written in the programming-language {\tt C}. +Due to its graphical nature, Pd is a {\em object-oriented} system. +Unfortunately {\tt C} does not support very well the use of classes. +Thus the resulting source-code is not as elegant as {\tt C++}-code would be, for instance. + +In this document, the expression {\em class} refers to the realisation of a concept +combining data and manipulators on this data. + +Concrete {\em instances of a class} are called {\em objects}. + +\subsection{internals, externals und libraries} + +To avoid confusion of ideas, the expressions {\em internal}, {\em external} and +{\em library} should be explained here. + +\paragraph{Internal} +An {\em internal} is a class that is built into Pd. +Plenty of primitives, such as ``+'', ``pack'' or ``sig\pdtilde'' are {\em internals}. + +\paragraph{External} +An {\em external} is a class that is not built into Pd but is loaded at runtime. +Once loaded into Pd's memory, {\em externals} cannot be distinguished from +{\em internals} any more. + +\paragraph{Library} +A {\em library} is a collection of {\em externals} that are compiled into a +single binary-file. + +{\em Library}-files have to follow a system dependent naming convention: + +\begin{tabular}{c||c|c|c} +library & linux&irix&Win32 \\ +\hline +{\tt my\_lib}&{\tt my\_lib.pd\_linux}&{\tt my\_lib.pd\_irix}& +{\tt my\_lib.dll}\\ +\end{tabular} + +The simplest form of a {\em library} includes exactly one {\em external} +bearing the same name as the {\em library}. + +Unlike {\em externals}, {\em libraries} can be imported by Pd with special operations. +After a {\em library} has been imported, +all included {\em externals} have been loaded into memory and are available as objects. + +Pd supports two modes to import {\em libraries}: + +\begin{itemize} +\item via the command line-option ``{\tt -lib my\_lib}'' +\item by creating an object ``{\tt my\_lib}'' +\end{itemize} + +The first method loads a {\em library} when Pd is started. +This method is preferably used for {\em libraries} that contain several {\em externals}. + +The other method should be used for {\em libraries} that contain exactly +one {\em external} bearing the same name. +Pd checks first, whether a class named ``my\_lib'' is already loaded. +If this is not the case\footnote{ +If a class ``my\_lib'' is already existent, an object ``my\_lib'' will be instantiated +and the procedure is done. +Thus, no {\em library} has been loaded. +Therefore no {\em library} that is named like an already used class-name like, say, ``abs'', +can be loaded.}, all paths are searched for a file called +``{\tt my\_lib.pd\_linux}''\footnote{or another system-dependent filename-extensions (s.a.)}. +If such file is found, all included {\em externals} are loaded into memory by calling a +routine \verb+my_lib_setup()+. +After loading, a class ``my\_lib'' is (again) looked for as a (newly loaded) {\em external}. +If so, an instance of this class is created, else the instantiation fails and an error is +printed. +Anyhow, all {\em external}-classes declared in the {\em library} are loaded by now. + + +\section{my first external: {\tt helloworld}} +Usually the first attempt learning a programming-language is a ``hello world''-application. + +In our case, an object class should be created, that prints the line ``hello world!!'' to +the standard error every time it is triggered with a ``bang''-message. + + + +\subsection{the interface to Pd} +To write a Pd-external a well-defined interface is needed. +This is provided in the header-file ``m\_pd.h''. + +\begin{verbatim} +#include "m_pd.h" +\end{verbatim} + +\subsection{a class and its data space} +First a new class has to be prepared and the data space for this class has to be defined. + +\begin{verbatim} +static t_class *helloworld_class; + +typedef struct _helloworld { + t_object x_obj; +} t_helloworld; +\end{verbatim} + +\verb+hello_worldclass+ is going to be a pointer to the new class. + +The structure \verb+t_helloworld+ (of the type \verb+_helloworld+) is +the data space of the class. + +An absolutely necessary element of the data space is a variable of the type +\verb+t_object+, which is used to store internal object-properties like +the graphical presentation of the object or data about inlets and outlets. + +\verb+t_object+ has to be the first entry in the structure ! + +Because a simple ``hello world''-application needs no variables, +the structure is empty apart from the \verb+t_object+. + + +\subsection{method space} +Apart from the data space, a class needs a set of manipulators (methods) to +manipulate the data with. + +If a message is sent to an instance of our class, a method is called. +These methods are the interfaces to the message system of Pd. +On principal they have no return argument and are therefore of the +type \verb+void+. + +\begin{verbatim} +void helloworld_bang(t_helloworld *x) +{ + post("Hello world !!"); +} +\end{verbatim} + + +This method has an argument of the type \verb+t_helloworld+, +which would enable us to manipulate the data space. + +Since we only want to output ``Hello world!'' +(and, by the way, our data space is quite sparse), +we renounce a manipulation. + +The command \verb+post(char *c,...)+ sends a string to the standard error. +A carriage return is added automatically. +Apart from this, the \verb+post+-command works like the {\tt C}-command \verb+printf()+. + +\subsection{generation of a new class} +To generate a new class, information of the data space and the method space of this class, +have to be passed to Pd when a library is loaded. + +On loading a new library ``my\_lib'', +Pd tries to call a function ``my\_lib\_setup()''. +This function (or functions called by it) +declares the new classes and their properties. +It is only called once, when the library is loaded. +If the function-call fails (e.g., because no function of the specified name is present) +no external of the library will be loaded. + +\begin{verbatim} +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); +} +\end{verbatim} + +\paragraph{class\_new} + +The function \verb+class_new+ creates a new class and returns a pointer to this prototype. + +The first argument is the symbolic name of the class. + +The next two arguments define the constructor and destructor of the class. + +Whenever a class object is created in a Pd-patch, +the class-constructor \verb+(t_newmethod)helloworld_new+ instantiates the object +and initialises the data space. + +Whenever an object is destroyed +(either by closing the containing patch or by deleting the object from the patch) +the destructor frees the dynamically reserved memory. +The allocated memory for the static data space 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 data space, +the size of the data structure has to be passed as the fourth argument. + +The fifth argument has influence on the graphical representation of the class objects. +The default-value is \verb+CLASS_DEFAULT+ or simply ``0''. + +The remaining arguments define the arguments of an object and its type. + +Up to six numeric and symbolic object-arguments can be defined via +\verb+A_DEFFLOAT+ and \verb+A_DEFSYMBOL+. +If more arguments are to be passed to the object +or if the order of atom types should by more flexible, +\verb+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. + +\paragraph{class\_addbang} +We still have to add a method space to the class. + +\verb+class_addbang+ adds a method for a ``bang''-message to the class that is +defined in the first argument. +The added method is defined in the second argument. + + +\subsection{constructor: instantiation of an object} +Each time, an object is created in a Pd-patch, the +constructor that is defined with the \verb+class_new+-command, +generates a new instance of the class. + +The constructor has to be of type \verb+void *+. + +\begin{verbatim} +void *helloworld_new(void) +{ + t_helloworld *x = (t_helloworld *)pd_new(helloworld_class); + + return (void *)x; +} +\end{verbatim} + + +The arguments of the constructor-method depend on the object-arguments +defined with \verb+class_new+. + +\begin{tabular}{l|l} +\verb+class_new+-argument&constructor-argument\\ +\hline +\verb+A_DEFFLOAT+&\verb+t_floatarg f+ \\ +\verb+A_DEFSYMBOL+&\verb+t_symbol *s+ \\ +\verb+A_GIMME+&\verb+t_symbol *s, int argc, t_atom *argv+ +\end{tabular} + +Because there are no object-arguments for our ``hello world''-class, +the constructor has anon too. + +The function \verb+pd_new+ reserves memory for the data space, +initialises the variables that are internal to the object and +returns a pointer to the data space. + +The type-cast to the data space is necessary. + +Normally, the constructor would initialise the object-variables. +However, since we have none, this is not necessary. + + +The constructor has to return a pointer to the instantiated data space. + +\subsection{the code: \tt helloworld} + +\begin{verbatim} +#include "m_pd.h" + +static t_class *helloworld_class; + +typedef struct _helloworld { + t_object x_obj; +} t_helloworld; + +void helloworld_bang(t_helloworld *x) +{ + post("Hello world !!"); +} + +void *helloworld_new(void) +{ + t_helloworld *x = (t_helloworld *)pd_new(helloworld_class); + + return (void *)x; +} + +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); +} +\end{verbatim} + + +\section{a simple external: {\tt counter}} + +Now we want to realize a simple counter as an external. +A ``bang''-trigger outputs the counter-value on the outlet and +afterwards increases the counter-value by 1. + +This class is similar to the previous one, +but the data space is extended by a variable ``counter'' and the +result is written as a message to an outlet instead of +a string to the standard error. + +\subsection{object-variables} +Of course, a counter needs a state-variable to store the actual counter-value. + +State-variables that belong to class instances belong to the data space. + +\begin{verbatim} +typedef struct _counter { + t_object x_obj; + t_int i_count; +} t_counter; +\end{verbatim} + +The integer variable \verb+i_count+ stores the counter-value. + +\subsection{object-arguments} +It is quite useful for a counter, if a initial value can be defined by the user. +Therefore this initial value should be passed to the object at creation-time. + +\begin{verbatim} +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); +} +\end{verbatim} + +So we have an additional argument in the function \verb+class_new+: +\verb+A_DEFFLOAT+ tells Pd, that the object needs one argument of the +type \verb+t_floatarg+. +If no argument is passed, this will default to ``0''. + +\subsection{constructor} +The constructor has some new tasks. +On the one hand, a variable value has to be initialised, +on the other hand, an outlet for the object has to be created. + +\begin{verbatim} +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); + + return (void *)x; +} +\end{verbatim} + +The constructor-method has one argument of type \verb+t_floatarg+ as declared +in the setup-routine by \verb+class_new+. +This argument is used to initialise the counter. + +A new outlet is created with the function \verb+outlet_new+. +The first 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 ``float''. + +\verb+outlet_new+ returns a pointer to the new outlet and saves this very pointer +in the \verb+t_object+-variable \verb+x_obj.ob_outlet+. +If only one outlet is used, the pointer need not additionally be stored in the data space. +If more than one outlets are used, the pointers have to be stored in the data space, +because the \verb+t_object+-variable can only hold one outlet pointer. + +\subsection{the counter method} +When triggered, the counter value should be sent to the outlet +and afterwards be incremented by 1. + +\begin{verbatim} +void counter_bang(t_counter *x) +{ + t_float f=x->i_count; + x->i_count++; + outlet_float(x->x_obj.ob_outlet, f); +} +\end{verbatim} + +The function \verb+outlet_float+ sends a floating-point-value (second argument) to the outlet +that is specified by the first argument. + +We first store the counter in a floating point-buffer. +Afterwards the counter is incremented and not before that the buffer variable is sent +to the outlet. + +What appears to be unnecessary on the first glance, makes sense after further +inspection: +The buffer variable has been realized as \verb+t_float+, +since \verb+outlet_float+ expects a floating point-value and a typecast is +inevitable. + +If the counter value was sent to the outlet before being incremented, +this could result in an unwanted (though well defined) behaviour: +If the counter-outlet directly triggered its own inlet, +the counter-method would be called although the counter value 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 {\em reentrant}-problem. + +\subsection{the code: \tt counter} + +\begin{verbatim} +#include "m_pd.h" + +static t_class *counter_class; + +typedef struct _counter { + t_object x_obj; + t_int i_count; +} t_counter; + +void counter_bang(t_counter *x) +{ + t_float f=x->i_count; + x->i_count++; + outlet_float(x->x_obj.ob_outlet, f); +} + +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); + + return (void *)x; +} + +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); +} +\end{verbatim} + + +\section{a complex external: \tt counter} + +The simple counter of the previous chapter can easily be extended to more complexity. +It might be quite useful to be able to reset the counter to an initial value, +to set upper and lower boundaries 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. + +\subsection{extended data space} + +\begin{verbatim} +typedef struct _counter { + t_object x_obj; + t_int i_count; + t_float step; + t_int i_down, i_up; + t_outlet *f_out, *b_out; +} t_counter; +\end{verbatim} + +The data space has been extended to hold variables for step width and +upper and lower boundaries. +Furthermore pointers for two outlets have been added. + +\subsection{extension of the class} +The new class objects should have methods for different messages, +like ``set'' and ``reset''. +Therefore the method space has to be extended too. + +\begin{verbatim} + counter_class = class_new(gensym("counter"), + (t_newmethod)counter_new, + 0, sizeof(t_counter), + CLASS_DEFAULT, + A_GIMME, 0); +\end{verbatim} + +The class generator \verb+class_new+ has been extended by the argument \verb+A_GIMME+. +This enables a dynamic number of arguments to be passed at the instantiation of the object. + +\begin{verbatim} + class_addmethod(counter_class, + (t_method)counter_reset, + gensym("reset"), 0); +\end{verbatim} + +\verb+class_addmethod+ adds a method for an arbitrary selector to an class. + +The first 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. + +\begin{verbatim} + 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); +\end{verbatim} + +A method for ``set'' followed by a numerical value is added, +as well as a method for the selector ``bound'' followed by two numerical values. + +\begin{verbatim} + class_sethelpsymbol(counter_class, gensym("help-counter")); +\end{verbatim} + +If a Pd-object is right-clicked, a help-patch describing the object-class can be opened. +By default, this patch is located in the directory ``{\em doc/5.reference/}'' and +is named like the symbolic class name. + +An alternative help-patch can be defined with the +\verb+class_sethelpsymbol+-command. + +\subsection{construction of in- and outlets} + +When creating the object, several arguments should be passed by the user. + +\begin{verbatim} +void *counter_new(t_symbol *s, int argc, t_atom *argv) +\end{verbatim} +Because of the declaration of arguments in the \verb+class_new+-function +with \verb+A_GIMME+, +the constructor has following arguments: + +\begin{tabular}{c|l} +\verb+t_symbol *s+ & the symbolic name,\\ +& that was used for object creation \\ +\verb+int argc+ & the number of arguments passed to the object\\ +\verb+t_atom *argv+ & a pointer to a list of {\tt argc} atoms +\end{tabular} + +\begin{verbatim} + t_float f1=0, f2=0; + + x->step=1; + 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: + break; + } + if (argc<2)f2=f1; + x->i_down = (f1i_up = (f1>f2)?f1:f2; + + x->i_count=x->i_down; +\end{verbatim} + +If three arguments are passed, these should be the {\em lower boundary}, +the {\em upper boundary} and the {\em step width}. + +If only two arguments are passed, the step-width defaults to ``1''. +If only one argument is passed, this should be the {\em initial value} of the counter with +step-width of ``1''. + +\begin{verbatim} + inlet_new(&x->x_obj, &x->x_obj.ob_pd, + gensym("list"), gensym("bound")); +\end{verbatim} + +The function \verb+inlet_new+ creates a new ``active'' inlet. +``Active'' means, that a class-method is called each time +a message is sent to an ``active'' inlet. + +Due to the software-architecture, the first inlet is always ``active''. + +The first two arguments of the \verb+inlet_new+-function are +pointers to the interna of the object and to the graphical presentation of the object. + +The symbolic selector that is specified by the third argument is to be +substituted by another symbolic selector (fourth argument) for this inlet. + +Because of this substitution of selectors, +a message on a certain right inlet can be treated as a message with +a certain selector on the leftmost inlet. + +This means: +\begin{itemize} +\item The substituting selector has to be declared by \verb+class_addmethod+ +in the setup-routine. +\item It is possible to simulate a certain right inlet, by sending a message with +this inlet's selector to the leftmost inlet. +\item 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. +\end{itemize} + +\begin{verbatim} + floatinlet_new(&x->x_obj, &x->step); +\end{verbatim} +\verb+floatinlet_new+ generates a new ``passive'' inlet for numerical values. +``Passive'' inlets allow parts of the data space-memory to be written directly +from outside. +Therefore it is not possible to check for illegal inputs. + +The first argument is a pointer to the internal infrastructure of the object. +The second argument is the address in the data space-memory, +where other objects can write too. + +``Passive'' inlets can be created for pointers, symbolic or +numerical (floating point\footnote{ +That's why the {\tt step}-width of the class\/data space is realized as {\tt t\_float}.}) +values. + + +\begin{verbatim} + x->f_out = outlet_new(&x->x_obj, &s_float); + x->b_out = outlet_new(&x->x_obj, &s_bang); +\end{verbatim} + +The pointers returned by \verb+outlet_new+ have to be saved in the class\/data space +to be used later by the outlet-routines. + +The order of the generation of inlets and outlets is important, +since it corresponds to the order of inlets and outlets in the +graphical representation of the object. + +\subsection{extended method space} + +The method for the ``bang''-message has to full fill the more complex tasks. + +\begin{verbatim} +void counter_bang(t_counter *x) +{ + 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); +} +\end{verbatim} + +Each outlet is identified by the \verb+outlet_...+-functions via the +pointer to this outlets. + +The remaining methods still have to be implemented: + +\begin{verbatim} +void counter_reset(t_counter *x) +{ + x->i_count = x->i_down; +} + +void counter_set(t_counter *x, t_floatarg f) +{ + x->i_count = f; +} + +void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2) +{ + x->i_down = (f1i_up = (f1>f2)?f1:f2; +} +\end{verbatim} + +\subsection{the code: \tt counter} + +\begin{verbatim} +#include "m_pd.h" + +static t_class *counter_class; + +typedef struct _counter { + t_object x_obj; + t_int i_count; + t_float step; + t_int i_down, i_up; + t_outlet *f_out, *b_out; +} t_counter; + +void counter_bang(t_counter *x) +{ + 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); +} + +void counter_reset(t_counter *x) +{ + x->i_count = x->i_down; +} + +void counter_set(t_counter *x, t_floatarg f) +{ + x->i_count = f; +} + +void counter_bound(t_counter *x, t_floatarg f1, t_floatarg f2) +{ + x->i_down = (f1i_up = (f1>f2)?f1:f2; +} + +void *counter_new(t_symbol *s, int argc, t_atom *argv) +{ + t_counter *x = (t_counter *)pd_new(counter_class); + t_float f1=0, f2=0; + + x->step=1; + 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: + break; + } + if (argc<2)f2=f1; + + x->i_down = (f1i_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); + + x->f_out = outlet_new(&x->x_obj, &s_float); + x->b_out = outlet_new(&x->x_obj, &s_bang); + + return (void *)x; +} + +void counter_setup(void) { + counter_class = class_new(gensym("counter"), + (t_newmethod)counter_new, + 0, sizeof(t_counter), + CLASS_DEFAULT, + A_GIMME, 0); + + class_addbang (counter_class, counter_bang); + class_addmethod(counter_class, + (t_method)counter_reset, gensym("reset"), 0); + 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")); +} +\end{verbatim} + + +\section{a signal-external: {\tt pan\pdtilde}} +Signal classes are normal Pd-classes, that offer additional methods for signals. + + +All methods and concepts that can be realized with normal object classes can +therefore be realized with signal classes too. + +Per agreement, the symbolic names of signal classes end with a tilde \pdtilde. + +The class ``pan\pdtilde'' shall demonstrate, how signal classes are written. + +A signal on the left inlet is mixed with a signal on the second inlet. +The mixing-factor between 0 and 1 is defined via a \verb+t_float+-message +on a third inlet. + +\subsection{variables of a signal class} +Since a signal-class is only an extended normal class, +there are no principal differences between the data spaces. + +\begin{verbatim} +typedef struct _pan_tilde { + t_object x_obj; + + t_sample f_pan; + t_float f; + + t_inlet *x_in2; + t_inlet *x_in3; + + t_outlet*x_out; + +} t_pan_tilde; +\end{verbatim} + +Only one variable \verb+f_pan+ for the {\em mixing-factor} of the panning-function is needed. + +The other variable \verb+f+ is needed whenever a signal-inlet is needed too. +If no signal but only a float-message is present at a signal-inlet, this +variable is used to automatically convert the float to signal. + +Finally, we have the members \verb+x_in2+, \verb+x_in3+ and \verb+x_out+, +which are needed to store handles to the various extra inlets (resp. outlets) of the object. + +\subsection{signal-classes} + +\begin{verbatim} +void pan_tilde_setup(void) { + pan_tilde_class = class_new(gensym("pan~"), + (t_newmethod)pan_tilde_new, + (t_method)pan_tilde_free, + 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); +} +\end{verbatim} + +Something has changed with the \verb+class_new+ function: +the third argument specifies a ``free-method'' (aka {\em destructor}), which is called whenever an instance of the object +is to be deleted (just like the ``new-method'' is called whenever an instance is to be created). +In the prior examples this was set to \verb+0+ (meaning: we don't care), +but in this example we have to clean up some ressources when we don't need them any more. + +More interestingly, a method for signal-processing has to be provided by each signal class. + +Whenever Pd's audio engine is started, a message with the selector ``dsp'' +is sent to each object. +Each class that has a method for the ``dsp''-message is recognised as signal class. + +Signal classes that want to provide signal-inlets have to +declare this via the \verb+CLASS_MAINSIGNALIN+-macro. +This enables signals at the first (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 \verb+t_float+-messages any longer. + +The first argument of the macro is a pointer to the signal class. +The second argument is the type of the class's data space. + +The last argument is a dummy-variable out of the data space that is needed +to replace non-existing signal at the signal-inlet(s) with \verb+t_float+-messages. + +\subsection{construction of signal-inlets and -outlets} + +\begin{verbatim} +void *pan_tilde_new(t_floatarg f) +{ + t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class); + + x->f_pan = f; + + x->x_in2 = inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_in3 = floatinlet_new (&x->x_obj, &x->f_pan); + + x->x_out = outlet_new(&x->x_obj, &s_signal); + + return (void *)x; +} +\end{verbatim} + +Additional signal-inlets are added like other inlets with the routine \verb+inlet_new+. +The last two arguments are references to the symbolic selector ``signal'' +in the lookup-table. + +Signal-outlets are also created like normal (message-)outlets, +by setting the outlet-selector to ``signal''. + +The newly created inlets/outlets are ``user-allocated'' data. +Pd will keep track of all the ressources it automatically creates (like the default inlet), +and will eventually free these ressources once they are no longer needed. +However, if we request an ``extra'' ressource (like the additional inlets/outlets in this example; +or - more commonly - memory that is allocated via \verb+malloc+ or similar), +we have to make sure ourselves, that these ressources are freed when no longer needed. +If we fail to do so, we will invariably create a dreaded {\em memory leak}. + +Therefore, we store the ``handles'' to the newly created inlets/outlets as returned by the \verb+..._new+ routines +for later use. + + +\subsection{DSP-methods} +Whenever Pd's audio engine is turned on, +all signal-objects declare their perform-routines that are to be added to the DSP-tree. + +The ``dsp''-method has two arguments, the pointer to the class-data space, and +a pointer to an array of signals. + +The signals are arranged in the array in such way, +that they are ordered in a clockwise way in the graphical representation of the +object.\footnote{ +If both left and right in- and out-signals exist, this means: +First is the leftmost in-signal followed by the right in-signals; +after the right out-signals, finally there comes the leftmost out-signal.} + +\begin{verbatim} +void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp) +{ + dsp_add(pan_tilde_perform, 5, x, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} +\end{verbatim} + +\verb+dsp_add+ adds a {\em perform}-routine (as declared in the first argument) +to the DSP-tree. + +The second argument is the number of the following pointers to diverse variables. +Which pointers to which variables are passed is not limited. + +Here, sp[0] is the first in-signal, sp[1] represents the second in-signal and +sp[3] points to the out-signal. + +The structure \verb+t_signal+ contains a pointer to the +its signal-vector \verb+().s_vec+ (an array of samples of type \verb+t_sample+), +and the length of this signal-vector \verb+().s_n+. + +Since all signal vectors of a patch (not including it's sub-patches) are of the same length, +it is sufficient to get the length of one of these vectors. + +\subsection{perform-routine} +The perform-routine is the DSP-heart of each signal class. + +A pointer to an integer-array is passed to it. +This array contains the pointers, that were passed via \verb+dsp_add+, +which must be casted back to their real type. + +The perform-routine has to return a pointer to integer, +that points to the address behind the stored pointers of the routine. +This means, that the return argument equals the +argument of the perform-routine plus +the number of pointer variables (as declared as the second argument +of \verb+dsp_add+) plus one. + +\begin{verbatim} +t_int *pan_tilde_perform(t_int *w) +{ + t_pan_tilde *x = (t_pan_tilde *)(w[1]); + 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; + + while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan; + + return (w+6); +} +\end{verbatim} + +Each sample of the signal vectors is read and manipulated in the \verb+while+-loop. + + +Optimisation of the DSP-tree tries to avoid unnecessary copy-operations. +Therefore it is possible, that in- and out-signal are located +at the same address in the memory. +In this case, the programmer has to be careful not to write into the out-signal +before having read the in-signal to avoid overwriting data that is not yet saved. + +\subsection{destructor} + +\begin{verbatim} +void pan_tilde_free(t_pan_tilde *x) +{ + inlet_free(x->x_in2); + inlet_free(x->x_in3); + outlet_free(x->x_out); +} +\end{verbatim} + +The object has some dynamically allocated ressources, +namely the additional inlets and outlets we created in the constructor. + +Since Pd doesn't track dynamically allocated ressources for us, +we have to free them manually in the ``free-method'' (aka: destructor). +We do so by calling \verb+inlet_free+ (resp. \verb+outlet_free+) on the handles to +our additional iolets. + +Note that we do not need to free the default first outlet. +As it is created automatically by Pd, it is also freed automatically. + +\subsection{the code: \tt pan\pdtilde} + +\begin{verbatim} +#include "m_pd.h" + +static t_class *pan_tilde_class; + +typedef struct _pan_tilde { + t_object x_obj; + t_sample f_pan; + t_sample f; + + t_inlet *x_in2; + t_inlet *x_in3; + t_outlet*x_out; +} t_pan_tilde; + +t_int *pan_tilde_perform(t_int *w) +{ + t_pan_tilde *x = (t_pan_tilde *)(w[1]); + 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; + + while (n--) *out++ = (*in1++)*(1-f_pan)+(*in2++)*f_pan; + + return (w+6); +} + +void pan_tilde_dsp(t_pan_tilde *x, t_signal **sp) +{ + dsp_add(pan_tilde_perform, 5, x, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +void pan_tilde_free(t_pan_tilde *x) +{ + inlet_free(x->x_in2); + inlet_free(x->x_in3); + outlet_free(x->x_out); +} + +void *pan_tilde_new(t_floatarg f) +{ + t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class); + + x->f_pan = f; + + x->x_in2=inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_in3=floatinlet_new (&x->x_obj, &x->f_pan); + x->x_out=outlet_new(&x->x_obj, &s_signal); + + return (void *)x; +} + +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); +} +\end{verbatim} + +\vfill +\newpage +\begin{appendix} + +\section{Pd's message-system} +Non-audio-data are distributed via a message-system. +Each message consists of a ``selector'' and a list of atoms. + +\subsection{atoms} + +There are three kinds of atoms: +\begin{itemize} +\item {\em A\_FLOAT}: a numerical value (floating point) +\item {\em A\_SYMBOL}: a symbolic value (string) +\item {\em A\_POINTER}: a pointer +\end{itemize} + +Numerical values are always floating point-values (\verb+t_float+), +even if they could be displayed as integer values. + +Each symbol is stored in a lookup-table for reasons of performance. +The command \verb+gensym+ looks up a string in the lookup-table and +returns the address of the symbol. +If the string is not yet to be found in the table, +a new symbol is added. + +Atoms of type {\em A\_POINTER} are not very important (for simple externals). + +The type of an atom \verb+a+ is stored in the structure-element \verb+a.a_type+. + +\subsection{selectors} +The selector is a symbol that defines the type of a message. +There are five predefined selectors: +\begin{itemize} +\item ``{\tt bang}'' labels a trigger event. +A ``bang''-message consists only of the selector and contains no lists of atoms. +\item ``{\tt float}'' labels a numerical value. +The list of a ``float''-Message contains one single atom of type {\em A\_FLOAT} +\item ``{\tt symbol}'' labels a symbolic value. +The list of a ``symbol''-Message contains one single atom of type {\em A\_SYMBOL} +\item ``{\tt pointer}'' labels a pointer value. +The list of a ``pointer''-Message contains one single atom of type {\em A\_POINTER} +\item ``{\tt list}'' labels a list of one or more atoms of arbitrary type. +\end{itemize} + +Since the symbols for these selectors are used quite often, +their address in the lookup-table can be queried directly, +without having to use \verb+gensym+: + +\begin{tabular}{l||l|l} +selector&lookup-routine&lookup-address\\ +\hline +\tt bang &\verb+gensym("bang")+ & \verb+&s_bang+ \\ +\tt float &\verb+gensym("float")+ & \verb+&s_float+ \\ +\tt symbol &\verb+gensym("symbol")+ & \verb+&s_symbol+ \\ +\tt pointer &\verb+gensym("pointer")+ & \verb+&s_pointer+ \\ +\tt list &\verb+gensym("list")+ & \verb+&s_list+ \\ +--- (signal) &\verb+gensym("signal")+&\verb+&s_symbol+ +\end{tabular} + +Other selectors can be used as well. +The receiving class has to provide a method for a specifique selector +or for ``anything'', which is any arbitrary selector. + +Messages that have no explicit selector and start with a numerical value, +are recognised automatically either as ``float''-message (only one atom) or as +``list''-message (several atoms). + +For example, messages ``\verb+12.429+'' and ``\verb+float 12.429+'' are identical. +Likewise, the messages ``\verb+list 1 for you+'' is identical to ``\verb+1 for you+''. + +\section{Pd-types} +Since Pd is used on several platforms, +many ordinary types of variables, like \verb|int|, are re-defined. +To write portable code, it is reasonable to use types provided by Pd. + +Apart from this there are many predefined types, +that should make the life of the programmer simpler. + +Generally, Pd-types start with \verb|t_|. + +\begin{tabular}{c|l} +Pd-type & description \\ +\hline\hline +\verb+t_atom+& atom \\ +\verb+t_float+ & floating point value\\ +\verb+t_symbol+ & symbol \\ +\verb+t_gpointer+ & pointer (to graphical objects) \\ +\hline +\verb+t_int+ & integer value \\ +\verb+t_signal+ & structure of a signal \\ +\verb+t_sample+ & audio signal-value (floating point)\\ +\verb+t_outlet+ & outlet of an object \\ +\verb+t_inlet+ & inlet of an object \\ +\verb+t_object+ & object-interna \\ +\hline +\verb+t_class+ & a Pd-class \\ +\verb+t_method+ & class-method \\ +\verb+t_newmethod+ & pointer to a constructor (new-routine) \\ +\end{tabular} + + +\section{important functions in ``m\_pd.h''} + +\subsection{functions: atoms} + +\subsubsection{SETFLOAT} +\begin{verbatim} +SETFLOAT(atom, f) +\end{verbatim} +This macro sets the type of \verb+atom+ to \verb+A_FLOAT+ +and stores the numerical value \verb+f+ in this atom. + +\subsubsection{SETSYMBOL} +\begin{verbatim} +SETSYMBOL(atom, s) +\end{verbatim} +This macro sets the type of \verb+atom+ to \verb+A_SYMBOL+ +and stores the symbolic pointer \verb+s+ in this atom. + +\subsubsection{SETPOINTER} +\begin{verbatim} +SETPOINTER(atom, pt) +\end{verbatim} +This macro sets the type of \verb+atom+ to \verb+A_POINTER+ +and stores the pointer \verb+pt+ in this atom. + +\subsubsection{atom\_getfloat} +\begin{verbatim} +t_float atom_getfloat(t_atom *a); +\end{verbatim} +If the type of the atom \verb+a+ is \verb+A_FLOAT+, +the numerical value of this atom else ``0.0'' is returned. + +\subsubsection{atom\_getfloatarg} +\begin{verbatim} +t_float atom_getfloatarg(int which, int argc, t_atom *argv) +\end{verbatim} +If the type of the atom -- that is found at in the atom-list +\verb+argv+ with the length \verb+argc+ at the place \verb+which+ -- +is \verb+A_FLOAT+, the numerical value of this atom else ``0.0'' is returned. + +\subsubsection{atom\_getint} +\begin{verbatim} +t_int atom_getint(t_atom *a); +\end{verbatim} +If the type of the atom \verb+a+ is \verb+A_FLOAT+, +its numerical value is returned as integer else ``0'' is returned. + +\subsubsection{atom\_getsymbol} +\begin{verbatim} +t_symbol atom_getsymbol(t_atom *a); +\end{verbatim} +If the type of the atom \verb+a+ is \verb+A_SYMBOL+, +a pointer to this symbol is returned, else a null-pointer ``0'' is returned. + +\subsubsection{atom\_gensym} +\begin{verbatim} +t_symbol *atom_gensym(t_atom *a); +\end{verbatim} +If the type of the atom \verb+a+ is \verb+A_SYMBOL+, +a pointer to this symbol is returned. + +Atoms of a different type, are ``reasonably'' converted into a string. +This string is -- on demand -- inserted into the symbol-table. +A pointer to this symbol is returned. + +\subsubsection{atom\_string} +\begin{verbatim} +void atom_string(t_atom *a, char *buf, unsigned int bufsize); +\end{verbatim} +Converts an atom \verb+a+ into a {\tt C}-string \verb+buf+. +The memory to this char-Buffer has to be reserved manually and +its length has to be declared in \verb+bufsize+. + +\subsubsection{gensym} +\begin{verbatim} +t_symbol *gensym(char *s); +\end{verbatim} +Checks, whether the C-string \verb+*s+ has already been inserted into the symbol-table. +If no entry exists, it is created. +A pointer to the symbol is returned. + +\subsection{functions: classes} +\subsubsection{class\_new} +\begin{verbatim} +t_class *class_new(t_symbol *name, + t_newmethod newmethod, t_method freemethod, + size_t size, int flags, + t_atomtype arg1, ...); +\end{verbatim} +Generates a class with the symbolic name \verb+name+. +\verb+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 \verb+freemethod+ (without any return argument), +when the object is destroyed. + +\verb+size+ is the static size of the class-data space, +that is returned by \verb+sizeof(t_mydata)+. + +\verb+flags+ define the presentation of the graphical object. +A (more or less arbitrary) combination of following objects is possible: + +\begin{tabular}{l|l} +flag&description\\ +\hline +\verb+CLASS_DEFAULT+ & a normal object with one inlet \\ +\verb+CLASS_PD+ & \em object (without graphical presentation) \\ +\verb+CLASS_GOBJ+ & \em pure graphical object (like arrays, graphs,...)\\ +\verb+CLASS_PATCHABLE+ & \em a normal object (with one inlet) \\ +\verb+CLASS_NOINLET+ & the default inlet is suppressed \\ +\end{tabular} + +Flags the description of which is printed in {\em italic} +are of small importance for writing externals. + +The remaining arguments \verb+arg1,...+ define the +types of object-arguments passed at the creation of a class-object. +A maximum of six type checked arguments can be passed to an object. +The list of argument-types are terminated by ``0''. + +Possible types of arguments are: + +\begin{tabular}{l|l} +\verb+A_DEFFLOAT+ & a numerical value \\ +\verb+A_DEFSYMBOL+ & a symbolical value \\ +\verb+A_GIMME+ & a list of atoms of arbitrary length and types \\ +\end{tabular} + +If more than six arguments are to be passed, +\verb+A_GIMME+ has to be used and a manual type-check has to be made. + +\subsubsection{class\_addmethod} +\begin{verbatim} +void class_addmethod(t_class *c, t_method fn, t_symbol *sel, + t_atomtype arg1, ...); +\end{verbatim} +Adds a method \verb+fn+ for a selector \verb+sel+ to a class \verb+c+. + +The remaining arguments \verb+arg1,...+ define the +types of the list of atoms that follow the selector. +A maximum of six type-checked arguments can be passed. +If more than six arguments are to be passed, +\verb+A_GIMME+ has to be used and a manual type-check has to be made. + +The list of arguments is terminated by ``0''. + + +Possible types of arguments are: + +\begin{tabular}{l|l} +\verb+A_DEFFLOAT+ & a numerical value \\ +\verb+A_DEFSYMBOL+ & a symbolical value \\ +\verb+A_POINTER+ & a pointer \\ +\verb+A_GIMME+ & a list of atoms of arbitrary length and types \\ +\end{tabular} + +\subsubsection{class\_addbang} +\begin{verbatim} +void class_addbang(t_class *c, t_method fn); +\end{verbatim} +Adds a method \verb+fn+ for ``bang''-messages to the class \verb+c+. + +The argument of the ``bang''-method is a pointer to the class-data space: + +\verb+void my_bang_method(t_mydata *x);+ + +\subsubsection{class\_addfloat} +\begin{verbatim} +void class_addfloat(t_class *c, t_method fn); +\end{verbatim} +Adds a method \verb+fn+ for ``float''-messages to the class \verb+c+. + +The arguments of the ``float''-method is a pointer to the class-data space and +a floating point-argument: + +\verb+void my_float_method(t_mydata *x, t_floatarg f);+ + +\subsubsection{class\_addsymbol} +\begin{verbatim} +void class_addsymbol(t_class *c, t_method fn); +\end{verbatim} +Adds a method \verb+fn+ for ``symbol''-messages to the class \verb+c+. + +The arguments of the ``symbol''-method is a pointer to the class-data space and +a pointer to the passed symbol: + +\verb+void my_symbol_method(t_mydata *x, t_symbol *s);+ + +\subsubsection{class\_addpointer} +\begin{verbatim} +void class_addpointer(t_class *c, t_method fn); +\end{verbatim} +Adds a method \verb+fn+ for ``pointer''-messages to the class \verb+c+. + +The arguments of the ``pointer''-method is a pointer to the class-data space and +a pointer to a pointer: + +\verb+void my_pointer_method(t_mydata *x, t_gpointer *pt);+ + +\subsubsection{class\_addlist} +\begin{verbatim} +void class_addlist(t_class *c, t_method fn); +\end{verbatim} +Adds a method \verb+fn+ for ``list''-messages to the class \verb+c+. + +The arguments of the ``list''-method are -- apart from a pointer to the class-data space -- +a pointer to the selector-symbol (always \verb+&s_list+), +the number of atoms and a pointer to the list of atoms: + +\verb+void my_list_method(t_mydata *x,+ + +\verb+ t_symbol *s, int argc, t_atom *argv);+ + +\subsubsection{class\_addanything} +\begin{verbatim} +void class_addanything(t_class *c, t_method fn); +\end{verbatim} +Adds a method \verb+fn+ for an arbitrary message to the class \verb+c+. + +The arguments of the anything-method are -- apart from a pointer to the class-data space -- +a pointer to the selector-symbol, +the number of atoms and a pointer to the list of atoms: + +\verb+void my_any_method(t_mydata *x,+ + +\verb+ t_symbol *s, int argc, t_atom *argv);+ + +\subsubsection{class\_addcreator} +\begin{verbatim} + void class_addcreator(t_newmethod newmethod, t_symbol *s, + t_atomtype type1, ...); +\end{verbatim} +Adds a creator-symbol \verb+s+, alternative to the symbolic class name, +to the constructor \verb+newmethod+. +Thus, objects can be created either by their ``real'' class name or +an alias-name (p.e. an abbreviation, like the internal ``float'' resp. ``f''). + +The ``0''-terminated list of types corresponds to that of \verb+class_new+. + +\subsubsection{class\_sethelpsymbol} +\begin{verbatim} +void class_sethelpsymbol(t_class *c, t_symbol *s); +\end{verbatim} + +If a Pd-object is right-clicked, a help-patch for the corresponding object class +can be opened. +By default this is a patch with the symbolic class name in the +directory ``{\em doc/5.reference/}''. + +The name of the help-patch for the class that is pointed to by \verb+c+ +is changed to the symbol \verb+s+. + +Therefore, several similar classes can share a single help-patch. + +Path-information is relative to the default help path {\em doc/5.reference/}. + +\subsubsection{pd\_new} +\begin{verbatim} +t_pd *pd_new(t_class *cls); +\end{verbatim} +Generates a new instance of the class \verb+cls+ and +returns a pointer to this instance. + +\subsection{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 data space-variable of the \verb+t_object+-type is initialised. +This variable has to be passed as the \verb+owner+-object to the +various inlet- and outlet-routines. + +\subsubsection{inlet\_new} +\begin{verbatim} +t_inlet *inlet_new(t_object *owner, t_pd *dest, + t_symbol *s1, t_symbol *s2); +\end{verbatim} +Generates an additional ``active'' inlet for the object +that is pointed at by \verb+owner+. +Generally, \verb+dest+ points at ``\verb+owner.ob_pd+''. + +The selector \verb+s1+ at the new inlet is substituted by the selector \verb+s2+. + +If a message with selector \verb+s1+ appears at the new inlet, +the class-method for the selector \verb+s2+ is called. + +This means +\begin{itemize} +\item The substituting selector has to be declared by \verb+class_addmethod+ +in the setup-routine. +\item It is possible to simulate a certain right inlet, by sending a message with +this inlet's selector to the leftmost inlet. + +Using an empty symbol (\verb+gensym("")+) as selector +makes it impossible to address a right inlet via the leftmost one. + +\item 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. +\end{itemize} + +\subsubsection{floatinlet\_new} +\begin{verbatim} +t_inlet *floatinlet_new(t_object *owner, t_float *fp); +\end{verbatim} +Generates a new ``passive'' inlet for the object that is pointed at by \verb+owner+. +This inlet enables numerical values to be written directly into the +memory \verb+fp+, without calling a dedicated method. + +\subsubsection{symbolinlet\_new} +\begin{verbatim} +t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp); +\end{verbatim} +Generates a new ``passive'' inlet for the object that is pointed at by \verb+owner+. +This inlet enables symbolic values to be written directly into the +memory \verb+*sp+, without calling a dedicated method. + + +\subsubsection{pointerinlet\_new} +\begin{verbatim} +t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp); +\end{verbatim} +Generates a new ``passive'' inlet for the object that is pointed at by \verb+owner+. +This inlet enables pointer to be written directly into the +memory \verb+gp+, without calling a dedicated method. + +\subsubsection{outlet\_new} +\begin{verbatim} +t_outlet *outlet_new(t_object *owner, t_symbol *s); +\end{verbatim} +Generates a new outlet for the object that is pointed at by \verb+owner+. +The Symbol \verb+s+ indicates the type of the outlet. + +\begin{tabular}{c|l||l} +symbol & symbol-address & outlet-type \\ +\hline\hline +``bang'' & \verb+&s_bang+ & message (bang)\\ +``float'' & \verb+&s_float+ & message (float)\\ +``symbol'' & \verb+&s_symbol+ & message (symbol) \\ +``pointer'' & \verb+&s_gpointer+ & message (pointer)\\ +``list'' & \verb+&s_list+ & message (list) \\ +--- & 0 & message \\ +\hline +``signal'' & \verb+&s_signal+ & signal \\ +\end{tabular} + +There are no real differences 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 \verb+&s_signal+. + +Variables if the type \verb+t_object+ provide pointer to one outlet. +Whenever a new outlet is generated, its address is stored in the +object variable \verb+(*owner).ob_outlet+. + +If more than one message-outlet is needed, +the outlet-pointers that are returned by \verb+outlet_new+ +have to be stored manually in the data space +to address the given outlets. + +\subsubsection{outlet\_bang} +\begin{verbatim} +void outlet_bang(t_outlet *x); +\end{verbatim} +Outputs a ``bang''-message at the outlet specified by \verb+x+. + +\subsubsection{outlet\_float} +\begin{verbatim} +void outlet_float(t_outlet *x, t_float f); +\end{verbatim} +Outputs a ``float''-message with the numeric value \verb+f+ +at the outlet specified by \verb+x+. + +\subsubsection{outlet\_symbol} +\begin{verbatim} +void outlet_symbol(t_outlet *x, t_symbol *s); +\end{verbatim} +Outputs a ``symbol''-message with the symbolic value \verb+s+ +at the outlet specified by \verb+x+. + +\subsubsection{outlet\_pointer} +\begin{verbatim} +void outlet_pointer(t_outlet *x, t_gpointer *gp); +\end{verbatim} +Outputs a ``pointer''-message with the pointer \verb+gp+ +at the outlet specified by \verb+x+. + +\subsubsection{outlet\_list} +\begin{verbatim} +void outlet_list(t_outlet *x, + t_symbol *s, int argc, t_atom *argv); +\end{verbatim} +Outputs a ``list''-message at the outlet specified by \verb+x+. +The list contains \verb+argc+ atoms. +\verb+argv+ points to the first element of the atom-list. + +Independent of the symbol \verb+s+, the selector ``list'' will precede +the list. + +To make the code more readable, +\verb+s+ should point to the symbol list +(either via \verb+gensym("list")+ or via \verb+&s_list+) + +\subsubsection{outlet\_anything} +\begin{verbatim} +void outlet_anything(t_outlet *x, + t_symbol *s, int argc, t_atom *argv); +\end{verbatim} +Outputs a message at the outlet specified by \verb+x+. + +The message-selector is specified with \verb+s+. +It is followed by \verb+argc+ atoms. +\verb+argv+ points to the first element of the atom-list. + +\subsection{functions: DSP} +If a class should provide methods for digital signal-processing, +a method for the selector ``dsp'' (followed by no atoms) +has to be added to this class + +Whenever Pd's audio engine is started, +all objects that provide a ``dsp''-method are identified as instances of signal classes. + +\paragraph{DSP-method} + +\begin{verbatim} +void my_dsp_method(t_mydata *x, t_signal **sp) +\end{verbatim} + +In the ``dsp''-method a class method for signal-processing +is added to the DSP-tree by the function \verb+dsp_add+. + +Apart from the data space \verb+x+ of the object, +an array of signals is passed. +The signals in the array are arranged in such a way, +that they can be read in the graphical representation of the object +clockwisely. + +In case there are both two in- and out-signals, this means: + +\begin{tabular}{c|r} +pointer & to signal \\ +\hline\hline +sp[0] & left in-signal \\ +sp[1] & right in-signal \\ +sp[2] & right out-signal \\ +sp[3] & left out-signal \\ +\end{tabular} + +The signal structure contains apart from other things: + +\begin{tabular}{c|l} +structure-element & description \\ +\hline +\verb+s_n+ & length of the signal vector \\ +\verb+s_vec+ & pointer to the signal vector \\ +\end{tabular} + +The signal vector is an array of samples of type \verb+t_sample+. + +\paragraph{perform-routine} +\begin{verbatim} +t_int *my_perform_routine(t_int *w) +\end{verbatim} + + +A pointer \verb+w+ to an array (of integer) is passed to +the perform-routine that is inserted into the DSP-tree by \verb+class_add+. + +In this array the pointers that are passed via \verb+dsp_add+ are stored. +These pointers have to be casted back to their original type. + +The first pointer is stored at \verb+w[1]+ !!! + +The perform-routine has to return a pointer to integer, +that points directly behind the memory, where the object's pointers are stored. +This means, that the return-argument equals the routine's argument \verb+w+ +plus the number of used pointers +(as defined in the second argument of \verb+dsp_add+) plus one. + +\subsubsection{CLASS\_MAINSIGNALIN} +\begin{verbatim} +CLASS_MAINSIGNALIN(, , ); +\end{verbatim} +The macro \verb+CLASS_MAINSIGNALIN+ declares, +that the class will use signal-inlets. + +The first macro-argument is a pointer to the signal-class. +The second argument is the type of the class-data space. +The third argument is a (dummy-)floating point-variable of the data space, +that is needed to automatically convert ``float''-messages into signals +if no signal is present at the signal-inlet. + +No ``float''-methods can be used for signal-inlets, that are created this way. + +\subsubsection{dsp\_add} +\begin{verbatim} +void dsp_add(t_perfroutine f, int n, ...); +\end{verbatim} +Adds the perform-routine \verb+f+ to the DSP-tree. +The perform-routine is called at each DSP-cycle. + +The second argument\verb+n+ defines the number of following pointer-arguments + +Which pointers to which data are passes is not limited. +Generally, pointers to the data space of the object and to the +signal-vectors are reasonable. +The length of the signal-vectors should also be passed to manipulate signals effectively. + +\subsubsection{sys\_getsr} +\begin{verbatim} +float sys_getsr(void); +\end{verbatim} +Returns the sampler ate of the system. + +\subsection{functions: memory} +\subsubsection{getbytes} +\begin{verbatim} +void *getbytes(size_t nbytes); +\end{verbatim} +Reserves \verb+nbytes+ bytes and returns a pointer to the allocated memory. + +\subsubsection{copybytes} +\begin{verbatim} +void *copybytes(void *src, size_t nbytes); +\end{verbatim} +Copies \verb+nbytes+ bytes from \verb+*src+ into a newly allocated memory. +The address of this memory is returned. + +\subsubsection{freebytes} +\begin{verbatim} +void freebytes(void *x, size_t nbytes); +\end{verbatim} +Frees \verb+nbytes+ bytes at address \verb+*x+. + +\subsection{functions: output} +\subsubsection{post} +\begin{verbatim} +void post(char *fmt, ...); +\end{verbatim} + +Writes a {\tt C}-string to the standard error (shell). + +\subsubsection{error} +\begin{verbatim} +void error(char *fmt, ...); +\end{verbatim} + +Writes a {\tt C}-string as an error-message to the standard error (shell). + +The object that has output the error-message is marked and +can be identified via the Pd-menu {\em Find->Find last error}. + +\end{appendix} + +\end{document} + -- cgit v1.2.1