Commit eced1303 authored by Jonathan Wilkes's avatar Jonathan Wilkes
Browse files

keep midr-mjpeg.mov in pd/doc/media, but remove all the copies of external...

keep midr-mjpeg.mov in pd/doc/media, but remove all the copies of external media files. There aren't any references to those copied files in any of the docs, so they are useless. This means the root "doc" directory is now gone.
parent 6f822aef
#==============================================================================#
#
# Centralized build system for "doc".
#
# see for instructions: http://puredata.org/docs/developer/build
# <hans@at.or.at>
#
#==============================================================================#
CWD := $(shell pwd)
# these are designed to be overridden by the packages/Makefile
cvs_root_dir := $(shell cd $(CWD)/.. && pwd)
DESTDIR = $(CWD)/build/
BUILDLAYOUT_DIR = $(cvs_root_dir)/packages
CURL := curl --connect-timeout 600 --max-time 3600
# default target
all:
@echo "this currently does nothing"
include $(BUILDLAYOUT_DIR)/Makefile.buildlayout
#==============================================================================#
#
# OVERARCHING BUILD TARGETS
#
#==============================================================================#
DOC_TARGETS = media
# clean up after everything is installed
final_setup:
chmod -R ugo-w $(pddocdir)
install: $(objectsdir) $(helpdir) $(manualsdir) $(examplesdir) \
$(patsubst %, %_install,$(DOC_TARGETS))
@echo " "
@echo "doc install succeeded!"
#==============================================================================#
#
# PROJECT-SPECIFIC TARGETS
#
#==============================================================================#
#------------------------------------------------------------------------------#
# TEMPLATE
TEMPLATE_NAME = template
template_install: $(manualsdir)
install -d $(helpdir)$(manualsdir)/$(TEMPLATE_NAME)
install -p $(doc_src)/template/*.* \
$(helpdir)$(manualsdir)/$(TEMPLATE_NAME)
template_clean:
-rm -f -- $(helpdir)$(manualsdir)/$(TEMPLATE_NAME)/*.*
-rmdir -- $(helpdir)$(manualsdir)/$(TEMPLATE_NAME)
#------------------------------------------------------------------------------#
# MEDIA
MEDIA_NAME = media
media_install: $(pddocdir)
install -d $(DESTDIR)$(pddocdir)/$(MEDIA_NAME)
install -p $(doc_src)/media/*.* \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)
# ln is cp on MinGW, so these won't work since the target files don't exist at
# the time that the media_install target is generally run. For MinGW, this is
# instead handled in packages/win32_inno/pd-inno.iss.in
ifneq (MINGW,$(findstring MINGW,$(UNAME)))
# random sound files
ln -sf ../../doc/sound/bell.aiff \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/bell.aiff
ln -sf ../../doc/sound/voice.wav \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/voice.wav
ln -sf ../../doc/sound/voice2.wav \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/voice2.wav
ln -sf ../../extra/bsaylor/examples/noiseburst.wav \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/noiseburst.wav
ln -sf ../../extra/ekext/examples/stink.wav \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/stink.wav
ln -sf ../../extra/ekext/examples/beauty.wav \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/beauty.wav
ln -sf ../../extra/ekext/examples/drummach.wav \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/drummach.wav
# Gem videos
ln -sf ../../extra/Gem/examples/data/alea.mpg \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/alea.mpg
ln -sf ../../extra/Gem/examples/data/homer.avi \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/homer.avi
ln -sf ../../extra/Gem/examples/data/anim-1.mov \
$(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/anim-1.mov
endif
media_clean:
-rm -f -- $(DESTDIR)$(pddocdir)/$(MEDIA_NAME)/*.*
-rmdir -- $(DESTDIR)$(pddocdir)/$(MEDIA_NAME)
#==============================================================================#
#
# CLEAN TARGETS
#
#==============================================================================#
# the destination-specific clean targets are in Makefile.buildlayout
clean: $(patsubst %, %_clean,$(DOC_TARGETS))
distclean: clean cruft_clean
test_locations:
@echo "PD_VERSION: $(PD_VERSION)"
@echo "PACKAGE_VERSION: $(PACKAGE_VERSION)"
@echo "CWD $(CWD)"
@echo "DESTDIR $(DESTDIR)"
@echo "PREFIX $(prefix)"
@echo "BINDIR $(bindir)"
@echo "LIBDIR $(libdir)"
@echo "OBJECTSDIR $(objectsdir)"
@echo "PDDOCDIR $(pddocdir)"
@echo "LIBPDDIR $(libpddir)"
@echo "LIBPDBINDIR $(libpdbindir)"
@echo "HELPDIR $(helpdir)"
@echo "MANUALSDIR $(manualsdir)"
@echo "EXAMPLESDIR $(examplesdir)"
- add externals HOWTO to manuals/Pd
- create Makefile for Pd-extended build system
- add pd-fileformat doc: http://student-kmt.hku.nl/%7Etjeerd/pd/pd_fileformat.html
- replace [pddp] with "pddp" in all help files so that it doesn't cause bugs
with the pddp/pddp.pd meta file, and other things in the future. Here's a
pattern: sed -n 's|obj \([0-9]*\) \([0-9]*\) pddp;|msg \1 \2 pddp;|p' *.pd
- add doc/pddp/about, help, etc. to doc/Makefile
- mv doc/pddp/pddp.pd to doc/pddp/about/pddp-about.pd
- add [symbol 1( and [1 ( test messages to the bottom right example in
[pd some odd cases of list handling] in
doc/pddp/all_about_lists_vs_anythings.pd
% format latexg -*- latex -*-
\documentclass[12pt, a4paper,austrian, titlepage]{article}
%% HOWTO write an external for Pd
%% Copyright (c) 2001-2006 by IOhannes m zmölnig
%%
%% Permission is granted to copy, distribute and/or modify this document
%% under the terms of the GNU Free Documentation License, Version 1.2
%% or any later version published by the Free Software Foundation;
%% with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
%% Texts. A copy of the license is included in the LICENSE.txt file.
%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 Pure data}
}
\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 Pure data}.
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++} 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