< 

Marek Slipek

 Synth-Generator 
 Envelope-Generator 
 Parameteroptionalität 
 Generatorverkettung 
 Funktions-Schachtelung 
 Range-Wertanpassung 
 Routine-Komposition 
 TempoClock-Komposition 
 Quantisierung 
 Pbind-Komposition 
 Sound-Array 
 OSC-Messages 

Supercollider

Super­collider ist eine Entwicklungs­umgebung für Soun­dprogram­mierung. Super­collider besteht aus einem Server und einem Inter­preter. Zwischen Server und Inter­preter wird aus­schließ­lich mit OSC-Messages kommuniziert. Aus diesem Grund lässt sich Superc­ollider aus anderen Ent­wicklungs­umge­bungen wie zum Beispiel Processing ver­gleichs­weise leicht an­steuern. Für Processing gibt es bereits eine Super­collider-­Library, die genau diesen Zweck verfolgt. Es ist möglich mit Processing selbst oder mit anderen Libraries wie Minim in einem ein­geschränk­ten Umfang, Sound und Musik zu pro­gram­mieren – Sound­programmierung ist jedoch eine rechen­inten­sive und vor allem zeit­kriti­sche An­gelegen­heit. Entwicklungs­umgebungen wie Processing, die auf Java­script basieren, liefern weder die nötige stabile Perfor­mance noch die Prä­zision in der Kontrolle von Klang­parametern. Super­collider hin­gegen basiert auf C++.

Mit Command-D blenden wir ein Such­fenster ein, um nach Stich­worten suchen zu können. Positio­nieren wir den Cursor auf ein blau markiertes Wort im Code-Editor, er­halten wir mit der Tasta­tur­kombi­nation Command-Shift-D einen passenden Hilfstext im Help Browser angezeigt.

 


Synth-Generator

SinOsc

OSC-Messages

Bevor wir Sound mit Supercollider erzeugen können, starten wir den Server mit Command-B, der für die Klangerzeugung verantwortlich ist. Den Code tippen wir in den Interpreter. Dieser wandelt im Hintergrund unsere Anweisungen in OSC-Messages um, die vom Server verstanden werden. Im Gegensatz zu Processing können wir bereits Teile des Programmcodes unabhängig vom Rest starten und testen.

Live-Coding

Supercollider ist an die Struktur von Smalltalk angelegt. Es reicht bereits eine Zeile, die wir mit Command-Return aufrufen, damit wir Sound hören können. Die Klangausgabe stoppen wir wieder mit einem Command-Punkt. Auf Grund der strukturellen Besonderheit der Sprache ist Supercollider ebenfalls für Livecoding geeignet: Wir können Programmteile im laufenden Betrieb hinzufügen oder wieder entfernen und so nach und nach ein Kompositionsstück programmierend aufbauen.

Syntaktik

Wollen wir einen Codeblock mehrerer Zeilen auf einmal ausführen, setzen wir in der Regel diesen zwischen zwei runde Klammern. Funktionsaufrufe erkennen wir an geschweiften Klammern. Methoden werden mit einem Punkt hinter dem Funktionsnamen notiert. An Methoden übergeben wir Parameter als Zahlen mit Komma getrennt. Zur besseren Verständlichkeit können wir der jeweiligen Zahl ein Schlüsselwort voranstellen und mit einem Doppelpunkt vom Wert trennen. Es werden nur richtig geschriebene Schlüsselwörter zugelassen und mit einer grünen Färbung des Wortes bestätigt. Rufen wir eine Funktion samt Methode ohne Parameter auf, greift Supercollider auf vordefinierte Standardwerte zurück. Welche Parameter wir mit individuellen Werten überschreiben und wie viele davon, bleibt uns überlassen – Supercollider kommt auch mit verkürzten Aufrufen zurecht.

Klangerzeugung in drei Stufen

Eine einfache Funktion zum Abspielen von Sound klingt zunächst verlockend, weil sie den notwendigen syntaktischen Aufbau im Hintergrund für uns erledigt. Sie schränkt uns gleichzeitig jedoch in möglichen strukturellen Manipulationen des Klangs ein. Die erweiterte Funktion als Zwischenstufe erlaubt uns genauere Steuermöglichkeiten. Die Synth-Definition mit anschließendem Synth-Aufruf ist der Goldstandard, die größte Flexibilität erlaubend. Sogar das Ansteuern der Klangerzeugung aus anderen Entwicklungsumgebungen ist damit möglich.

UGen

Im Hintergrund werkeln für uns verschiedene Generatoren – in Supercollidersprache UGen genannt. Es gibt welche zur synthetischen Klangerzeugung, welche zum Abspielen von Samples, Effekt-Generatoren und Hüllkurven-Generatoren. Einer davon ist der Sinus-Oszilator und erzeugt Sinustöne. Der Funktionsname dafür lautet SinOsc. Dem Funktionsnamen folgt meist ein Methodenaufruf mit einem Punkt angehängt.

Audiorate und Kontrolrate

Klang-Generatoren folgt entweder die Methode ar oder kr – Abkürzungen für Audiorate und Kontrolrate. Die Audiorate ist hochauflösend, erzeugt 44.100 Werte pro Sekunde in der Standardeinstellung, ist dementsprechend performanceintensiv. ar wird für die Ausgabe von Audio verwendet. Die Kontrolrate ist dagegen niedrigauflösend und erzeugt nur einen Bruchteil von Werten pro Sekunde – ein 64tel der 44.100 Werte. kr wird für bevorzugt für Steuerungsaufgaben verwendet wie beispielsweise eine Parameterfahrt für die Frequenz eines Oszillators.

Einfache Play-Funktion

{SinOsc.ar(freq: 440, phase: 0, mul: 0.1, add: 0)}.play

Erweiterte Play-Funktion

(
b = {
  arg freq = 440, phase = 0;
  var sig;
  sig = SinOsc.ar(freq, phase);
  sig = sig * 0.1; // 1/10 der Gesamtlautstärke
}.play;
)

b.set(\freq, 880); // Zugriff auf Synth-Parameter

Synth-Definition und Synth-Aufruf

(
SynthDef.new(\sinus, {
  arg freq = 440, phase = 0;
  var sig;
  sig = SinOsc.ar(freq, phase);
  sig = sig * 0.1; // Lautstärke auf 10 Prozent
  Out.ar(0, sig!2); // Multiple Expansion: Stereosound
}).add; // Synth zum Server hinzufügen
)

c = Synth.new(\sinus); // Lokale Variable: nur Zeichen
c.set(\freq, 880); // Zugriff auf Synth-Parameter
c.free;

~sinSynth = Synth.new(\sinus); // Globale Variable: auch Wörter
~sinSynth.set(\freq, 880); // Zugriff auf Synth-Parameter
~sinSynth.free;

Synth-Aufruf mit Arguments

(
SynthDef.new(\sinus, {
  arg freq = 440, phase = 0;
  var sig;
  sig = SinOsc.ar(freq, phase);
  sig = sig * 0.1;
  Out.ar(0, sig!2);
}).add;
Synth.new(\sinus, [\freq, 220, \phase, 0]);

Mono und Stereo

// Mono
{SinOsc.ar(freq: 440, phase: 0, mul: 0.1)}.play

// Stereo über Array
{SinOsc.ar(freq: [440, 440], phase: 0, mul: 0.1)}.play

// Stereo über Faktorierung
{SinOsc.ar(freq: 440!2, phase: 0, mul: 0.1)}.play

Klassische Klang-Generatoren

{SinOsc.ar(freq: 440, phase: 0, mul: 0.1, add: 0)}.play
{Saw.ar(freq: 440, phase: 0, mul: 0.1, add: 0)}.play
{Pulse.ar(freq: 440, phase: 0, mul: 0.1, add: 0)}.play
{Impulse.ar(freq: 440, phase: 0, mul: 0.1, add: 0)}.play

Tieffrequente Steuer-Generatoren

{LFPar.ar(freq: 440, phase: 0, mul: 0.1, add: 0)}.play
{LFSaw.ar(freq: 440, phase: 0, mul: 0.1, add: 0)}.play
{LFPulse.ar(freq: 440, phase: 0, mul: 0.1, add: 0)}.play
{LFTri.ar(freq: 440, phase: 0, mul: 0.1, add: 0)}.play

 

 zurück zur Übersicht 


Envelope-Generator

Individuelle Hüllkurven

Eine individuelle Hüllkurve besteht aus drei Teilen. level gibt Amplitudenhöhen zwischen 0 und 1 vor, times die Zeiten in Sekunden dazwischen und curve die Biegung der Verbindungslinie zwischen zwei Amplitudenhöhen. Die Anzahl der Level ist frei wählbar, sinnvoll ab drei Werten. Die Anzahl der Zeiten dazwischen ist immer eins weniger als die Anzahl der Level, ebenso die Anzahl der curve-Angaben. level, times und curve werden als Arrays angegeben. Arrays werden mit eckigen Klammern zusammengefasst. Die optionale Angabe eines doneAction-Status bestimmt, was nach Abspielen der Hüllkurve passieren soll. Bei 2 wird der Hüllkurvengenerator vom Server gelöscht, sobald die Hüllkurve abgespielt ist. Diese Angabe ist ein sinnvoller ressourcensparender Umgang mit der Performance-Power des Rechners.

Einfache Playfunktion mit Standard-Hüllkurve

{SinOsc.ar(freq: 440, phase: 0, mul: EnvGen.kr(Env.new), add: 0)}.play;

Standard-Hüllkurven

{SinOsc.ar(freq: 440!2, phase: 0, mul: EnvGen.kr(Env.new), add: 0)}.play;
{SinOsc.ar(freq: 440!2, phase: 0, mul: EnvGen.kr(Env.perc), add: 0)}.play;
{SinOsc.ar(freq: 440!2, phase: 0, mul: EnvGen.kr(Env.sine), add: 0)}.play;
{SinOsc.ar(freq: 440!2, phase: 0, mul: EnvGen.kr(Env.triangle), add: 0)}.play;
{SinOsc.ar(freq: 440!2, phase: 0, mul: EnvGen.kr(Env.linen), add: 0)}.play;

Erweiterte Playfunktion mit Standard-Hüllkurve

(
p = {
  arg freq = 440, phase = 0;
  var sig, env;
  env = EnvGen.kr(Env.perc, doneAction: 2); // nach Verstummen löschen
  sig = SinOsc.ar(freq, phase);
  sig = sig * env;
}.play;
)

Synth-Definition und Synth-Aufruf mit Standard-Hüllkurve

(
SynthDef.new(\env, {
  arg freq = 440, phase = 0;
  var sig, env, amp = 0.1;
  env = EnvGen.kr(Env.perc, doneAction: 2); // Verschachtelung von Funktionen
  sig = SinOsc.ar(freq, phase);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

d = Synth.new(\env);
d.set(\freq, 880);
d.free;

Erweiterte Playfunktion mit individueller Hüllkurve

(
p = {
  arg freq = 440, phase = 0;
  var sig, env, amp = 0.1;
  env = EnvGen.kr(Env(levels: [0,1,0], times: [1,1], curve: [0,0]), doneAction: 2);
  sig = SinOsc.ar(freq!2, phase);
  sig = sig * amp * env;
}.play;
)

Synth-Definition und Synth-Aufruf mit individueller Hüllkurve

(
SynthDef.new(\env, {
  arg freq = 440, phase = 0;
  var sig, env, amp = 0.1;
  env = EnvGen.kr(Env(levels: [0,1,0], times: [1,1], curve: [0,0]), doneAction: 2);
  sig = SinOsc.ar(freq, phase);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\env);

 

 zurück zur Übersicht 


Parameteroptionalität

Optionales Auslassen bei Synth-Definition

(
SynthDef.new(\sinusA, {
  arg freq = 440, phase = 0, mul = 0.1, add = 0;
  var sig;
  sig = SinOsc.ar(freq, phase, mul, add);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\sinusA);

(
SynthDef.new(\sinusB, {
  arg freq = 440;
  var sig;
  sig = SinOsc.ar(freq);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\sinusB);

(
SynthDef.new(\sinusC, {
  arg freq = 440;
  var sig;
  sig = SinOsc.ar(freq);
  sig = sig * 0.1;
  Out.ar(0, [sig, sig]);
}).add;
)
Synth.new(\sinusC);

(
SynthDef.new(\sinusD, {
  arg freq = 440;
  var sig, amp = 0.1;
  sig = SinOsc.ar(freq);
  sig = sig * amp;
  Out.ar(0, sig!2);
}).add;
)

Optionales Auslassen bei Synth-Definition mit Hüllkurve

(
SynthDef.new(\envA, {
  arg freq = 440, phase = 0, mul = 0.1, add = 0;
  var sig, env;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = SinOsc.ar(freq, phase, mul, add);
  sig = sig * env;
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\envA);

(
SynthDef.new(\envB, {
  arg freq = 440;
  var sig, evn, amp = 0.1;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = SinOsc.ar(freq);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\envB);

Schlüsselwörter in Methodenaufrufen von Funktionen

// mit Schlüsselwörtern
{SinOsc.ar(freq: 440, phase: 0, mul: 0.1)}.play

// ohne Schlüsselwörter
{SinOsc.ar(440, 0, 0.1)}.play

 

 zurück zur Übersicht 


Generatorverkettung

Verkettung eines Hall-Synths mit Ton-Synth

(
SynthDef.new(\ton, {
  arg out;
  var sig, env, amp = 0.1, freq = 440;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = SinOsc.ar(freq);
  sig = sig * amp * env;
  Out.ar(out, sig!2); // Ausgangsbus wird extern festgelegt
}).add;
)

(
SynthDef.new(\hall, {
  arg in;
  var sig, out = 0;
  sig = In.ar(in, 1); // Eingangsbus wird extern festgelegt
  sig = FreeVerb.ar(sig, mix: 0.5, room: 0.8, damp: 0.1)!2;
  Out.ar(out, sig); // [0,1] = Audioausgang Computer, [1,2] = Audioeingang Computer
}).add;
)

// freien Bus auf Server für Hall automatisch generieren
~freierBus = Bus(s, 1);

// Zuerst Eingangsbus für Hall-Synth festlegen
a = Synth(\hall, [\in, ~freierBus]);

// Danach Ausgangsbus für Ton-Synth festlegen
b = Synth(\ton, [\out, ~freierBus]);

 

 zurück zur Übersicht 


Funktions-Schachtelung

Ansammlung von Funktionsvarianten

Supercollider baut auf sehr vielen Funktionen auf. Für jede Aufgabe gibt es eine andere Funktion. Oft gibt es für die gleiche Funktion unterschiedliche Varianten mit leicht veränderten Funktionsnamenbezeichnungen. Bevor wir in Supercollider einen eigenen Algorithmus schreiben, sollten wir prüfen, ob es nicht dafür bereits eine Funktion gibt – meistens gibt es eine. Unsere Aufgabe besteht darin, die vielen Funktionen mit ihrem Parameterumfang zu internalisieren.

Musikalische und klangliche Gestaltung in Supercollider bedeutet, diese vorhandenen Strukturen zu nutzen und sie vor allem durch Schachtelung von Funktionen zu erweitern. An jeder Stelle eines Parameters einer Funktion lässt sich erneut eine neue Funktion einsetzen – und das in beliebiger Tiefe. Darin liegt die Stärke von Supercollider.

Die Verschachtelung von Funktionen wirkt im einfachen Playmodus schnell unübersichtlich. Hier lohnt zumindest der Einsatz einer erweiterten Playfunktion, wenn nicht gar der Synthdefinition. Die zu verschachtelnden Teile können wir in einer Synthdefinition verständlich aufbröseln und benennen. Im Anschluß daran können wir Zeile für Zeile die Einzelteile verketten. Dieses Vorgehen garantiert eine höhere Verständlichkeit und eine bessere Übersicht – Zeile für Zeile.

Um Verläufe in Parametern zu erzeugen, stehen uns die Generatoren Line und XLine zur Verfügung. Line erzeugt einen absolut linearen Verlauf, XLine dagegen einen exponentiellen. Der exponentielle Verlauf entspricht eher unserem Gehörempfinden und hört sich richtiger an.

Modulation mit einfacher Playfunktion

Frequenzmodulation

{SinOsc.ar(freq: SinOsc.kr(5)*500)!2}.play;

Phasenmodulation

{SinOsc.ar(freq: 440!2, phase: SinOsc.kr(5)*5)}.play;

Amplitudenmodulation

{SinOsc.ar(freq: 440!2, phase: 0, mul: SinOsc.kr(5))}.play;

Modulation mit erweiterter Playfunktion

Frequenzmodulation

(
a = {
  var sig, amp;
  mod = SinOsc.kr(5)*500;
  sig = SinOsc.ar(freq: mod!2);
}.play;
)

Phasenmodulation

(
b = {
var sig, mod;
mod = SinOsc.kr(5)*5;
sig = SinOsc.ar(freq: 440!2, phase: mod);
}.play;
)

Amplitudenmodulation

c = {
var sig, mod;
mod = SinOsc.kr(5);
sig = SinOsc.ar(freq: 440!2, phase: 0, mul: mod);
}.play;
)

Modulation mit Synthdefinition

Frequenzmodulation

(
SynthDef.new(\frequenzmodulation, {
  var sig, mod;
  mod = SinOsc.kr(5)*500;
  sig = SinOsc.ar(freq: mod!2);
  Out.ar(0, sig);
}).add;
)
Synth.new(\frequenzmodulation);

Phasenmodulation

(
SynthDef.new(\phasenmodulation, {
  var sig, mod;
  mod = SinOsc.kr(5)*5;
  sig = SinOsc.ar(freq: 440!2, phase: mod);
  Out.ar(0, sig);
}).add;
)
Synth.new(\phasenmodulation);

Amplitudenmodulation

(
SynthDef.new(\amplitudenmodulation, {
  var sig, mod;
  mod = SinOsc.kr(5);
  sig = SinOsc.ar(freq: 440!2, phase: 0, mul: mod);
  Out.ar(0, sig);
}).add;
)
Synth.new(\amplitudenmodulation);

Verlauf mit einfacher Playfunktion

Linearer Verlauf

{SinOsc.ar(freq: Line.kr(start: 2000, end: 100, dur: 2)!2, phase: 0, mul: 0.1)}.play;

Exponentieller Verlauf

{SinOsc.ar(freq: XLine.kr(start: 2000, end: 100, dur: 2)!2, phase: 0, mul: 0.1)}.play;

Verlauf mit erweiterter Playfunktion

Exponentieller Verlauf

(
b = {
  var sig, mod;
  mod = XLine.kr(start: 2000, end: 100, dur: 2)!2;
  sig = SinOsc.ar(freq: mod, phase: 0, mul: 0.5);
}.play;
)

Verlauf mit Synthdefinition

Exponentieller Verlauf

(
SynthDef.new(\exponentiellerVerlauf, {
  var sig, mod;
  mod = XLine.kr(start: 2000, end: 100, dur: 2);
  sig = SinOsc.ar(freq: mod, phase: 0, mul: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\exponentiellerVerlauf);

Lineare Verlaufsmodulation

Lineare Frequenzverlaufsmodulation mit XLine

(
SynthDef.new(\freqModGradient, {
  var sig, mod, modmod;
  modmod = XLine.kr(start: 5, end: 1, dur: 5);
  mod = SinOsc.kr(modmod)*500;
  sig = SinOsc.ar(freq: mod, phase: 0, mul: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\freqModGradient);

Lineare Phasenverlaufsmodulation mit XLine

(
SynthDef.new(\phaseModGradient, {
  var sig, mod, modmod;
  modmod = XLine.kr(start: 20, end: 1, dur: 5);
  mod = SinOsc.kr(modmod)*5;
  sig = SinOsc.ar(freq: 440, phase: mod, mul: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\phaseModGradient);

Lineare Amplitudenverlaufsmodulation mit XLine

(
SynthDef.new(\ampModGradient, {
  var sig, mod, modmod;
  modmod = XLine.kr(start: 20, end: 1, dur: 5);
  mod = SinOsc.kr(modmod);
  sig = SinOsc.ar(freq: 440, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\ampModGradient);

Kontinuierliche Verlaufsmodulation

Kontinuierliche Frequenzverlaufsmodulation mit SinOsc

(
SynthDef.new(\freqContinousMod, {
  var sig, mod, modmod;
  modmod = SinOsc.kr(0.1)*5;
  mod = SinOsc.kr(modmod)*500;
  sig = SinOsc.ar(freq: mod, phase: 0, mul: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\freqContinousMod);

Kontinuierliche Phasenverlaufsmodulation mit SinOsc

(
SynthDef.new(\phaseContinousMod, {
  var sig, mod, modmod;
  modmod = SinOsc.kr(0.1)*20;
  mod = SinOsc.kr(modmod)*5;
  sig = SinOsc.ar(freq: 440, phase: mod, mul: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\phaseContinousMod);

Kontinuierliche Amplitudenverlaufsmodulation mit SinOsc

(
SynthDef.new(\ampModGradient, {
  var sig, mod, modmod;
  modmod = SinOsc.kr(0.1)*10;
  mod = SinOsc.kr(modmod);
  sig = SinOsc.ar(freq: 440, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\ampModGradient);

 

 zurück zur Übersicht 


Range-Wertanpassung

Poll-Methode

{LFNoise0.kr(freq: 1).poll}.play;

Range-Methode

{SinOsc.ar(freq: LFNoise0.kr(freq: 10).range(500, 1500)!2, mul: 0.1)}.play;

Modulation mit LFPulse

Frequenzmodulation mit LFPulse

(
SynthDef.new(\freqPulseMod, {
  var sig, mod;
  mod = LFPulse.kr(freq: 1).range(100, 200);
  sig = SinOsc.ar(freq: mod, phase: 0, mul: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\freqPulseMod);

Amplitudenmodulation mit LFPulse

(
SynthDef.new(\ampPulseMod, {
  var sig, mod;
  mod = LFPulse.kr(freq: 1).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\ampPulseMod);

Phasenmodulation mit LFPulse

(
SynthDef.new(\phasePulseMod, {
  var sig, mod;
  mod = LFPulse.kr(freq: 50).range(5, 25);
  sig = SinOsc.ar(freq: 200, phase: mod, mod: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\phasePulseMod);

Modulation mit LFTri

Frequenzmodulation mit LFTri

(
SynthDef.new(\freqTriMod, {
  var sig, mod;
  mod = LFTri.kr(freq: 1).range(100, 200);
  sig = SinOsc.ar(freq: mod, phase: 0, mul: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\freqTriMod);

Amplitudenmodulation mit LFTri

(
SynthDef.new(\ampTriMod, {
  var sig, mod;
  mod = LFTri.kr(freq: 1).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\ampPulseMod);

Phasenmodulation mit LFTri

(
SynthDef.new(\phaseTriMod, {
  var sig, mod;
  mod = LFTri.kr(freq: 50).range(5, 25);
  sig = SinOsc.ar(freq: 200, phase: mod, mod: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\phasePulseTri);

Modulation mit LFSaw

Frequenzmodulation mit LFSaw

(
SynthDef.new(\freqSawMod, {
  var sig, mod;
  mod = LFSaw.kr(freq: 1).range(100, 200);
  sig = SinOsc.ar(freq: mod, phase: 0, mul: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\freqSawMod);

Amplitudenmodulation mit LFSaw

(
SynthDef.new(\ampSawMod, {
  var sig, mod;
  mod = LFSaw.kr(freq: 1).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\ampSawMod);

Phasenmodulation mit LFSaw

(
SynthDef.new(\phaseSawMod, {
  var sig, mod;
  mod = LFSaw.kr(freq: 50).range(5, 25);
  sig = SinOsc.ar(freq: 200, phase: mod, mod: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\phaseSawMod);

Modulation mit LFPar

Frequenzmodulation mit LFPar

(
SynthDef.new(\freqParMod, {
  var sig, mod;
  mod = LFPar.kr(freq: 1).range(100, 200);
  sig = SinOsc.ar(freq: mod, phase: 0, mul: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\freqParMod);

Amplitudenmodulation mit LFPar

(
SynthDef.new(\ampParMod, {
  var sig, mod;
  mod = LFPar.kr(freq: 1).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\ampParMod);

Phasenmodulation mit LFPar

(
SynthDef.new(\phaseParMod, {
  var sig, mod;
  mod = LFPar.kr(freq: 50).range(5, 25);
  sig = SinOsc.ar(freq: 200, phase: mod, mod: 0.5);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\phaseParMod);

Rhythmische Modulation mit SinOsc

(
SynthDef.new(\ampPulseMod, {
  var sig, mod, modmod;
  modmod = SinOsc.kr(freq: 0.1).range(1,10);
  mod = LFPulse.kr(freq: modmod).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\ampPulseMod);

Rhythmische Modulation mit BrownNoise

(
SynthDef.new(\ampMod, {
  var sig, mod, modmod;
  modmod = BrownNoise.kr().range(1,10);
  mod = LFPulse.kr(freq: modmod).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)
Synth.new(\ampMod);

 

 zurück zur Übersicht 


Routine-Komposition

Rhythmische Komposition mit LFPulse und rrand

(
SynthDef.new(\ampMod, {
  arg freq = 1;
  var sig, mod;
  mod = LFPulse.kr(freq).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)

(
Routine({
  x = Synth.new(\ampMod);
  64.do({
    x.set(\freq, rrand(1,10));
    0.5.wait;
  })
}).play;
)

Zufällige Zeitintervalle

(
SynthDef.new(\ampMod, {
  arg freq = 1;
  var sig, mod;
  mod = LFPulse.kr(freq).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)

(
Routine({
  x = Synth.new(\ampMod);
  64.do({
    x.set(\freq, rrand(1,10));
    rrand(0.5,1.5).wait;
  })
}).play;
)

Array mit Zufallsauswahl

(
SynthDef.new(\ampMod, {
  arg freq = 1;
  var sig, mod;
  mod = LFPulse.kr(freq).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)

(
Routine({
  x = Synth.new(\ampMod);
  64.do({
    x.set(\freq, [1,5,10].choose);
    0.5.wait;
  })
}).play;
)

 

 zurück zur Übersicht 


TempoClock-Komposition

TempoClock

Für Zeitplanung in SuperCollider gibt es drei Uhren: TempoClock, SystemClock und AppClock. TempoClock ist für Komposition am Geeignesten. Neben der Standard-TempoClock können wir so viele TempoClocks unterschiedlicher Schnelligkeit anlegen wie wir wollen. Dagegen gibt es nur eine SystemClock und eine AppClock. Wobei die SystemClock genauer ist als die AppClock, weil sie eine höhere Priorität besitzt.

TempoClock stellen wir in Beats pro Sekunde ein. Gewohnt sind wir Angaben in Beats pro Minute. Um von Beats pro Minute in Beats pro Sekunde umzuwandeln, teilen wir die BMP-Zahl durch 60. Beispielsweise entsprechen 120 Beats pro Minute 120/60 also 2 Beats pro Sekunde. Wir können also auch gleich unser Tempo als Bruch in der Form – BPM / 60 – angeben und müssen nicht mehr umdenken. Voreingestellt für tempo in TempoClock ist 1 Beat pro Sekunde (BPS) oder 60 Beats pro Minute (BPM). Mit thisThread.clock erhalten wir Auskunft über den Zeitverlauf einer spezifischen Funktion.

Standard-TempoClock festlegen

// 1 beats/sek oder 60 BPM
TempoClock.default.tempo = 2;

// 2 beats/sek oder 120 BPM
TempoClock.default.tempo = 2;

Zeitplanung mit Schedule

// SystemClock
SystemClock.sched(5, { "hello".postln });

// TempoClock
TempoClock.default.sched(5, { "hello".postln });

Rhythmische Komposition mit TempoClock-Schedule

(
SynthDef.new(\ampMod, {
  arg freq = 1;
  var sig, mod;
  mod = LFPulse.kr(freq).range(0, 1);
  sig = SinOsc.ar(freq: 800, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)

r = Routine({
  var delta;
  x = Synth.new(\ampMod);
  loop {
    x.set(\freq, [1, 5, 10].choose);
    delta = rrand(1,3)*0.2;
    delta.yield;
  }
});

// TempoClock.default.sched(0,r);

a = TempoClock(120/60);
a.sched(0,r);

TempoClock-Schedule mit BPM und Env

(
SynthDef.new(\env, {
  arg freq = 440;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = SinOsc.ar(freq);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

r = Routine({
  var delta;
  loop {
    Synth.new(\env, [\freq, 440]);
    delta = rrand(1,3)*0.2;
    delta.yield;
  }
});

a = TempoClock.new(tempo: 120/60);
a.sched(0,r);

Quantisierung mit play

(
SynthDef.new(\ampMod, {
  arg freq = 1;
  var sig, mod;
  mod = LFPulse.kr(freq).range(0, 1);
  sig = SinOsc.ar(freq: 400, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)

(
f = {
  Task({
    var delta;
    x = Synth.new(\ampMod);
    loop {
      x.set(\freq, [1, 5, 10].choose);
      delta = rrand(1,3)*0.2;
      delta.yield;
    }
  });
};
)

t = f.value.play(quant: 4);
u = f.value.play(quant:4);

Einzelnen Ton rhythmisch wiederholen

(
SynthDef.new(\env, {
  arg freq = 440;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = SinOsc.ar(freq);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

r = Routine({
  var delta;
  loop {
    Synth.new(\env, [\freq, 440]);
    delta = 2;
    delta.yield;
  }
});

a = TempoClock.new(tempo: 120/60);
a.sched(0,r);

 

 zurück zur Übersicht 


Quantisierung

Zwei Routines mit TempoClock synchronisiert

(
SynthDef.new(\env, {
  arg freq = 440;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = SinOsc.ar(freq);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

r = Routine({
  var delta;
  loop {
    Synth.new(\env, [\freq, 440]);
    delta = 2;
    delta.yield;
  }
});

m = Routine({
  var delta;
  loop {
    Synth.new(\env, [\freq, 220]);
    delta = 4;
    delta.yield;
  }
});

a = TempoClock.new(tempo: 120/60);
(
r.play(a, 0);
m.play(a, 0);
)

Zwei Tasks mit TempoClock synchronisiert

(
SynthDef.new(\env, {
  arg freq = 440;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = SinOsc.ar(freq);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

r = Task({
  var delta;
  loop {
    Synth.new(\env, [\freq, 440]);
    delta = 2;
    delta.yield;
  }
});

m = Task({
  var delta;
  loop {
    Synth.new(\env, [\freq, 220]);
    delta = 4;
    delta.yield;
  }
});

a = TempoClock.new(tempo: 120/60);
(
r.start(a, 0);
m.start(a, 0);
)

Vier Spuren mit TempoClock und play quantisieren

(
SynthDef.new(\tone, {
  arg freq = 1, pitch = 400;
  var sig, mod;
  mod = LFPulse.kr(freq).range(0, 1);
  sig = SinOsc.ar(freq: pitch, phase: 0, mul: mod);
  Out.ar(0, sig!2);
}).add;
)
a = TempoClock.new(tempo: 120/60);
a.play({ Synth.new(\tone, [\freq, 1, \pitch, 200]); });
a.play({ Synth.new(\tone, [\freq, 2, \pitch, 400]); });
a.play({ Synth.new(\tone, [\freq, 4, \pitch, 300]); });
a.play({ Synth.new(\tone, [\freq, 8, \pitch, 500]); });

Quantisierung mit Pbind und Synth-Def

(
SynthDef.new(\env, {
  arg freq = 440;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = Pulse.ar(freq);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

t = TempoClock(120/60);
Pbind(\instrument, "env", \freq, 400, \dur, 2).play(t, quant: 1);
Pbind(\instrument, "env", \freq, 200, \dur, 1).play(t, quant: 1);
Pbind(\instrument, "env", \freq, 300, \dur, 0.5).play(t, quant: 1);
Pbind(\instrument, "env", \freq, 500, \dur, 0.25).play(t, quant: 1);

 

 zurück zur Übersicht 


Pbind-Komposition

Quantisierung mit quant-Argument in play

t = TempoClock(120/60);
Pbind(\freq, 400, \dur, 2).play(t, quant: 1);
Pbind(\freq, 200, \dur, 1).play(t, quant: 1);
Pbind(\freq, 300, \dur, 0.5).play(t, quant: 1);
Pbind(\freq, 500, \dur, 0.25).play(t, quant: 1);

Quantisierung mit runden Klammern

t = TempoClock(120/60);
(
Pbind(\freq, 400, \dur, 2).play(t);
Pbind(\freq, 200, \dur, 1).play(t);
Pbind(\freq, 300, \dur, 0.5).play(t);
Pbind(\freq, 500, \dur, 0.25).play(t);
)

Pseq mit Sequenz aus Durations

~mySeq = [0.1,0.5,0.5,0.1,0.3];
Pbind(\freq, 440, \dur, Pseq(~mySeq, inf)).play;

Pseq mit Sequenz aus Frequenzen

~myFreq = [200, 400, 300, 500];
Pbind(\freq, Pseq(~myFreq, inf), \dur, 0.125).play;

Akkorde spielen mit Pbind

~myAkkord = [200, 400, 300];
Pbind(\freq, ~myAkkord, \dur, 0.5).play;

~myAkkorde = [[200, 400, 300], [500, 100, 250]];
Pbind(\freq, Pseq(~myAkkorde, inf), \dur, 0.5).play;

Zwischen Inhalt und Aktion trennen

~myFreq = [200, 400, 300, 500];
~inhalt = Pbind(\freq, Pseq(~myFreq,inf), \dur, 0.125);
~aktion = ~inhalt.play;
~aktion.stop;

Pbind-Komposition mit Pwhite

(
SynthDef.new(\env, {
  arg freq = 440;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = SinOsc.ar(freq)*env;
  sig = sig * amp;
  Out.ar(0, sig!2);
}).add;
)

t = TempoClock(120/60);
Pbind(\instrument, "env", \freq, 400, \dur, Pwhite(0.1,1)).play(t, quant: 1);

Pbind-Komposition mit Prand

(
SynthDef.new(\ampMod, {
  arg freq = 440;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env(
    levels: [0,1,1,0],
    times: [0,0.05,0],
    curve: [0,0,0]),
    doneAction: 2);
  sig = SinOsc.ar(freq!2);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

t = TempoClock(120/60);
p = Pbind(
  \instrument, "ampMod",
  \freq, 400,
  \dur, Prand([0.15,0.45,0.9], inf));
p.play(t, quant: 1);

Pbind-Komposition mit Prand, if und sustain

(
SynthDef.new(\ampMod, {
  arg freq = 440, sustain = 0.05;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env(
    levels: [0,1,1,0],
    times: [0,sustain,0],
    curve: [0,0,0]),
    doneAction: 2);
  sig = SinOsc.ar(freq!2);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

t = TempoClock(120/60);
Pbind(
  \instrument, "ampMod",
  \freq, 400,
  \dur, ~zufall = Prand([0.3,0.6,0.9], inf),
  \sustain, if (~zufall == 0.3, { 0.05 }, { 0.1});
).play(t, quant: 1);

Pbind-Sequenz und sustain

(
SynthDef.new(\ampMod, {
  arg freq = 440, sustain = 0.05;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env(
    levels: [0,1,1,0],
    times: [0,sustain,0],
    curve: [0,0,0]),
    doneAction: 2);
  sig = SinOsc.ar(freq!2);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

Pbind(
  \instrument, \ampMod,
  \freq, Pseq([400, 300, 200, 500], inf),
  \dur, Pseq([0.5,1,0.5, 2], inf),
  \sustain, Pseq([0.1, 0.2, 0.1, 0.4], inf)
).play;

Einfaches Pbind mit Synth-Def

(
SynthDef.new(\env, {
  arg freq = 440;
  var sig, env, amp = 0.5;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = Pulse.ar(freq);
  sig = sig * amp * env;
  Out.ar(0, sig!2);
}).add;
)

t = TempoClock(120/60);
Pbind(\instrument, "env", \freq, 400, \dur, 2).play(t, quant: 1);

Einfaches Pbind mit Synth-Def und Hall

(
SynthDef.new(\ton, {
  arg out;
  var sig, env, amp = 0.5, freq = 440;
  env = EnvGen.kr(Env.perc, doneAction: 2);
  sig = SinOsc.ar(freq);
  sig = sig * amp * env;
  Out.ar(out, sig!2);
}).add;
)

(
SynthDef.new(\hall, {
  arg in;
  var sig, out = 0;
  sig = In.ar(in, 1);
  sig = FreeVerb.ar(sig, mix: 0.5, room: 2, damp: 0.1)!2;
  Out.ar(out, sig);
}).add;
)

~freierBus = Bus(s, 1);
a = Synth(\hall, [\in, ~freierBus]);
b = Synth(\ton, [\out, ~freierBus]);

t = TempoClock(120/60);
Pbind(\instrument, "ton", \freq, 400, \dur, 6).play(t, quant: 1);

 

 zurück zur Übersicht 


Sound-Array

Array mit Methode fill

Array.fill(10, {rrand(1,10)});
Array.fill(10, {arg zaehler; zaehler * 3});
Array.fill(10, {arg zaehler; zaehler + 1 * 440});

Array mit Methode series und geom

Array.series(size: 6, start: 10, step: 3);
Array.geom(size: 10, start: 1, grow: 2);

Zugriff auf Elemente des Arrays

a = [10, 11, 12, 13, 14];
a.at(0);
a[0];
a.choose;

Array mit Methode do und SinOsc-Generator

a = Array.fill(10, {rrand(440, 880)});
a.do({arg i, z; ("a[" ++ z ++ "] = " ++ i++ " Hz").postln});
a.do({arg i; {SinOsc.ar(i!2, 0, 0.1)}.play});

Array mit Methode do und SynthDef

(
SynthDef.new(\ton, {
  arg freq;
  var sig, amp = 0.1;
  sig = SinOsc.ar(freq);
  sig = sig * amp;
  Out.ar(0, sig!2);
}).add;
)

a = Array.fill(10, {rrand(440, 880)});
a.do({arg i; Synth.new(\ton, [\freq, i]) });

Sound-Array mit Pbind-Synchronisation

(
SynthDef.new(\ton, {
  arg freq;
  var sig, amp = 0.1;
  sig = SinOsc.ar(freq);
  sig = sig * amp;
  Out.ar(0, sig!2);
}).add;
)

t = TempoClock.new(tempo: 120/60);
a = Array.fill(10, {rrand(440, 880)});
a.do({
  arg i;
  Pbind(
    \instrument, "ton",
    \freq, i,
    \dur,
  inf).play(t, quant: 1)
});

 

 zurück zur Übersicht 


OSC-Messages

SynthDef für Supercollider / Processing

In Supercollider SynthDef mit store-Methode

SynthDef.new(\ampMod, {
  arg outbus = 0, freq = 1;
  var sig, mod;
  mod = LFPulse.kr(freq).range(0, 1);
  sig = SinOsc.ar(freq: 800, phase: 0, mul: mod);
  Out.ar(outbus, sig!2);
}).store;

In Processing OSC-Kommunikation

import supercollider.*;
import oscP5.*;
Group group;
Synth synth;
float f;
void setup () {
  size(800, 200);
  group = new Group();
  group.create();
  synth = new Synth("ampMod");
  synth.set("freq", 1);
  synth.addToTail(group);
}

void draw () {
  background(0);
  stroke(255);
  line(mouseX, 0, mouseX, height);
}

void mouseMoved () {
  f = map(mouseX, 0, width, 1, 20);
  synth.set("freq", f);
}

void exit() {
  synth.free();
  group.free();
  super.exit();
}

Morsecode für Supercollider / Processing

In Supercollider SynthDef

SynthDef.new(\ampMod, {
  arg outbus = 0, freq = 1;
  var sig, mod;
  mod = LFPulse.kr(freq).range(0, 1);
  sig = SinOsc.ar(freq: 800, phase: 0, mul: mod);
  Out.ar(outbus, sig!2);
}).store;

In Processing Modulo und OSC

import supercollider.*;
import oscP5.*;
Group group;
Synth synth;
float f; int zaehler, index;
int[] freq = { 1, 5, 10 };
void setup () {
  size(800, 200);
  group = new Group();
  group.create();
  synth = new Synth("ampMod");
  synth.set("freq", 1);
  synth.addToTail(group);
  zaehler = 0;
}

void draw () {
  background(0);
  noStroke();
  fill(255);
  line(mouseX, 0, mouseX, height);
  if (zaehler % 5 == 0) {
    index = int(random(3));
    f = freq[index];
    synth.set("freq", f);
  }
  zaehler++;
  rect(0, 0, f * 10, height);
}

void exit() {
  synth.free();
  group.free();
  super.exit();
}

Zwei Spuren für Supercollider / Processing

In Supercollider zwei SynthDefs

SynthDef.new(\ampMod, {
  arg outbus = 0, freq = 1;
  var sig, mod;
  mod = LFPulse.kr(freq).range(0, 1);
  sig = SinOsc.ar(freq: 800, phase: 0, mul: mod);
  Out.ar(outbus, sig!2);
}).store;

SynthDef.new(\subbass, {
  arg outbus = 0, freq = 50;
  var sig, amp = 0.5;
  sig = SinOsc.ar(freq: 50);
  sig = sig * amp;
  Out.ar(outbus, sig!2);
}).store;

In Processing Modulo und OSC

import supercollider.*;
import oscP5.*;
Group group;
Synth synthA, synthB;
float f;
int zaehler, index;
int[] freq = { 1, 5, 10 };

void setup () {
  size(800, 200);
  group = new Group();
  group.create();
  synthA = new Synth("ampMod");
  synthA.set("freq", 1);
  synthA.addToTail(group);
  synthB = new Synth("subbass");
  synthB.set("freq", 50);
  synthB.addToTail(group);
  zaehler = 0;
}

void draw () {
  background(0);
  noStroke();
  fill(255);
  line(mouseX, 0, mouseX, height);
  if (zaehler % 5 == 0) {
    index = int(random(3));
    f = freq[index];
    synthA.set("freq", f);
  }
  zaehler++;
  rect(0, 0, f * 10, height);
}

void exit() {
  synthA.free();
  synthB.free();
  group.free();
  super.exit();
}

 

 zurück zur Übersicht