diff options
Diffstat (limited to 'doc/tutorials/externals-howto')
-rw-r--r-- | doc/tutorials/externals-howto/HOWTO-externals-de.tex | 1802 | ||||
-rw-r--r-- | doc/tutorials/externals-howto/HOWTO-externals-en.tex | 1750 | ||||
-rw-r--r-- | doc/tutorials/externals-howto/example1/helloworld.c | 92 | ||||
-rw-r--r-- | doc/tutorials/externals-howto/example2/counter.c | 86 |
4 files changed, 3730 insertions, 0 deletions
diff --git a/doc/tutorials/externals-howto/HOWTO-externals-de.tex b/doc/tutorials/externals-howto/HOWTO-externals-de.tex new file mode 100644 index 00000000..2bd05f63 --- /dev/null +++ b/doc/tutorials/externals-howto/HOWTO-externals-de.tex @@ -0,0 +1,1802 @@ +% format latexg -*- latex -*- + +\documentclass[12pt, a4paper,austrian, titlepage]{article} + + +%% HOWTO write an external for pd +%% by IOhannes m zmölnig +%% +%% this document is released under the CreativeCommons Attribution-ShareAlike 2.5 +%% +%% http://creativecommons.org/licenses/by-sa/2.5/ + +%sprache +\usepackage[latin1]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{babel} + +% add hypertext support (fine for latex2html) +\usepackage{html} + +% add landscape support (for rotating text through 90deg) +\usepackage{lscape} + + +%\begin{latexonly} +% pdf kompatibilität +\newif\ifpdf +\ifx\pdfoutput\undefined + \pdffalse % we are not running PDFLatex +\else + \pdfoutput=1 % yes, we are running PDFLatex + \pdftrue +\fi + +\latexhtml{ +\ifpdf + \usepackage[pdftex]{graphicx} + \pdfcompresslevel=9 +\else + \usepackage{graphicx} +\fi +}{ +\usepackage{graphicx} +} + + +\title{ +HOWTO \\ +write an External \\ +for {\em puredata} +} + +\author{ +johannes m zmölnig \\ +\\ +{\em +\latexhtml{institut für elektronische musik und akustik} +{\htmladdnormalink{institut für elektronische musik und akustik}{http://iem.at}} +} +} + +\date{} + +\begin {document} +\maketitle + +\hyphenation{Echt-zeit} +\hyphenation{Computer-musik-program-men} +\hyphenation{Echt-zeit-Computer-musik-pro-gramm} + +\begin{abstract} +pd ist ein graphisches Computermusiksystem in der Tradition von IRCAMs {\em ISPW-max}. + +Obwohl eine Fülle von Funktionen von pd selbst zur Verfügung gestellt +werden, stößt man doch manchmal an die Grenzen dessen, +das mit diesen Primitiven und ihren Kombinationen möglich ist. + +Deswegen bietet pd die Möglichkeit, eigene Primitive (``objects'', Objekte) in komplexen +Programmiersprachen wie {\tt C/C++} zu erstellen. + +In diesem Dokument soll beschrieben werden, wie man solche Primitive mit Hilfe der +Sprache {\tt C}, in der auch pd selbst realisiert wurde, schreibt. +\end{abstract} + + +\vfill +\newpage + +\tableofcontents + +\vfill +\newpage + +\section{Voraussetzungen und Begriffsbestimmungen} + +pd bezieht sich auf das graphische Echtzeit-Computermusikprogramm von +Miller~S.~Puckette. +{\em puredata}. + +Zum Verständnis dieses Dokumentes wird der Umgang mit pd sowie +Verständnis von Programmiertechniken, insbesondere {\tt C} vorausgesetzt. + +Zum Schreiben von eigenen Primitiven wird weiters ein {\tt C}-Compiler, +der dem {\tt ANSI-C}-Standard genügt, notwendig sein. +Solche Compiler sind beispielsweise der {\em Gnu C-Compiler} (gcc) auf linux-Systemen oder +{\em Visual-C++ 6.0} (vc6) auf Windows-Systemen. + +\subsection{Klassen, Instanzen und Objekte} +pd ist in der Programmiersprache {\tt C} geschrieben. +Allerdings ist pd auf Grund seiner graphischen Natur ein {\em objektorientiertes} System. +Da {\tt C} die Verwendung von Klassen nicht sehr gut unterstützt, ist der resultierende +Quellcode nicht so elegant wie er zum Beispiel unter {\tt C++} wäre. + +Der Ausdruck {\em Klasse} bezieht sich in diesem Dokument auf die Realisierung eines +Konzeptes, bei dem Daten und Manipulatoren eine Einheit bilden. + +Konkrete {\em Instanzen einer Klasse} sind {\em Objekte}. + +\subsection{Internals, Externals und Libraries} +Um Begriffsverwirrungen von vorneherein auszuschließen, seien hier kurz die Ausdrücke +{\em Internal}, {\em External} und {\em Library} erklärt. + +\paragraph{Internal} +Ein {\em Internal} ist eine Klasse, die in pd eingebaut ist. +Viele Primitive wie ``+'', ``pack'' oder ``sig\~\/`` sind {\em Internals} + +\paragraph{External} +Ein {\em External} ist eine Klasse, die nicht in pd eingebaut ist und erst zur Laufzeit +nachgeladen wird. +Sind sie einmal im Speicher von pd, so sind {\em Externals} nicht mehr von {\em Internals} zu +unterscheiden. + +\paragraph{Library} +Eine {\em Library} bezeichnet eine Sammlung von {\em Externals}, +die gemeinsam in eine Binärdatei kompiliert werden. + +{\em Library}-Dateien müssen eine betriebssystemabhängige Namenskonvention einhalten: + +\begin{tabular}{c||c|c|c} +Bibliothek&linux&irix&Win32 \\ +\hline +{\tt my\_lib}&{\tt my\_lib.pd\_linux}&{\tt my\_lib.pd\_irix}& +{\tt my\_lib.dll}\\ +\end{tabular} + +Die einfachste Form einer {\em Library} beinhaltet genau ein {\em External}, +das den selben Name trägt, wie auch die {\em Library} + +Im Gegensatz zu Externals können {\em Libraries} mit bestimmten Befehlen +von pd importiert werden. +Ist eine {\em Library} importiert worden, +so sind alle {\em Externals}, die sie beinhaltet, +in den Speicher geladen und stehen als Objekte zur Verfügung. + +pd stellt zwei Methoden zur Verfügung, um {\em Libraries} zu laden: +\begin{itemize} +\item mit der commandline-Option ``{\tt -lib my\_lib}'' +\item durch Kreieren eines Objektes ``{\tt my\_lib}'' +\end{itemize} + +Die erste Methode lädt die {\em Library} sofort beim Starten von pd. +Dies ist die zu bevorzugende Methode für {\em Libraries}, +die mehrere {\em Externals} beinhalten. + +Die zweite Methode ist für {\em Libraries} zu bevorzugen, die genau +ein {\em External} mit dem selben Namen beinhalten. +Bei der zweiten Methode wird zuerst geprüft, ob eine Klasse namens ``my\_lib'' bereits +in den Speicher geladen ist. +Ist dies nicht der Fall\footnote +{Ist eine solche Klasse bereits im Speicher, wird ein +Objekt namens ``my\_lib'' instanziiert und der Vorgang bricht ab. +Es wird also keine neue {\em Library} geladen. +Man kann daher keine {\em Libraries} mit bereits verwendeten Klassennamen, +wie zum Beispiel ``abs'', laden.} +so werden alle Pfade untersucht, +ob darin eine Datei namens ``{\tt my\_lib.pd\_linux}''\footnote{ +oder einer anderen betriebssystemabhängigen Dateinamenerweiterung (s.o.)} +existiert. +Wird eine solche Datei gefunden, so werden alle in ihr enthaltenen {\em Externals} +in den Speicher geladen. +Danach wird nachgesehen, ob nun eine Klasse namens ``my\_lib'' +als (neu geladenes) {\em External} im Speicher existiert. +Ist dies der Fall, so wird eine Instanz dieser Klasse geschaffen. +Ansonsten wird eine Fehlermeldung ausgegeben, die Instanziierung ist gescheitert. + + +\section{mein erstes External: {\tt helloworld}} +Wie das beim Erlernen von Programmiersprachen so üblich ist, +beginnen wir mit ``Hello world''. + +Ein Objekt soll geschaffen werden, dass jedesmal, wenn es +mit ``bang'' getriggert wird, die Zeile ``Hello world!!'' auf +die Standardausgabe schreibt. + +\subsection{die Schnittstelle zu pd} +Um ein pd-External zu schreiben, braucht man eine wohldefinierte Schnittstelle. +Diese wird in der Datei ``m\_pd.h'' zur Verfügung gestellt. + +\begin{verbatim} +#include "m_pd.h" +\end{verbatim} + + +\subsection{eine Klasse und ihr Datenraum} +Als nächstes muß eine neue Klasse vorbereitet und der +Datenraum für diese Klasse definiert werden. + +\begin{verbatim} +static t_class *helloworld_class; + +typedef struct _helloworld { + t_object x_obj; +} t_helloworld; +\end{verbatim} + +\verb+hello_worldclass+ wird der Zeiger auf die neue Klasse. + +Die Struktur \verb+t_helloworld+ (vom Typ \verb+_helloworld+) +stellt den Datenraum der Klasse dar. +Ein unverzichtbares Element ist dabei eine Variable des Type \verb+t_object+. +In ihr werden interne Objekteigenschaften abgelegt, wie zum Beispiel +die Größe der Objekt-Box bei der graphischen Darstellung, aber auch +Daten über Inlets und Outlets. +\verb+t_object+ muss der erste Eintrag in die Struktur sein ! + +Da bei einer einfachen ``Hello world''-Anwendung keine Variablen gebraucht werden, +ist die Struktur ansonsten leer. + +\subsection{Methodenraum} +Zu einer Klasse gehören neben einem Datenraum auch ein Satz von +Manipulatoren (Methoden) mit denen diese Daten manipuliert werden können. + +Wird eine Message an eine Instanz unserer Klasse geschickt, +so wird eine Methoden aufgerufen. +Diese Mehtoden, die die Schnittstelle zum Messagesystem von pd bilden, +haben grundsätzlich kein Rückgabeargument, sind also vom Typ \verb+void+. + +\begin{verbatim} +void helloworld_bang(t_helloworld *x) +{ + post("Hello world !!"); +} +\end{verbatim} + +Diese Methode hat ein Übergabeargument vom Typ \verb+t_helloworld+, +sodass wir also unseren Datenraum manipulieren könnten. + +Da wir nur ``Hello world!'' ausgeben wollen (und ausserdem unser Datenraum +recht spärlich ist), verzichten wir auf eine Manipulation. + +Mit dem Befehl \verb+post(char *c,...)+ wird eine Meldung an die Standardausgabe +geschickt. +Ein Zeilenumbruch wird automatisch angehängt. +Ansonsten funktioniert \verb+post()+ gleich wie der {\tt C}-Befehl \verb+printf()+. + +\subsection{Generierung einer neuen Klasse} +Um eine neue Klasse zu generieren, müssen Angaben über +den Datenraum und den Methodenraum dieser Klasse +beim Laden einer Library an pd übergeben werden. + +Wird eine neue Library ``my\_lib'' geladen, +so versucht pd eine Funktion ``my\_lib\_setup()'' aufzurufen. +Diese Funktion (oder von ihr aufgerufene Funktionen) teilt pd mit, +welche Eigenschaften die neuen Klassen haben. +Sie wird nur einmal, beim Laden der Library aufgerufen. + +\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} + +Der Befehl \verb+class_new+ kreiert eine neue Klasse und gibt einen Zeiger auf diesen +Prototyp zurück. + +Das erste Argument ist der symbolische Name der Klasse. + +Die nächsten beiden Argumente definieren Konstruktor und Destruktor der Klasse. +Wenn in einen pd-Patch ein Objekt kreiert wird, +instanziiert der Konstruktor \verb+(t_newmethod)helloworld_new+ diesses Objekt +und initialisiert den Datenraum. +Wird ein pd-Patch geschlossen oder ein Objekt daraus entfernt, +so gibt der Destruktor, wenn notwendig, dynamisch reservierten Speicher wieder frei. +Der Speicherplatz für den Datenraum selbst wird von pd automatisch freigegeben. +Deshalb kann in diesem Beispiel auf einen Destruktor verzichtet werden, +folglich wird dieses Argument auf ``0'' gesetzt. + +Damit pd genug Speicher für den Datenraum allozieren und wieder freigeben kann, +wird die Größe dieser Datenstruktur als viertes Argument übergeben. + +Das fünfte Argument bestimmt, wie Klasseninstanzen graphisch dargestellt werden und +ob sie mit anderen Objekten verknüpfbar sind. +Der Standardwert \verb+CLASS_DEFAULT+ (oder einfacher: ``0'') bezieht sich auf +ein Objekt mit mindestens einem Inlet. +Würde man keinen Eingang wollen (wie zum Beispiel beim Internal ``receive''), +so kann man diesen Wert auf \verb+CLASS_NOINLET+ setzen. + +Die restlichen Argumente definieren die Übergabeargumente eines Objektes und deren Typ. + +Bis zu sechs numerische und symbolische Objektargumente können in beliebiger Reihenfolge +mit \verb+A_DEFFLOAT+ und \verb+A_DEFSYMBOL+ angegeben werden. +Sollen mehr Argumente übergeben werden oder die Atomtyp-Reihenfolge flexibler sein, +so bietet \verb+A_GIMME+ die Übergabe einer beliebigen Liste von Atomen. + +Die Objektargumentliste wird mit ``0'' terminiert. +In unserem Beispiel sind also keine Übergabeargumente für die Klasse vorgesehen. + +\paragraph{class\_addbang} +Jetzt muss zur Klasse noch ein Methodenraum hinzugefügt werden. + +Mit \verb+class_addbang+ wird der durch das erste Argument definierten Klasse +eine Methode für eine ``bang''-Message hinzuzugefügt. +Diese Methode ist das zweite Argument. + + + +\subsection{Konstruktor: Instanziierung eines Objektes} +Jedesmal, wenn in einem pd-Patch ein Objekt einer Klasse kreiert wird, +schafft der mit \verb+class_new+ angegebene Konstruktor eine neue Instanz der Klasse. + +Der Konstruktor ist immer vom Typ \verb+void *+ + +\begin{verbatim} +void *helloworld_new(void) +{ + t_helloworld *x = (t_helloworld *)pd_new(helloworld_class); + + return (void *)x; +} +\end{verbatim} + +Die Übergabeargumente der Konstruktorfunktion hängen von den mit +\verb+class_new+ angegebenen Objektargumenten ab. + +\begin{tabular}{l|l} +\verb+class_new+-Argument&Konstruktorargument\\ +\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} + +Da in diesem Beispiel keine Objektargumente existieren, hat auch +der Konstruktor keine. + +Die Funktion \verb+pd_new+ reserviert Speicher für den Datenraum, initialisiert +die objektinternen Variablen und gibt einen Zeiger auf den Datenraum zurück. + +Der Typ-Cast auf den Datenraum ist notwendig. + +Normalerweise würden im Konstruktor auch die Objektvariablen initialisiert werden. +In diesem Beispiel ist dies aber nicht notwendig. + +Der Konstruktor muss einen Zeiger auf den instanziierten Datenraum zurückgeben. + +\subsection{der 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{ein komplexes External: {\tt counter}} + +Als nächstes soll ein einfacher Zähler als External geschrieben werden. +Ein ``bang''-Trigger soll den aktuellen Zählerstand am Outlet ausgeben +und anschließend um 1 erhöhen. + +Diese Klasse unterscheidet sich nicht sonderlich von der vorherigen, +ausser dass nun eine interne Variable ``Zählerstand'' benötigt +wird und das Ergebnis nicht mehr auf die Standardausgabe geschrieben sondern +als Message zu einem Outlet geschickt wird. + +\subsection{Variablen eines Objektes} +Ein Zähler braucht natürlich eine Zustandsvariable, +in der der aktueller Zählerstand gespeichert ist. + +Solche zum Objekt gehörigen Zustandsvariablen werden im Datenraum abgelegt. + +\begin{verbatim} +typedef struct _counter { + t_object x_obj; + t_int i_count; +} t_counter; +\end{verbatim} + +Die Ganzzahlvariable \verb+i_count+ beschreibt den Zählerstand. +Natürlich könnte man sie auch als Gleitkommawert realisieren, +doch traditionell werden Zähler ganzzahlig ausgeführt. + +\subsection{Übergabeargumente} +Für einen Zähler ist es durchaus sinnvoll, wenn man den Startwert festlegen kann. +Hier soll der Startwert dem Objekt bei der Kreation übergeben werden. + +\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} + +Es ist also ein Argument zur Funktion \verb+class_new+ hinzugekommen: + +\verb+A_DEFFLOAT+ teilt mit, dass das Objekt ein Übergabeargument +vom Typ \verb+t_floatarg+ hat. + + + +\subsection{Konstruktor} +Dem Konstruktor kommen nun mehrere neue Aufgaben zu. +Zum ersten muss eine Variable initialisiert werden, +zum anderen muss auch ein Outlet für das Objekt geschaffen werden. +\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} + +Die Konstruktorfunktion hat jetzt ein Argument fom Typ \verb+t_floatarg+, wie es in +der Setup-Routine \verb+class_new+ deklariert worden ist. +Dieses Argument initialisiert den Zähler. + +Einer neuer Outlet wird mit der Funktion \verb+outlet_new+ geschaffen. +Das erste Argument ist ein Zeiger auf die Objektinterna, +in denen der neue Ausgang geschaffen wird. + +Das zweite Argument ist eine symbolische Typbeschreibung des Ausgangs. +Da der Zähler numerische Werte ausgeben soll, ist er vom Typ ``float''. +Sollte der Ausgang für Messages mit verschiedenen Selectoren verwendet werden, +so ist dieser Wert ``0''. + +\verb+outlet_new+ gibt einen Zeiger auf den neuen Outlet zurück und speichert diesen +Zeiger in der \verb+t_object+-Variablen \verb+x_obj.ob_outlet+. +Wird nur ein Outlet verwendet, muss daher der Zeiger nicht extra im Datenraum gespeichert +werden. +Werden mehrere Outlets verwendet, so müssen diese Zeiger im Datenraum gespeichert werden. + +\subsection{die Zählermethode} +Bei einem Triggerevent soll der alte Zählerstand ausgegeben und um eins inkrementiert werden. + +\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} + +Die Funktion \verb+outlet_float+ gibt an dem Outlet, auf den das erste Argument verweist, +eine Gleitkommazahl (zweites Argument) aus. + +Hier wird zuerst der Zählerstand in eine Gleitkomma-Buffervariable gespeichert. +Danach wird er inkrementiert und dann wird erst die Buffervariable ausgegeben. + +Was auf den ersten Blick unnötig erscheint, macht bei näherer Betrachtung Sinn: +Die Buffervariable wurde gleich als \verb+t_float+ realisiert, +da sich \verb+outlet_float+ sowieso einen Gleitkommawert erwartet +und ein Cast unvermeidlich ist. + +Würde der Zählerstand zuerst an den Outlet geschickt werden und +danach erst inkrementiert werden, würde dies unter Umständen zu einem etwas seltsamen +Verhalten führen. +Wenn nämlich der Zählerausgang wieder an den Inlet zurückgeführt würde, der +Zähler sich also selbst triggerte, so würde die Zählermethode erneut +aufgerufen, ohne dass der Zählerstand inkrementiert worden wäre. +Dies ist im Allgemeinen aber unerwünscht. + +Man kann übrigens das gleiche Ergebnis wie hier mit nur einer einzigen Zeile erreichen, +doch sieht man das {\em Reentrant}-Problem dann nicht sehr gut. + +\subsection{der 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{ein komplexeres External: \tt counter} + +Man kann natürlich auch einen einfache Zähler ein bißchen komplexer gestalten. +Es wäre zum Beispiel sinnvoll, +wenn der Zählerstand auf einen Startwert zurückgesetzt werden könnte, +wenn man Start- und Endwert bestimmen könnte und auch die Schrittweite variabel wäre. + +Bei jedem Zählerüberlauf soll ein zweiter Outlet eine ``bang''-Message schicken und der +Zähler auf den Startwert zurückgesetzt werden. + +\subsection{erweiterter Datenraum} + +\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} + +Der Datenraum wurde also erweitert um Variablen für Schrittweite und Start- bzw. Stopwert. +Weiters werden Zeiger auf zwei Outlets zur Verfügung gestellt. + +\subsection{Erweiterung der Klasse} +Da nun die Klassenobjekte verschiedene Messages, wie ``set'' und ``reset'', +verstehen können sollen, mussen der Methodenraum entsprechend erweitert werden. + +\begin{verbatim} + counter_class = class_new(gensym("counter"), + (t_newmethod)counter_new, + 0, sizeof(t_counter), + CLASS_DEFAULT, + A_GIMME, 0); +\end{verbatim} + +Der Klassengenerator \verb+class_new+ ist um das Objektübergabeargument +\verb+A_GIMME+ erweitert. +Damit kann eine dynamische Anzahl von Argumenten bei der Objektinstanziierung +verwaltet werden. + +\begin{verbatim} + class_addmethod(counter_class, + (t_method)counter_reset, + gensym("reset"), 0); +\end{verbatim} + +\verb+class_addmethod+ fügt einer Klasse eine Methode mit für einen +beliebigen Selector hinzu. + +Das erste Argument ist die Klasse, +zu der die Methode (zweites Argument) hinzugefügt wird. + +Das dritte Argument ist der symbolische Selector, +der mit der Methode assoziiert wird. + +Die restlichen ``0''-terminierten Argumente +beschreiben die Atomliste, die dem Selector folgt. + +\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} + +Eine Methode für den Selector ``set'', gefolgt von einem numerischen Wert, +wird hinzugefügt. + +Für den Selector ``bound'', gefolgt von zwei numerischen Werten, +wird ebenfalls eine Methode zur Klasse hinzugefügt. + +\begin{verbatim} + class_sethelpsymbol(counter_class, gensym("help-counter")); +\end{verbatim} + +Clickt man mit der rechten Maustaste auf ein pd-Objekt, +so kann man sich einen Hilfe-Patch für die zugehörige Objektklasse anzeigen lasse. +Standardmäßig wird ist dies ein Patch mit dem symbolischen Klassennamen +im Verzeichnis ``{\em doc/5.reference/}'' gesucht. +Mit dem Befehl \verb+class_sethelpsymbol+ kann ein alternativer Patch angegeben werden. + +\subsection{Konstruktion von In- und Outlets} + +Bei der Objektkreation sollten dem Objekt verschiedene Argumente übergeben +werden. + +\begin{verbatim} +void *counter_new(t_symbol *s, int argc, t_atom *argv) +\end{verbatim} +Durch die Argumentendeklaration in der \verb+class_new+-Funktion +mit \verb+A_GIMME+, werden dem Konstruktor folgende Argumente +übergeben: + +\begin{tabular}{c|l} +\verb+t_symbol *s+ & der symbolische Namen,\\ +& mit dem das Objekt kreiert wurde \\ +\verb+int argc+ & die Anzahl, der dem Objekt übergebenen Argumente\\ +\verb+t_atom *argv+ & ein Zeiger auf eine Liste von {\tt argc} Atomen +\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: + } + if (argc<2)f2=f1; + x->i_down = (f1<f2)?f1:f2; + x->i_up = (f1>f2)?f1:f2; + + x->i_count=x->i_down; +\end{verbatim} + +Werden drei Argumente übergeben, so sollten dies {\em untere Zählergrenze}, +{\em obere Zählergrenze} und {\em Schrittgröße} sein. +Werden nur zwei Argumente übergeben, +so wird die Schrittgröße standardmäßig auf ``1'' gesetzt. +Bei nur einem Argument, sei dies der {\em Startwert} des Zählers, +die {\em Schrittgröße} sei ``1''. + +\begin{verbatim} + inlet_new(&x->x_obj, &x->x_obj.ob_pd, + gensym("list"), gensym("bound")); +\end{verbatim} +Die Funktion \verb+inlet_new+ erzeugt einen neuen ``aktiven'' Inlet. +``Aktiv'' heißt, dass eine Klassenmethode ausgeführt wird, +wenn eine Message in den einen ``aktiven'' Inlet geschickt wird. + +Von der Software-Architektur her ist der erste Inlet immer ``aktiv''. + +Die ersten beiden Argumente der \verb+inlet_new+-Funktion +sind Zeiger auf die Objektinterna und die graphische Darstellung des Objektes. + +Der symbolische Selector, der durch das dritte Argument spezifiziert wird, +wird für diesen Inlet durch einen anderen symbolischen Selector (viertes Argument) +substituiert. + +Durch die Substitution von Selectoren kann eine Message +an einem bestimmten rechten Eingang wie eine Message mit einem bestimmten Selector +am linken Eingang betrachtet werden. + +Dies bedeutet +\begin{itemize} +\item Der substituierende Selector muss mit \verb+class_addmethod+ angegeben werden. +\item Man kann einen bestimmten rechten Eingang simulieren, +indem man dem ersten Eingang eine Message mit dem Selector dieses Eingangs schickt. +\item Es ist nicht möglich, einem rechten Eingang Methoden für mehr als einen Selector +zuzuweisen. Insbesondere ist es nicht möglich, ihm eine allgemeine Methode +für einen beliebigen Selector zuzuweisen. +\end{itemize} + +\begin{verbatim} + floatinlet_new(&x->x_obj, &x->step); +\end{verbatim} +\verb+floatinlet_new+ generiert einen ``passiven'' Inlet für numerische Werte. +``Passive'' Eingänge erlauben, dass ein Speicherplatz bestimmten Typs im +Variablenraum des Objektes von außen direkt beschrieben werden kann. +Dadurch ist zum Beispiel eine Abfrage nach illegalen Eingaben nicht möglich. +Das erste Argument ist dabei ein Zeiger auf die interne Objektinfrastruktur. +Das zweite Argument ist ein Zeiger auf den Speicherplatz, auf den geschrieben wird. + +Es können ``passive'' Eingänge für numerische (Gleitkomma\footnote{ +Deswegen ist der {\tt step}-Wert des Klassendatenraums als {\tt t\_float} realisiert.}) +-Werte, symbolische Werte und Pointer geschaffen werden. + +\begin{verbatim} + x->f_out = outlet_new(&x->x_obj, &s_float); + x->b_out = outlet_new(&x->x_obj, &s_bang); +\end{verbatim} + +Die von \verb+outlet_new+ zurückgegebenen Zeiger auf die geschaffenen Outlets, +müssen im Klassendatenraum gespeichert werden, +damit sie später von den Ausgaberoutinen angesprochen werden. + +Die Reihenfolge der Generierung von In- und Outlets ist wichtig, +da sie der Reihenfolge der Ein- und Ausgänge der graphischen Repräsentation +des Objektes entsprechen. + +\subsection{erweiterter Methodenraum} + +Der Methode für die ``bang''-Message muss natürlich der komplexeren Zählerstruktur +genüge tun. + + +\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} + +Die einzelnen Outlets werden von den \verb+outlet_...+-Funktionen über +die Zeiger auf diese Ausgänge identifiziert. + +Die übrigen Methoden müssen noch implementiert werden: + +\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 = (f1<f2)?f1:f2; + x->i_up = (f1>f2)?f1:f2; +} +\end{verbatim} + +\subsection{der 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 = (f1<f2)?f1:f2; + x->i_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: + } + if (argc<2)f2=f1; + + x->i_down = (f1<f2)?f1:f2; + x->i_up = (f1>f2)?f1:f2; + + x->i_count=x->i_down; + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, + gensym("list"), gensym("bound")); + floatinlet_new(&x->x_obj, &x->step); + + 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{ein Signal-External: {\tt pan\~\/}} +Signalklassen sind normale Klassen, die zusätzlich Methoden +für Signale bereitstellen. + +Alle Methoden und Konzepte die mit normalen Objektklassen realisierbar sind, +sind also auch mit Signalklassen zuverwirklichen. + +Per Konvention enden die symbolischen Namen mit einer Tilde \~\/. + +Anhand einer Klasse ``pan\~\/`` soll demonstriert werden wie Signalklassen geschrieben +werden können. + +Ein Signal am linken Inlet wird mit einem Signal am zweiten Inlet gemischt. +Der Mischungsgrad wird als \verb+t_float+-Message an einen dritten Eingang festgelegt. + +\subsection{Variablen einer Signalklasse} +Da eine Signalklasse nur eine erweiterte normale Klasse ist, +gibt es keine prinzipielle Unterschiede zwischen den Datenräumen. + +\begin{verbatim} +typedef struct _pan_tilde { + t_object x_obj; + + t_sample f_pan; + t_float f; +} t_pan_tilde; +\end{verbatim} + +Es wird nur eine Variable für den {\em Mischfaktor} der Panningfunktion benötigt. + +Die Variable \verb+f+ wird gebraucht, falls kein Signal am Signalinlet liegt. +Wird dann an diesen Signalinlet ein numerischer Wert als Message geschickt, +so ersetzt dieser das Signal und wird in der Variable \verb+f+ gespeichert. + +\subsection{Signalklassen} + +\begin{verbatim} +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} + +Jeder Signalklasse muss eine Methode für die Signalverarbeitung zugeordnet werden. +Wenn die Audioengine von pd gestartet wird, wird allen Objekten eine +Message mit dem Selector ``\verb+dsp+'' geschickt. +Alle Klassen, die eine Methode für die ``dsp''-Message haben, sind Signalklassen. + +Signalklassen, die Signal-Inlets zur Verfügung stellen wollen, +müssen dies mit dem \verb+CLASS_MAINSIGNALIN+-Makro anmelden. +Dadurch ist der erste Inlet als Signalinlet deklariert. +\verb+t_float+-Messages können nicht mehr an einen solchen Eingang +gesendet werden. + +Das erste Argument des Makros ist ein Zeiger auf die Signalklasse. +Das zweite Argument ist der Typ des Datenraums der Klasse. +Das dritte Argument ist eine Dummy-Variable aus dem Datenraum, die gebraucht wird, +um bei nicht vorhandenen Signalen am Signalinlet diese durch \verb+t_float+-Messages +einfach ersetzen zu können. + +\subsection{Konstruktion von Signal-In- und 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; + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + floatinlet_new (&x->x_obj, &x->f_pan); + + outlet_new(&x->x_obj, &s_signal); + + return (void *)x; +} +\end{verbatim} + +Zusätzliche Signal-Eingänge werden normal mit der Routine \verb+inlet_new+ +hinzugefügt. +Die letzen beiden Argumente sind dann jeweils ein Verweis auf den symbolischen Selector +``signal'' in der lookup-Tabelle. + +Signal-Outlets werden ebenfalls wie Message-Outlets generiert, deren Outlet mit dem +Selector ``signal'' versehen ist. + + +\subsection{DSP-Methode} +Wenn die Audio-Engine von pd eingeschalten wird, +so teilen ihr alle Signal-Objekte mit, +welche Methode von ihrer Klasse zur digitalen Signalverarbeitung herangezogen werden soll. + +Die ``DSP''-Methode hat als Argumente einen Zeiger auf den Klassendatenraum und +einen Zeiger auf ein Array von Signalen. + +Die Signale im Array sind so angeordnet, dass sie am graphischen Objekt +im Uhrzeigersinn gelesen werden.\footnote{ +Sofern linke und rechte Ein- und Ausgangssignale vorhanden sind, gilt also: +Zuerst kommt das linke Eingangssignal, danach die rechten Eingangssignale; +nach den rechten Ausgangssignalen kommt das linke Ausgangssignal. +} + +\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+ fügt eine ``Perform''-Routine (erstes Argument) zum DSP-Baum hinzu. +Das zweite Argument ist die Anzahl der nachfolgenden Zeiger auf diverse Variablen. +Welche Zeiger auf welche Variablen übergeben werden, unterliegt keiner Beschränkung. + +sp[0] bezeichnet hier das erste Eingangssignal, sp[1] das zweite Eingangssignal, +sp[3] das Ausgangssignal. + +Die Struktur \verb+t_signal+ enthält einen Zeiger auf den +zugehörigen Signalvektor \verb+.s_vec+ (ein Array von Samples \verb+t_sample+), +sowie die Länge dieses Signalvektors \verb+.s_n+. +Da innerhalb eines Patches alle Signalvektoren die gleiche Länge haben, +genügt es, die Länge eines dieser Vektoren abzufragen. + +\subsection{perform-Routine} +Die perform-Routine ist das eigentliche DSP-Herzstück einer Signalklasse. + +Ihr wird ein Zeiger auf ein Integer-Array übergeben. +In diesem Array sind die Zeiger gespeichert, die mit \verb+dsp_add+ übergeben wurden. +Sie müssen auf ihren ursprünglichen Typ zurückgecastet werden. + +Die perform-Routine muß einen Zeiger auf Integer zurückgeben, der hinter den +Speicherplatz zeigt, in dem die eigenen Zeiger gespeichert sind. +Dies bedeutet, dass das Rückgabeargument gleich dem Übergabeargument plus der +Anzahl der eigenen Zeigervariablen (wie sie als zweites Argument in +\verb+dsp_add+ angegeben wurde) plus eins. + +\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} + +In der \verb+while+-Schleife wird jedes Sample der Signalvektoren einzeln +abgearbeitet. + +Eine Optimierungsroutine bei der Erstellung des DSP-Baumes wird darauf geachtet, +keine unnötigen Kopieroperationen durchzuführen. +Es kann daher geschehen, dass ein Eingangs- und ein Ausgangssignal an der +gleichen Stelle im Speicher stehen. +Es ist daher in solchem Falle darauf zu achten, +dass nicht in das Ausgangssignal geschrieben wird, +bevor dort das Eingangssignal ausgelesen wurde. + +\subsection{der Code: \tt pan\~\/} + +\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_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_new(t_floatarg f) +{ + t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class); + + x->f_pan = f; + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + floatinlet_new (&x->x_obj, &x->f_pan); + 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{das Message-System von \em pd} +Nicht-Audio-Daten werden über ein Message-System verteilt. +Jede Message besteht aus einem ``Selector'' und einer Liste von Atomen. + +\subsection{Atome} + +Es gibt drei Arten von Atomen: +\begin{itemize} +\item {\em A\_FLOAT}: ein numerischer Wert (Gleitkommazahl) +\item {\em A\_SYMBOL}: ein symbolischer Wert (String) +\item {\em A\_POINTER}: ein Zeiger +\end{itemize} + +Numerische Werte werden immer als Floating-Point-Werte (\verb+double+) dargestellt, +auch wenn es sich um Ganzzahlwerte handelt. + +Jedes Symbol wird aus Performancegründen in einer lookup-Tabelle abgelegt. +Der Befehl \verb+gensym+ speichert, wenn nötig, +einen String in dieser Symboltabelle und gibt seine Addresse in der Tabelle zurück. + +Atome vom Typ {\em A\_POINTER} haben in der Praxis +(für einfache Externals) eher untergeordnete Bedeutung. + +Der Typ eines Atoms \verb+a+ wird im Strukturelement \verb+a.a_type+ gespeichert. + +\subsection{Selectoren} + +Der Selector ist ein Symbol und bestimmt, welchen Typ eine Message hat. +Es gibt fünf vordefinierte Selectoren: +\begin{itemize} +\item ``{\tt bang}'' bezeichnet ein Triggerevent. +Die Message besteht nur aus dem Selector und enthält keine Liste von Atomen. +\item ``{\tt float}'' bezeichnet einen numerischen Wert. Die Liste enthält nur ein Atom. +\item ``{\tt symbol}'' bezeichnet einen symbolischen Wert. Die Liste enthält nur ein Atom. +\item ``{\tt pointer}'' bezeichnet einen Zeiger. Die Liste enthält nur ein Atom. +\item ``{\tt list}'' bezeichnet eine Liste von mehreren Atomen. +\end{itemize} + +Da die Symbole für diese Selectoren relativ häufig verwendet werden, +kann man deren Symboltabellen-Adresse auch direkt, +ohne den Umweg über \verb+gensym+ abfragen: + +\begin{tabular}{l||l|l} +Selector&lookup-Routine&lookup-Addresse\\ +\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} + +Es können auch andere Selectoren verwendet werden, +doch muss dann die Empfängerklasse entweder selbst eine Methode +für diesen Selector zur verfügung stellen, +oder eine Methode für ``anything'', also jeden beliebigen Selector, anbieten. + +Messages die ohne Selector sofort mit einem Zahlenwert beginnen, werden automatisch +entweder als numerischer Wert (nur ein Atom) oder als Liste (mehrere Atome) erkannt. + +Zum Beispiel sind also die Messages ``\verb+12.429+'' und ``\verb+float 12.429+'' ident. +Ebenfalls ident sind auch die Listen-Messages +``\verb+list 1 kleines Haus+'' und ``\verb+1 kleines Haus+''. + +\section{pd-Typen} +Da pd auf mehreren Plattformen benutzt wird, +werden viele gewöhnliche Variablentypen, wie \verb|int|, neu definiert. +Um portablen Code zu schreiben ist es daher angebracht, die von pd bereitgestellten +Typen zu verwenden. + +Weiters gibt es viele vordefinierte Typen, +die das Leben des Programmierers vereinfachen sollten. +pd-Typen beginnen im Allgemeinen mit \verb|t_|. + +\begin{tabular}{c|l} +pd-Type & Beschreibung \\ +\hline\hline +\verb+t_atom+& Atom \\ +\verb+t_float+ & Gleitkomma-Zahl \\ +\verb+t_symbol+ & Symbol \\ +\verb+t_gpointer+ & Zeiger (auf graphische Objekte) \\ +\hline +\verb+t_int+ & Ganzzahl \\ +\verb+t_signal+ & Struktur auf ein Signal \\ +\verb+t_sample+ & Audio-Signalwert (Gleitkomma)\\ +\verb+t_outlet+ & Outlet eines Objekts \\ +\verb+t_inlet+ & Inlet eines Objekts \\ +\verb+t_object+ & Objekt-Interna \\ +\hline +\verb+t_class+ & eine pd-Klasse \\ +\verb+t_method+ & Zeiger auf Klassenmethode \\ +\verb+t_newmethod+ & Zeiger auf Klasseninstanziierungsmethode (new-Routine) \\ +\end{tabular} + + +\section{Wichtige Funktionen aus ``m\_pd.h''} +\subsection{Funktionen: Atome} + +\subsubsection{SETFLOAT} +\begin{verbatim} +SETFLOAT(atom, f) +\end{verbatim} +Dieses Makro setzt den Typ von \verb+atom+ auf \verb+A_FLOAT+ +und setzt den numerischen Wert dieses Atoms auf \verb+f+. + +\subsubsection{SETSYMBOL} +\begin{verbatim} +SETSYMBOL(atom, s) +\end{verbatim} +Dieses Makro setzt den Typ von \verb+atom+ auf \verb+A_SYMBOL+ +und setzt den symbolischen Wert dieses Atoms auf \verb+s+. + +\subsubsection{SETPOINTER} +\begin{verbatim} +SETPOINTER(atom, pt) +\end{verbatim} +Dieses Makro setzt den Typ von \verb+atom+ auf \verb+A_POINTER+ +und setzt den Zeiger-Wert dieses Atoms auf \verb+pt+. + +\subsubsection{atom\_getfloat} +\begin{verbatim} +t_float atom_getfloat(t_atom *a); +\end{verbatim} +Wenn der Typ des Atoms \verb+a+ \verb+A_FLOAT+ ist, wird dessen numerischer Wert, +ansonsten ``0.0'' zurückgegeben. + +\subsubsection{atom\_getfloatarg} +\begin{verbatim} +t_float atom_getfloatarg(int which, int argc, t_atom *argv) +\end{verbatim} +Wenn das Atom, +das in der Atomliste \verb+argv+ mit der Länge \verb+argc+ an der Stelle \verb+which+ +zu finden ist, +vom Typ \verb+A_FLOAT+ ist, wird dessen numerischer Wert, +ansonsten ``0.0'' zurückgegeben. + +\subsubsection{atom\_getint} +\begin{verbatim} +t_int atom_getint(t_atom *a); +\end{verbatim} +Wenn der Typ des Atoms \verb+a+ \verb+A_FLOAT+ ist, wird dessen numerischer +Wert als Ganzzahlwert, ansonsten ``0'' zurückgegeben. + +\subsubsection{atom\_getsymbol} +\begin{verbatim} +t_symbol atom_getsymbol(t_atom *a); +\end{verbatim} +Wenn der Typ des Atoms \verb+a+ \verb+A_SYMBOL+ ist, wird ein Zeiger +auf dessen Symbol ansonsten auf das Symbol ``float'' zurückgegeben. + +\subsubsection{atom\_gensym} +\begin{verbatim} +t_symbol *atom_gensym(t_atom *a); +\end{verbatim} +Wenn der Typ des Atoms \verb+a+ \verb+A_SYMBOL+ ist, wird ein Zeiger +auf dessen Symbol zurückgegeben. + +Atome anderen Typs werden zuerst ``sinnvoll'' in Strings umgewandelt. +Diese Strings werden, falls nötig, in die Symbol-Tabelle eingetragen. +Die Zeiger auf das Symbol wird zurückgegeben. + + +\subsubsection{atom\_string} +\begin{verbatim} +void atom_string(t_atom *a, char *buf, unsigned int bufsize); +\end{verbatim} +Konvertiert ein Atom \verb+a+ in einen {\tt C}-String \verb+buf+. +Der char-Buffer muss selbst reserviert und seine Länge in \verb+bufsize+ angegeben werden. + +\subsubsection{gensym} +\begin{verbatim} +t_symbol *gensym(char *s); +\end{verbatim} +Prüft, ob für den C-String \verb+*s+ bereits ein Eintrag in der Symbol-lookup-Tabelle +vorhanden ist. +Ist noch kein Eintrag vorhanden, so wird einer angelegt. +Ein Zeiger auf das Symbol in der Tabelle wird zurückgegeben. + + +\subsection{Funktionen: Klassen} +\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} +Generiert eine neue Klasse mit dem symbolischen Namen \verb+name+. + +\verb+newmethod+ ist eine Konstruktorfunktion, +die eine Instanz der Klasse konstruiert und einen Zeiger auf diese Instanz zurückgibt. + +Wird manuell dynamischer Speicher reserviert, +so muss dieser bei Zerstörung eines Objektes +mit der Destruktormethode \verb+freemethod+ (kein Rückgabeargument) +wieder freigegeben werden. + +\verb+size+ ist statische die Größe des Klassendatenraumes, +die mit der Funktion \verb+sizeof(t_mydata)+ berechnet werden kann. + +\verb+flags+ bestimmen das Aussehen des graphischen Objektes. +Eine beliebige Kombination folgender Flags ist möglich: + +\begin{tabular}{l|l} +Flag&Bedeutung\\ +\hline +\verb+CLASS_DEFAULT+ &Ein normales Objekt mit einem Inlet \\ +\verb+CLASS_PD+ & \em Objekte ohne Graphikdarstellung\\ +\verb+CLASS_GOBJ+ & \em reine Graphikobjekte (wie Arrays, Graphen,...)\\ +\verb+CLASS_PATCHABLE+ & \em normales Objekt (mit einem Inlet) \\ +\verb+CLASS_NOINLET+ & Der standardmäßige Inlet wird unterdrückt \\ +\end{tabular} + +Flags, deren Bedeutung {\em kursiv} gedruckt ist, +haben geringe Bedeutung beim Schreiben von Externals. + +Die restlichen Argumente \verb+arg1,...+ definieren +die Typen die Übergabeargumente bei der Objektkreation. +Höchstens sechs typgeprüfte Argumente können einem Objekt übergeben werden. +Die Argumententypeliste wird ``0'' terminiert. + +Mögliche Argumententypen sind: + +\begin{tabular}{l|l} +\verb+A_DEFFLOAT+ & ein numerischer Wert \\ +\verb+A_DEFSYMBOL+ & ein symbolischer Wert \\ +\verb+A_GIMME+ & eine Atomliste beliebiger Länge und Typen \\ +\end{tabular} + +Sollten mehr als sechs Argumente übergeben werden, muss man +\verb+A_GIMME+ verwenden und eine händische Typprüfung durchführen. + +\subsubsection{class\_addmethod} +\begin{verbatim} +void class_addmethod(t_class *c, t_method fn, t_symbol *sel, + t_atomtype arg1, ...); +\end{verbatim} +Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+ für +eine Message mit dem Selector \verb+sel+ hinzu. + +Die restlichen Argumente \verb+arg1,...+ definieren +die Typen der Atomliste die dem Selector folgt. +Höchstens sechs typgeprüfte Argumente angegeben werden. +Sollten mehr als sechs Argumente übergeben werden, muss man +\verb+A_GIMME+ verwenden und eine händische Typprüfung durchführen. + +Die Argumententypeliste wird ``0'' terminiert. + +Mögliche Argumententypen sind: + +\begin{tabular}{l|l} +\verb+A_DEFFLOAT+ & ein numerischer Wert \\ +\verb+A_DEFSYMBOL+ & ein symbolischer Wert \\ +\verb+A_POINTER+ & eine Zeiger \\ +\verb+A_GIMME+ & eine Atomliste beliebiger Länge und Typen \\ +\end{tabular} + +\subsubsection{class\_addbang} +\begin{verbatim} +void class_addbang(t_class *c, t_method fn); +\end{verbatim} +Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+ +für eine ``bang''-Message hinzu. +Die ``bang''-Methode hat als Übergabeargument einen Zeiger auf den Klassendatenraum: + +\verb+void my_bang_method(t_mydata *x);+ + +\subsubsection{class\_addfloat} +\begin{verbatim} +void class_addfloat(t_class *c, t_method fn); +\end{verbatim} +Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+ +für eine ``float''-Message hinzu. +Die ``float''-Methode hat als Übergabeargument einen Zeiger auf den Klassendatenraum und +ein Gleitkommaargument: + +\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} +Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+ +für eine ``symbol''-Message hinzu. +Die ``symbol''-Methode hat als Übergabeargument einen Zeiger auf den Klassendatenraum und +einen Zeiger auf das übergebene 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} +Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+ +für eine ``pointer''-Message hinzu. +Die ``pointer''-Methode hat als Übergabeargument einen Zeiger +auf den Klassendatenraum und einen Zeiger auf einen 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} +Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+ +für eine ``list''-Message hinzu. +Die ``list''-Methode hat als Übergabeargument neben einem Zeiger +auf den Klassendatenraum einen Zeiger auf das Selectorsymbol +(immer \verb+&s_list+), +die Anzahl der Atome in der Liste sowie einen Zeiger auf die Atomliste: + +\verb+void my_list_method(t_mydata *x,+ + +\verb+ t_symbol *s, int argc, t_atom *argv);+ + + +%\begin{verbatim} +%void my_list_method(t_mydata *x, +% t_symbol *s, int argc, t_atom *argv); +%\end{verbatim} + + + +\subsubsection{class\_addanything} +\begin{verbatim} +void class_addanything(t_class *c, t_method fn); +\end{verbatim} +Fügt der Klasse, auf die \verb+c+ zeigt, die Methode \verb+fn+ +für eine beliebige Message hinzu. +Die anything-Methode hat als Übergabeargument neben einem Zeiger +auf den Klassendatenraum einen Zeiger auf das Selectorsymbol, +die Anzahl der Atome in der Liste sowie einen Zeiger auf die Atomliste: + + +\verb+void my_any_method(t_mydata *x,+ + +\verb+ t_symbol *s, int argc, t_atom *argv);+ + + +%\begin{verbatim} +%void my_any_method(t_mydata *x, +% t_symbol *s, int argc, t_atom *argv); +%\end{verbatim} + +\subsubsection{class\_addcreator} +\begin{verbatim} + void class_addcreator(t_newmethod newmethod, t_symbol *s, + t_atomtype type1, ...); +\end{verbatim} +Fügt zu einem Konstruktor \verb+newmethod+ ein zum Klassennamen alternatives +Kreatorsymbol \verb+s+ hinzu. +Dadurch können Objekte mit dem richtigen Klassennamen und einem Aliasnamen +(zum Beispiel eine Abkürzung, wie das Internal ``float'' bzw. ``f'') kreiert werden. + +Die ``0''-terminierte Typenliste entspricht der von \verb+class_new+. + +\subsubsection{class\_sethelpsymbol} +\begin{verbatim} +void class_sethelpsymbol(t_class *c, t_symbol *s); +\end{verbatim} + +Clickt man mit der rechten Maustaste auf ein pd-Objekt, +so kann man sich einen Hilfe-Patch für die zugehörige Objektklasse anzeigen lasse. +Standardmäßig wird ist dies ein Patch mit dem symbolischen Klassennamen +im Verzeichnis ``{\em doc/5.reference/}'' gesucht. + +Für die Klasse, auf die \verb+c+ zeigt, wird der Name des Hilfepatches auf den +symbolischen Wert \verb+s+ geändert. + +Dadurch können sich mehrere verwandte Klassen einen Hilfepatch teilen. + +Pfadangaben erfolgen relativ zum Standardhilfepfad {\em doc/5.reference/}. + +\subsubsection{pd\_new} +\begin{verbatim} +t_pd *pd_new(t_class *cls); +\end{verbatim} +Generiert eine neue Instanz der Klasse \verb+cls+ und gibt einen Zeiger auf diese +Instanz zurück. + +\subsection{Funktionen: In- und Outlets} +Alle Inlet- und Outletroutinen benötigen eine Referenz auf die Objektinterna +der Klasseninstanz. +Die notwendige Variable vom Typ \verb+t_object+ im Datenraum wird bei der +Objektinstanziierung initialisiert. +Diese Variable muß als \verb+owner+-Objekt den Inlet- und Outletroutinen übergeben werden. + +\subsubsection{inlet\_new} +\begin{verbatim} +t_inlet *inlet_new(t_object *owner, t_pd *dest, + t_symbol *s1, t_symbol *s2); +\end{verbatim} +Generiert einen zusätzlichen ``aktiven'' Inlet des Objektes, auf das \verb+owner+ zeigt. +\verb+dest+ zeigt im Allgemeinen auf ``\verb+owner.ob_pd+''. + +Der Selector \verb+s1+ am neuen Inlet, wird durch den Selector \verb+s2+ substituiert. + +Tritt also eine Message mit dem Selector \verb+s1+ am neuen Inlet auf, +wird die Klassenmethode für den Selector \verb+s2+ ausgeführt. + +Dies bedeutet +\begin{itemize} +\item Der substituierende Selector muss mit \verb+class_addmethod+ angegeben werden. +\item Man kann einen bestimmten rechten Eingang simulieren, +indem man dem ersten Eingang eine Message mit dem Selector dieses Eingangs schickt. + +Verwendet man ein leeres Symbol (\verb+gensym("")+) als Selector, +so erreicht man, dass der rechte Eingang nicht über den ersten angesprochen werden kann. +\item Es ist nicht möglich, einem rechten Eingang Methoden für mehr als einen Selector +zuzuweisen. Insbesondere ist es nicht möglich, ihm eine allgemeine Methode +für einen beliebigen Selector zuzuweisen. +\end{itemize} + +\subsubsection{floatinlet\_new} +\begin{verbatim} +t_inlet *floatinlet_new(t_object *owner, t_float *fp); +\end{verbatim} +Schafft einen neuen ``passiven'' Eingang für das Objekt, auf das \verb+owner+ zeigt, +der es erlaubt, einen numerischen Wert von außen direkt auf einen +Speicherplatz \verb+fp+ zu schreiben, ohne eine eigene Methode aufzurufen. + +\subsubsection{symbolinlet\_new} +\begin{verbatim} +t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp); +\end{verbatim} +Schafft einen neuen ``passiven'' Eingang für das Objekt, auf das \verb+owner+ zeigt, +der es erlaubt, einen symbolischen Wert von außen direkt auf einen +Speicherplatz \verb+sp+ zu schreiben, ohne eine eigene Methode aufzurufen. + +\subsubsection{pointerinlet\_new} +\begin{verbatim} +t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp); +\end{verbatim} +Schafft einen neuen ``passiven'' Eingang für das Objekt, auf das \verb+owner+ zeigt, +der es erlaubt, einen Zeigerwert von außen direkt auf einen +Speicherplatz \verb+gp+ zu schreiben, ohne eine eigene Methode aufzurufen. + +\subsubsection{outlet\_new} +\begin{verbatim} +t_outlet *outlet_new(t_object *owner, t_symbol *s); +\end{verbatim} +Generiert einen neuen Ausgang für das Objekt, auf das \verb+owner+ zeigt. +Das Symbol, auf das \verb+s+ zeigt, zeigt den Typ des Ausgangs an. + +\begin{tabular}{c|l||l} +Symbolwert & Symboladresse & Outlet-Typus \\ +\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 (List)\\ +``list'' & \verb+&s_list+ & Message \\ +--- & 0 & Message \\ +\hline +``signal'' & \verb+&s_signal+ & Signal \\ +\end{tabular} + +Zwischen den verschiedenen Message-Outlet-Typen gibt es keinen Unterschied. +Allerdings macht es den Code leichter lesbar, +wenn schon bei der Outlet-Generierung angezeigt wird, wozu der Ausgang verwendet wird. +Für allgemeine Message-Outlets verwendet man einen ``0''-Pointer. + +Variablen vom Typ \verb+t_object+ stellen einen Zeiger auf einen Outlet zur Verfügung. +Bei der Generierung eines neuen Outlets, +wird seine Addresse in der Objektvariablen \verb+(*owner).ob_outlet+ gespeichert. + +Werden mehrere Message-Ausgänge benötigt, müssen die Outletzeiger, +die von \verb+outlet_new+ zurückgegeben werden, manuell im Datenraum gespeichert werden, +um die jeweiligen Ausgänge ansprechen zu können. + +\subsubsection{outlet\_bang} +\begin{verbatim} +void outlet_bang(t_outlet *x); +\end{verbatim} +Gibt am Outlet, auf den \verb+x+ zeigt, eine ``bang''-Message aus. + +\subsubsection{outlet\_float} +\begin{verbatim} +void outlet_float(t_outlet *x, t_float f); +\end{verbatim} +Gibt am Outlet, auf den \verb+x+ zeigt, eine ``float''-Message mit dem +numerischen Wert \verb+f+ aus. + +\subsubsection{outlet\_symbol} +\begin{verbatim} +void outlet_symbol(t_outlet *x, t_symbol *s); +\end{verbatim} +Gibt am Outlet, auf den \verb+x+ zeigt, eine ``symbol''-Message mit dem +symbolischen Wert von \verb+s+ aus. + +\subsubsection{outlet\_pointer} +\begin{verbatim} +void outlet_pointer(t_outlet *x, t_gpointer *gp); +\end{verbatim} +Gibt am Outlet, auf den \verb+x+ zeigt, eine ``pointer''-Message mit dem +Zeiger \verb+gp+ aus. + +\subsubsection{outlet\_list} +\begin{verbatim} +void outlet_list(t_outlet *x, + t_symbol *s, int argc, t_atom *argv); +\end{verbatim} +Gibt am Outlet, auf den \verb+x+ zeigt, eine ``list''-Message mit +\verb+argc+ Atomen aus. +\verb+argv+ zeigt auf das erste Atom der Liste. + +Unabhängig davon, auf welches Symbol \verb+s+ zeigt, wird der Selector +``list'' der Liste vorangestellt. + +Aus Lesbarkeitsgründen sollte man aber trotzdem einen Zeiger auf das +Symbol ``list'' (\verb+gensym("list")+ oder \verb+&s_list+) angeben. + +\subsubsection{outlet\_anything} +\begin{verbatim} +void outlet_anything(t_outlet *x, + t_symbol *s, int argc, t_atom *argv); +\end{verbatim} +Gibt am Outlet, auf den \verb+x+ zeigt, eine Message mit +dem Selector, auf den \verb+s+ zeigt, aus. +Dem Selector folgen \verb+argc+ Atome. +\verb+argv+ zeigt auf das erste Atom dieser Liste. + + +\subsection{Funktionen: DSP} +Soll eine Klasse Methoden zur digitalen Signalsverarbeitung zur Verfügung stellen, +so muss ihr eine Methode für den Selector ``dsp'' hinzugefügt werden. + +Wird die Audio-Engine gestartet, so werden alle Objekte, die eine ``dsp''-Methode +zur Verfügung stellen, als Instanzen von Signalklassen identifiziert. + +\paragraph{DSP-Methode} + +\begin{verbatim} +void my_dsp_method(t_mydata *x, t_signal **sp) +\end{verbatim} + +In der ``dsp''-Methode wird mit der Funktion \verb+dsp_add+ die +Klassenroutine für Signalverarbeitung in den DSP-Baum eingebunden. + +Neben dem eigenen Datenraum \verb+x+, wird auch ein Array von Signalen übergeben. +Die Signale im Array sind so angeordnet, dass sie am graphischen Objekt +im Uhrzeigersinn gelesen werden. + +Sofern je zwei Ein- und Ausgangssignale vorhanden sind, gilt also: + +\begin{tabular}{c|r} +Zeiger & auf Signal \\ +\hline\hline +sp[0] & linkes Eingangssignal \\ +sp[1] & rechtes Eingangssignal \\ +sp[2] & rechtes Ausgangssignal \\ +sp[3] & linkes Ausgangssignal \\ +\end{tabular} + +Die Signalstruktur enthält unter anderem: + +\begin{tabular}{c|l} +Strukturelement & Bedeutung \\ +\hline +\verb+s_n+ & Länge des Signalvektors \\ +\verb+s_vec+ & Zeiger auf den Signalvektor \\ +\end{tabular} + +Der Signalvektor ist ein Array auf Samples vom Typ \verb+t_sample+. + +\paragraph{Perform-Routine} +\begin{verbatim} +t_int *my_perform_routine(t_int *w) +\end{verbatim} + +Der Perform-Routine die mit \verb+class_add+ in den DSP-Baum eingefügt wurde, +wird ein Zeiger \verb+w+ auf ein (Integer-)Array übergeben. +In diesem Array sind die Zeiger gespeichert, die mit \verb+dsp_add+ übergeben wurden. +Sie müssen auf ihren ursprünglichen Typ zurückgecastet werden. +Der erste Zeiger ist an der Stelle \verb+w[1]+ gespeichert !!! + +Die perform-Routine muß einen Zeiger auf Integer zurückgeben, der hinter den +Speicherplatz zeigt, in dem die eigenen Zeiger gespeichert sind. +Dies bedeutet, dass das Rückgabeargument gleich dem Übergabeargument plus der +Anzahl der eigenen Zeigervariablen (wie sie als zweites Argument in +\verb+dsp_add+ angegeben wurde) plus eins. + + + + +\subsubsection{CLASS\_MAINSIGNALIN} +\begin{verbatim} +CLASS_MAINSIGNALIN(<class_name>, <class_data>, <f>); +\end{verbatim} +Das Makro \verb+CLASS_MAINSIGNALIN+ meldet an, dass die Klasse +Signal-Inlets brauchts. + +Das erste Argument des Makros ist ein Zeiger auf die Signalklasse. +Das zweite Argument ist der Typ des Datenraums der Klasse. +Das dritte Argument ist eine (Dummy-)Gleitkomma-Variable aus dem Datenraum, +die gebraucht wird, um bei nicht vorhandenen Signalen am Signalinlet, +``float''-Messages wie Signale behandeln zu können. + +An so kreierten Signaleingängen können daher keine zusätzlichen ``float''-Messages +geschickt werden. + +\subsubsection{dsp\_add} +\begin{verbatim} +void dsp_add(t_perfroutine f, int n, ...); +\end{verbatim} +Fügt dem DSP-Baum eine Perform-Routine \verb+f+ hinzu, +die jeden DSP-Zyklus neu aufgerufen wird. + +Das zweite Argument \verb+n+ legt die Anzahl der nachfolgenden Zeigerargumente fest. + +Welche Zeiger auf welche Variablen übergeben werden, unterliegt keiner Beschränkung. +Sinnvoll sind im Allgemeinen Zeiger auf den Datenraum und auf die Signalvektoren. +Auch die Länge der Signalvektoren sollte übergeben werden, +um effektiv Signale manipulieren zu können. + +\subsubsection{sys\_getsr} +\begin{verbatim} +float sys_getsr(void); +\end{verbatim} +Gibt die Abtastrate des Systems zurück. + +\subsection{Funktion: Memory} +\subsubsection{getbytes} +\begin{verbatim} +void *getbytes(size_t nbytes); +\end{verbatim} +Reserviert \verb+nbytes+ Bytes und gibt einen Zeiger auf den reservierten Speicher zurück. + +\subsubsection{copybytes} +\begin{verbatim} +void *copybytes(void *src, size_t nbytes); +\end{verbatim} +Kopiert \verb+nbytes+ Bytes von \verb+*src+ in einen neu alloziierten Speicher. +Die Addresse dieses Speichers wird zurückgegeben. + +\subsubsection{freebytes} +\begin{verbatim} +void freebytes(void *x, size_t nbytes); +\end{verbatim} +Gibt \verb+nbytes+ Bytes an der Addresse \verb+*x+ frei. + +\subsection{Funktionen: Ausgabe} +\subsubsection{post} +\begin{verbatim} +void post(char *fmt, ...); +\end{verbatim} + +Schreibt einen {\tt C}-String auf den Standarderror (Shell). + +\subsubsection{error} +\begin{verbatim} +void error(char *fmt, ...); +\end{verbatim} + +Schreibt einen {\tt C}-String als Fehlermeldung auf den Standarderror (Shell). +Das Objekt, das die Fehlermeldung ausgegeben hat, wird markiert und +ist über das pd-Menü {\em Find->Find last error} identifizierbar. + +\end{appendix} + +\end{document} + diff --git a/doc/tutorials/externals-howto/HOWTO-externals-en.tex b/doc/tutorials/externals-howto/HOWTO-externals-en.tex new file mode 100644 index 00000000..8c77d3c6 --- /dev/null +++ b/doc/tutorials/externals-howto/HOWTO-externals-en.tex @@ -0,0 +1,1750 @@ +% format latexg -*- latex -*- + +\documentclass[12pt, a4paper,austrian, titlepage]{article} + +%% HOWTO write an external for pd +%% by IOhannes m zmölnig, 2001 +%% +%% this document is released under the following license +%% CreativeCommons Attribution-ShareAlike 2.5 +%% +%% http://creativecommons.org/licenses/by-sa/2.5/ + +\usepackage[latin1]{inputenc} +\usepackage[T1]{fontenc} +\usepackage{babel} + +\title{ +HOWTO \\ +write an External \\ +for {\em puredata} +} + +\author{ +johannes m zmölnig \\ +\\ +{\em institut for electronic music and acoustics\footnote{http://iem.at}} +} + +\date{} + +\begin{document} +\maketitle + +\hyphenation{Echt-zeit} +\hyphenation{Computer-musik-program-men} +\hyphenation{Echt-zeit-Computer-musik-pro-gramm} + +\begin{abstract} +pd is a graphical realtime-computermusicsystem 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 selfmade 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. +\end{abstract} + + +\vfill +\newpage + +\tableofcontents + +\vfill +\newpage + +\section{definitions and prerequisites} +pd refers to the graphical realtime-computermusicprogramme {\em puredata} +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++ 6.0} (vc6) 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 ist 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\~\/'' 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 systemdependent 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 to modes to import {\em libraries}: + +\begin{itemize} +\item via the commandline-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 objectclass should be created, that prints the line ``hello world!!'' to +the standarderror everytime it is triggered witha ``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 dataspace} +First a new class has to be prepared and the dataspace 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 dataspace of the class. + +An absolutely necessary element of the dataspace 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{methodspace} +Apart from the dataspace, 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 messagesystem of pd. +On principal they have no return argument and are therefore are 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 dataspace. + +Since we only want to output ``Hello world!'' +(and, by the way, our dataspace is quite sparse), +we renounce a manipulation. + +The command \verb+post(char *c,...)+ sends a string to the standarderror. +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 dataspace and the methodspace 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 functionn 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. + +Das erste Argument ist der symbolische Name der Klasse. + +The next two arguments define the constructor and dstructor of the class. + +Whenever a classobject is created in a pd-patch, +the class-constructor \verb+(t_newmethod)helloworld_new+ instantiates the object +and initializes the dataspace. + +Whenever an object is destroyed +(either by closing the containing patch or by deleting the object from the patch) +the destructor frees the dynamically reserved memory. +The allocated memory for the static dataspace is automatically reserved and freed. + +Therefore we do not have to provide a destructor in this example, the argument +is set to ``0''. + +To enable pd to reserve and free enough memory for the static dataspace, +the size of the datastructure has to be passed as the fourth argument. + +The fifth argument has influence on the graphical representaion of the classobjects. +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 atomtypes 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 methodspace 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 dataspace, +initializes the variables that are internal to the object and +returns a pointer to the dataspace. + +The type-cast to the dataspace is necessary. + +Normally, the constructor would initialize the object-variables. +However, since we have none, this is not necessary. + + +The constructor has to return a pointer to the instantiated dataspace. + +\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 dataspace is extended by a variable ``counter'' and the +result is written as a message to an outlet instead of +a string to the standarderror. + +\subsection{object-variables} +Of course, a counter needs a state-variable to store the actual counter-value. + +State-variables that belong to classinstances belong to the dataspace. + +\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 usefull 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 initialized, +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 initialize 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 dataspace. +If more than one outlets are used, the pointers have to be stored in the dataspace, +because the \verb+t_object+-variable can only hold one outletpointer. + +\subsection{the countermethod} +When triggered, the countervalue 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 floatingpoint-buffer. +Afterwards the counter is incremented and not before that the buffervariable is sent +to the outlet. + +What appears to be unnecessary on the first glance, makes sense after further +inspection: +The buffervariable has been realized as \verb+t_float+, +since \verb+outlet_float+ expects a floatingpoint-value and a typecast is +inevitable. + +If the countervalue was sent to the outlet before being incremented, +this could result in an unwanted (though welldefined) behaviour: +If the counter-outlet directly triggered its own inlet, +the counter-method would be called although the countervalue was not yet incremented. +Normally this is not what we want. + +The same (correct) result could of course be obtained with a single line, +but this would obscure the {\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 usefull to be able to reset the counter to an initial value, +to set upper and lower boudaries and to control the step-width. +Each overrun should send a ``bang''-Message to a second outlet and reset the counter to +the initial value. + +\subsection{extended dataspace} + +\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 dataspace has been extended to hold variables for stepwidth and +upper and lower boundaries. +Furthermore pointers for two outlets have been added. + +\subsection{extension of the class} +The new classobjects should have methods for different messages, +like ``set'' and ``reset''. +Therefore the methodspace 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 classgenerator \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 classname. + +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 numer 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: + } + if (argc<2)f2=f1; + x->i_down = (f1<f2)?f1:f2; + x->i_up = (f1>f2)?f1:f2; + + x->i_count=x->i_down; +\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 dataspace-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 dataspace-memory, +where other objects can write too. + +``Passive'' inlets can be created for pointers, symbolic or +numerical (floatingpoint\footnote{ +That's why the {\tt step}-width of the class\/dataspace 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\/dataspace +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 methodspace} + +The method for the ``bang''-message has to fullfill 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 = (f1<f2)?f1:f2; + x->i_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 = (f1<f2)?f1:f2; + x->i_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: + } + if (argc<2)f2=f1; + + x->i_down = (f1<f2)?f1:f2; + x->i_up = (f1>f2)?f1:f2; + + x->i_count=x->i_down; + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, + gensym("list"), gensym("bound")); + floatinlet_new(&x->x_obj, &x->step); + + 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\~\/}} +Signalclasses are normal pd-classes, that offer additional methods for signals. + + +All methods and concepts that can be realized with normal objectclasses can +therefore be realized with signalclasses too. + +Per agreement, the symbolic names of signalclasses end with a tilde \~\/. + +The class ``pan\~\/'' shall demonstrate, how signalclasses are written. + +A signal on the left inlet is mixed with a signal on the second inlet. +Der mixing-factor between 0 and 1 is defined via a \verb+t_float+-message +on a third inlet. + +\subsection{variables of a signalclass} +Since a signal-class is only an extended normal class, +there are no principal differences between the dataspaces. + +\begin{verbatim} +typedef struct _pan_tilde { + t_object x_obj; + + t_sample f_pan; + t_float f; +} 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. + +\subsection{signal-classes} + +\begin{verbatim} +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} + +A method for signal-processing has to be provided by each signalclass. + +Whenever pd's audioengine is started, a message with the selector ``dsp'' +is sent to each object. +Each class that has a method for the ``dsp''-message is recognized as signalclass. + +Signalclasses that want to provide signal-inlets have to +declare this via the \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 signalclass. +The second argument is the type of the class's dataspace. + +The last argument is a dummy-variable out of the dataspace that is needed +to replace non-existing signal at the signal-inlet(s) with \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; + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + floatinlet_new (&x->x_obj, &x->f_pan); + + 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''. + +\subsection{DSP-methods} +Whenever pd's audioengine 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-dataspace, 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 signalvectors 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 signalclass. + +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 pointervariables (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 signalvectors is read and manipulated in the \verb+while+-loop. + + +Optimization 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{the code: \tt pan\~\/} + +\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_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_new(t_floatarg f) +{ + t_pan_tilde *x = (t_pan_tilde *)pd_new(pan_tilde_class); + + x->f_pan = f; + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + floatinlet_new (&x->x_obj, &x->f_pan); + 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 (floatingpoint) +\item {\em A\_SYMBOL}: a symbolic value (string) +\item {\em A\_POINTER}: a pointer +\end{itemize} + +Numerical values are always floatingpoint-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 triggerevent. +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 recognized 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 plattforms, +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+ & floatingpoint 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+ & audiosignal-value (floatingpoint)\\ +\verb+t_outlet+ & outletof 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-dataspace, +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 typechecked 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-dataspace: + +\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-dataspace and +a floatingpoint-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-dataspace 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-dataspace 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-dataspace -- +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-dataspace -- +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 classname, +to the constructor \verb+newmethod+. +Thus, objects can be created either by their ``real'' classname 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 objectclass +can be opened. +By default this is a patch with the symbolic classname 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 helppath {\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 dataspace-variable of the \verb+t_object+-type is initialized. +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-addresse & 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 +objectvariable \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 dataspace +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. + +Independet 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 audioengine is started, +all objects that provide a ``dsp''-method are identified as instances of signalclasses. + +\paragraph{DSP-method} + +\begin{verbatim} +void my_dsp_method(t_mydata *x, t_signal **sp) +\end{verbatim} + +In the ``dsp''-method a classmethod for signal-processing +is added to the DSP-tree by the function \verb+dsp_add+. + +Apart from the dataspace \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 signalstructure contains apart from other things: + +\begin{tabular}{c|l} +structure-element & description \\ +\hline +\verb+s_n+ & length of the signalvector \\ +\verb+s_vec+ & pointer to the signalvector \\ +\end{tabular} + +The signalvector 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(<class_name>, <class_data>, <f>); +\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-dataspace. +The third argument is a (dummy-)floatingpoint-variable of the dataspace, +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 dataspace 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 samplerate 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 standarderror (shell). + +\subsubsection{error} +\begin{verbatim} +void error(char *fmt, ...); +\end{verbatim} + +Writes a {\tt C}-string as an error-message to the standarderror (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/example1/helloworld.c b/doc/tutorials/externals-howto/example1/helloworld.c new file mode 100644 index 00000000..43682997 --- /dev/null +++ b/doc/tutorials/externals-howto/example1/helloworld.c @@ -0,0 +1,92 @@ +/* + * HOWTO write an External for Pure data + * (c) 2001-2006 IOhannes m zmölnig zmoelnig[AT]iem.at + * + * this is the source-code for the first example in the HOWTO + * it creates an object that prints "Hello world!" whenever it + * gets banged. + * + * this file is released under Pd's license (BSD-like) + */ + + + +/** + * include the interface to Pd + */ +#include "m_pd.h" + +/** + * define a new "class" + */ +static t_class *helloworld_class; + + +/** + * this is the dataspace of our new object + * we don't need to store anything, + * however the first (and only) entry in this struct + * is mandatory and of type "t_object" + */ +typedef struct _helloworld { + t_object x_obj; +} t_helloworld; + + +/** + * this method is called whenever a "bang" is sent to the object + * the name of this function is arbitrary and is registered to Pd in the + * helloworld_setup() routine + */ +void helloworld_bang(t_helloworld *x) +{ + /* + * post() is Pd's version of printf() + * the string (which can be formatted like with printf()) will be + * output to wherever Pd thinks it has too (pd's console, the stderr...) + * it automatically adds a newline at the end of the string + post("Hello world !!"); +} + + +/** + * this is the "constructor" of the class + * this method is called whenever a new object of this class is created + * the name of this function is arbitrary and is registered to Pd in the + * helloworld_setup() routine + */ +void *helloworld_new(void) +{ + /* + * call the "constructor" of the parent-class + * this will reserve enough memory to hold "t_helloworld" + */ + t_helloworld *x = (t_helloworld *)pd_new(helloworld_class); + + /* + * return the pointer to the class - this is mandatory + * if you return "0", then the object-creation will fail + */ + return (void *)x; +} + + +/** + * define the function-space of the class + * within a single-object external the name of this function is special + */ +void helloworld_setup(void) { + /* create a new class */ + helloworld_class = class_new(gensym("helloworld"), /* the object's name is "helloworld" */ + (t_newmethod)helloworld_new, /* the object's constructor is "helloworld_new()" */ + 0, /* no special destructor */ + sizeof(t_helloworld), /* the size of the data-space */ + CLASS_DEFAULT, /* a normal pd object */ + 0); /* no creation arguments */ + + /* attach functions to messages */ + /* here we bind the "helloworld_bang()" function to the class "helloworld_class()" - + * it will be called whenever a bang is received + */ + class_addbang(helloworld_class, helloworld_bang); +} diff --git a/doc/tutorials/externals-howto/example2/counter.c b/doc/tutorials/externals-howto/example2/counter.c new file mode 100644 index 00000000..afc72335 --- /dev/null +++ b/doc/tutorials/externals-howto/example2/counter.c @@ -0,0 +1,86 @@ +/* + * HOWTO write an External for Pure data + * (c) 2001-2006 IOhannes m zmölnig zmoelnig[AT]iem.at + * + * this is the source-code for the second example in the HOWTO + * it creates an object that increments and outputs a counter + * whenever it gets banged. + * + * this file is released under Pd's license (BSD-like) + */ + + + +/** + * include the interface to Pd + */ +#include "m_pd.h" + +/** + * define a new "class" + */ +static t_class *counter_class; + + + +/** + * this is the dataspace of our new object + * the first (mandatory) "t_object" + * and a variable that holds the current counter value + */ +typedef struct _counter { + t_object x_obj; + t_int i_count; +} t_counter; + + +/** + * this method is called whenever a "bang" is sent to the object + * a reference to the class-dataspace is given as argument + * this enables us to do something with the data (e.g. increment the counter) + */ +void counter_bang(t_counter *x) +{ + /* + * convert the current counter value to floating-point to output it later + */ + t_float f=x->i_count; + /* increment the counter */ + x->i_count++; + /* send the old counter-value to the 1st outlet of the object */ + outlet_float(x->x_obj.ob_outlet, f); +} + + +/** + * this is the "constructor" of the class + * we have one argument of type floating-point (as specified below in the counter_setup() routine) + */ +void *counter_new(t_floatarg f) +{ + t_counter *x = (t_counter *)pd_new(counter_class); + + /* set the counter value to the given argument */ + x->i_count=f; + + /* create a new outlet for floating-point values */ + outlet_new(&x->x_obj, &s_float); + + return (void *)x; +} + + +/** + * define the function-space of the class + */ +void counter_setup(void) { + counter_class = class_new(gensym("counter"), + (t_newmethod)counter_new, + 0, + sizeof(t_counter), + CLASS_DEFAULT, + A_DEFFLOAT, 0); /* the object takes one argument which is a floating-point and defaults to 0 */ + + /* call a function when object gets banged */ + class_addbang(counter_class, counter_bang); +} |