Sie sind auf Seite 1von 36

24 SuperCollider SuperCollider 25

unterstützt verschiedene Quantisierungen (8bit, 16bit, 24bit


S u p e r C o l l i d e r und 32bit Integer sowie 4byte und 8byte Floatingpoint).
Die Samplingrate der Soundfiles kann beliebig sein, die
Einführung in die Programmierung, Teil 1 Quellen und Installation Samplingrate für die Echtzeittonausgabe richtet sich dage-
SuperCollider ist ein kostenpflichtiges Programm. Alle Infor- gen nach der vom verwendeten ASIO-Treiber zur Verfügung
Andre Bartetzki mationen zum Bezug des Programms und die Software selbst gestellten Auswahl. Die ASIO-Treiber müssen sich vor dem
abart@snafu.de findet man auf der Homepage von SC: Start von SC im SC-Unterordner ASIO Drivers befinden,
www.audiosynth.com um vom Programm erkannt zu werden.
Mit diesem Beitrag soll ein mehrteiliger Einführungskurs zum Es ist auch möglich, die Software unregistriert als Demo- Beim Starten von SuperCollider werden, ähnlich wie in
Softwaresynthesizer SuperCollider (SC) beginnen. SC wurde Version laufen zu lassen. Dabei gibt es folgende Ein- MAX/MSP, die eingestellten Parameter der Audiohardware
von James McCartney für das MacOS entwickelt und läuft zur schränkungen: sowie die aktuelle Konfiguration von OMS, ohne das in SC
Zeit ausschließlich unter diesem Betriebssystem. Die Soft- keine MIDI-Ein- und Ausgabe funktioniert, angezeigt. Über
• die Echtzeitsynthese arbeitet jeweils nur 1 Minute lang,
ware stellt die bei Synthesizern üblichen Baugruppen das File-Menü erreicht man den Audio-Setup-Dialog,
danach unterbricht das Programm die Klangerzeugung
(Oszillatoren, Sampleplayer und -recorder, Hülllkurven- und mit dem sich die gewünschte Soundkarte (Hardware), die
• 30 Minuten nach dem Start von SC beendet sich das Pro-
Zufallsgeneratoren, Filter, Delays u.a.) in vielen Varianten zur Samplingrate, die Wordclocksynchronisation, der I/O-Buffer
gramm selbständig und muß wieder gestartet werden
Verfügung. Im Gegensatz zu vielen anderen Software- sowie der Signalbuffer (Default Block Size) einstellen lassen,
• es können keine Soundfiles auf die Festplatte geschrieben
synthesizern, wie Csound oder Reaktor, ist SC aber auch eine sofern die Hardware und die entsprechenden ASIO-Treiber
werden, SC-Dateien lassen sich aber speichern
komplette Programmiersprache. Diese Sprache weist gewisse Änderungen dieser Parameter erlauben:
Neben dem sehr nützlichen eingebauten Help-Mechanismus
Ähnlichkeiten mit C und Lisp auf, basiert aber auf der im Mit dem üblichen [CMD-N] oder Anwahl von New im File-
werden zur Einarbeitung mit dem Programm viele Beispiel-
Bereich musikalischer Anwendungen bisher eher selten ver- Menü erzeugt man ein leeres Textfenster, in das die nun fol-
dateien und einige Tutorials mitgeliefert. Eine weitere sehr
wendeten Programmiersprache Smalltalk. Die Realisierung genden Beispiele eingetippt werden können.
empfehlenswerte und ständig wachsende Quelle für Bei-
klanglicher und musikalischer Vorstellungen mit Hilfe einer
spiele, musikalische Anwendungen, Tutorials und spezielle Anleitungen zum Erlernen von Programmiersprachen begin-
Programmiersprache stellt für viele SC-Anfänger gewöhnlich
Fragen der objektorientierten Programmierung befindet sich nen zumeist mit einen „Hello World“-Progrämmchen, das
eine große Hürde dar. Wer aber bereits über Erfahrung in der
unter dieser Internetadresse: eben diese beiden Worte am Bildschirm druckt. Das „Hello
gezielten Programmierung von Synthesizern und Effekt-
swiki.hfbk.uni-hamburg.de:8080 World“ eines Audioprogramms sollte dagegen etwas
geräten sowie im Umgang mit Tonstudiotechnik verfügt, soll-
/MusicTechnology/6 Klingendes sein - also beginnen wir mit einem einfachen
te genügend fachliche Voraussetzungen für das Erlernen und
Weitere Links, vor allem zu SC-Anwendern und Projekten, Sinuston:
kreative Anwenden von SuperCollider besitzen. Wer darüber
hinaus schon eine andere höhere Musik- oder Synthese- sind auf der SC-Homepage angegeben.
Synth.play({SinOsc.ar(440)})
sprache kennt, wie etwa CommonMusic oder OpenMusic
bzw. Csound, Cmix, MAX/MSP oder jMax, dem sollte der Um das Programm nach dem Download zu installieren, muß
Umstieg auf SC nicht besonders schwer fallen. Ich kann es nur entpackt und an den gewünschten Ort verschoben
berichten, daß es selbst in Klangsynthese, Tontechnik oder werden. Es werden keinerlei Systemerweiterungen oder
Informatik gänzlich unerfahrenen Studenten nach relativ kur- Libraries im Systemordner installiert, aus diesem Grunde ist
zer Einarbeitungszeit gelungen ist, eigene durchaus komple- auch kein Neustart notwendig. Besitzer von Soundkarten Erste Schritte
xe klangliche und strukturelle Ideen mit Hilfe von SC selb- sollten lediglich in den SC-Unterordner ASIO Drivers
Beim Starten von SuperCollider erscheinen in einem
ständig umzusetzen. den entsprechenden Treiber ihrer Hardware kopieren.
Startfenster Meldungen über die Audiohardware und das
Bevor dieser Kurs nun beginnt, hier noch einmal einige wich- MIDI-Setup sowie das erfolgreiche Kompilieren der
Konfiguration
tige Features dieser Software: Klassenbibliothek. Dieses Startfenster sollte nun im nächsten
Eine Sitzung mit SuperCollider sollte, nach dem Starten des Schritt als Fenster für alle Arten von Fehlermeldungen und
• umfangreiche Bibliothek von Synthese-, Sampling- und
Programms, mit der Überprüfung und gegebenenfalls der Anzeige von Text angemeldet werden. Dies geschieht durch
Controllerobjekten
Änderung der Audiokonfiguration beginnen. SC benutzt die Markieren des Menüeintrags Post here always im
• optimiert für Echtzeitsynthese und dynamische
etablierte ASIO-Schnittstelle, um mit verschiedenen Sound- Menü Lang, was auch mit dem Tastaturkürzel [CMD-7]
Alloziierung von Audio- und Controlobjekten
karten oder den internen Ein- und Ausgängen des Apple- erreicht werden kann. Wichtig ist, daß das Startfenster oder
• Unterstützung von MIDI Input und Output, darüber hinaus
Rechners zu kommunizieren. Während letztere nur Stereo-In- ein anderes Fenster, in das man alle Meldungen ausgeben
auch OpenSoundControl Beim Abtippen dieser Zeile sollte unbedingt auf die exakte
und Output ermöglichen, kann SC mit entsprechenden lassen möchte, gerade im Vordergrund ist, wenn man diese
• programmierbare graphische Oberfläche zur Steuerung Groß- und Kleinschreibung geachtet werden, da diese
Soundkarten und Interfaces auch mehrkanalig arbeiten. Das Einstellung vornimmt. Vergißt man diese Einstellung, werden
und Kontrolle Unterschiede für SC signifikant sind und eine falsche Schreib-
gilt übrigens auch für Soundfiles. Zur Zeit gibt es kaum ein alle späteren Fehlermeldungen und Ergebnisse an der aktu-
• Grafiksynthesizer (ab Version 3) weise zu manchmal schwer auffindbaren Fehlern führen
Programm, das mehrkanalige Soundfiles direkt unterstützt, ellen Cursorposition im Programmtext eingefügt, also an
• komplette objektorientierte Programmiersprache kann. Die beiden Punkte vor play und ar sind syntaktische
obwohl die Formate AIFF, SoundDesignerII und WAV seit lan- einer Stelle, wo man diese Meldungen normalerweise am
• erlaubt die Programmierung eigener Objekte in C++ als Einheiten und dürfen ebenfalls nicht mit anderen Zeichen,
gem die Möglichkeit bieten, mehr als nur 2 Kanäle zu spei- wenigsten benötigt!
PlugIns (ab Version 3) wie etwa Komma oder Semikolon, verwechselt werden.
chern. SC dagegen liest und schreibt auch mehrkanalige
• fertige Patches können zusammen mit SCPlay als „stand- Geschweifte Klammern sind auf Macintosh-Tastaturen über
Files, kennt viele unterschiedliche Soundfileformate und
alone“- Programm verteilt werden [ALT-(] bzw. [ALT-)] zu erreichen.
26 SuperCollider SuperCollider 27
Um nun den Kammerton zu hören, positioniert man den Selbstverständlich kennt ein Sinusoszillator in SC neben der Welche Parameter bzw. Argumente die einzelnen Oszillatoren Aus Platzgründen habe ich für den Abdruck in diesem Heft
Schreib-Cursor irgendwo in der mit dem Wort Synth.... Frequenz [Hz] auch die beiden anderen Parameter Amplitude und die anderen Audioobjekte erwarten, kann man mit der bereits dieses einfache Patch auf mehrere Zeilen aufgeteilt.
beginnenden Zeile und betätigt die am Ziffernblock befindli- und Phase. Ein 300 Hz-Ton mit 50%er Amplitude und einer Online-Hilfe in Erfahrung bringen. Dazu wird das fragliche Es ist natürlich weiterhin möglich, dieses Beispiel, wie alle
che [ENTER]-Taste , die nicht mit der für das Einfügen von Phasenverschiebung von 90° schreibt sich in SC so: Wort per Doppelklick selektiert, in unserem Beispiel Pulse, vorhergehenden, in einer Zeile zu schreiben. Das Aufteilen in
neuen Zeilen zuständigen [RETURN]-Taste verwechselt wer- und dann wird per [CMD-H] der Hilfetext aufgerufen. mehrere Zeilen empfiehlt sich aber ohnehin, um die Struktur
den darf. Falls die Freude über diesen Oboenersatz schnell Synth.scope({SinOsc.ar(300, pi/2, 0.5)}) Dieser Text gibt Auskunft über die Art des Objekts und seiner des Codes besser zu verdeutlichen, Kommentare einfügen zu
verfliegt, kann man die Audiosignalerzeugung mit [CMD-.] Parameter: können und die Fehlersuche sowie nachträgliche Änderun-
(Apfel-Punkt) gleich wieder abbrechen. gen zu erleichtern.
Signale können mit SC auf einfache Weise auch sichtbar Pulse.ar(freq, width, mul, add) Zum Selektieren von mehrzeiligem Code reicht es nicht, den
gemacht werden, indem man die Message play durch die Cursor irgendwo im Code zu positionieren, vielmehr muß das
Message scope ersetzt: Oft finden sich in diesen Hilfetexten kleine Beispiele, die die gesamte Patch, d.h. alle Zeilen, selektiert werden. Dies
Wirkungsweise des Objekts und seiner Parameter illustrieren. erreicht man ganz einfach durch doppelklicken auf die ein-
Synth.scope({SinOsc.ar(440)}) Diese Beispiele sind ganz normaler SC-Code und können mit leitende oder schließende Klammer, die das Patch umfassen:
der üblichen Methode (Selektieren und [ENTER]) abgespielt
Zusätzlich zum Ozillogramm kann auch das Spektrum ange- werden.
zeigt werden:
UGens - unit generators
Synth.dualScope({Saw.ar(1000)}) Saw, Pulse, Impulse, SinOsc und weitere Os-
zillatoren sowie alle anderen Bausteine, die Klang erzeugen,
bearbeiten oder steuern, wie Rauschgeneratoren, Filter, De-
Wie in jedem anderen Softwaresynthesizer stehen auch in lays, Controller usw., werden in SuperCollider unit generators
SuperCollider unterschiedliche Wellenformen zur Verfügung: genannt – abgekürzt: UGen. Die „Eingänge“ dieser UGens
können in der Regel durch andere UGens gesteuert werden.
// eine Sägezahnwellenform
Zum Beispiel kann die Amplitude des Saw-Generators ent-
// mit 300 Hz und 50% Amplitude: weder eine konstante Zahl sein oder per UGen generierte Eine solche Verschaltung von UGens wie im letzten Beispiel
Synth.scope({Saw.ar(300, 0.5)}) veränderliche Werte annehmen: heißt in SC ugenGraph. Ein ugenGraph ist das Pendant zu
MSP-Patches, DX7-Algorithmen und Ähnlichem. Selbst ein
// eine Rechteckwelle: // ziemlich leise: einzelner ungesteuerter unit generator, wie z.B. unser erster
Sinusoszillator kann als ugenGraph angesehen werden.
Synth.scope({Pulse.ar(300)}) Synth.play({Saw.ar(300, 0.1)}) Die Bezeichnung Graph läßt einen eher an eine Zeichnung
// 300 Impulse pro Sekunde: oder an das mathematische Konzept der Graphen denken
// crescendo in 10 Sekunden: und weniger an einen Text. Tatsächlich kann das letzte
Synth.scope({Impulse.ar(300)})
Synth.play({Saw.ar(300,Line.kr(0,1,10))}) Beispiel auch in grafischer Form dargestellt werden:
Die Parameter verschiedener Generatoren sind nicht immer
gleich. So kennen z.B. Saw und Impulse keine Phase und // periodisch schwankend:
Pulse hat als 2. Parameter anstelle der Phase die relative Synth.play({Saw.ar(300,SinOsc.kr(2))})
Pulsbreite:
Natürlich können die steuernden UGens wiederum von ande-
// Pulswelle mit geringer Breite: ren UGens gesteuert werden:
Synth.scope({Pulse.ar(300, 0.9)})
(
Synth.play({
Saw.ar(
300,
// immer schneller schwankend:
SinOsc.kr(Line.kr(1, 20, 10))
)


Oder das Spektrum alleine: })
)
Synth.fftScope({Saw.ar(4000)})
28 SuperCollider SuperCollider 29
Bei dieser Art von Darstellung werden die Zusammenhänge // drucke einen Zufallswert // ein neuer Sinusoszillator Ausnahme von dieser Regel bilden Funktionen und
(Graphen) zwischen den einzelnen Synthesebausteinen Messages:
postln(rrand(64, 127)) // mit Audio-Samplingrate (ar)
(Knotenpunkte) besser verdeutlicht, als in Form eines Textes.
Andere, zumeist kommerzielle Softwaresynthesizer erlauben SinOsc.ar(300, pi, 0.7) 3 + pi.sin * 5
die Programmierung von Klängen in dieser grafischen Form, Das „Drucken“ mit Hilfe der Funktion bzw. Message post
// addiere 3 und den Sinus von Pi,
die eigentlich dem Modell eines modularen Hardware- bzw. postln (hierbei erfolgt zusätzlich ein Zeilenvorschub) Die Parameter des Sinusoszillators (Frequenz, Phase,
erfolgt im zuvor mit post here always markierten // multipliziere dann mit 5
synthesizers mit Baugruppen, Buchsen und Patchkabeln Amplitude) und anderer UGens sind also - syntaktisch
nachempfunden sind. Visualisierung und grafische Editierung Fenster: betrachtet - Argumente der Messages ar (Samplingrate)
Mit runden Klammern kann man natürlich auch hier eine
sind unbestritten von Vorteil, wenn es um das Verständnis von oder kr (Controlrate). Aus der Perspektive der Hardware-
bestimmte Reihenfolge der Berechnung forcieren:
einfachen Zusammenhängen geht. Leider unterstützt Super- synthesizer handelt es sich dabei eher um Eingänge eines
Collider das Programmieren von Patches mit grafischen Moduls. Der Ausgang eines Moduls wäre dann das Ergebnis
(3 + pi).sin * 5
Methoden nicht. Die Textform ist aber keinesfalls ein Nachteil, einer Funktion bzw. der Datenstrom, den ein Audioobjekt
z.B. wenn es um die Programmierung dynamischer Strukturen erzeugt. Diese Sichtweise ist sehr hilfreich für die Konstruk- // addiere 3 und pi,
oder nicht-visualisierbarer abstrakter Zusammenhänge geht. tion von komplexen Synthesepatches. Allgemeiner dagegen // ermittle davon den Sinus
Als „Ersatz“ für eine übersichtliche Grafik sollte man sich unbe- ist das Konzept der Argumenten und Funktionen. Niemand // und multipliziere dann mit 5
dingt einen strukturierten Programmierstil zu eigen machen: wird z.B. bei dem (Maximalwert-)Argument eines Zufalls-
zahlengenerators von einem „Eingang“ sprechen, obwohl er Natürlich kann man sich die Ergebnisse solcher mathemati-
• Aufteilung in mehrere Zeilen
strukturell durchaus mit einem Gerät, einer Baugruppe oder schen Ausdrücke auch anzeigen lassen:
• Einrücken (mit Tabulator), um Hierarchien zu verdeutlichen
einem Modul vergleichbar wäre. Allerdings macht es Sinn bei
• schließende Klammern in eine eigene Zeile
einem Rauschgenerator, der eigentlich nichts weiter als ein postln((3 + pi).sin * 5)
• Kommentare einfügen (mit //)
mit der Samplingrate getakteter Zufallsgenerator ist, von
Als weiteres Hilfmittel zur Erhöhung der Lesbarkeit erlaubt einem (Amplituden-)Eingang zu sprechen. Wir können das letzte Beispiel auch anders schreiben:
SC über das Menu Color das Syntax Colorizing, wodurch
z.B. Objekte blau, Kommentare rot oder Zeichenketten grün // Zufallszahl zwischen 0.0 und 1.0 ((3 + pi).sin * 5).postln
gefärbt werden. rand(1.0)
Weitere Bespiele:
Zu der hier verwendeten Schreibweise ().sin anstelle von
Funktionen und Argumente // Rauschen mit der Amplitude 1.0
sin() oder ().postln anstelle von postln()
Nun sollten wir etwas genauer auf die sicherlich erklärungs- // erzeuge ein neues Array WhiteNoise.ar(1.0) kommen wir etwas später.
bedürftige Klammernschreibweise eingehen, die uns das // mit Hilfe der Methode rand,
Das Anzeigen von Ergebnissen mathematischer Berech-
Abtippen der bisherigen Beispiele ziemlich erschwert hat.Wir // die 3 Argumente übergeben bekommt Immer dann, wenn es um die Programmiersprache geht, wer-
nungen bzw. ganz allgemein von SC-Ausdrücken läßt sich
kommen damit zu einigen allgemeinen Grundlagen der den wir eher von Argumenten sprechen. Bei Synthese-
// die Anzahl der Listenelemente (8) auch ohne post-Funktion mit Hilfe der Tastenkombination
Syntax (Grammatik) der Sprache SuperCollider, die zunächst modulen (allgemein: Objekte, spezieller: UGens) sprechen
// der Minimalwert (0) und [CMD-P] erreichen. Dazu muß man den Schreibcursor in die
nicht viel mit Klangsynthese zu tun haben. Auch im weiteren wir meist von Eingängen oder genauer input parameters.
entsprechende Zeile bringen und [CMD-P] betätigen.
Verlauf dieser Artikel werden wir zwischen Fragen der ange- // der Maximalwert (127)
wandten Klangprogrammierung und der Beschreibung der Runde Klammern fassen nicht nur die Argumente für eine
Array.rand(8, 0, 127) Alles zwischen geschweiften Klammern {} ist in Super-
Sprache hin und her wechseln. Funktionen zusammen, sie können auch eine bestimmte
Collider eine Funktionsdefinition.
Alles zwischen runden Klammern () sind Argumente einer Reihenfolge von Operationen forcieren, so, wie wir es von
// Erzeugen und Anzeigen des Arrays: mathematischen Ausdrücken gewohnt sind: Der Ausdruck innerhalb der geschweiften Klammern wird
Funktion oder einer Message bzw. Methode:
postln(Array.rand(8, 0, 127)) also nicht sofort ausgewertet, sondern definiert lediglich das
3 * (4 + 5) Verhalten der Funktion im Falle ihres Aufrufs.
// Sinus von Pi
// zuerst 4 plus 5, dann mal 3
sin(pi) rand(5) * 10
3 * 4 + 5 // Ergebnis ist ein Zufallswert
// drucke den Sinus von Pi
// zuerst 3 mal 4, dann plus 5 // aus der Menge: 0, 10, 20, 30, 40
postln(sin(pi))
In SC gelten übrigens nicht die üblichen Vorrangregeln
// rufe die Zufallsfunktion rrand mathematischer Operationen, wie z.B. „Punktrechnung geht { rand(5) * 10 }
// mit den Argumenten 64 (Minimum) vor Strichrechnung“, sondern eine strenge Abarbeitungs- // Ergebnis ist eine Funktion
reihenfolge von links nach rechts:
// und 127 (Maximum) auf // für einen Zufallswert aus
rrand(64, 127) 3 + 4 * 5 // 0, 10, 20, 30, 40
// zuerst 3 plus 4, dann mal 5 !!!
30 SuperCollider SuperCollider 31
Dieser Screenshot verdeutlicht noch einmal den Unterschied // schreibe ein 10 Sekunden wie Delays und Filter, Samplingobjekte, FFT-Objekte und vie- mul und add
zwischen einer Berechnung und einer Funktionsdefinition: le weitere UGens. Auch mathematische Operatoren können Beim Überfliegen der UGen-Liste fällt auf, daß nahezu alle Unit
// langes Soundfile mit dem Namen
in diesem Zusammenhang als Unit Generators zur Klang- Generators neben ihren speziellen Input Parametern auch
// „test.aiff“ auf die Festplatte: bearbeitung verstanden werden, z.B. ist die Addition von immer die beiden „Eingänge“ mul und add aufweisen:
Audiosignalen mit + nichts weiter als Mischen, die Multipli-
( kation mit * entspricht einer Verstärkung und die Absolut- SinOsc.ar(freq, phase, mul, add)
Synth.record( wertfunktion abs ist eine Gleichrichtung.
LFSaw.ar(freq, mul, add)
{PinkNoise.ar}, // ugenGraph Eine Übersicht über alle derzeit in SC implementierten UGens PinkNoise.ar(mul, add)
10, // duration erreicht man, indem man das Main Help File aufruft. Dazu
darf nichts selektiert sein, während man [CMD-H] tippt, sonst Dust.ar(density, mul, add)
„test.aiff“, // file name
wird das spezielle Helpfile für das selektierte Wort aufgeru- Resonz.ar(in, freq, rq, mul, add)
’AIFF’ ) // file type fen. Im Main Help File, das viele kleine einführende Tutorials
) und Dokumentationen bietet, findet sich unter der Über- Der Parameter mul wird mit dem vom jeweiligen UGen
schrift Unit Generator Classes ein Link zum produzierten Signal multipliziert. Daher könnte man diesen
Wie man solche selbstdefinierten Funktionen aufruft, werden Welche und wieviele Argumente es für play, scope, [UGen_Ref_Sheet]: Parameter auch als Amplitude des Signal auffassen - dies gilt
wir ebenfalls später sehen. plot usw. gibt und welche Werte für sie zulässig sind, aber nur dann, wenn das Signal vor dieser Multiplikation
Zunächst zurück zu den UGens und zum Synth: erfährt man wieder durch Aufrufen des Helpfiles, in diesem einen Maximalwert von ±1.0 aufweist. Immer wenn man
Fall für Synth. Die Worte play, scope, record, Signale mit irgendetwas Anderem, z.B. Konstanten oder
write und plot sind messages an das Objekt Synth. anderen Signalen, multiplizieren möchte, sollte man sich des
Synth und die ugenGraph function
Objekte in SC werden immer mit großem Anfangs- mul-Parameters bedienen. Der erzeugte Code arbeitet dann
Der Synth bzw. die Message play, mit deren Hilfe Klänge buchstaben geschrieben und haben eventuell weitere effizienter als wenn man explizit multiplizieren würde.
gespielt werden, bekommt also als Argument eine Funktion Großbuchstaben, wie z.B. SinOsc, LPF oder
übergeben – in Form eines Ausdrucks in geschweiften WhiteNoise. Messages bzw. Methoden werden dage- // nicht besonders effizient:
Klammern. Durch das Einklammern von UGens wird aus dem gen immer klein geschrieben und mit einem Punkt von dem Saw.ar(400) * 0.3;
Graphen eine Funktion, die erst dann, wenn sie aufgerufen Objekt getrennt, an das sie geschickt werden.
wird, als Ergebnis eben diesen ugenGraph zurückgibt. Den Saw.ar(400) * SinOsc.ar(5);
Vorteil dieser Schreibweise wird man möglicherweise an die- Synth.play( {Saw.ar(300) }, 3)
ser Stelle noch nicht ganz ermessen können. Um es verein- // besser:
facht zu sagen:Was der Synth abspielen wird, ist das Ergebnis bedeutet also:
Saw.ar(400, 0.3);
eines Funktionsaufrufs. Was für einen ugenGraph die Schicke die Message play an das Objekt Synth und Saw.ar(400, SinOsc.ar(5));
Funktion erzeugt, muß zum Zeitpunkt der Programmierung übergebe dabei als Argumente eine ugenGraph function und
noch nicht unbedingt festliegen. Das könnte z.B. einer von eine Dauer von 3 Sekunden. Die ugenGraph-Funktion gibt Gleiches gilt für add. Dieser Parameter wird, nach erfolgter
mehreren möglichen ugenGraphs sein, der per Zufalls- einen Sägezahn-Generator zurück, der durch die message Multiplikation mit mul zum Gesamtergebnis addiert.
entscheidung oder Echtzeitsteuerung ausgewählt wird. Wir ar (ar = audio rate) und das Argument 300 als 300 Hz-
übergeben dem Synth also keine bestimmten Module oder Audiogenerator spezifiziert wurde. Ein Vibrato mit 5 Hz und einem Frequenzhub von ± 10 Hz auf
Patches sondern ein Art Maschine (die Funktion!), die Patches einer Frequenz von 300 Hz könnte man nun so formulieren:
und Module erst später erzeugen wird. Generatoren
{Saw.ar(SinOsc.ar(5,0,10,300))}.play
Das Objekt Synth wird als Container für UGens betrachtet, die SC stellt eine Fülle von Oszillatoren, Rausch- und Impuls-
zusammen einen ugenGraph bilden, der von einer Funktion generatoren bereit. Die Palette der Oszillatoren umfaßt SinOsc erzeugt hier ein mit 5 Hz periodisch schwankendes
zurückgegeben wird. Standardwellenformen, wie SinOsc, Saw und Pulse Signal mit Werten zwischen -1.0 und 1.0. Die Phase ist 0°.
Diese ugenGraph function ist aber nur das erste Argument für und einfachen Wavetable-Generatoren, wie Osc, bis hin zu Das Signal wird durch Multiplikation auf einen Wertebereich
Die Übersicht ruft man durch Doppelklicken auf eine der
Synth.play oder Synth.scope. Das zweite und spezialisierten Objekten wie OscX4 (ein zwischen 4 Wave- zwischen -10.0 und 10.0 gebracht. Anschließend wird 300
eckigen Klammern [ ] - dadurch wird wie üblich alles zwi-
alle weiteren Argumente können ganz verschieden sein: tables interpolierender Oszillator, etwa wie in Wavestation- addiert, so daß sich Werte zwischen 290.0 und 310.0 erge-
schen den Klammern selektiert - und anschließendes
Synthesizern) oder Formant (ein FOF-Generator). ben. Diese Werte werden Saw als Frequenz übergeben.
[CMD-H] auf.
Synth.play({PinkNoise.ar}, 2) Rauschgeneratoren stehen in Form von WhiteNoise,
In dieser Hilfedatei werden alle UGens in Kategorien zusam-
// spiele nur 2 Sekunden PinkNoise, BrownNoise und vielen anderen Werden mul und add nicht spezifiziert, so nehmen sie die
mengefaßt präsentiert. Von hier aus erreicht man die Hilfe-
Objekten, z.B. auch Chaosfunktionen, bereit. Standardwerte 1.0 und 0.0 an. Die beiden folgenden Aus-
dateien der UGens, ohne den entsprechenden Namen eintip-
Synth.scope({PinkNoise.ar}, 0.4) Impulse können mit Impulse generiert werden und pen zu müssen. Das UGen_Ref_Sheet kann als Lexikon drücke sind daher äquivalent:
// zeige 0.4 Sekunden per Bild Dust stellt als Zufallsimpulsgenerator eine Brücke zu den und Erinnerungshilfe dienen, wenn man für die Lösung eines
Rauschgeneratoren dar. bestimmten Problems einen geeigneten Unit Generator Saw.ar(300, 1.0, 0.0)
Synth.plot({Saw.ar(80)},0.1,"test") Neben den Audiogeneratoren, von denen hier nur einige sucht. Saw.ar(300)
// generiere und zeige 100 ms wenige genannt wurden, gibt es eine Vielzahl von Generato-
ren für Steuerzwecke (Controls), klangbearbeitende Objekte, Fortsetzung folgt ...
32 SuperCollider SuperCollider 33
Variablen, Fehler und Literale Vergessene Variablendeklarationen gehören sicherlich zu
S u p e r C o l l i d e r den häufigsten Programmierfehlern im Umgang mit
Der besseren Lesbarkeit wegen, habe ich im letzten Beispiel
den von 0 auf 1 linear ansteigenden Modulationsgrad SuperCollider. Allerdings kann man solche Fehler als eher
Einführung in die Programmierung, Teil 2 Line.kr(0,1,20) an eine Variable namens modgrad harmlos bezeichnen, da sie vom Interpreter sofort entdeckt
übergeben. und angezeigt werden. Schwierigkeiten bereiten dagegen
Andre Bartetzki immer wieder die oft schwer zu lesenden Fehlermeldungen.
Variablen können ganz verschiedene Werte zugewiesen wer- Falls es syntaktische oder semantische Fehler sind (also
abart@snafu.de den, zum Beispiel: Fehler, die den Programmtext betreffen), wird die fehlerhafte
Programmzeile ausgedruckt. Dabei steht ein fetter schwarzer
Dieser mehrteilige Einführungskurs zum Softwaresynthesizer Dies läßt sich auch am Beispiel der Amplitudenmodulation illu- ( var a, b, c, d, e, f, g;
Punkt nach dem fehlerhaften oder falsch verwendeten
SuperCollider (SC) begann in den Mitteilungen 40 und wird strieren. Die Ringmodulation ist eine einfache Multiplikation // eine Zahl Sprachelement bzw. nach der Stelle, an der der Fehler vom
an dieser Stelle fortgesetzt. Am Schluß dieses 2. Teils finden zweier Signale, z.B. a = 3.5; Interpreter entdeckt wurde. Die vollständige Fehlermeldung,
sich noch einmal Hinweise auf die Quellen für die Software die beim Ausführen des letzten Beispiels gedruckt wird, lau-
und ergänzendes Informationsmaterial. {SinOsc.ar(300) * SinOsc.ar(40)}.play // ein String tet:
// oder besser: b = "Hallo!";
mul und add (Fortsetzung) {SinOsc.ar(300,0,SinOsc.ar(40))}.play // ein Symbol • ERROR: Variable 'test' not defined.
Die Input Parameter mul und add kommen sollten immer c = 'linear'; in file 'selected text'
dann zum Einsatz kommen, wenn es um die Anpassung des Daher wird für die Momente, in denen eines der beiden line 2 char 15 :
vom jeweiligen UGen erzeugten Signal an den gewünschten Eingangssignale 0 ist, ein Ausgangssignal mit dem Wert 0 // ein Array
test = 7.sqrt;•
Verwendungszweck geht. Das oben aufgeführte Vibrato- erzeugt. Dagegen wird bei der Amplitudenmodulation das d = [a,b,c];
Patch ist ein Beispiel für die Werteanpassung für eine Modu- test.postln;
Trägersignal nicht beeinflußt, wenn die Amplitude des // ein neues Objekt
lation. Mathematisch gesehen handelt es sich bei einer Mul- Modulators 0 ist. Die Amplitude (mul) des Modulators wird -----------------------------------
e = MouseX.new;
tiplikation mit nachfolgender Addition um eine einfache als Modulationsgrad aufgefaßt. Ist entweder der • ERROR: Variable 'test' not defined.
lineare Transformation: Modulationsgrad gleich 0 oder der Momentanwert der // eine Funktion in file 'selected text'
y=m*x+a Modulatorschwingung gleich 0 so ist der momentane Output f = {arg x; rand(2**x)};
line 3 char 5 :
Manchen Anwendern fällt es jedoch schwer, den von des Modulator-UGens wegen des Offsets (add) von 1 eben-
// der Wert eines Ausdrucks test•.postln;
Oszillatoren produzierten Wertebereich von -1.0 bis 1.0 in falls 1. Durch die Multiplikation mit 1 wird die Träger-
schwingung nicht verändert. Da in diesem Beispiel bei g = [1,2,3,4].scramble;
einen anderen Bereich zu transformieren, wie es bei Umgekehrt ist es für den Interpreter zunächst unproblema-
Modulationen häufig notwendig ist. Vielleicht ist folgende 100%iger Modulation die resultierende Amplitude 2 wäre, )
wird hier zur Vermeidung von Übersteuerungen die tisch, wenn man eine Variable deklariert, aber ihr keinen Wert
Sichtweise etwas hilfreicher: zuweist.Variablen, denen noch kein Wert zugewiesen wurde,
Amplitude anschließend noch halbiert. Variablen müssen vor einer Wertzuweisung immer erst dekla-
add ist der Mittelwert, das Zentrum des neuen Bereichs haben automatisch den Wert nil.
riert werden. Dies muß am Anfang eines Patches oder einer
mul ist die halbe Breite des neuen Bereichs Funktion geschehen. Dazu wird den Bezeichnern das
( var test;
Schlüsselwort var vorangestellt. Man kann die Deklaration
der Variablen auch auf mehrere logische Zeilen aufteilen, die test.postln;
alle mit var beginnen müssen. )
Eine Variable besteht somit aus einem Wert und einem -> nil
Bezeichner. Bezeichner für Variablen und Messages müssen
in SC immer mit einem kleinen Buchstaben beginnen, im Natürlich können auch hier Fehler entstehen: immer dann,
Gegensatz zu Objekten, deren Bezeichner immer mit einem wenn vom Programm oder vom Programmierer ein anderer
Großbuchstaben beginnen. Variablen, deren Bezeichner nur Wert als nil von so einer Variablen erwartet wird:
( var modgrad; aus einem einzigen Buchstaben a...z bestehen, müssen
übrigens nicht unbedingt deklariert werden. ( var test;
Synth.scope({ test.sqrt.postln;
Versucht man, einer Variablen, die noch nicht deklariert wur-
modgrad = Line.kr(0,1,20); de, einen Wert zuzuweisen, erhält man eine Fehlermeldung: )
SinOsc.ar(300, 0,
Mit Hilfe von SuperCollider könnte man es so formulieren: -> ERROR:
SinOsc.ar(40, 0, modgrad, 1) (
-> Message 'sqrt' not understood.
( var min, max, mul, add; * 0.5 test = 7.sqrt;
min = -50; max = 400; //Beispielwerte ) test.postln; Das heißt, man kann keine Quadratwurzel von nil bilden!
mul = (max - min)/2; }) ) Genauer: das Objekt nil versteht die Message sqrt nicht.
add = (min + max)/2; ) -> • ERROR: Variable 'test' not defined. Dagegen gab es im Beispiel davor keine Fehlermeldung, weil
) das Objekt nil die Message postln akzeptiert.
34 SuperCollider SuperCollider 35
Generell sind Variablen mit aussagekräftigen Namen ein Variablen können übrigens auch gleich bei ihrer Deklaration Nach diesem Abschnitt über Sprachliches nun wieder etwas Bei im Computer sehr leicht zu erzeugenden geometrisch
gutes Mittel, den Programmcode selbstdokumentierend zu einen Wert zugewiesen bekommen: zur Klangerzeugung mit SuperCollider: exakten Versionen von Sägezahn-, Rechteck- oder Dreiecks-
gestalten. Mit der Lesbarkeit wird gleichzeitig auch das wellenform ist also immer Aliasing zu erwarten, da dort theo-
Korrigieren und Warten von Programmen erleichtert. var test = 7; Bandbegrenzte und nicht-bandbegrenzte retisch unendlich viele Obertöne erzeugt werden. Um
Unerläßlich jedoch sind Variablen immer dann, wenn ein und Generatoren Aliasing zu vermeiden, müßte man ständig in Abhängigkeit
derselbe Wert mehrfach und an unterschiedlichen Stellen im Später werden wir noch einen besonderen Typ von Variablen vom Grundton und der Nyquistfrequenz die mögliche Anzahl
Die Oszillatoren Saw und Pulse zeigen bei der Betrachtung
Programm verwendet werden soll. kennenlernen: die internen Variablen von Objekten bzw. von Obertönen berechnen und diese dann additiv zur Ge-
mit scope keine geometrisch glatte Form, wie es ihre
Instanzenvariablen. samtwellenform zusammenfügen. Genau das tut SC bei den
Hier nun noch einmal unsere Amplitudenmodulation aus Namen - Sägezahn und Rechteck - eigentlich implizieren.
dem letzten Abschnitt, diesmal in der Luxusversion: bandbegrenzen Oszillatoren Saw und Pulse. Beide weisen
Im Unterschied zu Variablen, deren Wert sich hinter einem belie- genau soviele Obertöne auf, wie bei gegebener Funda-
{Pulse.ar(500)}.scope
bigen Bezeichner „versteckt“, nennt man die Sprachelemente, mentalfrequenz und voreingestellter SampleRate möglich
(
deren Wert mit ihrem Bezeichner „übereinstimmt“, Literale. sind.
var modgrad, modfreq, modulator; Literale sind also direkte syntaktische Repräsentationen von
var carrier, carrierfreq; Oft benötigt man jedoch solche Wellenformen wie Sägezahn,
Werten. Ein Programmtext besteht somit eigentlich in der
Rechteck oder Dreieck nicht wegen ihrer speziellen Klang-
Synth.scope({ Hauptsache aus Literalen, strukturierenden Klammern und
farben sondern gerade wegen ihrer Form. Dies ist insbeson-
Interpunktionen, wie (), [], {} und ; . , , und eventuell
modgrad = Line.kr(0,1,20); dere bei Modulationen der Fall. Deshalb stehen in SC auch
Kommentaren.
modfreq = 40; nicht-bandbegrenzte, d.h. geometrisch exakte Varianten der
Das SC-Helpfile [01 Literals] stellt Beispiele für die ver- Standardwellenformen zur Verfügung: LFSaw, LFPulse
modulator =
schiedenen Literale vor: und LFTri. Der Präfix LF steht für low frequency und weist
SinOsc.ar(modfreq,0,modgrad,1);
• Numbers sind ganze Zahlen, Fließkommazahlen (auch in auf das bevorzugte Einsatzgebiet hin: als Oszillatoren für tie-
carrier = fe Frequenzen, vor allem für Modulationen.
Exponentenschreibweise) und die mathematische Konstante
SinOsc.ar(carrierfreq,0,modulator); pi. Zahlen können auch binär, hexadezimal oder sogar in LFPulse weist die Besonderheit auf, daß dessen Aus-
carrier * 0.5 Zahlensystemen basierend auf beliebigen Zahlen zwischen 2 gangssignal nicht zwischen -1.0 und 1.0 liegt, wie bei den
}) und 36 notiert werden. meisten Oszillatoren, sondern zwischen 0.0 und 1.0 :
) • Characters (einzelne Zeichen) wird ein Dollarzeichen $ vor-
angestellt, z.B. $A, $X. Steuerzeichen, wie tab oder line-
Unglücklicherweise ist in diesem Patch ein Fehler enthalten! feed haben zusätzlich einen Backslash, z.B. $\t, $\n
• Strings (Zeichenketten) sind zwischen Anführungszeichen
INPUT: nil
eingeschlossen, z.B. "Hallo". Intern werden Strings als
• ERROR: input 1 to SinOsc is not a Arrays von Characters repräsentiert. Deutlich erkennt man die Welligkeit, die von der endlichen
UGen, Float, or Integer. Anzahl von Obertönen herrührt. Nur Wellenformen, die aus
• Symbols sind Zeichenketten, die mit einfachen einer unendliche Reihe von harmonischen Obertönen zusam-
ERROR: Anführungszeichen (Hochkomma) notiert werden, z.B.: mengesetzt sind, können mathematisch exakte Dreiecks-
Primitive '_SynthPlay' failed. 'ein Name'. Symbole,die nur aus einem Wort ohne Leer- oder Rechtecksformen aufweisen. In digitalen Systemen mit
zeichen bestehen, können auch mit Hilfe eines Backslash endlicher Abtastrate - heute zumeist zwischen 44.1 und 98
Ein syntaktischer Fehler wird nicht angezeigt, wo also liegt geschrieben werden: \Name. kHz - lassen sich Frequenzen nur bis zur halben Abtastrate
das Problem? • Identifier (Bezeichner) haben wir schon für Variablen und darstellen. Diese auch Nyquistfrequenz genannte Grenze
SC stolpert über den Wert nil als erstem Input für SinOsc. Messages kennengelernt. Als Bezeichner bzw. Namen sind liegt z.B. bei der Samplingrate der CD bei einem Wert von
Laut Helpfile ist der 1. Input eines Sinusoszillators die sie Literale, was sie bezeichnen, sind jedoch Variablen oder 22.05 kHz. Sollten höhere Frequenzen abgetastet werden
Frequenz in Hertz. Diese dürfte vielleicht Null sein, aber nie- Messages. Identifier beginnen immer mit einem kleinen oder innerhalb des digitalen Systems erzeugt werden, so
mals nil! nil ist weder UGen noch Fließkomma- oder Buchstaben. werden diese an der Nyquistfrequenz gespiegelt, d.h. sie
ganze Zahl. nil als Wert erfüllt nicht die Bedingungen, die • Class Names sind spezielle Bezeichner für die Klassen in SC. nehmen einen Wert an, der der Differenz von Nyquist-
an den Frequenzparameter eines Sinusoszillators gestellt Klassenbezeichner beginnen immer mit einem frequenz und dem Betrag, um den sie die Nyquistfrequenz
werden. Deshalb kann Synth diesen uGenGraph nicht spie- Großbuchstaben, z.B. SinOsc übersteigen, entspricht. Diesen Effekt nannt man Aliasing. Das folgende Beispiel, das den Helpfiles von SC entnommen
len. Ein Beispiel: bei einer Samplingrate von 48 kHz (DAT) kann
• Literal Arrays sind spezielle Arrays, die nicht mehr verän- wurde, verdeutlicht hörbar den Unterschied zwischen band-
Welcher der beiden SinOsc ist gemeint? Die Frequenz des dert werden dürfen: #["auto.aif","ball.aif"] eine Frequenz von 25 kHz nur als 23 kHz erscheinen. 25 kHz begrenzten und nicht-bandbegrenzten Oszillatoren:
Modulators ist per Variable modfreq angegeben, welche sind gemessen an der Grenze von 24 kHz ( = 48 : 2 !) genau
eine Zeile vorher mit 40 Hz initialisiert wurde. Die Frequenz • Special Values sind Bezeichner für Besonderheiten wie
um 1 kHz zu hoch, daher ergibt sich 24 - 1 = 23 kHz. // LFSaw will alias at high frequencies.
der Trägeroszillators ist eine Variable carrierfreq. Diese true, false, nil und inf. Letzteres bezeichnet das
Eine Frequenz von 25 kHz könnte zum Beispiel der 4. {LFSaw.ar(Line.ar(1000,8000,5))}.scope
Variable wurde zwar deklariert, aber ihr wurde noch kein Infinitum:
Oberton einer harmonischen Reihe über 5 kHz sein. Bei 48
Wert zugewiesen! Wir müssen also noch folgende Zeile ein- kHz kann ein Grundton von 5 kHz daher maximal 3 Obertöne // Saw is band limited
fügen, um das Patch lauffähig zu machen: ( 1 / 0 ).postln
-> inf
haben, bevor das Alisiasing einsetzt und damit die Klang- {Saw.ar(Line.ar(1000,8000,5))}.scope
farbe drastisch verändert.
carrierfreq = 300;
36 SuperCollider SuperCollider 37
Mit Hilfe einer Sonagrammanalyse läßt sich die Spiegelung Das bedeutet aber auch, daß Rauschen Die von den LFNoise-Generatoren erzeugten Werte liegen
zu hoher Frequenzen besonders eindrucksvoll darstellen. Wie Saw.ar(12000) Neben den LF-Oszillatoren gibt es in SC auch besondere im Bereich ± 1.0.Aber auch bei ihnen gibt es die beiden Input
man sieht, findet die Spiegelung nicht nur an der halben Rausch- bzw. Zufallsgeneratoren, die wie LFSaw, Parameter mul und add, mit deren Hilfe der Wertebereich
bei einer Samplingrate von 44.1 kHz in Wirklichkeit eine
Samplingrate statt, sondern auch an der 0 Hz - Achse. LFPulse und LFTri ebenfalls speziell für Modulations- an die verschiedensten Aufgaben angepaßt werden kann.
Sinuswellenform produziert!
Negative Frequenzen werden als positive Frequenzen mit aufgaben konzipiert worden sind.
Bei Pulse tritt dieser Effekt sogar noch früher ein, da bei der Das folgende Beispiel erzeugt für einen Rechteckgenerator
dem gleichen Betrag wiedergegeben.
symmetrischen Rechteckwelle jeder 2. Teilton fehlt: Ihnen gemeinsam ist ein Input Parameter freq, mit dem an- 8 zufällige Frequenzen pro Sekunde zwischen 400 und
Beim bandbegrenzten Sägezahnoszillator Saw fehlen diese gegeben wird, wieviel neue zufällige Werte pro Sekunde 800 Hz:
Spiegeleffekte, weil die entsprechenden Frequenzen erst gar Pulse.ar(8000)
erzeugt werden.
nicht erzeugt werden. ist daher ebenfalls sinusförmig. {Pulse.ar(LFNoise0.kr(8,200,600))}.play
// ohne Interpolation = "Step Noise"
{LFNoise0.ar(100)}.plot Mit geeigneten Methoden lassen sich die in diesem Beispiel
erzeugten Frequenzen auch auf ein Halbton- oder ein ande-
res intervallisches Raster runden:

(
Synth.play({
Pulse.ar(
LFNoise0.kr(
8, // 8 Tonwechsel / Sekunde
6, // im Bereich ± Tritonus
73 // ueber cis
).round(2).midicps
)
// mit linearer Interpolation })
{LFNoise1.ar(100)}.plot )

Dieses Beispiel greift ein wenig vor, deshalb an dieser Stelle


nur eine kurze Erklärung: mit round(2) wird auf Vielfache
von 2 gerundet und midicps konvertiert von Halbtönen
mit MIDI-Numerierung in eine Frequenz. Das Ergebnis sind
Frequenzen, die aus der Ganztonskala zwischen g’ (67) und
g’’ (79) stammen.

Das Präfix LF dieser Zufallsgeneratoren hat im Gegensatz zu


den bereits besprochenen LF-Oszillatoren nichts mit Band-
begrenztheit oder dem Gegenteil zu tun, sondern weist nur
auf den hier vorhandenen Input Parameter freq hin, mit
dem die Zufallsrate auf niedrigere Frequenzen (low frequen-
cies!) als die Samplingrate eingestellt werden kann.
// mit quadratischer Interpolation
{LFNoise2.ar(100)}.plot Andere Rauschgeneratoren, wie WhiteNoise oder
PinkNoise erzeugen ihre Werte immer im Takt der
Samplingrate, produzieren also für jedes Sample einen neu-
en Wert. Es ist daher durchaus sinnvoll, den freq-Parameter
der LFNoise-Familie als spezielle Zufalls-Samplingrate
Unterstellt man für die Wiedergabe eine verzerrungsfreie als der höchste hörbare Sinuston. Allerdings scheint zur Zeit anzusehen. Damit wird auch die Verwandtschaft von
elektroakustische Kette aus Wandlern, Verstärkern und Laut- die Hörbarkeit von Frequenzen jenseits von 20 kHz eher eine LFNoise0 zu dessen analogem Vorläufer - dem Sample-
sprechern, so sollten die fehlenden höheren Obertöne einer Sache des Glaubens als eine wissenschaftlich entscheidbare and-hold-Generator - deutlich, bei dem ein verstärktes
solcherart beschnittenen Wellenform im Vergleich zu einer Frage zu sein. Verschiedene Versuche, die hörbare Aus- Widerstandsrauschen im geringen Zeitabständen gemessen
exakten Wellenform mit einer „kompletten“ Obertonreihe wirkungen des Fehlens von Ultraschallanteilen nachweisen (= gesamplet!) und diese Werte bis zum nächsten Messung
(dies funktioniert selbstverständlich annähernd nur im ana- sollen, wurden immer wieder kritisiert, z.B. wegen unzurei- beibehalten wurden.
logen Bereich!) vom menschlichen Ohr nicht vermißt werden, chender Meßbedingungen oder Fehlinterpretationen der
vorausgesetzt die Nyquistfrequenz des Systems liegt höher Ergebnisse.
38 SuperCollider SuperCollider 39
Zu den anderen Rauschgeneratoren zählen die bereits mehr- // chaotic noise function Trigger und Controls Bei Decay2 ist auch die Einschwingzeit einstellbar:
fach erwähnten Objekte WhiteNoise und PinkNoise, Crackle.ar(chaosParam, mul, add) Decay fügt einem Impuls, der als Input bereitgestellt wer-
zu denen sich noch BrownNoise gesellt. Spektral gesehen den muß, ein exponentielles Abklingen mit einstellbarer (
ergibt sich beim Weißen Rauschen eine von der Frequenz // chaotic function Dauer hinzu. Die Abklingzeit gibt an, wieviel Sekunden ver- Synth.scope({
unabhängige d.h. bei allen Frequenzen gleiche Leistung. Für gehen, bis das Inputsignal um -60 dB bzw. auf 1/1000 gesun-
Rossler.ar(chaosParam, dt, mul, add) var trig, att, dec, env ;
das menschliche Ohr klingt Weißes Rauschen allerdings sehr ken ist.
höhenbetont, da höhere Oktaven viel mehr Energie enthal- trig = Impulse.ar(8);
// Clifford Pickover's chaotic function
ten als tiefere. Beim Rosa Rauschen fällt die Energie zu hohen ( att = 0.03; // 30 ms Einschwingen
Latoocarfian.ar(a, b, c, d, mul, add)
Frequenzen hin mit 3 dB pro Oktave ab. Dies führt dazu, daß dec = 0.08; // 80 ms Abklingen
innerhalb eines oktavbreiten Frequenzbandes ungeachtet der Synth.scope({
Eine Übersicht über nahezu alle Rauschgeneratoren findet env = Decay2.ar(trig, att, dec);
absoluten Lage dieses Bandes statistisch immer die gleiche var trig, dec, env;
Energie vorhanden ist. Rosa Rauschen entspricht somit besser man über Main Help in der Rubrik [Noises]. [trig, env]
trig = Impulse.ar(8);
dem durchschnittlichen Spektralverlauf von Musikinstrumen- Ein weiterer Zufallsgenerator soll nicht unerwähnt bleiben: dec = 0.1; // 100 ms Abklingen }, 0.5))
ten als das Weiße Rauschen, klingt ausgewogener und wird Dust nimmt als Generator für in Zeit und Amplitude zufälli-
env = Decay.ar(trig, dec);
daher auch zum Einmessen tontechnischer Anlagen verwen- ge Impulse eine Zwischenstellung zwischen Impulse und
det. Beim Brownschen Rauschen (nicht Braunes Rauschen!) den (kontinuierlichen) Rauschgeneratoren ein. Anstelle einer [trig, env]
fällt die spektrale Energie sogar mit 6 db / Oktave ab, was sich Frequenz weist er als charakteristischen Input Parameter
}, 0.5)) // 0.5 Sekunden Anzeige
beim Hören eher als tiefenbetontes Rauschen äußert. Die cha- density auf, worunter eine mittlere Zufallsfrequenz, also
rakteristische Frequenzabhängigkeit der Leistungs- bzw. die durchschnittliche Anzahl von Impulsen pro Sekunde,zu
Amplitudenspektren dieser Rauscharten wird auch durch die verstehen ist.
Bezeichnung 1/fn - Rauschen deutlich gemacht:
Synth.plot({Dust.ar(3000)},0.01,"Dust")
1/f0 - Weißes Rauschen
1/f1 - Rosa Rauschen
1/f2 - Brownsches Rauschen

Fortsetzung folgt

Synth.plot({Impulse.ar(3000)},0.01,"Impulse") Bezugsquellen
SuperCollider ist ein kostenpflichtiges Programm. Alle Infor-
Was bedeutet diese Schreibweise? Beim Rosa Rauschen fällt mationen zum Bezug des Programms und die Software selbst
die spektrale Amplitude umgekehrt proportional zu Höhe der Die letzte Zeile im ugenGraph erzeugt einen Stereo-Output: findet man auf der Homepage von SC:
Frequenz ab - im Prinzip verhält es sich hier wie beim links (im Scope-Window oben) die Impulse und rechts (im www.audiosynth.com
Spektrum der Sägezahnwellenform, das dem gleichen Bil- Scope-Window unten) die getriggerten Hüllkurven. Es ist auch möglich, die Software unregistriert als Demo-
dungsgesetz unterliegt. Beim 1/f2 - Rauschen nimmt die Version mit einigen Einschränkungen laufen zu lassen. Zur
Amplitude sogar mit dem Quadrat der Frequenz ab - ähnlich [trig, env] Einarbeitung mit dem Programm werden viele Beispiel-
wie bei der Dreiecksschwingung. dateien und einige Tutorials mitgeliefert. Eine weitere sehr
Um anstelle eines Monosignals ein mehrkanaliges Signal zu empfehlenswerte und ständig wachsende Quelle für Bei-
Neben diesen z.T. auch bei anderen Synthesizern vorhande- erzeugen, schreibt man einfach alle gewünschten Signale in spiele, musikalische Anwendungen, Tutorials und spezielle
nen „klassischen“ Rauschgeneratoren finden sich in SC eini- ein Array, d.h. zwischen eckige Klammern und durch Fragen der objektorientierten Programmierung befindet sich
ge weitere eher unkonventionelle aber klanglich und struk- Kommata getrennt. Auf diese Weise können auch 4, 8 oder unter dieser Internetadresse:
turell sehr interessante Zufallsgeneratoren, die zumeist auf eine beliebige Anzahl von Kanälen erzeugt werden. Diese swiki.hfbk.uni-hamburg.de:8080
Gleichungen aus der Chaostheorie basieren: Mit Hilfe der Objekte Decay oder Decay2 können Technik, die in SC Multichannel Expansion genannt wird, ist /MusicTechnology/6
Impulsgeneratoren auf einfache Weise auch als Trigger für sehr leistungsstark. Wir werden später darauf ausführlich
Weitere Links, vor allem zu SC-Anwendern und Projekten,
percussive Klänge verwendet werden: zurückkommen. Hier sollte sie uns nur helfen, mehrere
sind auf der SC-Homepage angegeben.
Signale gleichzeitig darzustellen.
Andre Bartetzki
40 SuperCollider SuperCollider 41
Als sequence kann entweder eine Referenz auf ein Array (
S u p e r C o l l i d e r oder ein Objekt, das auf die Message value antworten Synth.play({
kann, dienen.
Einführung in die Programmierung, Teil 3 Mit dem Objekt Sequencer können wir jedem dieser var melo, tempo, trig, amp;
Ein Array ist eine Reihe von Elementen in eckigen
perkussiven Töne eine andere Tonhöhe zuweisen. Dazu Klammern, z.B. tempo = 5;
Andre Bartetzki wird im Takte eines Triggers ein neuer Wert von einer
trig = Impulse.ar(tempo);
abart@snafu.de Sequenz abgerufen: melo = [60,64,67,72,67,69,65,67].midicps;
amp = Decay.ar(
Dieser mehrteilige Einführungskurs zum Softwaresynthe- Sequencer.kr(sequence, trig, mul, add) Die Werte in diesem Array sind offensichtlich MIDI- // Impulstrigger:
sizer SuperCollider (SC) begann in den Mitteilungen 40 Notennummern, die hier an dieser Stelle gleich per trig,
und wird an dieser Stelle fortgesetzt. Am Schluß dieses Zum Verständnis müssen wir aber etwas weiter ausholen: midicps-Message in Frequenzen konvertiert werden. // Decayzeit:
3. Teils finden sich noch einmal Hinweise auf die Quellen Ein Triggerimpuls wird dann erzeugt, wenn ein Signal einen Eine Referenz auf ein Objekt erhält man durch Voranstellen 1/tempo);
für die Software und ergänzendes Informationsmaterial. Übergang von nicht-positiven Werten (Null und negativ) zu eines accent grave `.
positiven Werten (größer als Null) vollführt. Somit können melo = [60,64,67,72,67,69,65,67];
Trigger und Controls (Fortsetzung) Das folgende Beispiel zeigt die Verwendung eines Arrays und
alle Signale, die zwischen positiven Werten und Null bzw. melo = melo.midicps;
eines Triggers für Sequencer. Der Sequencer ruft bei
Um perkusssive Klänge zu erhalten, müssen die von negativen Werten schwanken, als Trigger dienen. Im fol- Saw.ar(
jedem Triggerimpuls, die hier von einem Rechteckgenerator
Decay bzw. Decay2 erzeugten Signale mit kontinuierli- genden Beispiel triggert ein Sinusoszillator ein Trig1-
kommen, eine neuen Wert aus der Liste melo auf. Diese // Frequenz:
chen Signalen multipliziert werden. Da Decay und Objekt, das beim Auftreten eines Triggerimpulses einen
vom Sequencer erzeugte Wertefolge wird als Sequencer.ar(`melo, trig),
Decay2 ebenso wie fast alle UGens in SC einen Impuls mit der Amplitude 1.0 und definierter Dauer abgibt:
Frequenzargument des Sägezahngenerators benutzt:
Parameter mul besitzen, können wir uns eine explizite // Amplitude:
Multiplikation sparen und statt dessen das 2. Signal als ( amp);
(
mul-Parameter von Decay2 schreiben: Synth.plot({
Synth.play({ })
var sig, trig, exp;
( )
sig = SinOsc.ar(5); var melo, trig;
Synth.scope({ melo =
// Impulse mit 20 ms Dauer: Alternativ kann als sequence auch eine Funktion oder ganz
var trig, dec, env, sig;
trig = Trig1.ar(sig, 0.02); [60,64,67,72,67,69,65,67].midicps; allgemein etwas verwendet werden, das auf die value-
trig = Impulse.ar(8); Message mit der Rückgabe eines geeigneten Wertes rea-
[sig, trig] trig = LFPulse.ar(5);
dec = 0.1; // 100 ms Abklingen giert. In diesem Falle wird bei jedem Triggerimpuls eine
}, 1)
sig = Saw.ar(300); // Sägezahn Saw.ar(Sequencer.kr(`melo, trig)); value-Message an die Sequenz geschickt.
) })
env = Decay.ar(trig, dec, sig);
) (
[sig, env]
Der Plot zeigt, daß die Triggerimpulse immer beim Synth.play({
}, 0.4) Nulldurchgang der ansteigenden Flanke der Als Trigger für Sequencer kann normalerweise kein
Impulse verwendet werden, da die Impulse zu schmal var seq, tempo, trig, amp;
) Sinusschwingung ausgelöst werden:
sind, um von dem mit Controlrate (kr) arbeitenden tempo = 5;
Sequencer sicher erfaßt werden zu können (zur Controlrate trig = Impulse.ar(tempo);
siehe nächsten Abschnitt). Aus diesem Grunde wurde hier
amp = Decay.ar( trig, 1/tempo);
auf einen Rechteckgenerator zurückgegriffen. Alternativ
hätte man die Impulse mit Trig1 „verbreitern“ können: // Funktion für Zufallstöne
// zwischen 60 und 72:
trig = Trig1.ar(Impulse.ar(5), 0.02); seq = {rrand(60, 72).midicps};
Saw.ar(Sequencer.ar(seq,trig), amp);
Eine andere Möglichkeit besteht darin, den Sequencer mit
Samplingrate (ar) laufen zu lassen: })
)
Sequencer.ar(`melo, Impulse.ar(5));
Der Sequencer braucht durchaus nicht nur im
Um nun den Tönen perkussive Hüllkurven zuzuordnen, gleichmäßigen Tempo zu laufen - die Triggerimpulse könn-
können wir einen gemeinsamen Impulsgenerator sowohl ten auch von Dust generiert werden:
für Decay als auch zum Triggern des Sequencer lau-
fen lassen:
42 SuperCollider SuperCollider 43
( In gewisser Weise simuliert Sequencer die noch aus SuperCollider bietet, ähnlich wie MAX/MSP, auch eine ar und kr
Synth.plot({ analogen Synthesizertagen stammenden Stepsequenzer, Reihe von interaktiven Steuerungsmöglichkeiten: Schon oft sind uns die beiden Abtastraten in diesem Kurs
var vals, trig; die eine einmal mit Potentiometern eingestellte Wertefolge • eine Maussteuerung mit MouseX und MouseY begegnet. Die Audiorate bzw. Sampligrate, die in SC
vals = [0,2,4,6,8,10] / 10; in einem festen oder veränderlichen Tempo zyklisch durch- Sample Rate genannt wird, kann je nach ASIO-Treiber im
laufen. Die Verwendung von Arrays und Sequencer ist • eine WACOM-Grafiktablett-Steuerung mit TabletX,
trig = Dust.ar(15); TabletY, TabletZ, TablettPressure und Audio-Setup-Dialog (erreichbar über’s File-Menü)
Sequencer.ar(`vals, trig); ein nur sehr einfaches Mittel für die zeitabhängige eingestellt werden. Die üblichen Werte sind 44.1, 48 oder
Erzeugung von Parametern für Klänge. Mit neueren anderen UGens
}, 1) 96 kHz.
Konzepten wie Pattern und Streams lassen sich, verglichen • alle MIDI-Channel-Messages: MIDIController,
) Die Controlrate ist eine geringere Frequenz, die immer ein
mit Arrays, weitaus kompliziertere Strukturen erzeugen. Und MIDITouch, MIDIPitchBend, MIDIPolyTouch,
mit Spawn und den davon abgeleiteten Klassen stehen MIDIProgram, MIDINoteGate, MIDIMost- ganzzahliger Teil der Samplingrate sein muß. Anders aus-
verschiedene Objekte zur zeitlichen Steuerung von Klang- RecentVelocity, MIDIMostRecentNote gedrückt: in einen Controlrate-Takt fallen immer mehrere
ereignissen zur Verfügung, die weitaus leistungsfähiger Audiosamples. Wir können uns diese Gruppe von
• eine TCP/IP-fähige Schnittstelle durch OpenSoundControl
sind, als getriggerte Sequenzer. Beide Konzepte sollen spä- Audiosamples, die in eine Periode der Controlrate fallen,
• eine programmierbare grafische Oberfläche (GUI) mit als einen Block vorstellen. Die Größe des Blocks, in SC typi-
ter besprochen werden.
Reglern, Checkboxen, Buttons u.v.a.m. scherweise 64 oder 128, stellt man ebenfalls im Audio-
Selbstverständlich gibt es neben Decay und Decay2 in Setup-Dialog unter Default Block Size ein.
Bei der Abfrage der Mausposition mit MouseX oder
SC auch Möglichkeiten, komplexere Hüllkurven zu generie-
MouseY wird die aktuelle Position des Mauszeigers zwi- Dieser Block von Samples, auch Signal Buffer genannt,
ren. Den dafür benötigten Klassen Env und EnvGen zum
schen linkem und rechtem (bzw. oberem und unterem) wird bei aktiviertem Synth von den UGens immer als
Deklarieren und Anwenden von Envelopes wird ebenfalls
Bildschirmrand auf die angegeben Werte minval und Ganzes berechnet und erst dann als Paket an andere
ein eigener Abschnitt in diesem Kurs gewidmet werden.
maxval interpolierend abgebildet. Die Interpolation kann UGens weitergegeben. Der Vorteil ist eine wesentlich ver-
Zunächst aber noch zu einigen weiteren einfacheren dabei linear oder exponentiell sein (warp). Zur zeitlichen ringerte Anzahl von Unterprogrammaufrufen, die immer
Objekten zur Steuerung bzw. Modulation: Glättung dient eine 60dB-Integrationszeit, mit der ange- relativ viel Rechenzeit in Anspruch nehmen.
Neben gleichmäßigen und irregluären Triggern können mit Zur Generierung von einfachen Signalverläufen stehen geben wird, in welcher Zeit der neue Wert bis auf 0.1 % Im folgenden Beispiel wird also pro Controlrate-Takt ein
Hilfe von StepClock auch zeitliche Muster für Line und XLine zur Verfügung. Line wurde bereits in Differenz angenähert wird. ganzer Block von Samples vom Sägezahnoszillator an das
Sequencer verwendet werden. Hierzu übergibt man vorangegangenen Beispielen verwendet. XLine generiert Tiefpaßfilter übergeben, von der Maus hingegen nur ein
StepClock, ähnlich wie dem Sequencer, eine innerhalb der angegebenen Dauer einen exponentiellen MouseX.kr(minval,maxval,warp,lagTime) einziger Wert:
Referenz auf ein Array, das die Triggerimpulsabstände in Verlauf zwischen zwei Werten, die beide das gleiche Vor-
Sekunden enthält, sowie einen Geschwindigkeitsfaktor zeichen besitzen müssen und ungleich Null sein müssen. Mit der Abfrage der Mausposition hat man in SC ein schnell (
rate: einsetzbares Mittel zur Hand, um geeignete Wertebereiche
Synth.play({
(Synth.plot({ für Modulationen interaktiv auszutesten.
( var in, freq;
var lin, exp;
( in = Saw.ar(200);
Synth.plot({ // von 0.1 nach 0.9 in 2 Sekunden:
Synth.play({ freq = MouseX.kr(200, 2000);
var vals, trig, rhythm; lin = Line.ar(0.1, 0.9, 2);
var fr, amp; LPF.ar(in, freq);
vals = [0, 0.5, 1]; exp = XLine.ar(0.1, 0.9, 2);
fr = MouseX.kr(100,800,‘exponential’); })
rhythm = 1 / [4, 16, 8, 16]; [lin, exp]
amp = MouseY.kr(0.0,1.0,‘linear’); )
trig = StepClock.ar(`rhythm, 1); }, 2.5)) // plotte 2.5 Sekunden
Sequencer.ar(`vals, trig); SinOsc.ar(fr, 0, amp); Der Datenaustausch zwischen UGens, die mit Audiorate
}, 3) }) laufen, findet also mit Controlrate statt! Das hat aber kei-
) ) ne negativen Auswirkungen auf die zeitliche Präzision der
Audiodatenübertragung.
Ganz ähnlich funktiert die Steuerung über ein Aber: je größer die Default Block Size, umso
Grafiktablett, auf die hier aber nicht weiter eingegangen effizienter arbeitet SC! Gleichzeitig aber werden die
werden soll. UGens, die tatsächlich mit Controlrate abgetastet werden,
Die GUI-Programmierung und der Umgang mit MIDI und also nur ein Sample pro Controlrate-Takt erzeugen, zeitlich
OpenSoundControl (OSC) werden später in je einem eige- schlechter aufgelöst:
nen Abschnitt ausführlicher behandelt. Rate von 44.1 kHz und einer
Bei einer Sample
Allen 4 letztgenannten Steuerungsmöglichkeiten ist ge- Default Block Size von 64 werden immer gleich
meinsam, daß sie ihre Werte nicht mit der Samplingrate ar 64 Audiosamples gemeinsam berechnet und übertragen.
produzieren, sondern maximal mit der Controlrate kr Die Controlrate beträgt in diesem Fall nur etwa 689 Hz
abgefragt werden können. (1/64 von 44100 Hz), die Dauer einer Controlperiode bzw.
44 SuperCollider SuperCollider 45
eines Blocks beträgt etwa 1.45 ms (64/44100). Höhere Nicht alle UGens lassen sich mit beiden Rates erzeugen: ( Genau genommen haben wir beim letzten Beispiel nicht
Frequenzen als etwa 340 Hz können mit einer solchen • SinOsc, Osc, alle Rauschgeneratoren, Line, XLine Synth.scope({
einfach nur 4 Oszillatoren erzeugt, sondern ein Array mit 4
Controlrate nicht mehr fehlerfrei erfaßt werden (Aliasing!). und die meisten Filter laufen mit beiden Frequenzen, Oszillatoren als Elemente:
Die Controlrate ist also nur zum Abtasten relativ langsamer [SinOsc.ar(100),
• die im letzten Abschnitt besprochenen externen
Vorgänge geeignet, wie z.B. Mausbewegegungen, langsa- K2A.ar(SinOsc.kr(100))] SinOsc.ar([100,300,517,943]).postln;
Controller (Maus, MIDI, GUI, Tablett) laufen nur mit
me Hüllkurven, Reglerbewegungen, MIDI-Messages etc. Controlrate, }) -> [ a SinOsc, a SinOsc, a SinOsc, a SinOsc ]
Immer dann, wenn von UGens sich nur langsam ändernde • die bandbegrenzten Oszillatoren Saw, Pulse und )
Signale erzeugt werden sollen, sollte man diese UGens mit Blip laufen dagegen nur mit Audiorate. Das Ergebnis ist so, als hätte man geschrieben:
Controlrate laufen lassen, um Rechenzeit zu sparen und Ebenso gibt es Einschränkungen für die Verwendung von Zusammenfassend hier noch einmal einige Entscheidungs-
auf diese Weise effizienter zu programmieren. In die glei- UGens als Input für andere UGens: Beispielsweise dürfen hilfen zum Einsatz der beiden Abtastraten: [
che Richtung wirkt sich eine Verringerung der Controlrate Filterfrequenzen maximal mit Controlrate geändert wer- SinOsc.ar(100), SinOsc.ar(300),
ar verwendet man, wenn:
(entspricht einer Erhöhung der Blocksize!) aus. den. Solche Parameter werden im Helpfile des jeweiligen SinOsc.ar(517), SinOsc.ar(943)
Andererseits kann im Bedarfsfall die Controlrate auch • der UGen ein Signal erzeugen soll, das in direkter oder
UGens durch ein vorangestelltes k gekennzeichnet, z.B.: ]
höher eingestellt werden, etwa wegen besserer zeitlicher modifizierter Form letztendlich als Output des Synth die-
Präzision. Sogar eine Default Block Size von 1 Resonz.ar(in, kfreq, krq, mul, add) nen soll und also hörbar wird
ist möglich, das hieße Audiorate gleich Controlrate. • das zu erzeugende Signal als Input für einen anderen Immer wenn man einem UGen anstelle eines einfachen
UGen vorgesehen ist und dieser Input auf ar limitiert ist, Arguments ein Array von Argumenten übergibt, wird ein
Die Abtastrate eines UGens wird durch Senden der Fehlt das k, kann meistens (nicht immer!) jede Rate ver- Array von UGens erzeugt.Wir können uns das auch als ver-
Message ar oder kr an das Klassenobjekt des UGens wendet werden. In seltenen Fällen dürfen sich die Para- • der UGen ausschließlich mit Audiorate betrieben wer-
den kann (wie z.B. bandbegrenzte Oszillatoren), kürzte Schreibweise für ein UGen-Array vorstellen.
festgelegt. ar bzw. kr erzeugen eine Instanz dieser meter überhaupt nicht ändern. Dann wird ein i vorange-
Klasse, die dann mit der jeweiligen Rate läuft. stellt - i steht für init rate, also für den Zeitpunkt der Ini- • das zu erzeugende Signal Frequenzen oberhalb der hal- Diese Technik wird in SC Multichannelexpansion genannt.
tialisierung der UGens. Leider werden diese Konventionen ben Controlrate enthalten soll. Warum Multichannel? Weil dadurch nicht nur mehrere
SinOsc.ar(100); // 100 Hz, Audio bei der Beschreibung der UGens in den Helpfiles nicht kon- kr verwendet man, wenn: Objekte gleichzeitig generiert werden, sondern diese auch
SinOsc.kr(100); // 100 Hz, Controller sequent verwendet. Meistens läßt sich jedoch die maxima- noch „mehrkanalig“ verteilt werden. Ein Array von UGens
• der UGen ein niederfrequentes (fg < Controlrate/2) wird vom Synth immer wie ein mehrkanaliges Soundfile
le Rate aus der Art und der Bedeutung des betreffenden Signal erzeugen soll, das nur zur Modulation eines
Üblicherweise erzeugt man Instanzen von Klassen mit der Parameters ableiten. interpretiert: jedes Element dieses Arrays wird einem ande-
anderen Signals dient, ren Ausgang der Soundkarte zugewiesen und erklingt
Message new. UGens können aber auschließlich über ar Gelegentlich ist es notwendig, ein Controlrate-Signal in • der UGen ausschließlich mit Controlrate betrieben wer-
bzw kr. instanziert werden. new, ar und kr sind somit aus einem anderen Lautsprecher:
eines mit Audiorate zu verwandeln (Oversampling). Zum den kann (z.B. externe Controller),
Klassenmethoden, die dazu dienen, Instanzen auf Beispiel, um sich solche Signale in einem Scope-Window • das zu erzeugende Signal als Input für einen anderen
bestimmte Weise zu erzeugen. Im Gegensatz können ansehen oder als Soundfile speichern zu können, weil als UGen vorgesehen ist und dieser Input auf kr limitiert
Instanzenmethoden nur von bereits erzeugten Instanzen Output eines Synth nur Signale mit Audiorate erlaubt ist (z.B. Filterfrequenzen)
einer Klasse ausgeführt werden. Dazu später mehr. sind. Der UGen K2A erzeugt aus einem kr-Signal per
Die Klassenmethoden ar und kr können nur innerhalb linearer Interpolation die fehlenden Zwischenwerte im Takt Multichannelexpansion, Mix und Pan
der ugenGraph function eines Synth verwendet werden. der Samplingrate. Mit seiner Hilfe können wir zum Beispiel
Außerhalb des Synthesizers funktionieren sie nicht; der die Unterschiede zwischen verschieden abgetasteten Kommen wir nun zu einem sehr leistungsfähigen Feature
Versuch, sie zu benutzen, ruft eine Fehlermeldung hervor: Sinusoszillatoren hör- und sichtbar machen: von SuperCollider, das diesem Programm einen großen
Vorteil gegenüber anderen Syntheseprogrammen, wie
( etwa Csound, MAX oder Reaktor verschafft.
var in, freq; Übergibt einem unit generator als Parameter ein Array mit
Werten anstelle eines einzigen Wertes, so werden genau-
in = Saw.ar(200);
so viele Kopien dieses unit generators erzeugt, wie
Synth.play({ Elemente im Array vorhanden sind. Jede dieser Kopien
freq = MouseX.kr(200, 2000); bekommt als Parameter einen anderen Wert aus dem Array
LPF.ar(in, freq); zugewiesen.
})
// 1 Oszillator:
) SinOsc.ar(100);
• ERROR:
// 4 Oszillatoren mit untersch. Freq.
input UGen is not part of a Synth.
SinOsc.ar([100, 300, 517, 943]);
INPUT UGEN:
Instance of Saw ....
46 SuperCollider SuperCollider 47
( Synth.scope({ Möchte man die unterschiedlichen UGens statt auf ver- ( ramawert benutzen. Die Amplitude des Sinusgenerators
SinOsc.ar([100, 300, 517, 943]) schiedenen Ausgängen nur über einen einzigen Ausgang var n, freq;
braucht hier nicht angepaßt zu werden, da Oszillatoren im
ausgeben, gibt man einfach die Summe der Signale aus. Allgemeinen schon die für Panoramaobjekte passenden
})) n = 12; // 12 Oszillatoren
Wegen der zu erwartenden Übersteuerung wird das Werte zwischen -1 und +1 liefern:
Summensignal anschließend halbiert: freq = 100; // mittlere Frequenz
(
Synth.scope({
(
Mix.ar( Synth.scope({
Synth.scope({
Saw.ar( Pan2.ar(
( Saw.ar(300) + PinkNoise.ar Array.rand(n,0.99,1.02)*freq, Pulse.ar(300), // Signal
+ Dust.ar(800)) / 2 1/n) SinOsc.kr(3) // Position
}) ) )
) }) }, 1) // 1 Sekunde anzeigen
)
)

Das Frequenzargument für Saw lautet in diesem Beispiel:


Array.rand(n,0.99,1.02) * freq

Diese Zeile generiert ein Array mit n zufälligen Zahlen zwi-


schen 0.99 und 1.02. :
[ 0.994382, 0.990063, 1.00795, 0.991763,
1.01091, 1.00803, 1.00571, 0.992787,
1.00693, 1.01078, 0.993691, 0.999414 ]

Aber auch ohne Multichannelexpansion können wir unter- Die erzeugten Werte werden anschließend mit dem Wert
schiedliche Signale auf unterschiedliche Lautsprecher auf- für die Basisfrequenz multipliziert, so daß dem Oszillator
teilen, indem wir diese Signale einfach einem Array über- schließlich ein Array mit 12 Werten zwischen 99.0 und
geben, also mit eckigen Klammern zusammenfassen: 102.0 als Frequenzargument übergeben wird. Das darauf-
hin erzeugte Array mit 12 gegeneinander verstimmten
( Synth.scope({ Sägezahnoszillatoren wird nun mit Mix auf einen Kanal Mit PanAz kann man ein Signal auf einem Kreis positio-
[Saw.ar(300),PinkNoise.ar,Dust.ar(800)] Das Objekt Mix mischt ein Mehrkanal-Array, also ein Array reduziert bzw. summiert. (Ohne Mix hätten wir an dieser nieren, der von beliebig vielen äquidistanten Laut-
von UGens, zu einem Mono-Kanal zusammen: Stelle eine 12-kanalige Soundkarte benötigt, um alle sprechern gebildet wird. Das Kürzel Az steht also für
}))
Klänge zu hören.) Azimuth. Zusätzlich zum Signal und zur Position muß man
( Selbstverständlich kann man mit SuperCollider für die noch die Anzahl der Lautsprecher angeben. Optional steht
Synth.scope({ Zuordnung von Klängen zu Audioausgängen bzw. Laut- noch ein Parameter width zur Verfügung, mit dem die
sprechern auch „klassische“ Panning-Objekte verwenden. „Breite“ des Signal, d.h. über wieviel Lautsprecher es sich
Mix.ar([ erstrecken soll, geregelt werden kann.
Pan2, Pan4 und PanAz sind Objekte für 2- und 4-kana-
Saw.ar(300), liges Panning bzw. Panning für eine beliebige (!) Anzahl Hier ein Beispiel für eine beschleunigte Rotation über 5
PinkNoise.ar, von Ausgängen. Hinzu kommen noch PanB für dreidi- Lautsprecher:
Dust.ar(800) mensionales Panorama im Ambisonics B-Format sowie
einige weitere Objekte, über die man sich im [Pan] - (
]) / 2
Helpfile informieren kann. Synth.scope({
})
Für eine einfache Positionierung im Stereopanorama über- PanAz.ar(
) gibt man dem Objekt Pan2 das zu positionierende Signal 5, // numChans
und als 2. Argument die Position in Form eines Wertes zwi- ClipNoise.ar, // in
Mit Hilfe von Mix und einer der etwas später noch aus- schen -1.0 (links) und 1.0 (rechts). Für die Positionsangabe
führlicher zu besprechenden Array-Methoden kann nun LFSaw.kr(XLine.kr(0.3,8,3)),//pos
sind, wie auch bei den UGens üblich, neben konstanten
ganz leicht ein „fetter“ Sägezahnklang erzeugt werden: Werten auch veränderliche Signale möglich. Man könnte 1, 2 // level, width
z.B. auch eine sinusförmige „Steuerspannung“ als Pano- )}, 3))
48 SuperCollider SuperCollider 49
Alle Pan-Objekte in SC arbeiten ausschließlich mit Array wird ein 1-dimensionales, aus einem 3-dimensiona- eigentlich ein 3-dimensionales Array: die Ebene der Cluster
Intensitätsunterschieden zwischen den Kanälen. Ein len Array wird ein 2-dimensionales usw. Dies geschieht (3 Sub-Arrays), die jeweils alle linken und alle rechten
Laufzeit-basiertes Panning-Objekt unter Ausnutzung des durch Summierung einander entsprechender Elemente. Anteile (2 Sub-Sub-Arrays) der jeweils 12 Sägezähne (12
Haas-Effekts gibt es im derzeitigen Standardumfang von Elemente) enthalten. Diese 3 Ebenen müssen nun durch
SC (Version 2.2.13) noch nicht, kann aber mit anderen geeignete Anwendung von Mix auf eine einzige Ebene
Objekten in SC programmiert werden. Doch zunächst reduziert werden. Dazu wird jeder Cluster zunächst zu
zurück zum Stereopanorama. einem Stereosignal zusammengefaßt. Die resultierenden 3
In einem Mischpult werden überlicherweise monophone Stereosignale werden dann in der letzten Zeile von einem
Kanäle bzw. Signale zunächst auf 2 Busse, links und rechts, weiteren Mix zu einem einzigen Stereosignal gemischt.
verteilt, bevor dann die jeweils linken und rechten Jeder der 3 Cluster belegt einen anderen Bereich auf der
Bussignale zusammengefasst werden. In SuperCollider Stereobasis: Grundton in der Mitte, Terz links-halblinks,
kann man mehrere stereophon verteilte Monosignale als Quinte halbrechts-rechts:
Array von Arrays auffassen. Aber wie sieht es eigentlich (
aus, wenn man ein Array von Stereosignalen erzeugt? Wie
var n, f, f1, f2, f3, cl1, cl2, cl3;
zum Beispiel, wenn man jedem der gegeneinander ver-
stimmten Oszillatoren aus dem vorletzten Beispiel eine n = 12; // 12 Oszill. pro Cluster
Während Pan2 und PanAz Objekte für Panning auf einer individuelle Position im Stereopanorama zuweisen würde: Im Falle unseres Arrays von Stereosignalen können wir f = 38; // Bezugston (D)
von den Lautsprechern begrenzten Kreisbahn sind, eignet sagen, daß Mix alle linken Anteile (= die jeweils 1. f1 = f.midicps; // Prime
sich Pan4 für die Einstellung einer Position in der durch 4 ( // Achtung: funktioniert noch nicht! Elemente der Sub-Arrays) und alle rechten Anteile ( = die f2 = (f + 3).midicps; // kl. Terz
quadratisch angeordnete Lautsprecher gebildeten Ebene. jeweils 2. Elemente der Sub-Arrays) getrennt summiert.
var n, freq; f3 = (f + 7).midicps; // Quinte
Hierzu existieren ein x-Parameter ( -1= links, +1=rechts) Das Ergebnis ist ein neues 2-elementiges 1-dimensionales
n = 12; // 12 Oszillatoren Array, das nun von SC problemlos den Audioausgängen Synth.scope({
und ein y-Parameter ( -1=vorne, +1=hinten):
zugeordnet werden kann. cl1 =
freq = 60; // mittlere Frequenz
Pan4.ar(in, xpos, ypos, level) Mix.ar(
Synth.scope({ Wir müssen also in das letzte Beispiel noch Mix einfügen,
um eine Stereosumme aller 12 Signale zu erhalten: Pan2.ar(
Pan2.ar(
Saw.ar(
Saw.ar( ( // jetzt funktioniert es! Array.rand(n,0.99,1.02) * f1,
Array.rand(n,0.99,1.02) * freq, var n, freq; 1/n),
2/n), n = 12; // 12 Oszillatoren Array.rand(n, -0.2, 0.2)));
Array.series(n, -1, 2 / (n-1)) freq = 60; // mittlere Frequenz cl2 =
) Synth.scope({ Mix.ar(
}) Mix.ar( Pan2.ar(
Pan2.ar( Saw.ar(
)
Array.rand(n,0.99,1.02) * f2,
Saw.ar(
In diesem Fall ist das Ergebnis der ugenGraph function ein 1/n),
Array.rand(n,0.99,1.02) * freq,
mehrdimensionales Array (ein Array von Arrays) der Form: 2/n), Array.rand(n, -1.0, -0.6)));
[ [ L1, R1 ] , [ L2, R2 ] , [ L3, R3 ] , [ L4, R4 ] , ... ] cl3 =
Array.series(n, -1, 2 / (n-1))
Mix.ar(
Jedes Stereosignal ist ein 2-elementiges Array. Diese 2-ele- )
Pan2.ar(
mentigen Arrays sind wiederum Elemente im äußeren )
Saw.ar(
Array. So ein mehrdimensionales Array kann SC aber nicht }) Array.rand(n,0.99,1.02) * f3,
Leider kann auch Pan4 das Problem nicht lösen, daß in ohne weiteres den Ausgängen der Soundkarte zuordnen! )
Wir müssen daher dieses Array wieder auf ein einfaches 1- 1/n),
der abgebildeten Konfiguration der Klang nicht wirklich
innerhalb der zwischen den Lautsprechern aufgespannten dimensionales Array reduzieren. Array.rand(n, 0.6, 1.0)));
Mix reduziert verschachtelte Arrays allerdings nur um eine
Ebene zu hören sein wird. Ein Klang wird immer nur in der Diese Aufgabe kann von Mix erledigt werden. Ganz allge- Ebene. Haben wir 3- oder höherdimensionale Arrays, muß Mix.ar([ cl1, cl2, cl3 ]);
Nähe der Kreislinie oder dahinter wahrzunehmen sein! })
mein wird durch Mix die Tiefe von verschachtelten Mix mehrfach angewendet werden. Um dies demonstrie-
Dieses Problem ist mit reiner Intensitätsstereophonie und Audio-Arrays um eine Ebene reduziert. Aus einem ren zu können, erweitern wir einfach das letzte Beispiel, )
einer kleinen Anzahl von Lautsprecher mit heutigen 1-dimensionalen Array wird ein 0-dimensionales Array indem wir Akkorde von gegeneinander verstimmten
Wandlerprinzipien wahrscheinlich generell nicht zu lösen. (d.h. ein einzelnes Element), aus einem 2-dimensionalen Sägezahnclustern stereophon anordnen. Das ergäbe
50 SuperCollider SuperCollider 51
Funktionen Eine Funktion, die Audioobjekte bzw. einen ugenGraph Soundfiles sein. Das heißt, die Anzahl der Kanäle des Soundfiles wird
Das vorangegangene Beispiel zeigt augenfällig, daß zurückgibt, darf nur innerhalb eines Synth aufgerufen Natürlich erlaubt SC auch die Speicherung der erzeugten nicht als Parameter angegeben, sondern resultiert aus der
Programmieren nicht nur Aufschreiben einer Idee sondern werden, da außerhalb von Synth die Messages ar und Klänge als Soundfiles. Die einfachste Möglichkeit, Klänge Größe des Arrays, das von der ugenGraph function zurück-
besser deren Formalisierung sein sollte. Wir wollen 3 mal kr nicht verstanden werden können. Trotzdem kann die auf die Festplatte zu schreiben, besteht darin, anstelle der gegeben wird. Beim folgenden Beispiel werden 8 dyna-
etwas ziemlich Ähnliches tun, nämlich 3 Cluster gleicher Funktion außerhalb von Synth deklariert werden. Message play die Message record an Synth zu sen- misch gefilterte Sägezähne in die 8 Kanäle eines 30
innerer Struktur und unterschiedlichen Parametern erzeu- Nun können wir den Akkord von stereophonen Clustern den. Dabei muß neben der ugenGraph function auch die Sekunden langen Soundfiles "filtered" gespeichert.
gen, und haben es umständlicherweise auch 3 mal aufge- etwas einfacher notieren: Gesamtdauer in Sekunden sowie ein Pfadname übergeben (
schrieben! werden:
( Synth.record({
In SuperCollider stehen aber verschiedene Werkzeuge Resonz.ar(
var f, f1, f2, f3, cluster; *record(ugenGraphFunc, duration,
bereit, um diese Idee einfacher formulieren zu können. Eine
f = 38; // Bezugston (D) pathName, headerFormat, sampleFormat) Saw.ar(
sehr einfache Möglichkeit besteht darin, den wiederkehren-
den Teil des Codes als Unterprogramm bzw. Funktion zu f1 = f.midicps; // Prime Array.rand(8, 80.0, 83.0)),
Der Pfadname kann absolut angegeben werden, etwa:
programmieren. (siehe dazu auch den Artikel von Torsten f2 = (f + 3).midicps; // kl. Terz LFNoise0.kr(
Anders in diesem Heft!) f3 = (f + 7).midicps; // Quinte path = "Mac HD:work:Audio:test.aiff" Array.rand(8, 0.3, 2.0),
Wir erinnern uns: alles zwischen { } ist eine Funktion. Eine cluster = { 500, 600),
oder relativ zum SuperCollider-Ordner, etwa:
Funktion kann Argumente haben. Eine Funktion gibt etwas arg freq, pan1, pan2, n = 12; 0.03)
zurück. Die Argumente können als Input und der path = ":Sounds:test.aiff" }, 30, "HD:Projekte:snd:filtered")
Mix.ar(
Rückgabewert als Output betrachtet werden. )
Pan2.ar( Relative Pfadnamen beginnen immer mit einem
cluster = { Saw.ar( Doppelpunkt.
arg freq, pan1, pan2, n = 12; Array.rand(n,0.99,1.02)*freq, record übernimmt eigentlich 2 Aufgaben: das Schreiben
Als 4. (optionales) Argument kann das Format des Sound-
Mix.ar( 1/n), files spezifiziert werden. Zur Verfügung stehen zur Zeit: von Soundfiles und die Wiedergabe der ugenGraph func-
Pan2.ar( Array.rand(n, pan1, pan2) tion. Bei sehr vielen UGens kann es schnell zu einer Über-
'AIFF', 'SD2', 'RIFF' (wav!) , 'Sun', lastung der CPU kommen. Trotzdem ist es möglich, das
Saw.ar( )) 'IRCAM', 'none' (ohne Header!) Ergebnis von Patches zu speichern, für dessen Echtzeit-
Array.rand(n,0.99,1.02)*freq, }; Das Format wird also immer als Symbol angegeben. generierung der Rechner zu langsam ist.
1/n), Synth.scope({ Entfällt die Angabe des Headerformats, wird ein AIFF-File Die Message write, deren Argumente genau die glei-
Array.rand(n, pan1, pan2) Mix.ar([ geschrieben. chen wie für record sind, schreibt Soundfiles, ohne sie
)) cluster.value(f1, -0.2, 0.2), Mit sampleFormat ist die Auflösung und Byte-Ord- gleichzeitig abzuspielen. Mit write können beliebig
}; cluster.value(f2, -1.0, -0.6), nung der Samples gemeint. Neben 16bit werden noch vie- komplexe Klänge abgespeichert werden. In der Regel geht
cluster.value(f3, 0.6, 1.0) le weitere Auflösungen unterstützt, so z.B. auch 64bit dies auch schneller als in Echtzeit. Allerdings ist dann die
Diese neue Funktion cluster bekommt als Argumente Fließkomma. Die genauen Symbole dafür findet man im Steuerung von Parametern durch externe Controller, wie
])
genau diejenigen Parameter, um die sich Cluster voneinan- Helpfile von SoundFile (nicht Synth!). Der default- Maus oder MIDI, nicht mehr möglich.
der unterscheiden sollen. Zusätzlich wurde auch die Anzahl })
Wert für sampleFormat ist 16bit Integer.
der Oszillatoren pro Cluster als Argument vorgesehen. )
Die Samplingrate der zu erzeugenden Soundfiles entspricht
Argumente, deren Deklaration mit Hilfe des Schlüssel- Der Programmtext ist nicht nur kürzer und besser lesbar immer dem im Audio-Setup eingestellten Wert.
wortes arg erfolgt, sind eigentlich nichts weiter als Fortsetzung folgt
geworden, es lassen sich jetzt auch eventuelle strukturelle
Variablen, die nur innerhalb der Funktion gültig sind - sol- (
Änderungen des Cluster einfacher vornehmen, weil man
che Variablen heißen lokal - und die von außen beim den Code jetzt nur noch an einer Stelle ändern muß. Synth.record(
Aufrufen der Funktion einen Wert zugewiesen bekommen. Außderdem ist es jetzt offenbar ganz einfach, weitere {
Man kann auch schon bei der Deklaration der Argumente Cluster hinzuzufügen. Es spricht also vieles für die // irgendein ugenGraph
einen Standardwert für jedes Argument festlegen, der Verwendung von Funktionen! },
immer dann verwendet wird, wenn dieses Argument beim Bezugsquellen
Bei diesem Beispiel ist allerdings auch Vorsicht geboten: wir 20, // 20 Sekunden
Aufrufen der Funktion nicht angegeben wird. So ein Die SuperCollider Homepage:
wollen hier sehr viele UGens erzeugen, die von langsameren "HD:Projekte:snd:klangx"
default-Wert wurde hier für das Argument n vorgegeben. www.audiosynth.com
Rechnern in Echtzeit möglicherweise nicht mehr generiert )
Eine Funktion wird aufgerufen, indem man die Message werden können! Die CPU-Last kann bei aktivem Synth in Weitere Informationen, Beispiele, Tutorials etc:
value an die Funktion schickt und die Argumente der )
der Informationsleiste oben links beobachtet werden: swiki.hfbk.uni-hamburg.de:8080
Funktion als Argumente von value notiert. Bei unserer SuperCollider beherrscht das Schreiben mehrkanaliger /MusicTechnology/6
Clusterfunktion könnte ein Aufruf so aussehen: Soundfiles problemlos. Dazu muß des Ergebnis der ugen-
cluster.value(440, -1.0, -0.0) Graph function eben ein Array mit mehr als 2 Elementen Andre Bartetzki
28 SuperCollider SuperCollider 29
Aber möglicherweise gibt es ein Problem beim Lesen des Diese und weitere Möglichkeiten im Umgang mit DiskIn
S u p e r C o l l i d e r Headers, z.B. weil das im Pfadnamen angegebene File gar werden im entsprechenden Helpfile ausführlich dargestellt.
nicht existiert oder nicht lesbar ist. Deshalb empfiehlt sich, Es sei an dieser Stelle darauf hingewiesen, daß auch ein ent-
Einführung in die Programmierung, Teil 4 gleich eine Prüfung vorzunehmen, ob das Lesen des sprechendes DiskOut Objekt existiert. In der Mehrzahl
Headers erfolgreich war: der Fälle wird man allerdings Synth.record benutzen,
Andre Bartetzki // Soundfile erzeugen:
um ein Soundfile auf die Festplatte zu schreiben.
abart@snafu.de ( if (
Die eben beschriebene Methode des direkten Lesens von
Synth.write( file.readHeader("HD:snd:klang"),
Dieser mehrteilige Einführungskurs zum Softwaresynthe- der Platte bringt einige Nachteile mit sich. Der größte
{ { „OK“.postln }, Mangel dürfte das Fehlen einer variablen Abspielgeschwin-
sizer SUPERCOLLIDER (SC) begann in den Mitteilungen
// irgendein ugenGraph { „Lesefehler!“.postln } digkeit bzw. Transposition sein. Darüberhinaus können
40 und wird an dieser Stelle fortgesetzt. Am Schluß dieses
}, ); beim Loopen mit DiskIn nur die im Header von AIFF-
4. Teils finden sich noch einmal Hinweise auf die Quellen
für die Software und ergänzendes Informationsmaterial. 20, "HD:Projekte:snd:sss" Files gespeicherten Loop-Punkte verwendet werden, eine
Das DiskIn Objekt muß innerhalb eines Synth erzeugt Veränderung der Loops während des Abspielens ist nicht
)
werden. Ihm werden das file und eventuell das optio- möglich.Außerdem wird beim Lesen einer größeren Anzahl
Soundfiles (Fortsetzung) ) nale Loop-Flag übergeben: Files von der Festplatte viel CPU-Kapazität verbraucht.
Ein per record bzw. write erzeugtes mehrkanaliges // Soundfile abspielen: Diese Probleme umgeht man durch das vorhergehende
Synth.play({
Soundfile kann möglicherweise nicht von jeder Audio- Laden der Files in den RAM. Die so geladenen Soundfiles
SoundFile.play("HD:Projekte:snd:sss") DiskIn.ar(file, true)
software geöffnet oder abgespielt werden, während es mit bzw. die entsprechenden Buffer können dann mit
Mono- oder Stereofiles in der Regel immer möglich sein })
Etwas komplizierter gestaltet sich das Lesen von Soundfiles PlayBuf hörbar gemacht werden.
sollte. In solchen Fällen kann man anstelle von record innerhalb von ugenGraph functions. Grundsätzlich gibt es
Im Falle eines Fehlers beim Lesen des Headers, würde hier ein Doch zuvor muß das komplette File mit Hilfe von read in
die Message recordMonoFiles verwenden, die in SC, wie auch in MAX/MSP, CSOUND und ähnlichen
vermeidbarer Folgefehler enstehen, weil DiskIn versuchen den Arbeitsspeicher eingelesen werden. Eigentlich müsste
anstelle eines mehrkanaligen Files entsprechend viele Programmen, 2 verschiedene Methoden, um Audiodateien
würde, ein fehlerhaftes oder nicht existierendes Soundfile zu an dieser Stelle ebenfalls ein Test erfolgen, auf den ich hier
Monofiles auf die Festplatte schreibt: zu importieren:
spielen! Deshalb ist es besser, den Synth mit DiskIn nur aber aus Platzgründen verzichte.
( 1. das unmittelbare Abspielen der Datei von der Festplatte dann zu starten, wenn das Lesen erfolgreich war.
var file;
f = Array.series(8, 100, 100); 2. das Einlesen der Datei in einen Buffer und anschließen- Hier das komplette Beispiel für das direkte Abspielen von
des Abspielen des Buffers file = SoundFile.new;
Synth.recordMonoFiles({ der Festplatte mit verbesserter Fehlerbehandlung:
file.read("HD:snd:klang");
SinOsc.ar(f)) Beide Methoden verwenden für den Zugriff auf die Dateien
das Objekt SoundFile. (
}, 5, "HD:Sinus") Die Message read bewirkt das Lesen des vollständigen
var file;
) In beiden Fällen besteht der erste Schritt zum Import von Soundfiles, das aus Header und Datenteil besteht.
Audiodateien darin, eine Instanz von SoundFile anzu- file = SoundFile.new;
PlayBuf erwartet nur die Audiodaten, jedoch nicht den
Hans Tutschku hat mit Hilfe von SUPERCOLLIDER ein legen: if ( Header. Daher müssen wir nun den Datenteil des Files in
eigenständiges Programm geschrieben, mit dem das file.readHeader("HD:snd:klang"), den eigentlichen signal buffer schreiben:
Splitten eines mehrkanaligen Soundfiles auch nachträglich var file;
{ Synth.play({
erfolgen kann. Darüber hinaus beherrscht dieses Pro- file = SoundFile.new; signal = file.data.at(0);
DiskIn.ar(file, true)
gramm auch das Zusammenfügen mehrerer Monofiles zu
})},
einem Mehrkanalfile sowie eine Reihe weiterer nützlicher Das direkte Abspielen von Soundfiles geschieht mit dem Der Datenteil, auf den mit data zugegriffen wird, ist ein
Bearbeitungsmöglichkeiten im Batchbetrieb, wie Filtern, Objekt DiskIn. Doch bevor wir unser file damit { „Lesefehler!“.postln } Array, dessen Elemente die Samples der einzelnen Kanäle
abspielen können, müssen wir noch den Pfad spezifizieren: );
Ein- und Ausblenden, Gleichspannungsfilter u.a. sind. Selbst bei Monosoundfiles ist der Datenteil ein Array,
) allerdings mit nur einem Element. Das erste Element - der
http://hanstutschku.multimania.com/ file.readHeader("HD:snd:klang") linke Kanal - trägt die Nummer 0. Mit at(index) kann
download/download.htm In der Version 2 von SUPERCOLLIDER darf das Lesen des man ein Element aus einem Array lesen. data.at(0)
Im Header eines Soundfiles stehen Informationen wie die Headers nicht bei aktiviertem Synth erfolgen. Das Lesen liest also immer den linken Kanal bzw. den einzigen Kanal.
Das Lesen von Soundfiles kann mit dem Objekt Samplingrate, das Sampleformat (Auflösung in bit), die von Dateien muß stets vorher vorgenommen werden! Die Audiosamples in einem Kanal werden als Signal
SoundFile und der Message play erfolgen. Hierzu ist Anzahl der Kanäle, Loop-Punkte, Originaltonhöhe und Möglicherweise wird man beim Ausführen dieses Beispiels interpretiert. Ein Signal in SC ist eigentlich nichts wei-
kein Synth nötig, da play in diesem Fall einen ent- Tastaturbereich sowie weitere Angaben. eine kleine Verzögerung zu Beginn wahrnehmen, die damit ter als ein Array für Fließkommazahlen.
sprechenden Synth selbst erzeugt. Auf diese Weise lässt Interessierte könnten sich diese Angaben nach dem Lesen zusammenhängt, daß das Lesen von der Festplatte immer
sich ein mit write „blind“ geschriebendes Soundfile mit In MAX/MSP heißt das entsprechende Objekt
des Headers anzeigen lassen: einige Zeit in Anspruch nimmt und grundsätzlich länger dau-
SC wieder abspielen. SoundFile.play ist besonders buffer~ , während PlayBuf in etwa die gleichen
ert, als das Lesen aus dem Arbeitsspeicher. Deshalb gibt es Aufgaben wie das MSP-Objekt groove~ übernehmen
gut für die unkomplizierte Wiedergabe mehrkanaliger file.dump; die Möglichkeit, mit preloadData einen kleinen Teil des
Soundfiles geeignet. kann: das Abspielen von im Buffer gehaltenen Samples.
Files zu laden, bevor es dann im Synth abgespielt wird.
30 SuperCollider SuperCollider 31
Das PlayBuf Objekt erwartet viele Argumente; die meis- Hier nun ein vollständiges Beispiel für das Laden von Objekte - Klassen und Instanzen Einige Eigenschaften von Objekten können mit der
ten dienen zur Modifzierung des Abspielens. Samples und das Abspielen aus dem RAM: SUPERCOLLIDER ist eine objekt-orientierte Programmier- Message dump summarisch angezeigt werden:
( sprache. Das heißt, Algorithmen werden durch
PlayBuf.ar(signal, sigSampleRate, 42.dump
Manipulationen von Objekten beschrieben.
playbackRate, offset, loopstart, var file, signal; -> Integer 42
loopend, mul, add) file = SoundFile.new; Ein Objekt ist etwas, das interne Zustände (Daten) hat und
verschiedene Verhaltensweisen bzw. Reaktionen (Opera- \a.dump
file.read("HD:snd:klang");
Die Parameter im Einzelnen: tionen) kennt. Objekte sind somit in gewisser Weise mit
signal = file.data.at(0); // 1. Kanal -> Symbol ‘a’
signal bezeichnet das Array bzw. den Buffer mit der realen physischen Objekten vergleichbar.
Synth.play({
abzuspielenden Wellenform. Alle Objekte, die sich durch gleiches Verhalten und gleiche [a, 2, 3.0].dump
var sr,transp,offset,start,end; interne Datenstruktur auszeichnen, gehören einer gemein-
sigSampleRate ist die Samplingrate in Hz. Hier kann -> Instance of Array { (...)
sr = file.sampleRate; samen Klasse an.
man beliebige Werte eintragen, die PlayBuf dann zur indexed slots [3]
Berechnung der Abspielgeschwindigkeit heranzieht. transp= LFNoise0.kr(1, 0.3, 1.0); Eine Klasse beschreibt die strukturellen Eigenschaften
offset = 0; 0 : nil
Sinnvoll ist allerdings entweder die Angabe der Sampling- (Daten und Operationen) von Objekten auf abstrakte
start = offset; 1 : Integer 2
rate aus dem Audiosetup oder besser die Samplingrate des Weise. Eine Klasse ist also eine Abstraktion einer Reihe von
gelesenen Soundfiles: file.sampleRate. Falls sich end = signal.size - 2; Objekten. 2 : Float 3
die für PlayBuf angegebene Samplingrate von der des PlayBuf.ar(signal, sr, transp, }
Dahingegen können Objekte als Realisationen einer Klasse
Synth unterscheidet, führt SC eine Konvertierung der offset, start, end); aufgefasst werden. In der objekt-orientierten Program-
Samplingrate in Echzeit durch. Instanzen können mit der Message new erzeugt werden,
}) mierung bezeichnet man solche konkreten Objekte als
playbackRate ist die relative Abspielgeschwindigkeit die dazu an das (abstrakte) Klassenobjekt geschickt wer-
) Instanzen einer Klasse.
bzw.. der Transpositionsfaktor. 1 bedeutet Originalton- den muss. Ein Klassenobjekt dient zur Notation der Klasse
Ein Beispiel: Die Zahl 42 ist, wie alles in SUPERCOLLIDER, selbst und nicht einer Instanz.
höhe, 0.5 eine Oktave tiefer und 2.0 eine Oktave höher. -1 PlayBuf ist auch in der Lage, mit mehrkanaligen ein Objekt. Genauer gesagt, 42 ist eine Instanz der Klasse
spielt das Sample rückwärts. Dieser Wert kann dynamisch Samples umzugehen. Hierfür übergibt man einfach anstel- Array, im folgenden Beispiel, ist also ein Klassenobjekt,
Integer (ganze Zahlen), also eine konkrete ganze Zahl.
verändert werden, z.B. von den diversen Controllern le eines Signals ein Array von Signals. PlayBuf ordnet während a eine neue Instanz der Klasse Array ist:
(Hüllkurvengeneratoren, Maus, MIDI, GUI etc.). dann, gemäß der Multichannelexpansion, jedem Signal- Zu welcher Klasse ein bestimmtes Objekt gehört, erfährt
buffer einen Output zu. Da file.data schon das man durch Senden der Message class an das Objekt: a = Array.new;
offset gibt den Startpunkt innerhalb des Sounds an.
Dieser Wert wird nicht in Sekunden sondern in Audio- gewünschte Array von Signals zurückgibt, ist eigentlich a.dump;
42.class.postln
samples, d.h. Abtastwerten, gemessen. Zum Beispiel nicht mehr viel zu tun. Vorsicht ist geboten bei der
-> Integer -> Instance of Array { ... }
bedeutet ein Wert von 1000 bei einer Samplingrate von Berechnung des Loopend-Punktes: signal.size gibt
44100 ca. 44 ms. Wer den offset statt in Samples lie- jetzt die Anzahl der Kanäle zurück! Wir können aber ein- Array.dump;
42.3.class.postln
ber in Sekunden angeben möchte, kann sich einer einfa- fach auf die Anzahl der Sampleframes im Soundfile zurück-
greifen. Hier das modifizierte Beispiel, das sowohl Mono- -> Float -> class Array (03F214E8) { ... }
chen Formel bedienen:
als auch Mehrkanalfiles verarbeiten kann:
Sekunden * file.sampleRate [a, 2, 3.0].class.postln Von fast jeder Klasse lassen sich Instanzen mit new erzeugen:
Die nach offset folgenden Looppunkte loopstart ( -> Array
und loopend werden ebenfalls in Audiosamples angege- var file, signals; b = SinOsc.new;
\a.class.postln
ben. Es ist wichtig zu wissen, daß sich PlayBuf im file = SoundFile.new; c = MouseX.new;
Gegensatz zu DiskIn immer im Loopmodus befindet. Das -> Symbol
file.read("HD:snd:klang");
heißt, man kann das Loopen nicht abschalten. Um einen signals = file.data; // alle Kanäle! `a.class.postln Aber für viele Klassen gibt es noch weitere Messages, die
Sound nur ein einziges Mal zu spielen, muß man sich ande- ebenfalls Instanzen erzeugen. Dies sind z.B. die bekannten
Synth.play({ -> Ref
rer Mittel bedienen, z.B. Hüllkurven - doch dazu später. Messages ar und kr, die bei den UGen-Klassen einge-
var sr,transp,offset,start,end;
Will man das komplette Soundfile loopen, stellt man 0 und "a".class.postln setzt werden:
sr = file.sampleRate;
signal.size-2 ein. Die Message size ermittelt die -> String
Größe eines Arrays. Das letzte Sample der Wellenform hat transp= LFNoise0.kr(1, 0.3, 1.0); b = SinOsc.ar;
den Index size-1. Für die Berechnung der Interpola- offset = 0; {}.class.postln
c = MouseX.kr;
tionen bei der Transposition mit PlayBuf muß der loo- start = offset; -> Function
pend-Punkt vor dem letzten Audiosample liegen. Daher end = file.numFrames - 2; Auch Synth ist eine Klasse bzw. ein Klassenobjekt. Die
size-2. SinOsc.new.class.postln
PlayBuf.ar(signals, sr, transp, Message play (mit ihren Argumenten) erzeugt eine
Die Looppunkte können auch in Echtzeit moduliert werden. -> SinOsc Instanz der Klasse Synth:
offset, start, end);
mul und add sind die schon von anderen Audioobjekten })
Synth.play(...)
bekannten Modulationsparameter. )
32 SuperCollider SuperCollider 33
Instanzen können weiterhin auch durch spezielle Dazu einige Beispiele: Functional und Receiver Notation Die unterschiedliche Bedeutung der Argumente von
Sonderzeichen erzeugt werden - solche direkt notierten Messages können auf verschiedene Arten notiert werden. round wird durch die Receiver-Schreibweise deutlicher:
Instanzen nennt man Literale: // Message new an ein Klassenobjekt:
Aus anderen Programmiersprachen ist die funktionale
a = List.new; 1.267.round(0.1).postln
Schreibweise geläufig, die wir auch in SC schon oft ver-
\bla.dump -> 1.3
// Message add an eine Instanz: wendet haben. Hier wird die Message als Funktion aufge-
-> Symbol 'bla' faßt, der man in der Regel eine oder mehrere Argumente
b = a.add(3); Diese gemischte Schreibweise ist in SC überall anzutreffen,
$c.dump übergeben muß: insbesondere bei den UGen-Objekten, z.B.
// Message postln an eine Instanz:
-> Character 99 'c'
b.postln; // function(arguments) SinOsc.ar(440)
Auch Zahlen und spezielle Werte sind Instanzen einer -> List[ 3 ] sqrt(6)
oder
Klasse und im Programmtext zugleich Literale:
sin(pi)
Synth.play({ Saw.ar(200) })
7.class.postln // Message series an ein Klassenobjekt: midicps(60)
-> Integer c = Array.series(5, 60, 3); postln("Hallo!") Man könnte stattdessen tatsächlich auch schreiben:
7.0.dump // Message postln an eine Instanz: min(6, pi) ar(SinOsc, 440)
-> Float 7.0 c.postln;
rand(10) bzw.
pi.dump -> [ 60, 63, 66, 69, 72 ] rrand(10, 20)
-> Float 3.14159 play(Synth, { ar(Saw,200) })
// Message midicps an eine Instanz:
true.class.postln Tatsächlich sind die Argumente dieser Funktionsaufrufe play und ar können also auch als Funktionen mit
c.midicps.postln; eigentlich Objekte. Deshalb lassen sich diese Ausdrücke
-> True Argumenten aufgefaßt werden
-> [261.626,311.127,369.994,440,523.251] auch durch eine gleichwertige Message-Receiver-
inf.class.postln Schreibweise darstellen: Der Vorteil der Receiver-Notation wird vor allem dann
-> Infinitum ersichtlich, wenn man eine Folge aufeinander bezogener
// Message new an ein Klassenobjekt: // receiver.message Operationen notieren will. Im folgenden Beispiel soll
d = Point.new(10, 50); Betrag des Sinus von 4.3 angezeigt werden. Hier zuerst in
Messages und Methoden 6.sqrt Form von Funktionen notiert:
// Message value an eine Instanz:
Das Verhalten eines Objekts wird in Form von Methoden pi.sin
beschrieben. Eine Methode ist eine klasseninterne d.value.postln; postln(abs(sin(4.3)))
Funktion, die die Reaktion eines Objekts definieren, wenn 60.midicps
-> 11.3214 Die Reihenfolge der Operationen - und damit der Aufbau
das Objekt eine Message gleichen Namens empfängt. "Hallo!".postln
des Ausdrucks selbst - wird aber wesentlich besser lesbar,
Eine Message ist also eine Anforderung an ein Objekt (den 10.rand wenn man die Message-Receiver-Schreibweise verwendet:
Receiver), eine bestimmte Operation auszuführen. Die // Message new an ein Klassenobjekt:
Methoden können interne Zustaende ändern, Werte e = Point.new(100, 122); Allerdings kann eine einzelne Message nicht an 2 Objekte 4.3.sin.abs.postln
berechnen und diese zurückgeben. // Messages x und y an Instanzen: gleichzeitig geschickt werden, wie das z.B. im Falle der min-
Die Operationen werden streng von links nach rechts abge-
Einige Methoden, besonders solche zum Erzeugen einer [ e.x, e.y ].postln; Funktion notwendig wäre. In so einem Fall schickt man die
arbeitet. Man beachte, daß die Message abs nicht etwa an
Instanz, wie z.B. new, sind nur für die Klasse selbst defi- Funktion als Message nur an das erste Objekt. Alle weiteren
sin geschickt wird, sondern an das Ergebnis von
niert. Solche Methoden werden Klassenmethoden ge- -> [ 100, 122 ] Objekte werden als Argumente der Message übergeben:
4.3.sin, also an eine Instanz von Float.
nannt. Typische Klassenmethoden sind neben new auch
ar, kr, play oder record, wie bereits oben gezeigt // receiver.message(arguments) Diese Punkt-Schreibweise hat eine gewisse Ähnlichkeit mit
// Message an ein Klassenobjekt: der Notation einfacher mathematischer Ausdrücke, wie:
wurde. Der Receiver einer solchen Message darf nur ein 6.min(pi)
Klassenobjekt sein. Wäre der Receiver eine Instanz einer Synth.sampleRate.postln;
10.rrand(20) 5 * 3 - 4 / 7
Klasse, würde man in den meisten Fällen eine Fehler- -> 44100
meldung erhalten. Auch hier gilt die Abarbeitung von links nach rechts. Die
// Message an ein Klassenobjekt: Einige Funktionen sollten besser in der Receiver-Notation
Andere Methoden beziehen sich nur auf Instanzen einer übliche Vorrangregel „Punktrechnung geht vor Strich-
f = Synth.new({SinOsc.ar}); geschrieben werden, weil sie so einfacher zu lesen sind.
Klasse und können nicht gleichzeitig auf Klassen bzw. rechnung“ gilt in SC nicht! Es wird also nicht zuerst 5*3
Beispiel: Die round-Funktion rundet ihr 1. Argument auf
Klassenobjekte angewendet werden. Solche Messages // Message an eine Instanz: und 4 / 7 gerechnet und dann subtrahiert! Die tatsächli-
ein Vielfaches des 2. Arguments:
setzen also voraus, daß eine entsprechende Instanz mit f.dump; che Reihenfolge könnte man so darstellen:
Hilfe eines Klassenobjekts und einer Klassenmethode round(1.267, 0.1).postln
-> Instance of Synth { ... } (((5 * 3) - 4) / 7)
bereits erzeugt wurde. -> 1.3
34 SuperCollider SuperCollider 35
Die beiden einzigen Abweichungen von der ansonsten all- verwenden müssen. Dies drückt sich auch in der üblichen Eine Übersicht über Operatoren für Zahlen erhält man im Ein Bag ist eine ungeordnete Menge von Objekten:
gemein gültigen Reihenfolge „links vor rechts“ sind: Darstellung als Flußdiagramm aus: Helpfile für SimpleNumber. Für listenähnliche Objekte
b = Bag[ 1, 2, 3, 4, 3, 2 ];
1. geklammerte Ausdrücke enthält die Hilfe zu SequenceableCollection eine
b.dump;
Aufzählung solcher Operatoren. BinaryOpUGen und
2. Funktionen bzw. Messages -> Instance of Bag { ... }
UnaryOpUGen haben ebenfalls Helpfiles. Eine kurzge-
Beide werden vorrangig behandelt. faßte Übersicht findet man auch am Anfang der b.size.postln;
Hier werden zuerst 5*3 und die Wurzel von 2 berechnet, [UGen_Ref_Sheet] - Liste. -> 6
dann werden beide Ergebnisse und 100 addiert und die
Summe anschließend angezeigt: Collections, Arrays, Listen und Iterationen Ein Set ist eine ungeordnete Menge von Objekten, die kei-
Im Verlaufe dieses Kurses sind wir schon mehrfach auf ne gleichen Objekte enthält. Aus diesem Grunde ist im fol-
(100 + (5 * 3) + 2.sqrt).postln listenähnliche Objekte gestoßen, zum Beispiel: genden Beispiel die Größe von c nicht 6 sondern 4, da nur
das Signal aus dem vorangegangenen Abschnitt über 4 unterschiedliche Elemente vorhanden sind:
Binäre und unäre Operatoren Soundfiles ist eigentlich ein FloatArray, also ein Array c = [ 1, 2, 3, 4, 3, 2 ].asSet;
Auch die einfachen mathematischen Operatoren +, -, * von Fließkommazahlen, c.dump;
und / sind eigentlich Funktionen bzw. Messages. Sie kön- der Datenteil eines mehrkanaligen Soundfiles wird von SC -> Instance of Set { ... }
nen allerdings nicht in einer der beiden oben besproche- als Array von Signals aufgefaßt,
nen Schreibweisen notiert werden. c.size.postln;
im Abschnitt über Controls haben wir ein Array mit MIDI-
-> 4
Diese Operatoren werden binäre Operatoren genannt, weil In SC gibt es eine beeindruckende Fülle von nützlichen Notennummern für einen Sequencer benutzt,
sie 2 Ausdrücke miteinander verknüpfen. Binäre Operatoren für UGens, die eine sehr effiziente Lösung ver- im Abschnitt über Multichannelexpansion wurde ein Array Eine andere Methode ist z.B. add, mit der ein neues
Operatoren werden in der Regel mit Sonderzeichen, wie schiedener Probleme erlauben. Zum Beispiel läßt sich mit mit Zufallswerten für die Frequenz eines Saw generiert. Element der Gruppe hinzugefügt werden kann.
! @ % & * - + = | < > ? / dargestellt. dem binären Operator trunc ein einfacher LoFi- Arrays sind nur eine von mehreren möglichen Daten-
Quantisierer konstruieren, mit dem man die Auflösung bzw. strukturen, mit denen gleichartige oder verschiedene a = [ 1, 2, 3, 4, 3, 2, 1, 2 ];
a + b // Summe
Sample-Wortbreite von 16bit auf geringere Werte dyna- Objekte auf unterschiedliche Weise zusammengefaßt wer- a.add(7);
c > d // ist c groesser als d ? misch reduziert kann: den können. Beispielsweise sind ungeordnete Mengen in a.postln; // a ist ein Array!
e % f // e modulo f der Mathematik etwas ganz anderes als Listen: erstere ent- -> [ 1, 2, 3, 4, 3, 2, 1, 2 ]
( // Quantisierung eines Sinustons
halten Elemente ohne irgendeine Reihenfolge oder Hier-
g ** h // g hoch h
Synth.scope({ archie, letztere bestehen aus einem ersten Element und b = [ 1, 2, 3, 4, 3, 2, 1, 2 ].asBag;
SinOsc.ar(200, 0, 0.9) einem Zeiger auf eine Subliste, die wiederum aus einem b.add(7);
In SC können Funktionen, die 2 Argumente erwarten, auch
ersten Element und einer Subliste besteht usw.
als binary operators notiert werden, indem dem Bezeichner trunc: b.postln;
ein Doppelpunkt nachgestellt wird: Das allgemeinste Objekt zum Gruppieren anderer Objekte -> Bag[ 1, 1, 2, 2, 2, 3, 3, 4, 7 ]
(2 ** MouseY.kr(0, -15));
in SUPERCOLLIDER ist die Collection.
10 rrand: 20 }, 0.01) c = Set[ 1, 2, 3, 4, 3, 2, 1, 2 ];
Collection ist eine abstrakte Klasse, d.h. von ihr kann
) keine Instanz gebildet werden. Collection dient ledig- c.add(7);
6 min: pi
lich zum Deklarieren allgemeiner Methoden für die Sub- c.postln;
Die Position auf der MouseY-Achse gibt hier die 2er- klassen von Collection. Subklassen von
Die (mathematischen) Funktionen, die nur ein Argument -> Set[ 1, 2, 3, 4, 7 ]
Potenz an, die als Quantisierungsraster des Truncation- Collection sind z.B. Set, Bag, Array, Signal,
erwarten, heißen unäre Operatoren.Also sind sqrt, sin,
Operators verwendet wird. Bei einem MouseY-Wert von Wavetable, List u.a.
abs, rand usw. unary operators. d = List[ 1, 2, 3, 4, 3, 2, 1, 2 ];
-2 ergibt sich beispielsweise eine Genauigkeit von 1/4 für
Alle Subklassen von Collection kennen z.B. eine d.add(7);
Binäre und unäre Operatoren sind nicht nur für einfache die Momentanwerte einer Wellenform, d.h. wir erhalten 4
Methode size, die die Anzahl der Elemente in einer d.postln;
Objekte, wie Zahlen, oder für zusammengesetzte Objekte, positive und 4 negative Stufen, also ingesamt 8
Instanz ermittelt.
wie Arrays, definiert. Sie gelten auch für UGens: hier wer- Quantisierungsstufen (3 bit): -> List[ 1, 2, 3, 4, 3, 2, 1, 2, 7 ]
den sie selbst unit generators, sogenannte BinaryOp- Ein Array ist eine nicht erweiterbare geordnete Menge
UGens bzw. UnaryOpUGens.
von Objekten: Bei Array funktioniert das Hinzufügen von 7 nicht, weil
Arrays nicht ohne weiteres vergrößert werden können!
Das Plus-Zeichen in diesem Beispiel erzeugt einen UGen, a = [ 1, 2, 3, 4, 3, 2 ];
Die Reihenfolge der Objekte in Set und Bag ist nicht die
der beiden Sinussignale addiert. a.dump; gleiche, wie bei der Deklaration! Dagegen ist List erwei-
-> Instance of Array { ... } terbar und enthält alle Elemente in ursprünglicher Reihen-
SinOsc.ar(200) + SinOsc.ar(233)
a.size.postln; folge. Es existieren viele in dieser und ähnlicher Weise spe-
In älteren Synthesesprachen, wie z.B. MusicV, hätte man zialisierte Subklassen von Collection.
-> 6
an dieser Stelle tatsächlich ein besonderes add-Modul
36 SuperCollider SuperCollider 37
Die Subklassen von Collection sowie die von ihnen Auf ein einzelnes Element eines Arrays, einer Liste oder Dazu noch 2 weitere Beispiele mit Soundfiles.
abgeleiteten weiteren Subklassen kann man sich durch die einer anderen geordneten Collection kann mit der Das nachfolgende Patch schreibt 50 durchnumerierte
Message dumpClassSubtree anzeigen lassen: Message at(index) zurückgegriffen werden, wobei Soundfiles à 20 Sekunden Dauer mit je unterschiedlichen
das erste Element eines Arrays den Index 0 hat. Tonimpulsfolgen auf die Festplatte:
Collection.dumpClassSubtree Mit put(index,item) kann ein Element überschrie-
ben werden: (
(siehe nebenstehende Abbildung ->)
x = Array.series(5, 7, 3); n = Array.rand(50, 80.0, 100.0);
Zu den wichtigsten subclasses von Collection
x.at(2).postln; n.do({arg pitch, nr;
gehören sicherlich Array und List.
x.put(2,333); Synth.write({
Instanzen von Array haben, wie bereits angedeutet wur-
de, eine einmal festgelegte Größe und eignen sich daher x.at(2).postln; SinOsc.ar(pitch.midicps, 0,
nicht für eine dynamische Erweiterung. Es gibt eine Reihe -> 13 Decay2.ar(Dust.ar(20),
interessanter Klassenmethoden, mit denen sich solche -> 333 0.01, 0.2, 0.5))
Instanzen erzeugen lassen: },20,"HD:snd:Schrabbel-" ++ nr)
Mit choose kann ein Element an einer zufälligen Position })
Array.series(5, 7, 3).postln; herausgelesen werden: )
-> [ 7, 10, 13, 16, 19 ]
x = "Dies ist ein String";
Im zweiten Beispiel iteriert do über eine Liste mit File-
Array.geom(5, 2, 1.5).postln; x.choose.postln;
namen. In jedem Schleifendurchlauf wird der jeweilige
-> [ 2, 3, 4.5, 6.75, 10.125 ] x.choose.postln; Header gelesen, die Dauer ermittelt und das File abgespielt:
-> n
Array.rand(5, 50, 80).postln; (
-> e
-> [ 78, 66, 64, 75, 53 ] var path, files;
Array, List, String und andere Collections kennen path = "HD:snd:fx:";
Mit fill(n,function) kann die Regel zum Er- den Konkatenator ++ zum Zusammenfügen:
zeugen des Arrays selbst programmiert werden. Diese files = [ "slap", "bang", "boom",
Regel schreibt man in Form einer Funktion, die als s = ("Alles " ++ "klar" ++ "?"); "clap", "crash"];
Argument den Index des gerade zu erzeugenden Elements s.postln; files.do({arg name;
übergeben bekommt. Das folgende Beispiel erzeugt 5 auf- a = Array.rand(5, 10, 30); var snd, dur;
einanderfolgende Primzahlen und ihre Ordnungszahlen als snd = SoundFile.new;
b = a ++ s.last;
Paare in einem Array beginnend mit Primzahl Nummer 8:
b.postln; snd.readHeader(path ++ name);
p = Array.fill(5, dur = snd.duration;
-> Alles klar?
{ arg i; [i+8, (i+8).nthPrime]; }); ("Playing " ++ name).postln;
-> [ 13, 10, 26, 18, 29, ? ]
p.postln; SoundFile.play(
-> [[8,23],[9,29],[10,31],[11,37],[12,41]] path ++ name, duration: dur);
Für die Bearbeitung aller Elemente einer Collection stehen
});
eine Reihe von Methoden zur Iteration bereit.
Listen, als erweiterbare Collections, lassen sich so wie Arrays )
mit series, geom, rand und ähnlichen Messages erzeu- do wendet die übergebene Funktionen nacheinander auf
gen. Man kann aber auch mit einer leeren Liste beginnen alle Elemente einer Collection an. Um innerhalb der Funk- Ein anderer Iterator ist collect. Diese Methode wendet
und diese später mit add(item) erweitern: tion Zugriff auf das aktuelle Element zu erhalten, übergibt ebenso wie do eine Funktion nacheinander auf alle
do dieser Funktion bei jedem Aufruf 2 Argumente: 1. das Elemente an und sammelt die Resultate der Funktions-
a = List.new; Element selbst, 2. dessen Index (beginnend mit 0): aufrufe in einer neuen Liste:
a.add(5);
h = [pi, 40, "Hi", 3.77]; k = [1,2,3,4,5].collect(
a.add("sc");
h.do({arg item,i; [i,item].postln;}); { arg item, i; item + 10 });
a.add(Array.geom(4, 1, 1 + 5.sqrt/2));
a.postln; -> [ 0, 3.14159 ] k.postln;
[ 1, 40 ] -> [ 11, 12, 13, 14, 15 ]
-> List[5,sc, [1,1.61803,2.61803,4.23607]]
[ 2, Hi ]
[ 3, 3.77 ]
38 SuperCollider SuperCollider 39
SequenceableCollections und ihre Subklassen ver- Filter Hier ein Vergleich zwischen LPF und RLPF: Durch Ringz wird der Umgang mit Resonatoren verein-
stehen darüber hinaus auch unäre und binäre Operatoren, In SUPERCOLLIDER steht eine große Auswahl verschiede- facht, wenn es auf das Zeitverhalten der Filter ankommt:
die ebenfalls auf alle Elemente angewendet werden. Die ner Filter zur Verfügung. Einen (unvollständigen) Überblick
Resultate werden wie bei collect gesammelt: (
erhält man im Helpfile [Filters]. Für die vollständige
Liste schlägt man im [UGen_Ref_Sheet] nach. Synth.play({
[1,2,3,4,5,6].even.postln; var decay;
Neben elementaren Filtern mit 1 und 2 Polen bzw. Null-
-> [false,true,false,true,false,true] decay = XLine.kr(0.01, 2.0, 10.0);
stellen, die eher als Bausteine für eigene DSP-Algorithmen
([1,2,3,4] * 10).postln; betrachtet werden können, finden sich auch eine ganze Ringz.ar(Dust.ar(5), 2000, decay)
Reihe von gebrauchsfertigen Tief-, Hoch- und Bandpässen })
-> [ 10, 20, 30, 40 ] sowie Resonatoren. )
([1,2,3,4] * (10**[0,1,2,3])).postln; Ein (Butterworth-) Tiefpaß hat folgende Parameter:
Das am Ende des vorhergehenden Abschnitts bereits er-
-> [ 1, 20, 300, 4000 ] LPF.ar(in, freq, mul, add) wähnte Objekt Klank stellt eine Resonanzfilterbank dar.
( Ähnlich wie die Oszillatorbank Klang, erwartet Klank
Der Input in des Filters nimmt jegliches Audiosignal auf. Synth.fftScope({ eine Referenz auf ein Array von 3 Arrays mit den Resonanz-
Es existiert neben den hier besprochenen Methoden eine
Vielzahl weiterer Iteratoren und anderer Methoden zum Die Grenzfrequenz freq kann sich theoretisch mit jeder LPF.ar(WhiteNoise.ar(0.3), 5000) frequenzen, den Amplituden und den Abklingzeiten (siehe
Geschwindigkeit ändern, ar oder kr oder konstant. }) Ringz). Auch bei Klank sind die Parameter nicht verän-
Bearbeiten von Collections, über die man sich im Helpfile
zu Collection und dessen Subklassen informieren Jedoch ist bei Filtern in dieser Hinsicht Vorsicht geboten: ) derlich, dafür sind wesentlich mehr Resonatoren erzeug-
kann. wenn die Koeffizienten bzw. Filterfrequenz und Güte sich bar, als mit Resonz bzw. Ringz.
sehr schnell ändern, kann es zu Instabilitäten kommen, die Klank eignet sich gut zum Simulieren resonanter physika-
Einige UGens erwarten als Argumente bzw. Parameter sich z.B. als Übersteuerung äußern können.
Arrays von Objekten statt einfacher Objekte. Einen solchen lischer Objekte:
UGen haben wir mit Mix bereits kennengelernt. Ein ande- Das Butterworth-Bandpaß-Filter besitzt neben der Mitten-
frequenz auch eine Filtergüte bzw. einen Quality-factor, der (
res Objekt, das Arrays als Parameter erfordert, ist die
Oszillatorbank Klang. Im Gegensatz zum SinOsc kön- in SC allerdings immer als Kehrwert rq der Güte angege- var pt, amp, dec, freq, in;
nen deren Frequenzen und andere Parameter nicht dyna- ben werden muß:
n = 20;
misch verändert werden. Dafür lassen sich mit Klang freq = 100.0;
BPF.ar(in, freq, rq, mul, add)
aber wesentlich mehr Oszillatoren in Echtzeit berechnen
pt = Array.fill(n, {arg i;
als mit SinOsc.
rq kann auch als relative Bandbreite verstanden werden: 1.27**(i+1) * (1 + 0.03.rand2) });
Die Werte für die Frequenzen,Amplituden und Phasen wer- je selektiver das Filter arbeiten soll, um so kleiner muß rq amp = Array.rand(n, 0.1, 1.0);
den als Reference auf ein Array von 3 Arrays notiert. sein. Bei einer Mittenfrequenz von 500 Hz und einer
Das Referenzieren ist notwendig, um die Multichannel- ( dec = Array.geom(n, 1.0, 0.8);
gewünschten Bandbreite von 10 Hz muß also 0.02 für rq
expansion zu verhindern, die sonst automatisch beim eingetragen werden: Synth.fftScope({ Synth.scope({
Übergeben von Arrays als Parameter in Kraft treten würde. RLPF.ar(WhiteNoise.ar(0.3),5000,0.1) in = AudioIn.ar(1);
( }) Klank.ar(`[pt*freq, amp/n, dec], in);
( Synth.play({ ) })
var freq, amp, ph; BPF.ar(AudioIn.ar(1), 500, 0.02)
)
n = 100; })
Neben dem Butterworth-Bandpaß gibt es auch den klassi-
freq = Array.rand(n, 200.0, 500.0); )
schen Resonator, der sich von BPF geringfügig in der Fortsetzung folgt
amp = Array.rand(n, 0.1, 1.0) / n; Durchlaßcharakteristik unterscheidet, aber ansonsten die
Der klassische Synthesizer-Tiefpaß kennt auch eine Reso-
ph = Array.rand(n, 0.0, 2*pi); gleichen Parameter besitzt.
nanz. In SC nennt sich das entsprechende Objekt RLPF: Bezugsquellen
Synth.play({ Resonz.ar(in, freq, rq, mul, add) Die SUPERCOLLIDER Homepage:
RLPF.ar(in, freq, rq, mul, add)
Klang.ar(`[freq, amp, ph]); www.audiosynth.com
}) Die Bandbreite eines Resonators kann auch durch die
Die Parameter sind genau die gleichen wie beim Bandpaß. Weitere Informationen, Beispiele, Tutorials etc:
) Abklingzeit determiniert werden. Ringz (ringing filter)
Im Unterschied zum normalen Tiefpaß gibt es beim reso- swiki.hfbk-hamburg.de:8080/
stellt hierfür einen entsprechenden Zeitparameter (in
nanten Tiefpaß bei der Grenzfrequenz eine Anhebung im MusicTechnology/6
Ein ähnliches Objekt, Klank, lernen wir im nächsten Sekunden) anstelle der inversen Güte rq bereit.
Frequenzgang, die umgekehrt proportional zum rq ist.
Abschnitt über Filter kennen.
Ringz.ar(in,freq,decaytime,mul,add) Andre Bartetzki
42 SuperCollider SuperCollider 43
So eine Filterantwort auf einen Impuls kann als Grain auf- (
S u p e r C o l l i d e r gefasst werden, wobei hier keine Grains überlagert werden
Synth.record({
müssen, wie das bei FOF der Fall ist, da bei Formlet das
Einführung in die Programmierung, Teil 5 Filter immer nur neu angestoßen werden muß. Mix.ar(

Andre Bartetzki Formlet.ar(in, freq, attacktime, Formlet.ar(


abart@snafu.de decaytime, mul, add) Impulse.ar(

Die zeitliche Dichte der Impulse, die als Input übergeben // Grundtonsteuerung:
Dieser mehrteilige Einführungskurs zum Softwaresynthe-
sizer SUPERCOLLIDER (SC) begann in den Mitteilungen werden, bestimmt den Tonhöheneindruck, während freq XLine.kr(2.0, 200.0, 6),
40 und wird an dieser Stelle fortgesetzt. die Mittenfrequenz des Formanten steuert. Ein- und
Ausschwingzeit beeinflussen schließlich Bandbreite und 0.5),
Informationen zum Download der Version 2 sowie zum
Form des Formanten: lange Zeiten entsprechen geringen // Formantfrequenzen:
Stand der neuen Version 3 für MacOSX siehe im grauen
Bandbreiten und umgekehrt.
Kasten am Ende des Artikels. [750, 1200, 2800, 3800],
Zur Illustrierung dieses FOF-Filters nun das klassische
Filter (Fortsetzung) Beispiel einer beschleunigten Tonimpulsfolge, die sich zu // Attack, Decay:
Zum Abschluß des Überblicks über die Filter in SC soll
Die Filtersektion von SUPERCOLLIDER ist sehr vielseitig. einem A-Vokalklang verdichtet. Dazu werden hier 4 paral- 0.005, 0.03,
Formlet besprochen werden. Mit Formlet wird die
Außer den bereits vorgestellten im Bereich der Klang-syn- lele Formlets erzeugt, die mit den Frequenzen und den
bekannte FOF-Synthese, eine Variante der Granular- // relative Amplituden:
these üblichen Tief-, Hoch- und Bandpassfiltern sowie den Amplituden der ersten vier Formanten des Vokals A einer
synthese, in Form eines Filters implementiert. Bei der FOF-
verschiedenen Versionen von Resonatoren finden wir eine durchschnittlichen Tenorstimme versehen werden. [0,-6,-16,-24].dbamp)
Synthese (FOF = Fonction d’ondes formantiques) werden
Reihe von Filtern, die in ihrer aus der Theorie der Signal- Während die Formanten fixiert bleiben, erhöht sich der
synthetische Grains mit spezieller Hüllkurvenform peri- )
verarbeitung stammenden „Rohfassung“ vorliegen. Dazu „Grundton“, d.h. die Frequenz der Impulsfolge, im Verlauf
odisch erzeugt. Die Anzahl der Grains pro Sekunde ergibt
gehören FOS, SOS, OnePole, OneZero, TwoPole, von 6 Sekunden von 2 auf 200 Hertz. }, 6, "ah.snd")
den Grundton des neuen Klangs, während die Frequenz der
und TwoZero, bei denen als Parameter statt Frequenz Sinuswelle im Innern der Grains als Formant gehört wird. Die untenstehende Abbildung enthält ein mit AUDIO- )
oder Filtergüte die Koeffizienten des zugrundeliegenden FOF ist also gut geeignet zur Synthese von Sprachlauten SCULPT angefertigtes Sonagramm des Resultats. Die dun-
linearen Filtermodells angegeben werden müssen. Weitere und anderen Klängen, bei denen es auf die genaue klen horizontalen Linien markieren die Orte der 4 Forman-
Filter stellen Spezialfälle der eben aufgezählten dar - bei Kontrolle von Formanten ankommt. Dieses Verfahren wur- ten. Das rechtsseitige Spektrum zeigt die Verhältnisse bei
ihnen entfallen auch die Koeffizienten als Parameter. Zu de mit der am IRCAM entwickelten Software CHANT etwa 5 Sekunden (an der Position des Mauszeigers).
diesen unveränderlichen Filtern gehören LPZ1 und LPZ2 populär. Ein solcher FOF-Generator steht auch in SC zur
(beides Tiefpässe bzw. Mittelwertfilter), HPZ1 und HPZ2 Verfügung und heißt hier Formant.
(Hochpässe bzw. Differenzfilter) sowie BPZ2 und BRZ2
Mit Formlet werden im Gegensatz zu FOF keine Grains
(Bandpass und -sperre). Andere Filter, wie Integrator
generiert. Vielmehr legt Formlet eine spezielle
(Tiefpaß), Slope, Decay und Median, finden ihre
Impulsantwort fest, die der eines engen Resonators mit
Verwendung eher bei der Modifikation von Controller-
einstellbarer Ein- und Ausschwingzeit entspricht. Diese
signalen und weniger bei der direkten Klangbearbeitung.
Impulsantwort kann nun durch impulsförmige Eingangs-
LeakDC dient zur Entfernung von Gleichspannungsantei- signale „getriggert“ werden. Das Ergebnis sind kurze
len im Signal, ist also eine Art Hochpass. Der Koeffizient Tonimpulse, von McCartney Formlets genannt:
von LeakDC regelt die „Grenzfrequenz“ des Hochpasses:
Werte nahe 1.0 entsprechen einer sehr geringen Grenz-
frequenz, kleinere Werte wirken sich wie höhere Grenz-
frequenzen aus. Das folgende Patch zeigt, wie der auf den
Sinuston addierte Offset von 0.5 (Argument add von
SinOsc) durch LeakDC wieder entfernt wird. Links das
Signal mit Offset, rechts die gleichspannungsfreie Version:

( { var sig;
sig = SinOsc.ar(700, 0, 0.3, 0.5);
[sig, LeakDC.ar(sig, 0.995)]
}.plot)
44 SuperCollider SuperCollider 45
Alle bisher vorgestellten Synthese-Patches hatten ein Da die Beschreibung einer Wellenform noch nichts mit Der hier verwendete Osc1 ist ein spezieller Oszillator, der Endpunkte sowie ihren Kurvenverlauf spezifiziert. Alle
gemeinsames Merkmal, egal im welchen Aspekt von ihrem „Abspielen“ zu tun hat, kann sie außerhalb der anstelle anstelle einer Wavetable-Instanz ein Signal Werte, Zeiten und Kurvenformen werden in je einem Array
Synthese oder Sampling es sich dabei handelte: was ugenGraph function erfolgen, während der Oszillator als erwartet und diese Tabelle nur einmal abspielt. zusammengefasst:
erzeugt wurde, war stets nur ein einziger Klang oder eine UGen natürlich nur in einem Synth funktionieren kann: Als zweites Argument wird anstelle der Frequenz die
Textur von gleichartigen Klängen. Mit Ausnahme von Abspieldauer in Sekunden erwartet. *new(levels, times, curves, ...)
Sequencer im Verbund mit Decay und ähnlichen (
Die Klasse Signal, die wir schon im Zusammenhang mit Betrachten wir eine konkrete Hüllkurve und ihre
Objekten haben wir bisher noch keine Methode kennen- var w; Samples und PlayBuf kennengelernt haben, stellt Spezifikation mit Env.new. Zunächst die grafische
gelernt, mit einem Patch mehrere Klangereignisse nach- w = Wavetable.sineFill(512, einige Fensterformen (wie hamming, hanning oder Repräsentation:
einander oder unabhängig voneinander generieren zu Array.geom(20, 1, 0.5)); welch) für die Fourieranalyse bereit, die auch als Hüll-
können. In gewisser Weise ist uns SUPERCOLLIDER bisher kurven für die Granularsynthese nützlich sein können.
Synth.play({
als ein mit vielen Reglern ausgestatteter Synthesizer bzw.
Sampler erschienen, dem aber eine Tastatur zum Spielen zu Osc.ar(w, 440);
(
fehlen scheint! })
s = Signal.hanningWindow(512);
Die Generierung von Events, seien es mehrere Samples, ) s.plot;
Töne oder Grains, erfolgt in SUPERCOLLIDER mit Hilfe von Synth.scope({
Spawn oder einigen anderen davon abgeleiteten Klassen. Man kann nun solche Wellenformen auch als Amplituden-
a = Osc1.ar(s, 2);
Im engen Zusammenhang mit der Erzeugung von Klang- Hüllkurven benutzen. Dazu muss der Wertebereich ange-
Saw.ar(400, a);
ereignissen steht natürlich die Gestaltung ihrer Hüllkurven passt werden, der sich bei Wellenformen typischerweise
}, 2)
mit Env und EnvGen, die darüber hinaus auch für das symmetrisch um den Nullpunkt von -1.0 bis 1.0 erstreckt,
aber bei Hüllkurven nur von 0.0 bis 1.0 gehen sollte. )
korrekte Funktionieren von Spawn wichtig werden.
Für die zeitliche Organisation und die algorithmische Außerdem wird eine Hüllkurve in der Regel nicht periodisch
wiedergegeben, sondern nur einmal durchlaufen: In runden Klammern sind hier die Koordinaten der
Strukturierung von Events zu musikalischen Ereignissen,
Eckpunkte als Paar (Wert, Zeit) angegeben. Zu beachten
wie Rhythmen, Motive, Melodien, Akkorde usw. steht eine
( ist, daß die Zeit die Dauer eines Segments wiedergibt und
umfangreiche Bibliothek von Pattern zur Verfügung, die
nicht die absolute (von Beginn an gerechnete) Zeit dieses
fast schon so etwas wie eine eigene kleine Sprache inner- w = Wavetable.sineFill(1024, Punktes. Die absolute Zeit ist hier trotzdem zum Vergleich
halb von SC darstellen.Auch die konventionelle Steuerung 1.0/[1,2,3,4,5,6]); unter der Zeitachse eingetragen.
von Events über MIDI-Note-Messages wird von SC unter-
w = w.asSignal.abs; Das Array der Level sieht in diesem Fall so aus:
stützt.
w.plot; [0, 1, 0.2, 0.5, 0]
Hüllkurven und Spawner sind die Voraussetzungen für die
Synth.scope({
Programmierung von Events und Pattern. Beschäftigen wir Das Array der Zeiten enthält nur 4 Elemente - ein
uns daher zunächst mit der Beschreibung und Erzeugung a = Osc1.ar(w, 2); Dauernwert für jedes Segment:
von Hüllkurven bevor wir uns dann Spawn und seinen Saw.ar(400, a); [0.01, 0.04, 0.1, 0.05]
Verwandten zuwenden: }, 2)
Da die Form der Segmente hier gleichbleibend linear ist,
) muß anstelle der Kurvenformliste nur das Symbol
Envelopes
'linear'eingetragen werden.
Die Arbeit mit Hüllkurven ist in SC ein zweistufiger
Prozess: zuerst muß die Form einer Hüllkurve beschrieben Die Spezifikation dieser Hüllkurve sieht nun so aus:
Diese Technik ist bei anderen Syntheseprogrammen, wie
werden, erst dann kann sie gestartet und ihre Abspiel- e = Env.new([0,1,0.2,0.5,0],
z.B. CSOUND, häufig anzutreffen. Allerdings ist der
parameter eventuell modifiziert werden.
Umgang mit solcherart zu programmierenden Hüllkurven [0.01,0.04,0.1,0.05], 'linear');
Eine ähnliche Zweiteilung findet man übrigens beim ungewohnt, vor allem, wenn man an die klassische ADSR-
Wavetable-Oszillator: hier muss zunächst die Wellenform Segmentstruktur denkt. Außerdem fehlen auf diese Weise Die Klasse Env kennt 2 Messages, mit denen Hüllkurven an
konstruiert und in einer Tabelle abgelegt werden. In SC einige für das Echtzeitspiel besonders wichtige Eigen- Ort und Stelle getestet werden können: plot und test.
gibt es dafür eine eigene Klasse Wavetable: schaften, wie die Bestimmung von Release-Phasen oder Für die eben spezifizierte Hüllkurve e ruft e.plot das fol-
geloopten Segmenten, völlig. Deshalb bietet SC eine eige- gende Fenster auf:
w = Wavetable.sineFill(512,
ne Klasse zur Spezifikation von segmentierten Hüllkurven
1 / [1,2,3,4,5,6]); sowie eine Klasse zu ihrem Abspielen an.
Dann kann diese Wellenform innerhalb eines Synth von Diese Spezifikation erfolgt über das Objekt Env. Dazu wird
einem Audio- oder Controlrate-Oszillator benutzt werden: die Message new zusammen mit mehreren Arrays, die die
Segmente beschreiben, an das Klassenobjekt Env
Osc.ar(w, 440); geschickt. Die Segmente werden durch Wert und Zeit ihrer
46 SuperCollider SuperCollider 47
Die Message test „spielt“ die Hüllkurve mit einem Mit den Zahlenwerten der curvature können beliebig stark Env.new kennt also letztlich 5 Argumente: levels, Soll die Hüllkurve die Amplitude eines Audiosignals steu-
800 Hz Sinuston ab. konkav oder konvex gekrümmte Verläufe erzeugt werden. times, curves, releaseNode und loopNode. ern, ordnet man dieses Audiosignal typischerweise dem
Für den Verlauf der Segmente stehen verschiedene Kurven- Der Wert 0 entspricht einem linearen Verlauf. Um den Umgang mit den Arrays und der Fülle der Para- mul-Eingang von EnvGen zu:
formen bereit, die entweder durch Symbole oder durch meter etwas zu erleichtern, wurden für Env eine Reihe von
Env([0,1,0],[2,1], -2).plot; ( var env, dur;
Zahlen spezifiziert werden. Hier die Bilder einer einfachen weiteren Messages programmiert, die einfache Standard-
2-teiligen Hüllkurve mit den unterschiedlichen Formen: verläufe in die für Hüllkurven notwendige times- und dur = 2.0;
levels-Arraystruktur nach Art eines Makros übersetzen. env = Env.perc(0.01, dur);
Env([0,1,0],[2,1],'linear').plot; Synth.play({
Um zum Beispiel einen Klang am Beginn sowie am Ende
nur kurz ein- bzw. auszublenden, var sig;
sig = Saw.ar(500);
Env([0,1,0],[2,1], 5).plot; EnvGen.ar(env, sig);
})
)

Bei exponentiellen Hüllkurven dürfen keine Level mit dem Da bei einer Multiplikation die Faktoren vertauscht werden
Wert 0 vorkommen, außerdem müssen alle Level das glei- können, kann man es äquivalent auch so schreiben:
che Vorzeichen haben:
( var env, dur;
Env([0.01,1,0.01],[2,1],'exponential').plot;
dur = 2.0;
Mehrere dieser Zahlen können zusammen in einem Array
übergeben werden, so daß jedes Segment eine eigene env = Env.perc(0.01, dur);
Krümmung aufweisen kann: Synth.play({
müsste man in etwa folgendes schreiben: var amp;
Env.new([0,1,0.3,0.8,0],[2,3,1,4],
amp = EnvGen.ar(env);
[0,-5,3,-3]).plot; dur = 10; // Gesamtdauer
Saw.ar(500, amp);
e = Env.new([0,1,1,0],
})
Env([0,1,0],[2,1],'sine').plot; [0.1,dur-0.3, 0.2], ‘linear’);
)
Mit Hilfe der Message linen läßt sich das Gleiche aber
kürzer formulieren: Der einzige Unterschied zwischen diesen beiden Methoden
besteht darin, daß im letzten Fall der Hüllkurvengenerator
dur = 10; // Gesamtdauer EnvGen durchaus auch mit Controlrate kr betrieben
Ein Teil der Segmente lassen sich der Release-Phase eines e = Env.linen( 0.1, dur-0.3, 0.2); werden könnte. Auf diese Weise ließe sich in kritischen
Klanges zuordnen. Dazu gibt man nach dem Kurvenwert Echtzeitanwendungen etwas CPU-Leistung einsparen.
Wie man sieht, muß man nur die Zeiten der 3 Phasen
bzw. -array einfach die Nummer der Knotenpunktes an, ab Env und EnvGen können selbstverständlich auch zur
Env([0,1,0],[2,1],'welch').plot; (Einschwingen, Haltephase, Ausschwingen) angeben. Alle
dem die Abklingphase beginnen soll. Die Zählung der Steuerung anderer Parameter herangezogen werden, z.B.
anderen Werte werden von linen selbst erzeugt!
Punkte beginnt mit 0. Falls ein Release-Knoten angegeben für die Tonhöhe:
wurde, wird die Hüllkurve beim Abspielen nur bis zu diesem triangle, sine, perc, adsr und asr erzeugen
Punkt durchlaufen, dann wird der letzte Wert beibehalten weitere häufig verwendete Hüllkurvenverläufe. Diese
( var mod, dur;
bis der Synth oder der Spawn, die die Hüllkurve enthal- Methoden und ihre Argumente findet man ausführlich im
dur = 2.0;
ten haben, eine Release-Message erhalten - erst dann wird Helpfile von Env beschrieben.
mod = Env.new([0, 24, 0],
der Rest der Hüllkurve abgearbeitet. Solche Release-
Messages werden z.B. bei der Steuerung durch MIDI-Note- Wie bereits angedeutet wurde, existiert für das Generieren [dur/2, dur/2], [6, -6]);
Env([0,1,0],[2,1],'step').plot; Messages oder durch Event-Pattern erzeugt. bzw. „Abspielen“ von mit Env spezifizierten Hüllkurven
Synth.play({
ein spezieller Unit Generator: EnvGen.
Schließlich kann auch noch ein Loop-Knoten festgelegt var pitch;
werden. Gelangt eine Hüllkurve an den Release-Knoten EnvGen, der als UGen nur innerhalb eines Synth ange-
pitch = EnvGen.kr(mod, 1, 69);
und wurde ein Loop-Knoten spezifiziert, werden die legt werden kann, erwartet als erstes Argument eine Env-
Spezifikation, danach folgen mul, add, einige Modi- Saw.ar(pitch.midicps)
Segmente zwischen diesen beiden Knoten in einer Schleife
fikationsparameter für die Hüllkurve und ein gate-Input })
solange abgespielt, bis eine Release-Message eintrifft.
zum Triggern und Re-Triggern der Hüllkurve. )
48 SuperCollider SuperCollider 49
Unter Zuhilfenahme der anderen EnvGen-Parameter Spawn können diese Werte deshalb auch nicht als nextTime Jetzt die Einsatzabstände ebenfalls variabel, aber leider
levelScale, levelBias und timeScale kann Sehen wir uns die do-Schleife des letzten Beispiels noch eintragen, zumal es mehrere Werte wären und nicht nur ein immer noch nicht mit den Dauern synchronisiert!
eine per Env beschriebene Hüllkurve nachträglich skaliert, einmal an: Es gibt eine ugenGraph function, die n-mal für einziger! Die Parameter numChannels, nextTime, Bevor wir eine Lösung für dieses Problem finden, soll noch
vertikal verschoben und zeitlich gedehnt bzw. gestaucht eine bestimmte Dauer aufgerufen wird. Gleichzeitig wird maxRepeats haben mit der übergebenen Funktion einmal genauer betrachtet werden, was das „Ausbrüten“
werden. Letzteres ermöglicht es zum Beispiel, Hüllkurven festgelegt, wann die nächste Wiederholung sein wird. nichts zu tun, es sind von ihr unabhängige Werte. Als von Events durch Spawn eigentlich bedeutet. Gehen wir
zunächst mit einer abstrakten Gesamtdauer von 1.0 zu „Notlösung“ wurde deshalb vorerst ein konstanter Wert sogar noch einen Schritt zurück: was macht ein Synth?
Dies sind auch die Eigenschaften, die das Objekt Spawn
definieren, und dann später beim Abspielen auf die von 0.5 Sekunden für die nextTime gewählt. Das Helpfile zu Synth gibt folgende Auskunft: „A Synth
auszeichnen:
gewünschte kürzere oder längere reale Dauer zu skalieren: Wenn man nun diesen Wert verkleinert, z.B. auf 0.3, kann is a container for one or more unit generators that execu-
Spawn.ar(newEventFunc, numChannels, man deutlich bemerken, daß sich einige Events überlap- te as a group“. Unter welchen Bedingungen diese Gruppe
( var e;
nextTime, maxRepeats, mul, add) pen! Spawn startet also die Events sequentiell, aber je von UGens ausgeführt wird, hängt von der aufrufenden
e = Env.sine(1.0); nach ihrer Dauer können sie sich überlagern oder ein Pause Message an Synth ab: play erzeugt eine Instanz von
15.do({ Spawn „brütet“ eine bestimmte Anzahl (maxRepeats) lassen. In unserem Beispiel sieht das Möglichkeitsfeld für Synth und spielt das Ergebnis hörbar ab, scope macht
var dur, freq; neuer Events gemäß der newEventFunc nach jeweils Dauern und Einsatzabstände so aus: es zusätzlich sichtbar, write schreibt es offline auf die
dur = rrand(0.1, 0.7); nextTime Sekunden aus und mischt die enstandenen Festplatte usw.
freq = rrand(500.0, 1000.0); ein- oder mehrkanaligen ( numChannels) Audiosignale Diese „Container“ können auch verschachtelt werden:
Synth.play({ in den Output. Spawn ist also ein ganz besonderer unit
generator, der weder Klänge generiert, wie etwa ein (
EnvGen.ar(e, Saw.ar(freq),
Oszillator, noch Klänge bearbeitet, wie etwa ein Filter oder Synth.play({
timeScale: dur) ein Delay. Vielmehr erfüllt Spawn eigentlich die Aufgaben,
}, dur + 0.2) die bisher ein Synth übernommen hat: komplexe Klang- Synth.play({SinOsc.ar(300)}, 0.5);
}) ereignisse gemäß einer ugenGraph function zu erzeugen. Synth.play({
) Trotzdem muß das Spawn-Objekt selbst auch in einem Resonz.ar(
Synth installiert werden.
Saw.ar(350),
Das vorige do-Beispiel mit Hilfe von Spawn formuliert,
Dieses Beispiel zeigt auch, auf welche Weise es möglich ist, XLine.kr(2000.0, 300.0, 1.5))
könnte in etwa so aussehen:
in SUPERCOLLIDER mehrere Klangereignisse nacheinan-
der zu erzeugen. do haben wir bereits im Abschnitt über }, 1.5);
( var e;
Collections und Iterationen kennengelernt. Hier sehen wir, Impulse.ar(20, 0.2);
daß do auch über ganze Zahlen iterieren kann: do zählt e = Env.sine(1.0);
}, 3.0)
dabei von 0 bis zum Wert-1 des Receivers. Synth.play({
Es ist allerdings auch erlaubt, als nextTime eine )
Diese Methode ist allerdings reichlich unflexibel, da sie nur Spawn.ar({
Funktion anstelle eines konstanten Wertes einzusetzen. In
streng sequentielle Abläufe gestattet, denn der nächste var dur, freq; Dabei wird auch hier die Audiofunktion des äußeren
diesem Fall wird bei jedem neuen Event diese Funktion auf-
Schleifendurchlauf wird erst dann gestartet, wenn alle da- Synthesizers Schritt für Schritt abgearbeitet. Das heißt,
dur = rrand(0.1, 0.7); gerufen und das Ergebnis als neue nextTime verwen-
rin enthaltenen Programmschritte abgearbeitet worden zuerst wird ein neuer Synth erzeugt, dessen Audio-
freq = rrand(500.0, 1000.0); det. Im folgenden Beispiel wurde die Zahl 0.5 durch eine
sind, also erst, wenn der aktuelle Synth nach dur+0.2 funktion evaluiert, gemäß der Message play gespielt und
EnvGen.ar(e, Saw.ar(freq), solche Funktion ersetzt:
Sekunden gestoppt wurde. nach 0.5 Sekunden wieder deinstalliert. Dann wird wieder
Besser wäre es, wenn die Events, d.h. die einzelnen timeScale: dur)
ein neuer Synth, diesmal mit einer komplexeren Audio-
Synth-Objekte, zeitlich unabhängig voneinander erzeugt }, 1, 0.5, 15) ( var e;
funktion aus 3 unit generators für eine gewisse Zeit
werden könnten. Genau für diesen Zweck wurde die Klasse }) e = Env.sine(1.0); gespielt. Schließlich wird ein normaler Impulsgenerator im
Spawn geschaffen - eines der leistungsfähigsten ) Synth.play({ Rahmen des äußeren Synth erzeugt und für 3 Sekunden
Konzepte in SUPERCOLLIDER. Spawn.ar({ gespielt. Wie in der do-Schleife, werden auch hier die
Dabei gibt es aber ein entscheidendes Problem: in der inneren Synth-Instanzen sequentiell abgearbeitet, wobei
var dur, freq;
ursprünglichen do-Schleife waren Dauer und Einsatz- mit dem Ende eines Synth auch die von ihm aufgerufe-
dur = rrand(0.1, 0.7); nen unit generators „ausgeschaltet“ werden und somit die
abstand synchronisiert. Nach verstrichener Dauer soll also
das nächste Event erzeugt werden. Aber in unserem freq = rrand(500.0, 1000.0); CPU nach getaner Arbeit nicht mehr belasten.
Spawn-Beispiel wird die Dauer der Hüllkurve im Inneren EnvGen.ar(e, Saw.ar(freq),
der Audiofunktion per Zufall generiert, während die timeScale: dur)
Angabe der nextTime außerhalb dieser Funktion als 3. }, 1, {rrand(0.1, 0.7)}, 15)
Argument von Spawn.ar erfolgen soll. Wir können aber
})
vorher die einzelnen Dauern noch gar nicht kennen und
)
50 SuperCollider SuperCollider 51
Die beim Ausführen von Synth-Instanzen erzeugte Erst der Einsatz einer Hüllkurve mit bestimmter Dauer kann die Um den gezielten Zugriff auf die Eigenschaft einer Instanz zu Ein Beispiel für die Verwendung des eventCounts:
Informationsleiste ermöglicht die Beobachtung der wech- von Spawn installierten Synthesizer auch wieder entfernen: ermöglichen, sollte dieses Objekt bei der Erzeugung
selnden Anzahl von UGens, je nachdem, welcher Synth benannt werden: (
zur Zeit aktiv ist: ( Synth.play({
Synth.play({ a = List[3,4,5,6];
Spawn.ar({ arg sp, i, sy;
Spawn.ar({ a.size.postln;
EnvGen.ar(Env.sine(0.2),
EnvGen.ar( -> 4 Saw.ar(i*200 + 200, 0.2))
Env.perc(0.01,rrand(0.1,2.0)), }, 1, 0.1, 24)
Damit auch in einer ugenGraph function die Instanz des
SinOsc.ar(rrand(3.0,5.0)*1000, aufrufenden Spawn oder Synth bekannt ist, wird diese }))
0, 0.1)) Instanz der Audiofunktion als Argument übergeben. Dabei
}, 1, {rrand(0.1, 0.3)}, nil) kann der Name dieses Arguments frei gewählt werden. Es soll an dieser Stelle noch einmal betont werden, daß
Kehren wir nun zurück zu Spawn. Die eigentlich Aufgabe nicht der Name des Arguments für dessen Bedeutung
von Spawn ist es, das Erzeugen solcher Synth-Instanzen }) Wie bei allen Funktionen erfolgt die Angabe der
Argumente zu Beginn der Funktion nach dem reservierten wichtig ist, sondern nur die Position dieses Names in der
in einer Art Schleife zu automatisieren und gleichzeitig )
Bezeichner arg: Deklaration der Argumente, d.h. nach dem Wort arg! Die
deren parallele Abarbeitung zu erlauben, indem die von Deklaration der Argumente kann außerdem nur unmittel-
ihnen erzeugten Audiosignale gemischt werden. Die von Ist eine Hüllkurve an ihrem Ende angelangt, wird der
Synth.play({ arg synth .... bar zu Beginn einer Funktion erfolgen. Direkt danach kön-
Spawn erzeugten Events, von denen bisher gesprochen „gespawnten“ Synth-Instanz ebenfalls das Ende signali-
siert. Daraufhin werden die von diesem Synth aktivierten nen sich dann eventuelle Variablendeklarationen ansch-
wurde, sind also eigentlich dynamisch erzeugte lokale Spawn.ar({ arg spawn .... ließen. Sinnvollerweise vergibt man für diese Argumente,
Synth-Instanzen, die alle die gleiche Audiofunktion UGens deinstalliert und der Synth gestoppt. Spawn soll-
Der erste Programmschritt in der folgenden Audiofunktion wie für alle Variablen, aussagekräftige Bezeichnungen.
besitzen - nämlich die, die dem Spawn als 1. Argument te also immer im Zusammenhang mit einer Amplituden-
druckt den Wert einer Variablen einer Synth-Instanz, Deshalb wurde im letzten Beispiel für eventCount das
übergeben wurde. Spawn ist also ein Art Maschine, die hüllkurve betrieben werden!
während in der darauffolgenden Zeile der Wert einer für Schleifenvariablen typische i gewählt und für die bei-
Synth-Objekte herstellt und sie mit Kopien ein und der- Auf den ersten Blick mag es als merkwürdige Umkehrung Variablen der Klasse Synth gedruckt wird. den Instanzenargumente entsprechende Abkürzungen.
selben Audiofunktion ausstattet. von Zuständigkeiten erscheinen, daß ein Hüllkurven-
generator einer ugenGraph function den Synthesizer, der ( Da wir nun innerhalb der Audiofunktion auch auf die
Die so erzeugten Synth-Instanzen kennen allerdings kein Spawn-Instanz zugreifen können, sind wir jetzt in der
Dauernargument, wie es bei der Message play existiert. die in dieser Funktion enthaltenen Generatoren ja erst ins Synth.play({ arg synth;
Leben ruft, beenden kann! In SUPERCOLLIDER stellt Lage, auch deren nextTime-Variable zu verändern!
Stattdessen muß in der Audiofunktion eine Hüllkurve auf- synth.blockSize.postln; Kommen wir also zu unserem ersten Beispiel zurück und
gerufen werden, nach deren Durchlauf die jeweilige jedoch der Zugriff auf Eigenschaften von „Mutter-
objekten“ wie Synth und Spawn ein weiteres wichtiges Synth.sampleRate.postln modifizieren es entsprechend, um Dauer und Einsatz-
Synth-Instanz terminiert wird. abstand zu synchronisieren:
und leistungsfähiges Konzept dar. Innerhalb einer Audio- WhiteNoise.ar(0.3)
Startet man das folgende Patch
funktion können damit eine ganze Reihe von Eigen- }, 4.0) ( var e;
schaften der aufrufenden Synth- oder Spawn-Instanzen
( ) e = Env.sine(1.0);
gelesen oder verändert werden.
Synth.play({ -> 128 Synth.play({
Hierbei muss man zwischen Eigenschaften der gesamten
Spawn.ar({ Klasse, sogenannten Klassenvariablen, und Eigenschaften -> 44100 Spawn.ar({ arg sp, i, sy;
Impulse.ar(rrand(1.0, 7.0), 0.2) der Instanzen, den Instanzenvariablen, unterscheiden. Auf var dur, freq;
erstere kann direkt zugegriffen werden, ohne daß man vor- Die Deklaration von Argumenten in der ugenGraph func- dur = rrand(0.1, 0.7);
}, 1, 3, nil) tion ist optional: wenn sie erfolgte, kann man auf den Wert
her eine Instanz dieser Klasse erzeugt hat:
des Arguments zugreifen, wenn man dagegen kein Argu- sp.nextTime = dur + 0.2;
})
Synth.hardwareName.postln ment angegeben hat, kann man es auch nicht verwenden! freq = rrand(500.0, 1000.0);
)
-> Apple Sound Manager Spawn übergibt seiner newEventFunc gleich 3 sol- EnvGen.ar(e, Saw.ar(freq),
und beobachtet gleichzeitig in der Informationsleiste die cher Argumente: timeScale: dur)
Anzahl der UGens, wird man feststellen, daß nach jeweils Synth.sampleRate.postln }, 1, nil, 15)
spawn - das 1 Argument ist die Instanz von Spawn, die
3 Sekunden (nextTime) ein neuer Impulsgenerator hin- -> 44100 durch Spawn.ar generiert wurde. })
zukommt. Da die Variable maxRepeats auf nil gesetzt
eventCount - das 2. Argument ist die Nummer des )
ist, da also unbegrenzt viele Synths „gespawnt“ wer- hardwareName und sampleRate sind also Eigen-
erzeugten Events, beginnend bei 0 für das erste Event.
den, ist die Überlastung der CPU nach einer gewissen Zeit schaften der Klasse Synth und somit allen Synth- Jedes Event kann dem Eventgenerator also mitteilen, wann
unvermeidlich! Dieses Patch sollte also spätestens beim Instanzen gemeinsam. synth - das 3. Argument ist die Instanz des von Spawn
das jeweils folgende Event starten soll. Damit lässt sich
Überschreiten der 90%-Marke der CPU-Auslastung mit erzeugten lokalen Synth-Objektes
zum Beispiel die folgende gleichzeitig von inneren und
[CMD-.] abgebrochen werden. äußeren Bedingungen abhängige Regel für Einsatz-
abstände implementieren: falls die zufällig erzeugte
52 SuperCollider SuperCollider 53
Frequenz einen bestimmten Wert überschreitet, folgt der EnvGen.ar( In SUPERCOLLIDER verwirklicht OrcScore dieses
nächste Ton nach 20 ms, ansonsten wird der Abstand zum Env.perc(0.01,dur), Prinzip durch zwei zu übergebende Listen: eine Liste mit
nächsten Ton von der aktuellen Mausposition bestimmt. PanAz.ar( „Instrumenten“, d.h. ugenGraph functions, und eine Liste
nchan, mit Events, die jeweils wiederum durch eine Liste mit
( RLPF.ar( Einsatzabstand, Instrumentennummer bzw. -name, Dauer
Synth.play({ Mix.ar( und beliebigen weiteren Parametern beschrieben werden,
m = MouseX.kr(1.0, 0.1); ähnlich den pfields in CSOUND.
Saw.ar(
Spawn.ar({ arg sp, i, sy; [freq, freq*rrand(0.9,1.1)], OrcScore.ar(orchestra, score, numChannels,
var dur, next, freq; amps.at(i) / 4)), nextTime, maxRepeats, mul, add)
dur = rrand(0.1, 0.4); XLine.ar(
Jedem „Instrument“ im orchestra müssen dabei nicht
freq = rrand(40.0,100.0).midicps; freq*2, freq/2, dur/2),
nur die normalen Spawn-Argumente spawn, i und
if(freq > 1000, rqs.at(i)), synth übergeben werden, sondern auch der Einsatz-
{ next = 0.02 }, pan)) abstand, die Nummer bzw. das Symbol für eben dieses
}, nchan);
Instrument sowie die anderen optionalen Parameter, die
{ next = m.poll });
für dieses Instrument Verwendung finden sollen, z.B.
sp.nextTime = next; }))
Dauer, Frequenz, Amplitude usw. Diese Parameter werden
EnvGen.ar(Env.perc(0.01, dur), }) dann aus der Score-Liste dem Instrument beim Aufruf über-
Saw.ar(freq, 0.2)) ) geben.
}, 1) Im Folgenden soll nur kurz die Struktur dargestellt werden.
In diesem Patch werden 3 Spawn-Objekte erzeugt, deren Für vollständige Beispiele sei auf das Helpfile zu
}) Events aus je 2 gegeneinander leicht verstimmten Oszilla- OrcScore sowie auf mitgelieferte Examples, wie z.B. in
) toren bestehen, die durch einen resonanten Tiefpass dyna- der Datei „time structures“, verwiesen.
Da das Objekt Spawn sich auch wie ein normaler UGen Parallele Eventstreams misch gefiltert, anschließend quadrophon positioniert und
verhält, kann eine Spawn-Instanz auch mit anderen mit einer perkussiven Hüllkurve versehen werden. Durch (
Ebenso können mehrere Spawner „parallel“ aktiviert wer-
UGens verknüpft werden. Hier wird ein Strom von Events die unterschiedlichen Werte für Frequenzbereich, Dauer, Synth.play({
den, um voneinander unabhängige Eventströme zu erzeu-
durch ein interaktiv gesteuertes Filter geleitet: Amplitude und Filtergüte, die aus jeweils 3-elementigen OrcScore.ar(
gen. Für die Ausgabe müssen diese dann selbstverständlich
Listen kommend mit Hilfe der collect-Schleife den drei // orchestra array:
noch zusammengemischt werden, wie im folgenden
( Spawns zugeordnet werden, werden diese 3 Eventströme [
Beispiel.
Synth.scope({ klanglich differenziert. Weil jeder der drei Spawns in die- // instr 1
var source, res, rq; sem Patch 4 Audiokanäle erzeugt, müssen diese 3 x 4 { arg spawn, i, synth, deltaTime,
(
res = MouseX.kr(60.0,130.0).midicps;
Kanäle mit Mix zu 4 Kanälen zusammengefasst werden. instrumentNum, ...;
Synth.scope({
rq = MouseY.kr(1.0,0.02, \exponential); // code
var franges, durs, amps, rqs, nchan; OrcScore
},
source =
franges = Will man ganz unterschiedliche Synthese-Patches mit Hilfe // instr 2
Spawn.ar({
[[80,150], [500,570], [5000,6000]]; von Parameterlisten steuern, bietet sich statt Spawn die { arg spawn, i, synth, deltaTime,
arg sp; davon abgeleitete Klasse OrcScore an. OrcScore rea-
durs = [1, 12.0, 0.07]; instrumentNum, ...;
var dur, freq, pan; rqs = [0.03, 0.1, 0.4]; lisiert den klassischen Ansatz der Zweiteilung der Klang- // code
freq = rrand(40.0, 80.0).midicps; amps = [0.7, 0.2, 0.1]; erzeugung in eine Beschreibung der inneren Struktur von }
dur = 20.0 / freq; nchan = 4; Klangerzeugern (Instrument, Patch, Preset u.ä.) und eine
],
sp.nextTime = dur / 4; Mix.ar( franges.collect({ arg range, i; Steuerung derselben (Partitur, MIDI-File u.ä.). Schon der
Name OrcScore verweist auf das aus den MUSICN- // score array:
pan = 1.0.rand2; Spawn.ar({ arg sp;
EnvGen.ar(
Sprachen entwickelte CSOUND, das lange Zeit mit 2 zwei [
var dur, freq, pan;
getrennten Dateien operierte: dem Orchestra- und dem [ <parameterliste event 1>],
Env.perc(0.01,dur), freq = Score-File. (Inzwischen ist die strenge Zweiteilung in [ <parameterliste event 2>],
Pan2.ar(Saw.ar(freq, 0.2), rrand(range.at(0),range.at(1)); CSOUND durch verschiedene Echtzeitversionen, Event- [ <parameterliste event 3>],
pan)) dur = steuerung in den Instrumenten und ein neues Dateiformat (...)
}, 2); durs.at(i) * rrand(0.6, 1.2); relativiert worden.) ],
RLPF.ar(source, res, rq) sp.nextTime =
1, nil, 1)
}) dur * rrand(0.5, 2.0);
})
pan = 1.0.rand2;
) )
54 SuperCollider SuperCollider 55
TSpawn und verschachtelte Eventstreams Hier triggern die unregelmäßig auftretenden positiven ( Note die jeweils älteste noch nicht beendete Note
Spawn, OrcScore sowie die meisten anderen, hier Impulsflanken von Dust hohe perkussive Klänge: Synth.play({ „gestohlen“.
nicht besprochenen Spawner, wie Cycle, TSpawn.ar({ Voicer.ar(newEventFunc, numChannels,
RandomEvent, SelectEvent, XFadeTexture (
var trans; midiChannel, maxVoices, mul, add)
und OverlapTexture erzeugen die Events zu einer Synth.play({
bestimmten Zeit nextTime, die entweder eine trans = XLine.kr(500.0, 2000.0, 2);
TSpawn.ar({ Die newEventFunc bekommt neben den üblichen 3
Konstante ist oder aus einer Liste stammt oder auf irgend- Spawn.ar({ Argumenten spawn, eventCount und synth noch
eine Weise errechnet wird. EnvGen.ar(
EnvGen.ar( jeweils deltaTime, channel, note und veloci-
Insbesondere in Echtzeitanwendungen wird es aber oft Env.perc(0.01,rrand(0.1,2.0)), ty übergeben. Die hier offensichtlich nutzlose
Env.perc(0.01,rrand(0.1,1.0)),
nötig sein, Klangereignisse in Abhängigkeit von nicht vor- SinOsc.ar( deltaTime (die Zeit bis zur nächsten Note ist unbe-
hersehbaren, meist externen Ereignissen auszulösen. SinOsc.ar( kannt!) ist aus Kompatibilitätsgründen zu OrcScore
rrand(3.0,5.0)*1000,
Die Spawn-Klassen TSpawn, TrigXFade und rrand(3.0,5.0)*trans.poll, eingefügt worden, damit die „Instrumente“ in beiden
0, 0.1)
Voicer erlauben die Verwendung von Triggersignalen 0, 0.1) Spawn-Varianten verwendet werden können.
) )
zur Erzeugung von Events. Es folgt ein sehr einfaches Beispiel für einen 8-stimmigen
}, 1, nil, Dust.ar(4)) }, 1,
Im Falle von TSpawn (= triggered spawn) wird der Trigger Voicer, der auf MIDI-Note-Messages von Channel 1
anstelle des nextTime-Arguments eingesetzt. Ansons- }) // nextTime 20...120 ms reagiert.
ten funktioniert TSpawn genauso wie Spawn: ) { rrand(0.02, 0.12) },
// 4 bis 20 Events (
TSpawn.ar(newEventFunc, numChannels, rrand(4, 20)) e = Env.adsr(0.03,0.1,0.3,0.5,1,-3);
maxRepeats, trig, mul, add) Synth.scope({
}, 1, nil, MouseX.kr(-1,1))

Zu beachten ist, daß das trig-Argument nach }) Voicer.ar({


) arg voicer, i, synth, deltaTime,
maxRepeats folgt, im Gegensatz zum nextTime-
channel, note, vel;
Argument von Spawn und anderen, das maxRepeats
Die Maus triggert beim Überfahren der Bildschirmmitte EnvGen.ar(e,
voran geht.
von links nach rechts ein neues TSpawn-Event, das Saw.ar(
Ein Trigger kann jedes Signal sein. Getriggert wird ein Event zunächst ein XLine startet und dann einen Spawn akti- note.midicps,
bei Signalübergängen von nicht-positiven (Null und nega- viert. Dieses Spawn-Objekt generiert zwischen 4 und 20 ((vel-127)/2).dbamp)
tive Werte) zu positiven Werten. Somit sind vor allem alle Events im Zeitabstand von 20 bis 120 ms. (siehe Abbildung )
Arten von impulsförmigen Signalen, wie Impulse und auf der linken Seite) Die Tonhöhe liegt zwischen dem 3- bis
Dust, aber auch externe Controller, wie Mouse oder }, 1, 1, 8)
5-fachen des Momentanwertes von trans, d.h. der
MIDIController, die natürlich entsprechend skaliert XLine-Steuerfunktion. (Der aktuelle Wert eines Control- })
werden müssen, geeignet. Spawn-Objekte können auch verschachtelt werden, um )
oder Audiorate-Signals kann mit der Message poll gele-
Hierarchien von Ereignissen aufzubauen. Jedes gespawnte
Letztlich kann aber jedes Signal, z.B. von einem Sinus- sen werden.) Die Dauern sind ebenfalls zufällig; sie wer-
Event kann selbst einen oder mehrere Spawns enthalten, Ein von Voicer gespawnter Ton bleibt hier solange aktiv,
Oszillator, verwendet werden: den in der Hüllkurve Env.perc errechnet.
die ihrerseits eigene Events auslösen usw. bis eine NoteOff-Message auf MIDI-Channel 1 eintrifft, die
Voicer die Release-Phase von 0.5 Sekunden einleitet und mit deren
Mit dieser Variante von Spawn kann SUPERCOLLIDER Ende das Event und damit dessen UGens wieder freigibt.
fast wie ein herkömmlicher MIDI-Synthesizer benutzt wer-
den. Der Klang entspricht der übergebenen ugenGraph
function.
Ein Event wird über ankommende MIDI-Note-Messages
auf dem angegebenen MIDI-Channel getriggert, wobei
NoteOff-Messages das Event wieder beenden. Deshalb
sollten hier Envelopes mit definiertem release-Knoten zur
Anwendung gelangen, z.B. per adsr oder asr Message,
ansonsten wäre die Tondauer unabhängig von der per
MIDI gespielten Dauer.
Außerdem kann die „Polyphonie“, d.h. die Anzahl der
Stimmen vorgegeben werden. Wie üblich wird beim Über-
schreiten der maximalen Stimmenanzahl durch jede neue
56 SuperCollider NINTH 57
Andere Spawn-Klassen Seit Mai 2002 ist SUPERCOLLIDER eine freie Software N I N T H f o r V i o l a a n d M A X - M S P
Neben den eben besprochenen existieren noch weitere und kann kostenlos von
Ableitungen von der Klasse Spawn. Sie alle unterschei- www.audiosynth.com
den sich vor allem dadurch, wie bzw. wodurch die NINTH for Viola and MAX-MSP
heruntergeladen werden.
Erzeugung von Events gesteuert wird:
Diese Version 2.2.16 ist gleichzeitig die letzte Version Vortrag für das „9th Symposium for Art & Technology“ am Drama (from the greek. drâma) means: series of exciting
TSpawn, Voicer, TrigXFade für MacOS 9 und wird nicht mehr weiterentwickelt wer- Connecticut College, New London (CT, USA) 2003. events.
- Eventsteuerung durch interne oder externe Trigger den. Javier Alejandro Garavaglia Music happens along time, it is an „on going process“ (and
XFadeTexture, OverlapTexture Die von James McCartney bereits begonnene Version gara@folkwang-hochschule.de if we analyze it from the SUBJECTIVE point of view, time
- Events in gleichmäßigem Zeitabstand 3d.5.1 für MacOS 9 weist einige Neuerungen auf, wie should be here a relative value). If we take a look to the
die Trennung von Audio- und Eventengine, die An approach to Music's Dramaturgy with the interaction of definition of Drama, we cannot ignore here the word
Cycle, RandomEvent, SelectEvent, OrcScore
Möglichkeit, eigene PlugIns für SC zu schreiben, eine technological devices. A composer's review of „NINTH „EVENT“. An event is an „on going process“ too.
- Eventsteuerung durch Abarbeitung von Listen (music for viola & computer)“ (2002)
erweiterte GUI-Programmierung sowie Grafikein- und The structure of every event is perceived by the „receptor“
bzw. Selektion eines „Instruments“ aus einem Array and is saved in his memory as a certain amount of infor-
ausgabe. Leider wird diese Version ebenfalls nicht wei-
Introduction mation regarding the contemplated event (for example his
Während für die meisten dieser Objekte in der terentwickelt.
newEventFunc ein Envelope-Generator zur Freigabe
My principal concern while composing a piece of music is own conception, his own „mental“ snapshots, etc).
Die neue Version 3 von SC wird auf nur auf MacOS X
der in ihr enthalten UGens enthalten sein sollte, generie- its ultimate perception. Here some questions about this This structure is the „dramaturgy“ of the event. If we con-
und höher laufen. Die Weiterentwicklung von SC wird
ren die drei Klassen TrigXFade, XFadeTexture subject: sider now that music is in fact an event, the description
jetzt von einem Team von Programmierern vorangetrie-
und OverlapTexture diese Envelopes je nach zu • How does the relationship "creator - receptor" in music above for Drama should be also applied to music.
ben. Zu diesem Zweck wurde ein SourceForge-Project
übergebender Crossfade- bzw. Transitionszeit selbst. work?
eingerichtet und eine Developer-Mailing-Liste ins Leben NINTH (music for Viola & computer).
• How does the receptor perceive a piece of music?
Auf diese Texture-Spawner sowie die anderen mit Listen gerufen: Short description
• What happens in his mind?
operierenden Varianten, wie Cycle etc., soll hier nicht • What type of effect does the creator like to produce in In order to illustrate with one possible example my con-
sourceforge.net/projects/supercollider
weiter eingegangen werden. Zu OverlapTexture the audience through a music composition? ception of dramatic interaction between technology and
gibt es zahlreiche Beispiele in den Example-Files und auch www.create.ucsb.edu/ music, I'd like to introduce my piece „Ninth“.
die anderen Spawn-Klassen sind in den Help-Files gut mailman/listinfo/sc-dev
Including technological devices into the creation and per-
formance of music, technology may constitute itself as a NINTH is a 12 minutes long piece involving only one viola
dokumentiert.
Eine Portierung von SC3 auf Linux ist in Arbeit. way of artistic expression, coexisting with traditional and a computer, both interacting without any kind of other
aesthetics principles. If this happens, a composer has to electronic devices.
Forstsetzung folgt SC3 für OSX weist eine von der Version 2 abweichende
make some kind of reflection about how the audience per- The compositional materials (sounds, rhythms and pitches)
Syntax auf. Die betrifft vor allem die Synthese-
ceives technology. Here two possibilities: are taken from the third movement - Adagio- of Anton
funktionen. Die wichtigste Neuerung ist der neu konzi-
pierte SC SERVER, der von der eigentlichen Sprache und 1. The audience perceives the dramatic of the work as a Bruckner's 9th Symphony in D minor. The composition for
dem Editor SC LANG abgekoppelt wurde, und auf dem WHOLE or the viola part was worked with advanced techniques and
Andre Bartetzki
die gesamte Klangsynthese stattfindet. Beide Pro- 2. Technology does create a new space in the perception, mostly all the pitches are played as flageolet (natural
grammkomponenten kommunizieren über OpenSound- where it is possible to understand different level of dra- harmonics) sounds [see FIGURE 1], which has also a direct
Control (OSC), d.h. es ist jetzt möglich, SC in Netzwerken matics during a musical piece performance. relation with the kind of live-electronics wanted for
zu benutzen. Synthese-Patches, Synth-Definitions oder With my final Diploma dissertation at the Folkwang the piece.
einzelne UGens werden nun mit SC LANG programmiert Hochschule Essen (Germany) some years ago, I tried to The computer part of „NINTH (music for Viola &
und dann zur Ausführung per Netzwerkprotokoll an SC categorize and systematize how music could be conceived Computer)“, was fully programmed with MAX-MSP.
SERVER übertragen. and perceived, independently of what kind of music should This is a DSP software developed by D. Zicarelli for the
be taken into consideration. The common denominator is Macintosh environment, following the IRCAM ISPW
Trotz der teilweise gravierenden Änderungen in der here the music's dramaturgy. (Ircam Signal Processing Workstation), which worked
Funktionsweise und in der Sprache lohnt sich die together in the 90's with Opcode's MAX on the Next
With the uninterrupted and quick development of always-
Beschäftigung mit SC2 weiterhin, zumal sich Version 3 Computers. MAX-MSP uses the so called patches, which
new ways of expression coming from the technological
noch immer in Entwicklung befindet. Außerdem hat are a combination of different objects, which have all diffe-
side, I ask myself if we should not begin to think about
man unter MacOS 9 nur Zugang zur Version 2 bzw. zur rent names and functions. There is a slight difference bet-
absolutely new aesthetics in the new multimedia times.
Version 3d, die allerdings einige bugs aufweist. ween MAX alone Objects (mostly used for MIDI and
Let me clear up very quickly my point of view beginning
Wie immer findet man weiterführende Hilfe unter: sequencing applications) and the MSP objects: the latter
with the meaning of the word:
add a tilde (~) after the name, and are able to pass a DSP
swiki.hfbk-hamburg.de:8888/ Dramaturgy: (from the greek. dramatourgía) means: theo- signal from one object to another. MAX objects, on the
MusicTechnology/6 ry of the external construction form of a DRAMA and the other side, can only pass normal data (including MIDI data).
laws of it's inner structure, and the word:
16 SuperCollider SuperCollider 17
Streams benutzt werden können. Innerhalb dieser (
S u p e r C o l l i d e r Funktion werden zurückzugebende Werte durch die var stream;
Message yield markiert. Wird nun an ein Routine-
Einführung in die Programmierung, Teil 6 Pattern umfassen jedoch nicht nur mehr oder weniger peri- Objekt die next-Message geschickt, antwortet dieses stream = Routine.new({
odisch wiederkehrende Muster. Der Begriff Muster wird Objekt mit dem jeweils nächsten yield-Wert. Ist die so loop ({4.rand.yield})
Andre Bartetzki weiter gefasst: ein Pattern ist eine wiederverwendbare erzeugte Sequenz von Werten endlich, erhält man nach });
abart@snafu.de Vorlage, ein Schema, das von einem oder mehreren dem Erreichen des letzten Wertes nur noch nil. Das 10.do({ stream.next.post; });
Streams benutzt werden kann, um Werte zu generieren. Zurücksetzen auf den ersten yield-Wert kann mit
Dieser mehrteilige Einführungskurs zum Softwaresynthe- reset erfolgen. Dann erfolgt erneut ein Durchlauf durch )
James McCartney spricht von der Plätzchenform, mit der
sizer SUPERCOLLIDER (SC) begann in den Mitteilungen aus dem Teig viele gleichartige Formen ausgestanzt werden den Funktionskörper des Routine-Objekts und somit -> 3011223200
40 und wird an dieser Stelle fortgesetzt. können. werden alle Werte erneut ausgegeben, wobei zuvor dieje-
Alle Beispiele beziehen sich noch auf Version 2.16 von SC. Es geht bei unseren Pattern also weniger um eine nigen yield-Werte, die von Funktionsberechnungen (
Wiederholung von Elementen, wie etwa bei Rhythmus- abhängen, neu berechnet werden.
var stream;
Komponieren mit Pattern pattern, Melodiesequenzen, Akkordfolgen etc., sondern
( stream = Routine.new({
vielmehr um die Wiederverwendbarkeit einer Vorlage bzw.
In dieser Folge liegt der Schwerpunkt in der komposito- var stream; var i = 0;
um diese Vorlage selbst. Diese Vorlage, das Muster, spezifi-
risch-strukturellen Arbeit mit SUPERCOLLIDER. Neben der
ziert die Methode, nach der neue Werte erzeugt werden stream = Routine.new({ loop ({ i=i+1; i.yield})
grundsätzlichen Eignung für algorithmisches Komponieren
sollen. Die Erzeugung bzw. das Auslesen der Werte nach "eins".yield; });
durch Eigenschaften einer echten Programmiersprache,
diesem Muster erfolgt in SC mit Hilfe der Klasse Stream [2, 'zwei'].choose.yield; 13.do({ stream.next.post; });
wie Kontrollstrukturen, verschiedenen Datentypen, I/O-
und ihren Methoden. 3.yield;
Funktionen usw., stellt SC mit den Klassen Stream, stream.reset;
Pattern, Environment und Event auch speziell }); "\n".post;
für die kompositorische Arbeit entwickelte Funktionen Streams
// print 5 values from stream 5.do({ stream.next.post; });
bereit. Ein Stream ist ein Objekt, das eine endliche oder unend- 5.do({ stream.next.postln; }); )
Hierbei liegt in SC, wie auch in den meisten anderen liche Folge von Werten repräsentiert. Die Message next
Kompositionssprachen, konzeptionell ein quasi-serieller stream.reset;
(oder value) ruft vom Stream einen neuen bzw. den
Ansatz zugrunde: musikalische Ereignisse werden nicht in nächsten Wert ab, während reset den Stream auf den // print 4 values from stream -> 12345678910111213
ihrer Gesamtheit beschrieben, sondern durch die separate Anfangswert zurücksetzt. Da in der allgemeinsten Klasse 4.do({ stream.next.postln; }); -> 12345
Generierung ihrer Klangparameter, wie Tonhöhe, Dauer, Object, aus der alle anderen Klassen abgeleitet sind, // print 1 value from stream
Lautstärke, Klangfarbe usw. Allerdings erlaubt das Pattern- bereits Methoden für next und reset deklariert wur- stream.reset; Was ist der Unterschied solcher Streams zu anderen
System von SC auch die Bündelung einzelner Parameter, den, kann jedes Objekt wie ein Stream behandelt werden. Datentypen, wie z.B. Listen, oder zu normalen Funktionen?
stream.next.postln;
umso charakteristische Beziehungen der Parameter unter- In diesem Fall gibt das Objekt sich selbst zurück. Zum einen ist die Art des Zugriffs auf die Werte anders: bei
)
einander, z.B. von Frequenz und Dauer oder Lautstärke und Listen,Arrays etc. benötigt man einen Index, um mit at, put
Dauer, gestalten zu können. 8.next.postln; etc. ein Element zugreifen zu können, wogegen man
-> eins
Vergleichbare Ansätze zum pattern-orientierten algorith- -> 8 Funktionswerte nur mit value erhält. Streams liefern Werte
-> zwei mit next. Bei Streams gibt es, mit Ausnahme zufälliger
mischen Komponieren finden sich in Iannis Xenakis’ ST-
PROGRAMM, Gottfried Michael Koenigs PROJECT, 8.reset.postln; -> 3 Sequenzen, immer eine Reihenfolge, wie auch bei Listen.
Heinrich Taubes COMMON MUSIC u.a. -> 8 -> nil Anderseits können Streams so konstruiert werden, daß sie
Pattern in SC umfassen keine analytischen Methoden, wie -> nil existierende Werte nicht einfach nur zurückgeben, wie Listen,
"bla".next.postln; sondern ihre Werte immer neu berechnen, wie Funktionen.
sie z.B. mit dem Begriff Pattern-Matching verbunden sind -> eins
und bei Tonhöhenanalyse, Score-Following, Sprach- -> bla Zum andern kennen Streams im Unterschied zu Listen oder
-> 2
erkennung, rhythmischer und motivischer Analyse in der Funktionen einen veränderlichen inneren Zustand, nämlich
SinOsc.ar.next.postln; -> 3 einen Zähler oder Zeiger, der auf das aktuelle Element der
mathematischen Musiktheorie usw. angewendet werden.
-> a SinOsc -> nil Sequenz verweist.Während Arrays, Listen etc. ein- oder mehr-
Der Begriff Pattern bezieht sich in SC ausschließlich auf die
Generierung und Transformierung von Daten bzw. -> eins dimensionale einfache Datenspeicher, also ein Art Behälter
[1, 2, 3].next.postln; sind, könnte man Streams mit Fließbändern vergleichen.
Klangparametern mit Hilfe von Streams, die nach einem
-> [ 1, 2, 3 ] Streams sind in ähnlicher Form überall in der Computer-
bestimmten Schema oder Muster - dem Pattern - fortlau-
loop erlaubt die Konstruktion unendlicher Sequenzen.
fend Werte erzeugen, vergleichbar einem Fließband oder technik zu finden: als Datenstrom von der Tastatur, beim
Automaten. Mit Routine können schon etwas interessantere Hier zwei Beispiele: Schreiben und Lesen von Dateien, bei seriellen und paral-
Streams konstruiert werden. Die Klasse Routine reali- lelen Schnittstellen, bei Bussystemen etc.
siert spezielle Funktionen mit „Gedächtnis“, die als
18 SuperCollider SuperCollider 19
Operationen und Filter auf Streams neuen Stream übernommen (select) bzw. ausgesondert Will man dieses Modell einer zyklisch auf- und absteigen- Möchte man das in der Routine a aufgebaute Schema
Auf Streams kann die ganze bekannte Palette mathemati- (reject) werden soll. den Folge auf einen zweiten Stream übertragen, etwa um trotzdem für zwei oder mehrere unabhängige Streams ver-
scher Operatoren, wie +, *, /, **, sqrt, cos, abs, eine 2. Stimme damit zu steuern, bekommt man ein klei- wenden, so muss man 2 oder mehr gleichartige
In diesem Beispiel für select werden nur die Primzahlen
neg, midicps, log, round usw., angewandt werden. nes Problem. Hier zunächst ein solches Beispiel: Routine-Objekte erzeugen oder man benutzt besser die
aus dem Stream a in den neuen Stream b übernommen:
Als Ergebnis wird ein neuer Stream zurückgegeben: eben zu diesem Zweck erdachten Pattern!
(
( Pattern
( var a, b;
var a, b;
a = Routine.new({ Wie eingangs schon erwähnt, stellen Pattern wiederver-
var a, b; a = Routine.new({ wendbare Muster oder Schemata dar. Ein Pattern kann
a = Routine.new({ loop ({ 1000.rand.yield}) loop ({
als Muster für einen oder mehrere Streams dienen.
10.do({ arg i; i.yield; }) }); 6.do({ arg i; i.yield; });
Die abstrakte Klasse Pattern enthält grundlegende
}); b = a.select({ arg item; 6.do({ arg i; (6-i).yield; }) Methoden zum Abspielen, Konvertieren und Modifizieren,
b = a.sqrt + 100; item.isPrime}); }) erlaubt aber nicht die Erzeugung von Pattern-Objekten mit
12.do({ b.next.postln; }); 9.do({ b.next.postln; }); }); new. Denn zu diesem Zweck gibt es eine nahezu unüber-
) schaubare Fülle verschiedener Subklassen von Pattern,
) a = a * 2 + 48;
von denen einige in den nächsten Abschnitten vorgestellt
b = a + 5; werden sollen.
Neben den unären und binären Operatoren sind auch die Der folgende infinite Stream a liefert die Zahlen von 0 bis Synth.play({ Eine dieser Subklassen, Prout, ist ein Pattern, das eine
Filter collect, select und reject auf Streams 6 und zurück. In einem Sequencer-Objekt wird der
anwendbar. Diese grundlegenden Filter funktionieren auf var trig; Routine zurückgibt. Damit können wir das Beispiel des
zuvor modifizierte Stream zur Tonhöhensteuerung verwen-
die gleiche Weise wie bei einer Collection, also trig = Impulse.kr(5); letzten Abschnitts neu realisieren.
det. (Sequencer erlaubt nicht nur die Angabe von Re-
Arrays, Listen usw. ferenzen auf Arrays sondern alternativ auch Streams oder LFSaw.ar( Doch zuvor ein anderes einfaches Beispiel, an dem wir den
Die der Message collect als Argument übergebene Funktionen.) Als Ergebnis hört man eine zyklisch auf- und Umgang mit Pattern als Vorlage für Streams kennenlernen.
Sequencer.kr([a,b],trig).midicps,
Funktion modifiziert jeden Wert des Streams. Alle absteigende Ganztonfolge über dem Ton c: Pseq (= Sequenzpattern) bekommt als erstes Argument
Decay2.kr(trig, 0.1, 0.4)) eine Liste mit Elementen, die später zyklisch ausgelesen
Ergebnisse sind nun die Werte eines neuen Streams:
( }) werden. Die Anzahl möglicher Zyklen wird mit dem 2.
( ) Argument spezifiziert. Die Message asStream nimmt
var a, b;
var a, b; die Konvertierung zum Stream vor. Beim Auslesen von so
a = Routine.new({ erzeugten Streams wird das Pattern als Mustervorlage
a = Routine.new({ Nachdem Stream a modifiziert wurde, wird ein Stream b
10.do({ arg i; i.yield; }) loop ({ von a abgeleitet, mit dem Ziel, später eine um eine Quarte nicht verbraucht, sondern tatsächlich jeder Stream separat:
}); 6.do({ arg i; i.yield; }); höhere Tonfolge zu erreichen. Im Sequencer wird ein Array
von beiden Streams übergeben. Somit sollen zwei (
b = a.collect({ arg item; 6.do({ arg i; (6-i).yield; })
Sequenzen parallel erklingen: die Ganztonfolge (a) und // Spezifizierung des Pattern:
if (item.isPrime, })
eine um eine Quarte versetzte Ganztonfolge (b). p = Pseq.new([60, 62, 64], 2);
{ [item, "Primzahl"] }, });
Leider geht der Plan nicht ganz auf, denn Stream b ist von // Konvertierung zu 2 Streams:
{ [item, "Zahl"] }); // Testausdruck: Stream a abhängig und „verbraucht“ dessen Werte!
}); a = p.asStream;
14.do({ a.next.post; }); Es werden also nicht einfach Werte von beiden Streams
9.do({ b.next.postln; }); gelesen, sondern a.next und b.next lesen von b = (p + 10).asStream;
a = a * 2 + 48;
) der gleichen Routine Werte aus, verschieben also // Lesen von beiden Streams:
a.reset;
beide deren internen Zeiger. Dadurch werden die Zahlen 8.do({ [a.next, b.next].postln })
-> [ 0, Zahl ] Synth.play({ 0 bis 6 usw. abwechselnd auf beide Streams a und b
)
var trig; verteilt und anschließend modifiziert bzw. transponiert.
-> [ 1, Zahl ]
trig = Impulse.kr(5); Es erklingen letztlich in beiden Stimmen unterschiedliche -> [ 60, 70 ]
-> [ 2, Primzahl ] Großterzfolgen! Der Ausdruck b = a + 5 erzeugt also
LFSaw.ar( -> [ 62, 72 ]
-> [ 3, Primzahl ] in Wirklichkeit keinen eigenen Stream, sondern führt dazu, -> [ 64, 74 ]
Sequencer.kr(a,trig).midicps, daß bei b.next von Stream a Werte gelesen und mit
usw. -> [ 60, 70 ]
Decay2.kr(trig, 0.1, 0.4)) 5 addiert werden. -> [ 62, 72 ]
Die den Messages select und reject übergebenen }) -> [ 64, 74 ]
Funktion erfüllt eine andere Aufgabe: der Wahrheitswert ) -> [ nil, nil ]
des Ergebnisses der auf jedes Element des Streams ange- -> [ nil, nil ]
wandten Funktion entscheidet, ob dieses Element in den -> 01234565432101
20 SuperCollider SuperCollider 21
Da hier die Anzahl der Zyklen (repeats) auf 2 festgelegt Generierende Pattern P w h i t e(lo, hi, length) P s e q(list, repeats, offset)
ist, können insgesamt nur je 6 Werte gelesen werden, alle Mit Pseq und Prout haben wir gleich zwei Vertreter erzeugt eine gleichverteilte Zufallsreihe (entspricht liest die Liste repeats-mal aus, startet bei offset + 1.
weiteren Leseversuche ergeben nil. Für ein unendliche ganz unterschiedlicher Patterntypen kennengelernt: Weißem Rauschen), analog zu
Anzahl von Zyklen gibt man inf (infinity) an: Listenpattern und Generatorpattern. Andere generierende Array.rand(n, min, max) Pseq(Array.series(10,1,1), inf, 3)
Pseq.new([60, 62, 64], inf) Pattern, also solche, bei denen die Werte nicht in Form von
Übrigens kann die Message new auch entfallen. Man Listen vorgegeben werden, sondern wie bei Prout nach
schreibt dann einfach bestimmten Prinzipien erst berechnet werden, sind z.B.
Pfunc, Pseries, Pgeom, Pwhite oder Pbrown.
Pseq([60, 62, 64], inf)
Pfunc ist ein Pattern, das seine Werte mit Hilfe einer nor-
Im Gegensatz zu Pseq kennt Prout nur ein Argument, malen Funktion erzeugt, ähnlich Array.fill.
nämlich eine Funktion, die das Verhalten einer Routine Ebenso sind auch andere unter den genannten Pattern
beschreibt. Das Routine-Beispiel aus dem vorangegan- Äquivalente zu Methoden der Klasse Array.
genen Abschnitt ließe sich mit Prout auf folgende Weise
formulieren: P s e r i e s(start, step, length)
erzeugt eine arithmetische Reihe, analog zu Pwhite(0, 24, 50)
( Array.series(n, start, step)
var p, a, b; P s e r(list, repeats, offset)
p = Prout({ Zur Veranschaulichung des Outputs eines Patterns folgen P b r o w n(lo, hi, step, length)
jedem neuen Patterntyp jeweils Diagramme, deren Werte liest genau repeats Elemente aus (nicht Zyklen!),
loop ({ erzeugt einen Randomwalk (entspricht Brownschem
nach dem Schema dieses Codes generiert wurden: startet bei offset + 1.
6.do({ arg i; i.yield; }); Rauschen) zwischen lo und hi
6.do({ arg i; (6-i).yield; }) (
Pbrown(-20, 20, 8, 50) Pser(Array.series(10,1,1), 35, 3)
}) p = Pseries(-40, 3, 50);
}); a = p.asStream;
50.do({ a.next.postln })
p = p * 2 + 48;
)
a = p.asStream;
b = p.asStream + 5;
Synth.play({
var trig;
trig = Impulse.kr(5);
LFSaw.ar(
Sequencer.kr([a,b],trig).midicps,
Decay2.kr(trig, 0.1, 0.4)) Listenpattern
Pseries(-40, 3, 50)
}) P s h u f(list, repeats)
Diese Pattern erwarten eine oder mehrere Listen als
) Argumente. Diese Listen müssen die Elemente enthalten, verwürfelt die Liste (shuffle) einmalig und wiederholt sie
die später von den Streams ausgegeben werden sollen. dann ohne weitere Veränderung repeats-mal.
Unter Umgehung der algorithmischen Beschreibung der P g e o m(start, grow, length)
Alle Listenpattern haben außerdem ein repeats-
auf- und absteigenden Zyklen mit Prout könnte man die- erzeugt eine geometrische Reihe, analog zu Argument und eventuell weitere Argumente, die das Pshuf(Array.series(10,1,1), inf);
ses Beispiel mit Pseq etwas kompakter formulieren: Verhalten dieses Patterns bestimmen.
Array.geom(n, start, grow)
( Für die grafischen Beispiele zum Verhalten der Listen-
pattern wurde der folgende Code verwendet, wobei der
var p, a, b;
Pgeom(1.0, 1.1, 50) Patterntyp (hier Pseq) jedesmal ausgetauscht wird, die
p = Pseq([48, 50, 52, 54, 56, 58, 60, Liste aber zumeist gleichbleibt (Zahlen von 1 bis 10).
58, 56, 54, 52, 50], inf);
a = p.asStream; (
p = P s e q(Array.series(10,1,1), inf);
b = p.asStream + 5;
a = p.asStream;
// usw. .... 50.do({ a.next.postln })
) )
22 SuperCollider SuperCollider 23
P r a n d(list, repeats) P l a c e(list, repeats, offset ) Verschachtelte Pattern
Pwrand([ 1, 2, 3, 4, 5],
liest genau repeats Elemente (nicht Zyklen!) an zufälli- ist ein zyklisches Pattern (ähnlich Pseq) für einfach ver- Die Möglichkeit, Pattern als Elemente von Listenpattern zu
[0.1,0.3,0.1,0.4,0.1], inf);
ger Position aus der Liste. Unmittelbare Wiederholungen schachtelte Listen, d.h. Listen, deren Elemente wiederum verwenden, erweitert den Anwendungsbereich von Pattern
von Elementen sind möglich. einfache Listen sein können. Von den inneren Listen wird in SUPERCOLLIDER enorm.
pro Zyklus schrittweise immer nur 1 Element gelesen. Offensichtlich fehlt ein Patterntyp heap im Patternsystem
Place ist nicht rekursiv, d.h. tiefere Listenverschach- von SC. Damit ist das Kartenstapelprinzip gemeint: die
Prand(Array.series(10,1,1), inf)
telungen werden nicht unterstützt. Elemente werden gemischt und ausgeteilt. Wenn alle ver-
braucht sind, wird erneut gemischt und ausgeteilt usw. Ein
Element kann erst dann wiederholt werden, wenn alle
Place([1,2, [3,4,5], 6, [7,8]],inf,0)
Elemente der Liste einmal gewählt wurden. Unter dem
Da es oft nicht einfach ist, bei größeren Listen eine ent- Namen SERIE taucht dieser Algorithmus auch im
sprechende weights-Liste aufzustellen, deren Quer- Kompositionsprogramm PROJEKT 2 von G.M. Koenig auf.
summe genau 1 ist, sollte man zunächst eine Liste mit
ganzzahligen relativen Gewichten aufstellen und dann die- Mit verschachtelten Pattern kann so etwas auch in SC
se Liste nach folgendem Prinzip normalisieren: leicht realisiert werden. Die verbale Beschreibung des
Kartenstapelprinzips liefert uns auch gleich einen
( Lösungsansatz: Wir müssen die Elemente mischen
(Pshuf), einmal auslesen (repeats = 1) und diesen
w = [10, 5, 3, 1, 1];
Vorgang beliebig oft wiederholen (z.B. mit Pseq).
P x r a n d(list, repeats) w = w / w.sum; Damit die Liste der Zahlen von 1 bis 10 nicht unnötig
liest genau repeats Elemente (nicht Zyklen!) an zufälli- p = Pwrand([1, 2, 3, 4, 5], w, inf); immer wieder neu aufgestellt wird, lagern wir die
ger Position aus der Liste. Unmittelbare Wiederholungen a = p.asStream; Erzeugung dieser Liste aus:
von Elementen treten nicht auf. 50.do({ a.next.postln }) l = Array.series(10, 1, 1);
Achtung: dieser Patterntyp entspricht nicht dem heap- ) Das waren einige der wichtigsten einfachen Listenpattern.
Pseq( [ Pshuf(l, 1) ], inf );
Pattern von COMMONMUSIC! Ein Äquivalent für heap, Man kann aber mit Hilfe der noch zu besprechenden
bei dem Elemente erst nach Abarbeiten der gesamten Liste Filterpattern und mathematischen Operatoren weitere
wiederholt werden dürfen, gibt es in SC nicht, kann aber beliebig komplexe Patterntypen bauen.
leicht aus anderen Patterntypen zusammengesetzt In den vorangegangenen Beispielen wurden der
werden. Einfachheit halber nur Zahlen als Elemente verwendet.
Tatsächlich aber können die Elemente von Listenpattern
Pxrand(Array.series(10,1,1), inf) beliebige Objekte sein: Zahlen, Symbole, Strings, Listen,
Ugens, Funktionen usw. Ebenso können die Listen auch
P s l i d e(list,repeats,len,step,start) verschiedene Objekttypen gleichzeitig enthalten. Es gibt
keine Einschränkung bezüglich des Inhalts der Listen, denn
gibt beginnend bei start einen Ausschnitt der Länge
Pattern bzw. Streams lesen die Elemente aus der Liste nur
len von der Liste aus. Der nächste Ausschnitt beginnt
heraus. Was mit den so erhaltenen Werten geschieht, ist
step Elemente weiter. Es gibt maximal repeats
Sache des Anwenders.
Ausschnitte.
Bei dem eben Gesagten gibt es allerdings eine sehr wich- Die Liste von Pseq enthält tatsächlich nur ein Element,
tige Ausnahme: Elemente der Listen können auch Pattern nämlich das Pattern Pshuf. Trotzdem sind die eckigen
Pslide(Array.series(10,1,1),inf,7,4,0) sein! In diesem Fall werden die Elemente der Liste, die Listenklammern notwendig, weil Pseq als erstes Argu-
inneren bzw. Subpattern, als Pattern interpretiert und in ment eine Liste (und kein Pattern!) erwartet. Um die
den übergeordneten Stream eingebunden. Wiederholung des shufflings zu erzwingen, wären auch
andere Patterntypen geeignet, u.a. auch Filterpattern, die
P w r a n d(list, weights, repeats) wir in einem späteren Abschnitt kennenlernen werden.
ist ein Pattern, das die Elemente der Liste zufällig, aber mit Für verschachtelte Pattern gilt generell, daß innere Pattern
gewichteter Häufigkeit liest. Dazu braucht man eine 2. jeweils so lange abgearbeitet werden, bis sie den Wert
Liste (weights) mit je einem Gewicht für jedes Element nil liefern würden, dann wird die Abarbeitung sofort an
von list. Je größer die Gewichtszahl eines Elements, der übergeordnete Pattern weitergegeben. Wann ein
umso häufiger wird es gewählt. Die Summe aller Gewichte Pattern durchlaufen wurde, also wieviel Elemente es liefern
muss 1 ergeben! repeats zählt, wie bei den anderen kann, bevor nil auftritt, hängt bei Listenpattern norma-
rand-Pattern, keine Zyklen sondern einzelne Elemente.
24 SuperCollider SuperCollider 25
lerweise von repeats ab und bei Generatorpattern um von der Liste l unterschiedlich viele Elemente zu lesen. Pswitch liest immer das komplette Pattern mit der (
(Pseries, Pgeom, Pwhite, Pbrown) von length. Das repeats-Argument von Pser, mit dem die Menge Nummer, die zuletzt vom index-Pattern gelesen wurde. var p1, p2, p3, px;
Dazu ein einfaches Beispiel, in dem ganz verschiedene der Elemente bestimmt wird, ist ein Stream r, der auf dem Wenn das Pattern durchlaufen wurde, wird vom index- p1 = Pseq([4,5,6,7], i n f);
Pattern in einer Sequenz wiederholt werden: Patterntyp Pseries beruht. Pseries liefert hier Pattern der nächste Index gelesen. Die Zählung der Indices
p2 = Pwhite(8.0, 10.0, i n f);
schlicht die Zahlen von 1 bis 10, aber nicht als Liste, wie in beginnt mit 0 (nicht mit 1).
( vielen vorangegangene Beispielen, sondern als Pattern p3 = Pseq([3,2,1,2], i n f);
Hier ein Beispiel mit 3 Subpattern. Die Subpattern und das
bzw. Stream, damit die Message value für repeats px = Pseq([1,0,0,2,1,2], inf);
p = Pseq([ Indexpattern wurden zur besseren Lesbarkeit über je eine
funktionieren kann. Auf Streams angewandt hat value Variable deklariert. p = P s w i t c h 1([p1, p2, p3], px );
Pwhite(5, 10, 5 ),
die gleiche Wirkung wie next. Der Stream r liefert a = p.asStream;
Pseq([Pseries(10,-2, 5 )], 2 ),
Die Liste l enthält die Zahlen von 20 bis 29. ( 50.do({ a.next.postln })
Pseq([6,7,8,9], 3 ),
var p1, p2, p3, px; )
Prand([1,2,3], 8 ) (
p1 = Pseq([4,5,6,7], 1);
], inf); r = Pseries(1, 1, 10).asStream;
p2 = Pwhite(8.0, 10.0, 7);
a = p.asStream; l = Array.series(10, 20, 1); p3 = Pseq([3,2,1,2], 2);
50.do({ a.next.postln }) p = Pseq([ Pser(l, r) ], 10) ; px = Pseq([1,0,0,2,1,2], inf);
) a = p.asStream; p = Pswitch([p1, p2, p3], px );
50.do({ a.next.postln }) a = p.asStream;
) 50.do({ a.next.postln })
)

Das index-Pattern px bestimmt hier, daß zuerst p2,


dann 2mal p1, dann p3 usw. gewählt wird.
Zwei weitere Pattern, Pstep2add und Pstep3add,
dienen zur additiven Verknüpfung von Pattern.

P s t e p 2 a d d(pattern1, pattern2)

Pstep2add erwartet 2 Pattern als Argumente. Alle


Elemente von Pattern2 werden nacheinander zum 1.
Pwhite liefert 5 Werte, Pseries in Pseq 2x5 Werte,
Element von Pattern1 addiert, danach zum 2. Element von
Pseq 3 Werte und Prand schließlich 8 Werte, bevor das
Pattern1 usw.
Ganze wiederholt wird.
Rekombination von Pattern
Man beachte, daß wir es hier bereits mit einem 2fach ver- (
Unter rekombinierenden Pattern sollen hier solche verstan-
schachtelten Pattern zu tun haben. Die Verschachtelungs- var p1,p2;
den werden, die ihrerseits Pattern oder Listen von Pattern
tiefe von Pattern in SC ist beliebig! p1 = Pseq([23,17,21,11], inf);
als Argumente haben und diese Subpattern auf spezielle
Das Argument repeats in den Listenpattern braucht Weise auslesen oder kombinieren. p2 = Pseries(0, 3, 6);
Der Patterntyp Pswitch1 dagegen liest immer nur ein
kein Zahl zu sein. Innerhalb der Patternmethoden wird mit Obwohl sie eigentlich Listenpattern sind, werden einzelnes Element des Pattern mit der Nummer, die gerade p = Pstep2add(p1, p2);
repeats.value zunächst der Wert von repeats Pswitch und Pswitch1 hier vorgestellt, denn sie die- vom index-Pattern gelesen wurde. Dann wird der näch- a = p.asStream;
ermittelt. Dies erlaubt die Verwendung von allen Objekten, nen gerade dazu, andere Pattern neu zu organisieren. ste Index genommen und ein einzelnes Element vom neu 50.do({ a.next.postln })
die auf die value-Message mit einer ganzen Zahl oder Beide erwarten eine Liste, die Pattern oder einfache indizierten Pattern gelesen usw. )
inf antworten, als Argument repeats. Dazu gehören, Objekte als Elemente enthält. Das zweite Argument, ein Das bedeutet, daß repeats- und length-Argumente
neben Integerzahlen, auch Funktionen oder Streams, die index-Pattern, dient zum Umschalten bzw. Auswählen, von Subpattern nicht mehr bestimmen, wie lang die jewei-
ganze Zahlen liefern. Dadurch ist es möglich, die Anzahl von welchem Pattern aus der Liste gelesen wird. ligen Submuster sind, sondern nur noch, wieviel Elemente
der Zyklen oder Elemente in einem Pattern variabel zu überhaupt von einem Subpattern gelesen werden können,
gestalten, z.B. mit Hilfe von Pattern! P s w i t c h(patternList, index)
bevor man von diesem nur noch nil erhält.
Das nächste Beispiel realisiert mit diesem Prinzip eine Art P s w i t c h 1(patternList, index)
Deshalb modifizieren wir das Beispiel von eben für
Akkumulationspattern. Von einer Liste l wird das 1. Pswitch1 dahingehend, daß wir repeats bzw.
Element gelesen, dann das 1. und 2., dann das 1., 2., und Diese beiden Pattern unterscheiden sich darin, auf welche
Weise von den Subpattern der Liste gelesen wird. length auf inf setzen, andernfalls hätte sich z.B. das
3. usw. Die hier vorgeschlagene Lösung verwendet Pser, Pattern p1 schon nach 4 Werten erschöpft.
26 SuperCollider SuperCollider 27
P s t e p 3 a d d(pattern1,pattern2,pattern3) Modifikation von Pattern Ein sehr interessantes und leistungsfähiges Pattern zur Ppatmod kann nicht nur Listenpattern modifizieren, wie
Pattern können mit mathematischen Operationen modifi- Modifikation ist Ppatmod: das nächste Beispiel zeigt. Hier werden alle Parameter von
Hier kommt noch eine weitere Ebene der zyklisch-additiven Pwhite bei jedem Durchlauf zufallsgesteuert verändert:
ziert werden, genauso wie Streams. Ebenso verhält es sich P p a t m o d(pattern, func, repeats)
Verknüpfung hinzu. Pattern3 ist das innerste Pattern, das
mit den von Collection und Stream bekannten
jedesmal durchlaufen wird, dann Pattern2 und zuletzt Das übergebene pattern wird nach jedem Durchlauf (
Methoden collect, select und reject.
Pattern1. der Funktion func übergeben, dort modifiziert und wie-
a = Ppatmod(
Dazu ein kleines Beispiel: der zurückgegeben. Nach einem weiteren Durchlauf geht
( es wieder durch die Funktion usw., so lange, wie es durch Pwhite(10, 20, 5),
( { arg pat, i;
var p1,p2,p3; repeats bestimmt ist. Dies soll am folgenden Beispiel
var p1, p2; illustriert werden: pat.lo = 8.rand * 10;
p1 = Pseries(0, 2, 4) * 10;
p1 = Pseries(0,1,9).s q u a r e d + 5; pat.hi = pat.lo + 10;
p2 = Pseq([23,11,17], 1); (
p2 = Pwhite(0, 70, 20); pat.length = rrand(5,8);
p3 = Pwhite(-2,2,5); a = Ppatmod(
p2 = p2.r e j e c t({arg item; pat},
p = Pstep3add(p1, p2, p3); Pseq([1, 2, 3], 1),
(item >= 30) and: (item <= 50)}); inf);
a = p.asStream; { arg pat, i;
p = Pseq([p1, p2], inf ); b = a.asStream;
50.do({ a.next.postln }) pat.list = (pat.list + 10) ++
a = p.asStream; 50.do({ b.next.postln });
) pat.list.reverse;
50.do({ a.next.postln }) )
) pat},
inf);
b = a.asStream;
50.do({ b.next.postln });
)

Der Funktion func werden 2 Argumente übergeben: das


Pattern (hier pat) und die Anzahl verstrichener Zyklen
(hier i). Um das Pattern modifizieren zu können, muss auf
die Objektvariablen des Pattern zugegriffen werden. In
unserem Beispiel liefert pat.list die Liste von Pseq.
pat.repeats würde die repeats-Variable von
Der binäre Operator + + hängt 2 Pattern aneinander und
Pseq liefern. Um ein Pattern mit func verändern zu können, muss man
kann somit als einfacher Ersatz für Pseq dienen:
p1 liefert die Quadratzahlen zu 0 bis 8 verschoben um 5. Im Beispiel wird die Liste von Pseq nach jedem Durchlauf die richtigen Bezeichnungen der Objektvariablen kennen.
p2 liefert Zufallszahlen zwischen 0 und 70, wobei an- auf folgende Weise verlängert: zu allen Elemente der Liste Diese erfährt man, indem man den Namen des fraglichen
(
schließend per reject alle Werte zwischen 30 und 50 wird 10 addiert und dann wird noch einmal die gesamte Objekts selektiert (hier Pwhite) und dann mit [CMD-J]
a = Pseq([10, 20, 30], 1); verworfen werden. Dies führt dazu, das die Zufallsfolge p2 die Datei mit der Klassendefinition öffnen lässt.
alte Liste rückwärts angehängt. Dadurch verdoppelt sich
b = Pseq([1,2,3,4],1); in den meisten Fällen weniger Werte enthält, als length jedesmal die Länge der Liste. Die Zeile mit der Variablendeklaration gibt uns die
c = ( a ++ b ).asStream; eigentlich vorgibt (hier 20). gewünschte Auskunft:
8.do({ c.next.postln }); Die Methoden collect, select und reject sind var <>lo, <>hi, <>length;
auch als sogenannte Filterpattern verfügbar:
)
P c o l l e c t(func, pattern)
-> 10
P s e l e c t(func, pattern)
-> 20
P r e j e c t(func, pattern)
-> 30
-> 1 Filterpattern erwarten immer ein oder mehrere Pattern als
-> 2 Argument und dienen der Modifikation der ihnen überge-
benen Pattern. Die meisten Filterpattern in SC stehen im
-> 3
Zusammenhang mit Events, die etwas später besprochen
-> 4 werden sollen.
-> nil
28 SuperCollider SuperCollider 29
Schließlich sei im Zusammenhang mit der Modifikation von Synth.play({ Events und Pbind Der vollständige play-Aufruf für Pbind müsste in unse-
Pattern noch Plazy erwähnt, mit dem aber keine Pattern t = StepClock.ar(durpat, 8); Die eigentliche Leistungsfähigkeit des Patternsystems in rem Beispiel daher eigentlich lauten:
modifiziert werden, sondern per Funktion erzeugt und in SUPERCOLLIDER zeigt sich erst im Zusammenhang mit
f = Sequencer.ar(freqpat, t); (
den Stream eingebunden werden. Events. Was ist ein Event in SC?
Decay2.ar(t, 0.01, 0.5, Pbind(
P l a z y(func) Saw.ar([ f, f + 0.2 ].midicps)); Events sind Zuordnungen von Werten zu Symbolen, die
musikalische Parameter repräsentieren. Das ist eigentlich \midinote, Pwhite(48, 72, 8)
})
Im Unterschied zu Prout oder Pfunc beschreibt func nichts weiter, als daß beispielsweise eine bestimmte Note ).play(E v e n t . p r o t o E v e n t);
nicht die Funktionsweise des Patterns. Vielmehr muss ) (ein Event!) für den Parameter Tonhöhe auch einen )
func ein Pattern als Ergebnis zurückgeben. bestimmten Wert bekommt, ebenso die Dynamik, die
Auch im Zusammenhang mit Spawn und den daraus Gibt man aber gar kein Event als Argument für play an,
Hier wird ein verschachteltes Pseq erzeugt, das 10 Dauer, die Einsatzzeit und eventuell weitere Werte für wei-
abgeleiteten Klassen, wie Tspawn, Cycle usw., können so wie wir es im Beispiel zuvor gemacht haben, so greift
Permutationen der ersten 10 Primzahlen enthält: tere Parameter.
Pattern genutzt werden. In diesem Fall muss man inner- Pbind selbständig auf den Standard zurück. Man könn-
halb der newEventFunc von Spawn mit der Message Events, also Instanzen der Klasse Event, werden in SC in
( der Regel nur durch ein bestimmtes Pattern erzeugt. te an dieser Stelle sogar ein eigenes ProtoEvent angeben.
next die Werte aus den Streams lesen. Aber dies wird wohl kaum jemals nötig werden, denn das
p = Plazy({ Dieses Pattern, Pbind, kopiert ein sogenanntes
Im folgenden Beispiel werden die Tonhöhen per Random- vordefinierte Event enthält eine Fülle von musikalischen
var len, primes, list, repeats; ProtoEvent, d.h. alle seine Eigenschaften, die durch eine
walk und der Rhythmus mit gewichteten zufällig gewähl- und Syntheseparametern, die teilweise auf raffinierte
Reihe von Wert-Symbol-Zuordnungen gegegeben sind.
len = 10; ten Dauernwerten bestimmt: Weise musikalisch-hierarchisch auseinander hervorgehen.
Beim Anlegen jeder einzelnen Kopie dieses ProtoEvents
primes = Array.fill(len, {arg i; können diese Eigenschaften durch andere Pattern verän- Einige diese Parameter sollen hier kurz aufgezählt werden,
i.nthPrime }); (
dert werden. Sehen wir uns zunächst ein sehr einfaches wobei immer das Symbol und der dazugehörende
var pnote,rhythms,weights,pdur,tempo; Standardwert angegeben sind:
list = Array.fill(len, {arg i; Beispiel an:
Pseq(primes.permute(i*20),1)}); pnote = Pbrown(48,92,7,inf).asStream;
tempo = 140; ( \midinote, 60 (das mittlere C bzw. c’)
repeats = primes.sum;
rhythms = [ 1/2, 1/4, 1/8, 1/16, Pbind( \freq, 261.626 ( = 60.midicps !)
Pseq(list, repeats)
1/12, 1/6]; \midinote, Pwhite(48, 72, 8) \detune , 0.0 (in Hertz)
});
weights = [2, 2, 4, 20, 12, 3]; ).play; \dur, 1.0 (in Sekunden bzw. in Beats)
a = p.asStream;
pdur = Pwrand( rhythms*60*4/tempo, \legato, 0.8 ( = Event-Overlap)
); )
weights/weights.sum, inf).asStream; \amp, 0.1
Es werden 8 zufällige Töne mit einem orgelartigen Klang \db, -20.0
Patternverwendung in Sequencer und Spawn Synth.scope({ gespielt, jeweils mit 1 Sekunde Dauer. Die Tonhöhen stam-
\pan, 0.0
Kommen wir nun zur Anwendung von Pattern für musika- Spawn.ar({arg sp, i, sy; men aus dem Bereich der MIDI note numbers 48 bis 72,
lische und Syntheseparameter. Daß man Streams und also c bis c’’. \env, Env.asr(0.01,1.0,0.5);
var freq, dur, res1, res2;
Pattern im Sequencer-Objekt verwenden kann, haben Wo wird die Dauer angegeben? Wo befindet sich der
freq = pnote.next.midicps; Ein Symbol beginnt übrigens immer mit einem
wir bereits gesehen. Die Triggerklassen Sequencer, Synthesealgorithmus? Wo ist der Synth?
dur = pdur.next; Backslash \ oder ist in Hochkommata '' eingeschlossen.
Impulsequencer und StepClock erwarten als
sp.nextTime = dur; Tatsächlich ist Pbind ein Pattern, das die Message play \dur ist daher gleichbedeutend mit 'dur'.
1. Argument eine sequence. Das kann eine Referenz
res1 = XLine.ar(8*freq, freq, versteht und das letztlich selbst einen Synth aufruft. Bevor wir weiter über dieses ProtoEvent sprechen, sehen
auf ein Array sein oder irgendein anderes Objekt, dem
Wie das genau vonstatten geht, soll uns hier nicht interes- wir uns an, wie man mit Pbind, Pattern und Events
dann bei jedem Triggerimpuls die Message value dur * 0.7);
sieren. Für uns reicht es, zu wissen, daß uns Pbind umgeht. Die Aufgabe von Pbind ist es, Werte an Symbole
geschickt wird. Aus diesem Grunde können auch res2 = XLine.ar(3*freq, freq, eine Menge Arbeit abnimmt: es erzeugt selbst die notwen- zu binden. Gibt es gleichnamige Symbole bereits im
Funktionen und Streams oder auch einfache Zahlen als dur * 0.5); digen Spawns und den Synth, greift selbst auf ein ProtoEvent, so werden die darin definierten Standardwerte
sequence dienen.
EnvGen.ar( Standard-ProtoEvent mit den Klangparametern zurück und durch die neuen Werte ersetzt. Umgekehrt gilt auch: alle
Hier regelt je ein Pattern bzw. Stream den Einsatzabstand Env.perc(0,dur,0.5), modifiziert die gewünschten Klangeigenschaften. definierten Symbole, die nicht per Pbind neue Werte
und die Tonhöhe:
RLPF.ar( Im ProtoEvent müssen alle fehlenden Informationen ent- bekommen, behalten ihren Standardwert.
( Saw.ar([freq, freq/4]), halten sein, so auch die hier scheinbar fehlende Dauer oder Das nächste kleine Beispiel greift auf den default-Wert von
[res1, res2], 0.09)) die ugenGraphFunction für die Klangerzeugung. \midinote zurück, da Pbind keine neuen Werte an
var durpat, freqpat;
}, 2); Das vordefinierte Standard-ProtoEvent, das alle diese mu- \midinote bindet. Damit Pbind aber überhaupt funk-
durpat = Pseq([2,1,1,1,2,1,3,1], tioniert, brauchen wir irgendeinen anderen Parameter, z.B.
}) sikalischen und Syntheseparameter bereits enthält, wird
inf).asStream;
mit Hilfe der Klassenmethode protoEvent erzeugt: die Dauer, die wir hier auf 0.33 Sekunden setzen:
freqpat = Pseq([0,7,3, 2,5,-2], )
inf).asStream + 48; Event.protoEvent Pbind( \dur, 1/3 ).play;
30 SuperCollider SuperCollider 31
Natürlich können mehrere Parameter des ProtoEvents Nun noch ein Patch, bei dem die Parameter Tonhöhe, Für das Pbind-Pattern soll nur diese logistische Tonhöhen im protoEvent
gleichzeitig geändert werden. Dazu werden dem Pattern Amplitude und Dauer alle auf der logistischen Gleichung Gleichung verwendet werden, allerdings gleich dreimal Anstelle von MIDI Notennummern, die ja auf der chroma-
Pbind immer Paare von Symbolen und Werten überge- beruhen. Die logistische Gleichung ist mit der Chaostheorie und jeweils mit anderem r. Außerdem sollen die tischen Skala der 12-fachen gleichmäßigen Unterteilung
ben, die alle durch Kommata getrennt werden. Die Werte populär geworden und mit den Namen Robert May und Ergebniswerte, die normalerweise alle zwischen 0 und 1 der Oktave beruhen, wurde im protoEvent von SC ein
können die Form einer Zahl oder eines Patterns haben. Mitchell Feigenbaum verknüpft, die ihre mathematischen liegen, dem jeweiligen Klangparameter angepaßt werden. viel allgemeineres Konzept von Tonhöhe implementiert.
Hier ist die Dauer konstant, die Tonhöhe folgt einem Eigenschaften in den 1970er Jahren untersucht haben. Zu diesem Zweck schreiben wir uns eine Funktion f, Dabei können die Skala \scale, eine modale Skalen-
Randomwalk, während die Lautstärke (in dB) einem peri- Diese Gleichung diente jedoch schon ab Mitte des 19. die uns wiederum eine Funktion zurückgibt - nämlich transposition \mtranspose, die Anzahl der Töne pro
odischen 8-stufigen Decrescendo entspricht: Jahrhunderts als Modell für Populationsdynamik, d.h. für die angepaßte logistische Gleichung. Die Anpassung Oktave \stepsPerOctave, die Oktavlage \octave,
das Wachstum einer biologischen Spezies in Abhängigkeit erfolgt über 4 Argumente: r, min und max als der Skalengrundton \root, eine chromatische Trans-
( von einer Umweltgröße, z.B. der Nahrungsmenge. Wertebereich sowie d, mit dem das Rundungsraster position \ctranspose, eine Verstimmung \detune
Pbind( Die logistische Gleichung beschreibt ein nichtlineares und festgelegt wird. In jedem Prout müssen wir nun u.a. verändert werden. Aus alldem wird letztlich eine
zugleich rückgekoppeltes System aus einer Eingangsgröße mittels f.value(args...) die gewünschte Funktion Frequenz \freq berechnet, die für die Klangerzeugung
\dur, 0.1,
x und einem Parameter r: erzeugen. zum Einsatz kommt.
\midinote, Pbrown(40, 90, 5, inf), Genaueres zu diesem Tonhöhenmodell kann man auch in
\db, Pn(Pseries(-3,-3,8), inf) xn+1 = r xn ( 1 - xn ) ( der Datei Streams-Pattern-Events 5 nachlesen,
).play; f = { arg r, min, max, d; welche sich zusammen mit anderen lesenswerten Kapi-
Zur Rückkopplung kommt es, weil das Ergebnis xn+1 beim { // logist. Gleichung teln zum Thema Streams und Pattern im SC-Unterordner
)
nächsten Mal als xn eingesetzt wird. In SC lässt sich da- Documentation befindet.
var x = 0.1, s;
Das Pattern Pn ist übrigens ein Filterpattern, das ein zu raus leicht eine entsprechende Programmzeile formen: Hier nur soviel: \degree bezeichnet die Nummer einer
s = max - min;
wiederholendes Pattern und ein repeats-Argument x = r * x * (1 - x); Skalenstufe, wobei die Zählung mit 0 beginnt. \scale
erwartet: inf.do({ ist die dazugehörige Skala, die als Array anzugeben ist.
Ebenso leicht kann man daraus ein Routine-Pattern x = r * x * (1 - x); Diese Skala, in unserem Beispiel der phrygische Modus,
P n(pattern, repeats) machen. Wir setzen r=3.71, so daß eine quasi-periodi- zählt Töne in einer n-teiligen Oktave, im Normalfall sind
(x*s + min).round(d).yield;
sche Folge entsteht. Der Startwert von x kann irgendeine das 12 Töne pro Oktave. Die Standardoktave ist die einge-
Möchte man gar keine halbtönig gerasterten Tonhöhen })
Zahl zwischen 0.0 und 1.0 sein, denn die resultierende strichene bzw. mittlere Oktave, der Skalengrundton ist
haben, gibt man für Pbrown einfach Fließkommazahlen an Folge hängt hauptsächlich von r ab. Die do-Schleife in der } // Ende logist. Gleichung
oder man benutzt das Symbol \freq, um mit Frequenzen immer 0.
Funktion ist mit inf so angelegt, daß die Folge immer wei- };
statt mit Tonhöhen zu arbeiten, wie in diesem Patch: \degree = 0 bezeichnet also für den Fall, daß wir die
ter iteriert.
Pbind( anderen Eigenschaften nicht ändern, den Skalen-
( ( \scale, [0, 1, 3, 5, 7, 8, 10], anfangston einer Durtonleiter (das ist der Standardwert für
Pbind( p = Prout({ \degree,Prout(f.value(3.73,-16,16,1)), \scale) in der mittleren Oktave bei 12facher
\dur, 0.1, Oktavteilung und einer modalen sowie chromatischen
var x = 0.1, r = 3.71; \dur, Prout(f.value(3.69, 0.01,
Transposition von 0 und ohne Verstimmung. Es ergibt sich
\legato, 10.0, inf.do({ 0.1, 0.01)),
schließlich der Ton c’.
\freq, Pbrown(300.0,2000.0,100,inf), x = r * x * (1 - x); \db, Prout(f.value(3.58,-25,0,1))
\db, -15, x.yield; ).play;
\env, Env.asr(0.2, 1.0, 0.2) }) ) Forstsetzung folgt
).play; });
Was verbirgt sich hinter \scale und \degree ?
) a = p.asStream; Beide sind Teil des raffinierten hierarchischen Tonhöhen- Andre Bartetzki
50.do({ a.next.postln }) modells im Standard-ProtoEvent, das im nächsten
Der Parameter \legato hat Einfluss auf die tatsächliche
) Abschnitt kurz angerissen werden soll.
Dauer der Events. \dur legt nämlich nicht nur die Dauer, Links
sondern vor allem auch den Einsatzabstand von Event zu Das dem Pbind-Pattern zu übergebende ProtoEvent, das
Event fest. Die eigentliche Dauer, genauer: die Sustain- sich mit Event.protoEvent erzeugen läßt, kann man SC 2.16
als eine Art „Urklang“ verstehen. Dessen vordefinierte www.audiosynth.com
Phase der Hüllkurve, wird dann durch Multiplikation von
\dur und \legato bestimmt. \legato verzehnfacht Eigenschaften können nun mit Pbind mit Hilfe von SC 3 - Sources + Binary
hier also die Dauer, während der Einsatzabstand weiterhin Pattern oder einfachen Werten überschrieben werden. sourceforge.net/projects/supercollider
kurz (100 ms) bleibt. Was dazu führt, daß sich immer 10 Klangeigenschaften, deren Werte von Pbind nicht geän-
dert werden, behalten ihre ursprünglichen Werte. Man gibt Hilfen, Tutorials, Code etc.
Events überlagern. Ausserdem wurde hier die Hüllkurve
im Pbind-Pattern also nur die Eigenschaften an, die man swiki.hfbk-hamburg.de:8888
\env etwas weicher gemacht, um den Eindruck eines /MusicTechnology/6
fließenden Klangbandes zu unterstützen. im Vergleich zum „Urklang“ verändern will.